ES进阶使用

发布时间:2024年01月19日

配置

pom依赖
        <dependency>
            <groupId>co.elastic.clients</groupId>
            <artifactId>elasticsearch-java</artifactId>
            <version>7.17.5</version>
        </dependency>
yml配置
elasticsearch:
  address: http://192.168.133.100:9200
数据库实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("tb_hotel")
public class Hotel {
    @TableId(type = IdType.INPUT)
    private Long id;
    private String name;
    private String address;
    private Integer price;
    private Integer score;
    private String brand;
    private String city;
    private String starName;
    private String business;
    private String longitude;
    private String latitude;
    private String pic;
}
ES实体类

location:将位置信息聚合【经纬度】成一个点,对应es中mapping的geo_bouding_box类型

@Data
@NoArgsConstructor
public class HotelDoc {
    private Long id;
    private String name;
    private String address;
    private Integer price;
    private Integer score;
    private String brand;
    private String city;
    private String starName;
    private String business;
    private String location;
    private String pic;
    private List<String> suggestion;

    public HotelDoc(Hotel hotel) {
        this.id = hotel.getId();
        this.name = hotel.getName();
        this.address = hotel.getAddress();
        this.price = hotel.getPrice();
        this.score = hotel.getScore();
        this.brand = hotel.getBrand();
        this.city = hotel.getCity();
        this.starName = hotel.getStarName();
        this.business = hotel.getBusiness();
        this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
        this.pic = hotel.getPic();
        this.suggestion = new ArrayList<>();
        if (this.business.contains("/")) {
            String[] split1 = this.business.split("/");
            Collections.addAll(this.suggestion, split1);
        } else if (this.business.contains("、")) {
            String[] split2 = this.business.split("、");
            Collections.addAll(this.suggestion, split2);
        } else
            this.suggestion.add(this.business);
        this.suggestion.add(this.brand);
    }
}
配置类
@Configuration
public class ElasticsearchConfig {
    @Value("${elasticsearch.address}")
    private String address;


    @Bean
    public ElasticsearchClient elasticsearchClient() {
        RestClient restClient = RestClient.builder(HttpHost.create(address)).build();
        RestClientTransport restClientTransport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        return new ElasticsearchClient(restClientTransport);
    }

}

如果使用代码生成映射【mapping】
public class HotelIndexConstants {
    public static final String MAPPING_TEMPLATE = "{\n" +
            "  \"mappings\": {\n" +
            "    \"properties\": {\n" +
            "      \"id\": {\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"name\": {\n" +
            "        \"type\": \"text\",\n" +
            "        \"analyzer\": \"ik_max_word\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"address\": {\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"index\": false\n" +
            "      },\n" +
            "      \"price\": {\n" +
            "        \"type\": \"integer\"\n" +
            "      },\n" +
            "      \"score\": {\n" +
            "        \"type\": \"integer\"\n" +
            "      },\n" +
            "      \"brand\": {\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"city\": {\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"starName\": {\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"business\": {\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"pic\": {\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"index\": false\n" +
            "      },\n" +
            "      \"location\": {\n" +
            "        \"type\": \"geo_point\"\n" +
            "      },\n" +
            "      \"all\": {\n" +
            "        \"type\": \"text\",\n" +
            "        \"analyzer\": \"ik_max_word\"\n" +
            "      }\n" +
            "    }\n" +
            "  }\n" +
            "}";
}

使用

