自己学习elasticsearch8.x写的Api方法,有点乱没怎么整理,如有不足不吝赐教。如果有帮助到您,麻烦点个赞~
废话不多说直接上api,配置有不明白的可以搜其他博主的文档解决一下~
部分方法可能跟我用于学习es8的业务耦合性较高,这个根据不同业务做调整~
public class HotelDoc {
private String id;
private String name;//酒店名
private String address;//酒店地址
private int price;//酒店价格
private int score;//打分
private String brand;//品牌
private String city;//城市
private String star_name;//酒店星级 1钻-5钻 1星-5星
private String business;//商圈
private String location;//酒店坐标
private String pic;//酒店图片
private double distance;//酒店距离
private boolean ad;//是否广告
private List<String> suggestion;
}
/**
* 判断索引库是否存在
*
* @param index 索引名
* @return boolean
* @throws IOException
*/
public static boolean ifExist(String index) throws IOException {
BooleanResponse response = client.indices().exists(builder -> builder.index(index));
return response.value();
}
/**
* 根据索引库名删除
*
* @param index 索引库名
* @return
* @throws IOException
*/
public static boolean deteleIndex(String index) throws IOException {
DeleteIndexResponse response = client.indices().delete(builder -> builder.index(index));
return response.acknowledged();
}
/**
* 代码创建索引库
*
* @param index 索引名
* @param map 字段以及字段对应的type类型
* @return boolean
* @throws IOException
*/
public static boolean createIndex(String index, Map<String, Property> map) throws IOException {
CreateIndexRequest indexRequest = CreateIndexRequest.of(builder ->
builder.index(index).mappings(objBuild -> objBuild.properties(map)));
// 发送请求
CreateIndexResponse response = client.indices().create(indexRequest);
return response.acknowledged();
}
/**
* 根据索引json内容创建索引库
*
* @param index 索引名
* @return boolean
* @throws IOException
*/
public static boolean createIndexForStr(String index) throws IOException {
String mapping_str = Templet.getTemplet(index);
if (mapping_str == null) return false;
log.info("mapping_str:【{}】", mapping_str);
CreateIndexRequest indexRequest = CreateIndexRequest.of(builder ->
builder.index(index)
.withJson(new StringReader(mapping_str)));
// 发送请求
CreateIndexResponse response = client.indices().create(indexRequest);
return response.acknowledged();
}
/**
* 根据索引json内容创建索引库 字符串只需要包含{"properties":{}}内容
*
* @param index 索引名
* @return boolean
* @throws IOException
*/
public static boolean createIndexForStr2(String index) throws IOException {
JsonpMapper mapper = client._transport().jsonpMapper();
String mapping_str = Templet.getTemplet(index);
if (mapping_str == null) return false;
log.info("mapping_str:【{}】", mapping_str);
JsonParser parser = mapper.jsonProvider().createParser(new StringReader(mapping_str));
// 发送请求
CreateIndexResponse response = client.indices().create(builder ->
builder.index(index).mappings(TypeMapping._DESERIALIZER.deserialize(parser, mapper)));
return response.acknowledged();
}
/**
* 根据索引库创建json文件路径创建索引库
*
* @param index 索引名
* @param path xx.json文件
* @return boolean
* @throws IOException
*/
public static boolean createIndexForPath(String index, String path) throws IOException {
JsonpMapper mapper = client._transport().jsonpMapper();
String mapping_str = new String(Files.readAllBytes(Paths.get(path)));
log.info("mapping_str:【{}】", mapping_str);
JsonParser parser = mapper.jsonProvider().createParser(new StringReader(mapping_str));
// 发送请求
CreateIndexResponse response = client.indices().create(builder ->
builder.index(index).mappings(TypeMapping._DESERIALIZER.deserialize(parser, mapper)));
return response.acknowledged();
}
/**
* 根据索引名以及文档id添加文档
*
* @param index 索引名
* @param obj 单个对象
* @return
* @throws IOException
*/
public static <T> void createDoc(String index, T obj) throws IOException, IllegalAccessException {
boolean b = ifExist(index);
if (!b) return;
Field[] fields = obj.getClass().getDeclaredFields();
fields[0].setAccessible(true);
String id = fields[0].get(obj).toString();
client.create(x -> x.index(index).id(id).document(obj));
}
/**
* 根据索引名以及文档id添加文档
*
* @param index 索引名
* @param id 文档id
* @param jsonStr json格式的文档数据
* @return
* @throws IOException
*/
public static <T> void createDocForStr(String index, String id, String jsonStr) throws IOException {
boolean b = ifExist(index);
if (!b) return;
// IndexRequest<Object> request = IndexRequest.of(builder -> builder.index(index).id(id).withJson(new StringReader(jsonStr)));
// client.index(request);
client.create(builder -> builder.index(index).id(id).withJson(new StringReader(jsonStr)));
}
/**
* 根据索引名批量添加文档 通过BulkOperation来操作
*
* @param index 索引名
* @param lst 对象集合
* @return
* @throws IOException
*/
public static <T> boolean createBatchDoc(String index, List<T> lst) throws IOException, IllegalAccessException {
boolean b = ifExist(index);
if (!b) return false;
List<BulkOperation> blkLst = new ArrayList<>();
for (T t : lst) {
Field[] fields = t.getClass().getDeclaredFields();
fields[0].setAccessible(true);
String id = fields[0].get(t).toString();
blkLst.add(BulkOperation.of(x -> x.create(y -> y.index(index).id(id).document(t))));
}
BulkResponse response = client.bulk(builder -> builder.operations(blkLst));
return !response.errors();
}
/**
* 根据索引名以及id查询文档
*
* @param index 索引名
* @param id 文档id
* @return
* @throws IOException
*/
public static <T> T getDoc(String index, String id, Class<T> t) throws IOException {
boolean b = ifExist(index);
if (!b) return null;
GetResponse<T> response = client.get(x -> x.index(index).id(id), t);
return response.source();
}
/**
* 根据索引名以及id修改文档
*
* @param index 索引名
* @param id 文档id
* @return
* @throws IOException
*/
public static void updateDoc(String index, String id, Map<String, Object> map) throws IOException {
boolean b = ifExist(index);
if (!b) return;
client.update(builder -> builder.index(index).id(id).doc(map), User.class);
}
/**
* 根据索引名以及id批量修改文档
*
* @param index 索引名
* @param lst List<Map<String, Object>> 包含文档id的map
* @return
* @throws IOException
*/
public static boolean updateBatchDoc(String index, List<Map<String, Object>> lst) throws IOException {
boolean b = ifExist(index);
if (!b) return false;
List<BulkOperation> bulkLst = new ArrayList<>();
for (Map<String, Object> map : lst) {
String id = map.get("id").toString();
if (id == null) continue;
bulkLst.add(BulkOperation.of(x -> x.update(y -> y.index(index).id(id).action(UpdateAction.of(z -> z.doc(map))))));
}
BulkResponse response = client.bulk(builder -> builder.operations(bulkLst));
return !response.errors();
}
/**
* 根据索引名以及id删除文档
*
* @param index 索引名
* @param id 文档id
* @return
* @throws IOException
*/
public static void deleteDoc(String index, String id) throws IOException {
boolean b = ifExist(index);
if (!b) return;
DeleteResponse response = client.delete(builder -> builder.index(index).id(id));
System.out.println(response.result().jsonValue());
}
/**
* 根据索引名以及文档id批量删除文档 通过BulkOperation来操作
*
* @param index 索引名
* @param lst 含有文档id对象集合
* @return
* @throws IOException
*/
public static <T> boolean deleteBatchDoc(String index, List<T> lst) throws IOException, IllegalAccessException {
boolean b = ifExist(index);
if (!b) return false;
List<BulkOperation> bulkLst = new ArrayList<>();
for (T t : lst) {
Field[] fields = t.getClass().getDeclaredFields();
fields[0].setAccessible(true);
String id = fields[0].get(t).toString();
bulkLst.add(BulkOperation.of(builder -> builder.delete(builder1 -> builder1.index(index).id(id))));
}
BulkResponse bulk = client.bulk(builder -> builder.operations(bulkLst));
return !bulk.errors();
}
/**
* @param index 索引库名
* @param t 文档id映射对象.class
* @param <T> 文档id映射对象
* @return List<T>
* @throws IOException
*/
public static <T> List<T> matchAll(String index, Class<T> t) throws IOException {
SearchResponse<T> response = client.search(x -> x.index(index).query(y -> y.matchAll(z -> z)), t);
List<Hit<T>> hits = response.hits().hits();
return hits.stream().map(Hit::source).collect(Collectors.toList());
}
/**
* field为汇总字段时效果与multiMatch一致且效率更高
*
* @param index 索引库名
* @param t t 文档id映射对象.class
* @param field 字段名
* @param value 搜索词条
* @param <T> 文档id映射对象
* @return
* @throws IOException
*/
public static <T> List<T> match(String index, String field, String value, Class<T> t) throws IOException {
SearchResponse<T> response = client.search(
x -> x.index(index).query(
y -> y.match(
z -> z.field(field).query(value))), t);
List<Hit<T>> hits = response.hits().hits();
return hits.stream().map(Hit::source).collect(Collectors.toList());
}
/**
* 与match查询类似,只不过允许同时查询多个字段
*
* @param index 索引库名
* @param t t 文档id映射对象.class
* @param fields 多个查询字段的集合
* @param value 搜索词条
* @param <T> 文档id映射对象
* @return
* @throws IOException
*/
public static <T> List<T> multiMatch(String index, String value, List<String> fields, Class<T> t) throws IOException {
SearchResponse<T> response = client.search(
x -> x.index(index).query(
y -> y.multiMatch(
z -> z.query(value).fields(fields))), t);
List<Hit<T>> hits = response.hits().hits();
return hits.stream().map(Hit::source).collect(Collectors.toList());
}
/**
* 精确查询,必须字段值与查询值一致,否则不匹配
*
* @param index 索引库名
* @param t t 文档id映射对象.class
* @param field 字段名
* @param value 搜索词条
* @param <T> 文档id映射对象
* @return
* @throws IOException
*/
public static <T> List<T> term(String index, String field, String value, Class<T> t) throws IOException {
SearchResponse<T> response = client.search(
x -> x.index(index).query(
y -> y.term(
z -> z.field(field).value(value))), t);
List<Hit<T>> hits = response.hits().hits();
return hits.stream().map(Hit::source).collect(Collectors.toList());
}
/**
* 范围查询
*
* @param index 索引库名
* @param field 字段名
* @param gteValue 范围要大于的值
* @param lteValue 范围要小于的值
* @param t t 文档id映射对象.class
* @param <T> 文档id映射对象
* @return
* @throws IOException
*/
public static <T> List<T> range(String index, String field, String gteValue, String lteValue, Class<T> t) throws IOException {
SearchResponse<T> response = client.search(
x -> x.index(index).query(
y -> y.range(
z -> z.field(field)
.gte(JsonData.of(gteValue))
.lte(JsonData.of(lteValue)))), t);
List<Hit<T>> hits = response.hits().hits();
return hits.stream().map(Hit::source).collect(Collectors.toList());
}
/**
* 地理坐标查询矩形范围内的文档数据
*
* @param index 索引库名
* @param field 字段名
* @param lat 经度
* @param lon 纬度
* @param t t 文档id映射对象.class
* @param <T> 文档id映射对象
* @return
* @throws IOException
*/
public static <T> List<T> geoBoundingBox(String index, String field, String topRightLon, String topRightLat, Double lat, Double lon, Class<T> t) throws IOException {
return null;
}
/**
* 地理坐标查询distance以内的文档数据
*
* @param index 索引库名
* @param field 字段名
* @param distance 查询距离
* @param lat 经度
* @param lon 纬度
* @param t t 文档id映射对象.class
* @param <T> 文档id映射对象
* @return
* @throws IOException
*/
public static <T> List<T> geoDistance(String index, String field, String distance, Double lat, Double lon, Class<T> t) throws IOException {
SearchResponse<T> response = client.search(
x -> x.index(index).query(
y -> y.geoDistance(
z -> z.distance(distance).field(field).location(
j -> j.latlon(LatLonGeoLocation.of(
k -> k.lat(lat).lon(lon)))))), t);
List<Hit<T>> hits = response.hits().hits();
return hits.stream().map(Hit::source).collect(Collectors.toList());
}
/**
* 复合查询,先对查询数据进行初次匹配,在通过filter过滤出符合条件的文档进行重新算分
*
* @param index 索引库名
* @param matchField 原始条件匹配字段
* @param matchValue 原始条件匹配字段值
* @param filterField 过滤字段(只有符合过滤条件的文档才会被重新算分)
* @param filterValue 过滤字段值
* @param weight 算分函数 funtion score 将来回去query score计算得到新算分进行排序
* @param functionBoostMode 加权模式 相乘/最大/最小/.....
* @param t t 文档id映射对象.class
* @param <T> 文档id映射对象
* @return
* @throws IOException
*/
public static <T> List<T> funtionScoreQuery(String index, String matchField, String matchValue, String filterField, String filterValue, Double weight, FunctionBoostMode functionBoostMode, Class<T> t) throws IOException {
SearchRequest request = new SearchRequest.Builder().index(index).query(
x -> x.functionScore(
y -> y.query(
z -> z.match(
j -> j.field(matchField).query(matchValue)
)
).functions(
k -> k.filter(
u -> u.term(
i -> i.field(filterField).value(filterValue)))
.weight(weight)).boostMode(functionBoostMode)
)).build();
SearchResponse<T> response = client.search(request, t);
List<Hit<T>> hits = response.hits().hits();
return hits.stream().map(Hit::source).collect(Collectors.toList());
}
/**
* 布尔查询
*
* @param index 索引库名
* @param matchField 匹配字段
* @param matchValue 匹配字段值
* @param priceField 价格字段
* @param priceValue 价格值
* @param distance 距离
* @param locationField 坐标字段
* @param lat 坐标经度
* @param lon 坐标纬度
* @param t t 文档id映射对象.class
* @param <T> 文档id映射对象
* @return
* @throws IOException
*/
// 搜索名字包含“如家”,价格不高于400,在坐标31.21,121.5周围10km范围内的酒店。
public static <T> List<T> booleanQuery(String index, String matchField, String matchValue, String priceField, String priceValue, String distance, String locationField, Double lat, Double lon, Class<T> t) throws IOException {
SearchRequest request = new SearchRequest.Builder().index(index).query(
x -> x.bool(
y -> y.must(
z -> z.match(
j -> j.field(matchField).query(matchValue)
)
).mustNot(
k -> k.range(u -> u.field(priceField).gte(JsonData.of(priceValue))
)
).should(
l -> l.geoDistance(u -> u.distance(distance).field("location").location(GeoLocation.of(i -> i.latlon(o -> o.lat(lat).lon(lon)))))
)
)).build();
SearchResponse<T> response = client.search(request, t);
System.out.println(response.hits());
List<Hit<T>> hits = response.hits().hits();
return hits.stream().map(Hit::source).collect(Collectors.toList());
}
/**
* 返回结果自定义排序
*
* @param index 索引库名
* @param sortField 排序的字段
* @param order 排序方式
* @param t t 文档id映射对象.class
* @param <T> 文档id映射对象
* @return
* @throws IOException
*/
public static <T> List<T> sort(String index, String sortField, SortOrder order, Class<T> t) throws IOException {
SearchRequest request = new SearchRequest.Builder().index(index)
.sort(
x -> x.field(FieldSort.of(
y -> y.field(sortField).order(order)
))
).build();
SearchResponse<T> response = client.search(request, t);
List<Hit<T>> hits = response.hits().hits();
return hits.stream().map(Hit::source).collect(Collectors.toList());
}
/**
* 根据地理坐标自定义排序
*
* @param index 索引库名
* @param sortField 排序的字段
* @param lat 经度
* @param lon 纬度
* @param order 排序方式
* @param t t 文档id映射对象.class
* @param <T> 文档id映射对象
* @return
* @throws IOException
*/
public static <T> List<T> sortByGeoDistance(String index, String sortField, Double lat, Double lon, SortOrder order, Class<T> t) throws IOException {
SearchRequest request = new SearchRequest.Builder().index(index).sort(
x -> x.geoDistance(
y -> y.field(sortField)
.location(
z -> z.latlon(
j -> j.lat(lat).lon(lon)
))
.unit(DistanceUnit.Kilometers)
.order(order)
)).build();
SearchResponse<T> response = client.search(request, t);
List<Hit<T>> hits = response.hits().hits();
return hits.stream().map(Hit::source).collect(Collectors.toList());
}
/**
* 分页查询
*
* @param index 索引库名
* @param size 分页开始的位置
* @param total 期望获取的文档总数
* @param t t 文档id映射对象.class
* @param <T> 文档id映射对象
* @return
* @throws IOException
*/
public static <T> List<T> page(String index, Integer total, Integer size, Class<T> t) throws IOException {
SearchRequest request = new SearchRequest.Builder().index(index)
.query(
x -> x.matchAll(y -> y)
)
// .sort(
// z->z.field(
// j->j.field("age").order(SortOrder.Desc)
// )
// )
.from(total)
.size(size)
.build();
SearchResponse<T> response = client.search(request, t);
List<Hit<T>> hits = response.hits().hits();
return hits.stream().map(Hit::source).collect(Collectors.toList());
}
/**
* 数据查询返回高亮字段
*
* @param index 索引库名
* @param matchField 匹配查询字段
* @param matchValue 匹配查询字段值
* @param highLightFields 需要高亮字段值的字段
* @param match 查询字段是否与高亮字段一致
* @param t t 文档id映射对象.class
* @param <T> 文档id映射对象
* @return
* @throws IOException
*/
public static <T> List<Hit<T>> highlight(String index, String matchField, String matchValue, List<String> highLightFields
, boolean match, Class<T> t) throws IOException {
SearchRequest request = new SearchRequest.Builder().index(index)
.query(
x -> x.match(
y -> y.field(matchField).query(matchValue)
)
).highlight(
x -> x.fields(
"name", y -> y.requireFieldMatch(match)
)
)
.build();
SearchResponse<T> response = client.search(request, t);
return response.hits().hits();
}
/**
* 首先根据queryField以及value指定查询范围,在通过bucket聚合进行数据聚合
*
* @param index 索引库名
* @param queryField 范围查询字段
* @param value 字段值
* @param aggName 聚合名称 自定义
* @param aggField 聚合字段
* @param aggSize 聚合数据显示条数
* @return
* @throws IOException
*/
public static List<StringTermsBucket> bucket(String index, String queryField, Double value, String aggName, String aggField, Integer aggSize) throws IOException {
SearchRequest.Builder builder = new SearchRequest.Builder().index(index);
builder.query(
x -> x.range(
y -> y.field(queryField).lte(JsonData.of(value))
)
);
builder.size(0)
.aggregations(aggName, x -> x
.terms(y -> y
.field(aggField)
.size(aggSize)
.order(NamedValue.of("_count", SortOrder.Desc))));
SearchResponse<TypeQuery> response = client.search(builder.build(), TypeQuery.class);
Map<String, Aggregate> map = response.aggregations();
Aggregate aggs = map.get(aggName);
StringTermsAggregate sterms = aggs.sterms();
return sterms.buckets().array();
}
/**
* 获取每个品牌的用户评分的min,max,avg值
* @param index 索引库名
* @param bucketName bucket聚合名称
* @param bucketField bucket聚合字段
* @param metricsName metrics聚合名称
* @param metricsField metrics聚合字段
* @return
* @throws IOException
*/
public static List<StringTermsBucket> metrics(String index, String bucketName,String bucketField, String metricsName, String metricsField) throws IOException {
SearchRequest.Builder builder = new SearchRequest.Builder().index(index);
builder.aggregations(bucketName,x->x
.terms(y->y
.field(bucketField)
.size(5)
.order(NamedValue.of(metricsName+".avg",SortOrder.Desc)))
.aggregations(metricsName,z->z
.stats(j->j
.field(metricsField)))
);
SearchResponse<HotelDoc> response = client.search(builder.build(), HotelDoc.class);
Map<String, Aggregate> aggregations = response.aggregations();
StringTermsAggregate sterms = aggregations.get(bucketName).sterms();
return sterms.buckets().array();
}
/**
* 实现对酒店品牌,城市,星级的数据聚合
* 搜索页面的品牌城市以及星级不应该是写死的,而是通过聚合索引库里的数据得到的
* @param index 索引库名
* @return
* @throws IOException
*/
public static Map<String, List<String>> hotelSearch(String index) throws IOException {
SearchRequest.Builder builder = new SearchRequest.Builder().index(index);
builder.aggregations("brandAgg",x->x
.terms(y->y
.field("brand")
.size(10)));
builder.aggregations("cityAgg",x->x
.terms(y->y
.field("city")
.size(10)));
builder.aggregations("star_nameAgg",x->x
.terms(y->y
.field("star_name")
.size(10)));
SearchResponse<HotelDoc> response = client.search(builder.build(), HotelDoc.class);
return getAggregateMap(response);
}
private static Map<String, List<String>> getAggregateMap(SearchResponse<HotelDoc> response) {
Map<String, Aggregate> map = response.aggregations();
StringTermsAggregate brandAgg = map.get("brandAgg").sterms();
StringTermsAggregate cityAgg = map.get("cityAgg").sterms();
StringTermsAggregate starNameAgg = map.get("star_nameAgg").sterms();
List<StringTermsBucket> brandLst = brandAgg.buckets().array();
List<StringTermsBucket> cityLst = cityAgg.buckets().array();
List<StringTermsBucket> starNameLst = starNameAgg.buckets().array();
HashMap<String, List<String>> retMap = new HashMap<>();
retMap.put("brand",brandLst.stream().map(x->x.key().stringValue()).collect(Collectors.toList()));
retMap.put("city",cityLst.stream().map(x->x.key().stringValue()).collect(Collectors.toList()));
retMap.put("star_name",starNameLst.stream().map(x->x.key().stringValue()).collect(Collectors.toList()));
return retMap;
}
还有包括一些业务示例应用:
/**
* 根据查询酒店参数:
name:酒店名 匹配查询
city:城市名 精确查询
brand:品牌名 精确查询
star_name:星级 精确查询
price:价格 精确查询
ad:是否为广告
查询数据,且打过广告的酒店靠前高亮显示
* @param hotel 查询条件对象
* @return
* @throws IOException
*/
@Override
public List<HotelDoc> search(HotelForSearch hotel) throws IOException {
SearchRequest.Builder builder = new SearchRequest.Builder().index("hotel");
BoolQuery.Builder bool = QueryBuilders.bool();
buildQuery(hotel, bool);
BoolQuery boolQuery = bool.build();
FunctionScoreQuery.Builder func = QueryBuilders.functionScore();
func.query(x->x.bool(boolQuery));
func.functions(x->x.filter(y->y.term(z->z.field("ad").value(true))).weight(10.0)).boostMode(FunctionBoostMode.Sum);
builder.query(y -> y.functionScore(func.build()));
builder.highlight(x->x.fields("name",y->y.requireFieldMatch(true)));
SearchRequest request = builder.from((hotel.getPage() - 1) * hotel.getSize())
.size(hotel.getSize()).build();
SearchResponse<HotelDoc> response = client.search(request, HotelDoc.class);
List<Hit<HotelDoc>> hits = response.hits().hits();
for (Hit<HotelDoc> hit : hits) {
List<String> lst = hit.highlight().get("name");
if (lst.isEmpty()) continue;
if (Objects.isNull(hit.source())) continue;
hit.source().setName(lst.get(0));
}
return hits.stream().map(Hit::source).collect(Collectors.toList());
}
/**
* 根据地理坐标附近的酒店远近排序并分页
* @param hotel 查询条件对象
* @return
* @throws IOException
*/
@Override
public List<HotelDoc> searchByDistance(HotelForSearch hotel) throws IOException {
String location = hotel.getLocation();
if (StrUtil.isBlank(location)) throw new RuntimeException("未获取到当前位置");
SearchRequest.Builder builder = new SearchRequest.Builder().index("hotel");
builder.sort(
x->x.geoDistance(
y->y.field("location")
.location(z->z.text(location))
.unit(DistanceUnit.Kilometers)
.order(SortOrder.Asc)
)
)
.from((hotel.getPage()-1)*hotel.getSize())
.size(hotel.getSize());
SearchRequest request = builder.build();
SearchResponse<HotelDoc> response = client.search(request, HotelDoc.class);
List<Hit<HotelDoc>> hitList = response.hits().hits();
hitList.forEach(hit -> {
HotelDoc doc = hit.source();
List<FieldValue> sortLst = hit.sort();
if (sortLst.isEmpty()) return;
doc.setDistance(sortLst.get(0).doubleValue());
});
return hitList.stream().map(Hit::source).collect(Collectors.toList());
}
/**
* 检索所有数据并使广告位数据靠前显示
* @param hotel 查询条件对象
* @return
* @throws IOException
*/
@Override
public List<HotelDoc> searchForTopAd(HotelForSearch hotel) throws IOException {
SearchRequest.Builder builder = new SearchRequest.Builder().index("hotel");
builder.query(x->x
.functionScore(y->y
.query(z->z
.matchAll(j->j))
.functions(j->j
.filter(k->k
.term(l->l
.field("ad").value(true)))
.weight(10.0))
.boostMode(FunctionBoostMode.Multiply)))
.from((hotel.getPage()-1)*hotel.getSize())
.size(hotel.getSize());
SearchRequest build = builder.build();
SearchResponse<HotelDoc> response = client.search(build, HotelDoc.class);
List<Hit<HotelDoc>> hits = response.hits().hits();
return hits.stream().map(Hit::source).collect(Collectors.toList());
}
/**
* 实现对酒店品牌,城市,星级的数据聚合
* 搜索页面的品牌城市以及星级不应该是写死的,而是通过聚合索引库里的数据得到的,且可实现搜索框内容的输入动态展示
* @param hotel 查询对象
* @return
*/
@Override
public Map<String, List<String>> filters(HotelForSearch hotel) throws IOException {
SearchRequest.Builder builder = new SearchRequest.Builder().index("hotel");
BoolQuery.Builder bool = new BoolQuery.Builder();
buildQuery(hotel, bool);
builder.query(x->x.bool(bool.build()));
builder.aggregations("brandAgg",x->x
.terms(y->y
.field("brand")
.size(100)));
builder.aggregations("cityAgg",x->x
.terms(y->y
.field("city")
.size(100)));
builder.aggregations("star_nameAgg",x->x
.terms(y->y
.field("star_name")
.size(100)));
SearchResponse<HotelDoc> response = client.search(builder.build(), HotelDoc.class);
return getAggregateMap(response);
}
/**
* 搜索框自动补全
* @param text 搜索框输入内容
* @return
* @throws IOException
*/
@Override
public List<String> autoComplete(String text) throws IOException {
SearchRequest.Builder builder = new SearchRequest.Builder().index("hotel");
builder.suggest(x->x
.suggesters("suggestions",y->y
.text(text)
.completion(z->z
.field("suggestion")
.skipDuplicates(true)
.size(10))));
SearchResponse<HotelDoc> response = client.search(builder.build(), HotelDoc.class);
List<Suggestion<HotelDoc>> lst = response.suggest().get("suggestions");
System.out.println(lst);
Suggestion<HotelDoc> suggestion = lst.get(0);
List<CompletionSuggestOption<HotelDoc>> retLst = suggestion.completion().options();
return retLst.stream().map(CompletionSuggestOption::text).collect(Collectors.toList());
}
/**
* 结果的处理 Map<String,List<String>>
*/
private static Map<String, List<String>> getAggregateMap(SearchResponse<HotelDoc> response) {
Map<String, Aggregate> map = response.aggregations();
StringTermsAggregate brandAgg = map.get("brandAgg").sterms();
StringTermsAggregate cityAgg = map.get("cityAgg").sterms();
StringTermsAggregate starNameAgg = map.get("star_nameAgg").sterms();
List<StringTermsBucket> brandLst = brandAgg.buckets().array();
List<StringTermsBucket> cityLst = cityAgg.buckets().array();
List<StringTermsBucket> starNameLst = starNameAgg.buckets().array();
HashMap<String, List<String>> retMap = new HashMap<>();
retMap.put("brand",brandLst.stream().map(x->x.key().stringValue()).collect(Collectors.toList()));
retMap.put("city",cityLst.stream().map(x->x.key().stringValue()).collect(Collectors.toList()));
retMap.put("star_name",starNameLst.stream().map(x->x.key().stringValue()).collect(Collectors.toList()));
return retMap;
}
/**
* 根据页面查询条件进行范围查询
*/
private void buildQuery(HotelForSearch hotel, BoolQuery.Builder bool) {
if (StrUtil.isBlank(hotel.getName())) {
bool.must(x->x.matchAll(y->y));
}else {
bool.must(x->x.match(y->y.field("name").query(hotel.getName())));
}
if (!StrUtil.isBlank(hotel.getCity())) {
bool.filter(x->x.term(y->y.field("city").value(hotel.getCity())));
}
if (!StrUtil.isBlank(hotel.getBrand())) {
bool.filter(x->x.term(y->y.field("brand").value(hotel.getBrand())));
}
if (!StrUtil.isBlank(hotel.getStar_name())) {
bool.filter(x->x.term(y->y.field("star_name").value(hotel.getStar_name())));
}
if (!ifZero(hotel.getMinPrice())) {
bool.mustNot(x->x.range(y->y.field("price").lte(JsonData.of(hotel.getMinPrice()))));
}
if (!ifZero(hotel.getMaxPrice())) {
bool.mustNot(x->x.range(y->y.field("price").gte(JsonData.of(hotel.getMaxPrice()))));
}
}
private boolean ifZero(int str){
return str == 0;
}