搜索结果处理
排序
默认根据相关度分数排序, 可以排序的类型分为keyword
, 数值类型, 地理坐标, 日期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| GET /idxName/_search { "query": { "match_all": {}, "sort": [ { "FIELD": "desc" }, { "_geo_distance": { "FIELD": "<lat, lon>", "order": "asc", "unit": "km" } } ] } }
|
分页
默认只会返回10条数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| GET /idxName/_search { "query": { "match_all": {} }, "from": 990, "size": 10, "sort": [ { "price": "asc" } ] } // from 文档开始位置 // size 文档总数
|
- 由于
ES
是倒排索引, 因此分页非常困难, 本身分页是逻辑分页
- 比如查询990开始的10条数据, 实际上是
ES
查询了0-1000的所有数据, 然后截取了其中的990-1000的部分
- 单点
ES
没有问题, 但是如果是集群, ES
中会将数据分片
- 因此需要将每个分片上的前1000, 然后再合并汇总, 重新排序得到前1000, 最后截取990-1000的部分
- 所以如果搜索页数过多, 或者结果集过深(
from + size
)过大, 资源消耗过多, 所以限制了from+size<10000
- 官方给出两个解决办法:
search after
分页时需要排序, 从上一次排序值开始, 查询下一页数据, 但是只能往后查询, 不能往前查询
scroll
: 将排序数据形成快照, 保存在内存中, 官方现在不推荐用, 对内存消耗较大, 并且快照更新无法实时
高亮
搜索结果突出显示, 提前用标签标记, 然后对标签添加css样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| GET /idxName/_search { "query": { "match": { "FIELD": "TEXT" } }, "highlight": { "fields": { "FIELD": { "require_field_match": "false", "pre_tags": "<em>", "posts_tags": "</em>" } } } } // FIELD 表示需要高亮的字段 // pre_tags表示关键字之前的标签 // 不能使用match_all, 因为需要关键字了 // 默认情况下, 搜索字段必须要和高亮字段一致, 也就是两个FIELD需要相同 // 使用require_field_match = false可以解决这个问题
|
RestClient
查询文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| void MatchAll() throws IOException { SearchRequest req = new SearchRequest("xxx"); req.source().query(QueryBuilders.matchAllQuery()); req.source().sort("xxx", SortOrder.ASC); req.source().from(0).size(5); SearchResponse resp = client.search(req, RequestOptions.DEFAULT); SearchHits hits = resp.getHits(); long total = hits.getTotalHits().value; SearchHit[] h = hits.getHits(); for (SearchHit hh : h) { String json = hh.getSourceAsString(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| void Highlight() throws IOException { SearchRequest req = new SearchRequest("xxx"); req.source().query(QueryBuilders.matchQuery("all", "xxxx")); req.source().highlighter(new HighlightBuilder().field("xxx").requireFieldMatch(false)); SearchResponse resp = client.search(req, RequestOptions.DEFAULT); HotelDoc hD = JSON.parseObject(hit.getSourceAsString(), HotelDoc.class); Map<String, HighlightField> hF = hit.getHighlightFields(); if (!CollectionUtils.isEmpty(hF)) { HighlightField hlF = hF.get("xxx"); if (hlF != null) { String name = hlF.getFragments()[0].string(); hD.setName(name); } } }
|
数据聚合
实现对文档数据的统计, 分析, 运算, 不能对Text
进行聚合
种类
桶聚合(Bucket
)
对文档分组, 默认是对所有文档进行搜索, 限定聚合范围可以使用query
条件
TermAggregation
: 按照文档字段值分组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| GET /idxName/_search { "query": { "range": { "price": { "lte": 200 // 限定聚合范围 } } }, "size": 0, // 设置size = 0, 结果不包含文档, 只包含聚合结果 "aggs": { // 定义聚合 "brandAgg": { // 给聚合起名字 "terms": { // 聚合的类型, 按照品牌值进行聚合, 所以选择term "field": "brand", // 参与聚合的字段 "size": 20 // 希望获取的聚合结果数量 } } } }
|
Date Histogram
: 按照日期阶梯分组, 比如一周一组
度量聚合(Metric
)
Avg
: 平均
Max
: 最大
Min
: 最小
Stats
: 同时求min
, max
, avg
, sum
等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| GET /idxName/_search { "query": { "range": { "price": { "lte": 200 // 限定聚合范围 } } }, "size": 0, // 设置size = 0, 结果不包含文档, 只包含聚合结果 "aggs": { // 定义聚合 "brandAgg": { // 给聚合起名字 "terms": { // 聚合的类型, 按照品牌值进行聚合, 所以选择term "field": "brand", // 参与聚合的字段 "size": 20 // 希望获取的聚合结果数量 "order": { "scoreAgg.avg": "desc" } }, "aggs": { // 对桶聚合的结果进行统计 "scoreAgg": { "stats": { "field": "score" } } } } } }
|
管道聚合(pipeline
)
其他聚合的结果为基础进行聚合