service层
@Service
@Slf4j
public class HotelServiceImpl extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
    @Autowired
    private ElasticsearchClient elasticsearchClient;
    @Autowired
    private HotelMapper hotelMapper;
}
索引
创建索引
@SneakyThrows
@Override
public Boolean createHotelIndex() {
    CreateIndexRequest request = CreateIndexRequest.of(
            builder -> builder.index("hotel")
                    .settings(s -> s.analysis(a -> a
                            .analyzer("chinese_analyzer", an -> an
                                    .custom(v -> v.tokenizer("ik_max_word").filter("py")))
                            .analyzer("pinyin_analyzer", an -> an
                                    .custom(v -> v.tokenizer("keyword").filter("py")))))
                    .mappings(mapperClass -> mapperClass
                            .properties("id", p -> p.keyword(k -> k))
                            .properties("name", p -> p.text(t -> t.analyzer("ik_max_word")
                                    .searchAnalyzer("ik_smart")
                                    .copyTo("all")))
                            .properties("address", p -> p.keyword(k -> k.index(false)))
                            .properties("price", p -> p.integer(i -> i))
                            .properties("score", p -> p.integer(i -> i))
                            .properties("brand", p -> p.keyword(k -> k.copyTo("all")))
                            .properties("city", p -> p.keyword(k -> k))
                            .properties("startName", p -> p.keyword(k -> k))
                            .properties("business", p -> p.keyword(k -> k.copyTo("all")))
                            .properties("location", p -> p.geoPoint(g -> g))
                            .properties("pic", p -> p.keyword(k -> k.index(false)))
                            .properties("all", p -> p.text(t -> t.analyzer("ik_max_word")))
                            .properties("suggest", p -> p.completion(v -> v.analyzer("pinyin_analyzer")))
                    )
    );
    CreateIndexResponse response = elasticsearchClient.indices().create(request);
    return response.acknowledged();
}
删除索引
@SneakyThrows
@Override
public Boolean deleteHotelIndex() {
    DeleteIndexRequest request = DeleteIndexRequest.of(builder -> builder.index("hotel"));
    DeleteIndexResponse delete = elasticsearchClient.indices().delete(request);
    return delete.acknowledged();
}
查询索引是否存在
@SneakyThrows
@Override
public Boolean existsHotelIndex() {
    return elasticsearchClient.indices().exists(builder -> builder.index("hotel")).value();
}
文档
创建文档
@SneakyThrows
@Override
public Boolean createHotelDocument() {
    Hotel hotel = hotelMapper.selectById(61083L);
    HotelDoc hotelDoc = new HotelDoc(hotel);
    elasticsearchClient.index(builder ->
            builder.index("hotel").document(hotelDoc).id(hotel.getId().toString()));
    return null;
}
查询文档
@SneakyThrows
@Override
public HotelDoc getHotelDocument() {
    GetResponse<HotelDoc> hotel = elasticsearchClient.get(builder ->
            builder.index("hotel").id("61083"), HotelDoc.class);
    log.debug("Hotel是否存在:{}", hotel.found());
    return hotel.source();
}
更新文档
@SneakyThrows
@Override
public Result updateHotelDocument() {
    HotelDoc hotelDoc = new HotelDoc();
    hotelDoc.setAddress("杭州市余杭区");
    Result hotel = elasticsearchClient.update(builder ->
            builder.index("hotel").id("61083").doc(hotelDoc), HotelDoc.class).result();
    log.debug("update:{}", hotel);
    return hotel;
}
删除文档
@SneakyThrows
@Override
public Result deleteHotelDocument() {
    return elasticsearchClient.delete(builder ->
            builder.index("hotel").id("61083")).result();
}
批量添加和删除文档
@SneakyThrows
@Override
public void bulkAddDocument() {
    List<Hotel> hotelList = hotelMapper.selectList(null);
    List<BulkOperation> bulkOperationsAdd = new ArrayList<>();
    List<BulkOperation> bulkOperationsRemove = new ArrayList<>();
    hotelList.forEach(hotel -> {
        HotelDoc hotelDoc = new HotelDoc(hotel);
        BulkOperation.Builder add = new BulkOperation.Builder();
        BulkOperation create = add.create(d ->
                d.document(hotelDoc).id(hotel.getId().toString()).index("hotel")).build();
        bulkOperationsAdd.add(create);
        BulkOperation.Builder remove = new BulkOperation.Builder();
        BulkOperation delete = remove.delete(d ->
                d.id(hotel.getId().toString()).index("hotel")).build();
        bulkOperationsRemove.add(delete);
    });
    BulkResponse del = elasticsearchClient.bulk(builder ->
            builder.index("hotel").operations(bulkOperationsRemove));
    BulkResponse hotel = elasticsearchClient.bulk(builder ->
            builder.index("hotel").operations(bulkOperationsAdd));
    log.debug("是否有错误:{}", hotel.errors());
}
高级查询
@SneakyThrows
@Override
public void matchAll() {
    // 1. 查全部
    SearchRequest request = SearchRequest.of(builder ->
            builder.index("hotel").query(Query.of(q -> q.matchAll(m -> m))
            )
    );
    log.debug("所有数据:{}", search(request));
    // 1.1 分页查询
    SearchRequest request11 = SearchRequest.of(builder -> builder
            .index("hotel")
            .query(Query.of(q -> q.matchAll(m -> m)))
            .from(2)
            .size(2)
    );
    log.debug("分页数据:{}", search(request11));
    // 1.2 排序查询
    SearchRequest request12 = SearchRequest.of(builder -> builder
            .index("hotel")
            .query(Query.of(q -> q.matchAll(m -> m)))
            .sort(s -> s.field(v -> v.field("price").order(SortOrder.Desc)))
    );
    log.debug("排序数据:{}", search(request12));
    // 1.3 条件查询
    SearchRequest request13 = SearchRequest.of(builder -> builder
            .index("hotel")
            .query(Query.of(q -> q.matchAll(m -> m)))
            .sort(s -> s.field(v -> v.field("price").order(SortOrder.Desc)))
            .source(s -> s.filter(v -> v.includes("id", "name", "price").excludes("address")))
    );
    log.debug("自定义查询输出的数据:{}", search(request13));
    // 2. 单字段查询
    // 2.1 全文检索
    SearchRequest request21 = SearchRequest.of(builder ->
            builder.index("hotel").query(q -> q.match(v -> v.field("all").query("如家")))
    );
    log.debug("单字段模糊查询:{}", search(request21));
    // 2.2 精确查询【适合查询部分不分词的字段】
    SearchRequest request22 = SearchRequest.of(builder ->
            builder.index("hotel").query(q -> q.term(v -> v.field("city").value("深圳")))
    );
    log.debug("单字段精确查询:{}", search(request22));
    // 2.3 范围查询
    SearchRequest request23 = SearchRequest.of(builder -> builder
            .index("hotel")
            .query(q -> q.range(v -> v.field("price").gte(JsonData.of(100)).lte(JsonData.of(150))))
    );
    log.debug("单字段范围查询:{}", search(request23));
    // 3. 多字段查询
    SearchRequest request3 = SearchRequest.of(builder -> builder
            .index("hotel")
            .query(q -> q.multiMatch(v -> v.fields("name", "brand", "business").query("宝安")))
    );
    log.debug("多字段模糊查询:{}", search(request3));
    // 4. 组合查询
    // 4.1 and
    SearchRequest request41 = SearchRequest.of(builder -> builder
            .index("hotel")
            .query(q -> q.bool(b -> b
                    .must(m -> m.match(v -> v.field("name").query("如家")))
                    .must(m -> m.term(v -> v.field("price").value(149)))
            ))
    );
    log.debug("and查询:{}", search(request41));
    // 4.2 or
    SearchRequest request42 = SearchRequest.of(builder -> builder
            .index("hotel")
            .query(q -> q.bool(b -> b
                    .should(m -> m.match(v -> v.field("name").query("如家")))
                    .should(m -> m.term(v -> v.field("price").value(149)))
            ))
    );
    log.debug("or查询:{}", search(request42));
    // 5. 模糊查询【补全】
    SearchRequest request5 = SearchRequest.of(builder -> builder
            .index("hotel")
            .query(q -> q.fuzzy(f -> f
                    .field("name").value("如加").fuzziness("2").prefixLength(1)
            ))
    );
    log.debug("模糊查询:{}", search(request5));
    // 6. 高亮
    SearchRequest request6 = SearchRequest.of(builder -> builder
            .index("hotel")
            .query(q -> q.match(qm -> qm.field("name").query("如家")))
            .highlight(h -> h.fields("name",
                    f -> f.preTags("<em>").postTags("<em>")))
    );
    log.debug("高亮查询:{}", searchSome(request6));
    // 7. 相关性算分
    SearchRequest request7 = SearchRequest.of(builder -> builder
            .index("hotel")
            .query(q -> q.functionScore(fs -> fs
                    .query(fq -> fq.match(f -> f.field("name").query("外滩")))
                    .functions(f -> f
                            .filter(fd -> fd.term(t -> t.field("city").value("上海")))
                            .weight(5.0)
                    )
                    .boostMode(FunctionBoostMode.Multiply)))
    );
    log.debug("相关性算分查询:{}", searchSome(request7));
    // 8. 拼音查询
    SearchRequest request8 = SearchRequest.of(builder ->
            builder.index("hotel").query(q -> q
                    .match(v -> v.field("name.pinyin").query("s8")))
    );
    log.debug("拼音单字段全文检索查询:{}", search(request8));
}

