配置
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);} elsethis.suggestion.add(this.business);this.suggestion.add(this.brand);}
}
配置类
@Configuration
public class ElasticsearchConfig {@Value("${elasticsearch.address}")private String address;@Beanpublic 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 {@Autowiredprivate ElasticsearchClient elasticsearchClient;@Autowiredprivate 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 andSearchRequest 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 orSearchRequest 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));
}@SneakyThrowsprivate 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;}@SneakyThrowsprivate SearchResponse<HotelDoc> searchSome(SearchRequest request) {return elasticsearchClient.search(request, HotelDoc.class);}
聚合查询
@SneakyThrows@Overridepublic 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@Overridepublic 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;}