配置
pom依赖
xml
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>7.17.5</version>
</dependency>
yml配置
yml
elasticsearch:
address: http://192.168.133.100:9200
数据库实体类
java
@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类型
java
@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);
}
}
配置类
java
@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】
java
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层
java
@Service
@Slf4j
public class HotelServiceImpl extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
@Autowired
private ElasticsearchClient elasticsearchClient;
@Autowired
private HotelMapper hotelMapper;
}
索引
创建索引
java
@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();
}
删除索引
java
@SneakyThrows
@Override
public Boolean deleteHotelIndex() {
DeleteIndexRequest request = DeleteIndexRequest.of(builder -> builder.index("hotel"));
DeleteIndexResponse delete = elasticsearchClient.indices().delete(request);
return delete.acknowledged();
}
查询索引是否存在
java
@SneakyThrows
@Override
public Boolean existsHotelIndex() {
return elasticsearchClient.indices().exists(builder -> builder.index("hotel")).value();
}
文档
创建文档
java
@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;
}
查询文档
java
@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();
}
更新文档
java
@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;
}
删除文档
java
@SneakyThrows
@Override
public Result deleteHotelDocument() {
return elasticsearchClient.delete(builder ->
builder.index("hotel").id("61083")).result();
}
批量添加和删除文档
java
@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());
}
高级查询
java
@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);
}
聚合查询
java
@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());
}
自动补全
java
@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;
}