@SneakyThrows
    private List<HotelDoc> search(SearchRequest request) {
        List<Hit<HotelDoc>> hits = elasticsearchClient.search(request, HotelDoc.class).hits().hits();
        List<HotelDoc> list = new ArrayList<>();
        hits.forEach(h -> list.add(h.source()));
        return list;
    }

    @SneakyThrows
    private SearchResponse<HotelDoc> searchSome(SearchRequest request) {
        return elasticsearchClient.search(request, HotelDoc.class);
    }
聚合查询
@SneakyThrows
    @Override
    public void aggregate() {
        // 1. Bucket桶
        SearchRequest request1 = SearchRequest.of(builder -> builder
                .index("hotel")
                .aggregations("brand_Agg", a -> a.terms(v -> v.field("brand")))
                .query(q -> q.term(v -> v.field("city").value("上海")))
                .query(q -> q.range(r -> r.field("price").lte(JsonData.of(300)).gte(JsonData.of(100))))
        );
        log.debug("聚合查询:Bucket桶:{}", searchSome(request1).aggregations());


    }
自动补全
@SneakyThrows
    @Override
    public List<HotelDoc> suggest(String value) {
        // 1. 自动补全
        SearchRequest request = SearchRequest.of(builder -> builder
                .index("hotel")
                .suggest(s -> s.suggesters("suggestions", fn -> fn.
                        prefix(value).completion(c -> c.field("suggestion").size(10)))
                )
        );
        List<CompletionSuggestOption<HotelDoc>> suggestions = searchSome(request).suggest().get("suggestions").get(0).completion().options();
        List<HotelDoc> list = suggestions.stream().map(CompletionSuggestOption::source).collect(Collectors.toList());
        log.debug("自动补全:{}", list);
        return list;
    }
文章来源:https://blog.csdn.net/treadsangerbraes/article/details/135706875
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。