一、概述
Elasticsearch
提供了基于JSON
的DSL
来定义查询。常见的查询类型包括:
-
查询所有:查询出所有数据,一般测试用。
例如:
match_all
-
全文检索(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。
例如:
match_query
multi_match_query
-
精确查询:根据精确词条值查找数据,一般是查找
keyword
、数值、日期、boolean
等类型字段。例如:
ids
range
term
-
地理(geo)查询:根据经纬度查询。
例如:
geo_distance
geo_bounding_box
-
复合(compound)查询:复合查询可以将上述各种查询条件组合起来,合并查询条件。
例如:
bool
function_score
二、查询全部
查询语法基本格式:
GET /索引库名称/_search
{
"query": {
"查询类型": {
"查询条件": "条件值"
}
}
}
查询所有的语法示例:
GET /indexName/_search
{
"query": {
"match_all": {
}
}
}
这里有两个注意点:
1.查询类型为match_all
2.{}中并没有查询条件
三、根据查询内容全文检索
常见的全文检索查询有以下两种:
match
查询:单字段查询multi_match
查询:多字段查询,任意一个字段符合条件就算符合查询条件
3.1 match查询
match查询语法格式:
GET /索引库名称/_search
{
"query": {
"match": {
"查询字段": "字段值"
}
}
}
match查询语法示例:
GET /indexName/_search
{
"query": {
"match": {
"all": "猪蹄"
}
}
}
3.2 multi_match查询
multi_match查询语法格式:
GET /索引库名称/_search
{
"query": {
"multi_match": {
"查询条件": "条件值",
"查询字段": "字段值列表"
}
}
}
multi_match查询语法示例:
GET /indexName/_search
{
"query": {
"multi_match": {
"query": "猪蹄",
"fields": ["brand", "name", "business"]
}
}
}
这种查询其实就相当于一种全文模糊查询。
实际使用时,不推荐使用第二种方式进行全文检索,因为字段过多会降低查询效率。
可以把要查询的几个字段拷贝到一个字段当中,然后只检索那一个字段就可以了。
四、精确查询
精确查询一般是查找keyword
、数值、日期、boolean
等类型字段,并不会对搜索条件分词。
常见的两种精确查询有:
term
:根据词条精确值查询range
:根据值的范围查询,可以是数值、日期的范围
4.1 term查询
因为精确查询的字段搜是不分词的字段,因此查询的条件也必须是不分词的词条。
查询时,用户输入的内容跟自动值完全匹配时才认为符合条件。如果用户输入的内容过多,反而搜索不到数据。
term查询语法示例:
GET /indexName/_search
{
"query": {
"term": {
"city": {
"value": "广州"
},
}
}
}
注意:
当搜索的内容不是词条,而是多个词语形成的短语时,反而搜索不到。
4.2 range查询
范围查询,一般应用在对数值类型做范围过滤的时候。比如做价格范围过滤或者日期范围过滤。
range查询语法示例:
GET /indexName/_search
{
"query": {
"range": {
"price": {
"gte": 10,
"lte": 20
}
}
}
}
gte代表大于等于,gt则代表大于
lte代表小于等于,lt则代表小于
五、地理坐标查询
地理坐标查询,其实就是根据经纬度查询。
官方文档:
https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-queries.html
常见的使用场景包括:
- 搜索我附近的酒店
- 搜索我附近的出租车
- 搜索我附近的人
5.1 矩形范围查询
矩形范围查询,也就是geo_bounding_box
查询,查询坐标落在某个矩形范围的所有文档。
查询时,需要指定矩形的左上、右下两个点的坐标,然后画出一个矩形,落在该矩形内的都是符合条件的点。
geo_bounding_box查询语法示例:
GET /indexName/_search
{
"query": {
"geo_bounding_box": {
"location": {
"top_left": {
"lat": 31.1,
"lon": 121.5
},
"bottom_right": {
"lat": 30.9,
"lon": 121.7
}
}
}
}
}
location是字段名称,是一种经纬度格式:31.21,121.5。如果location在矩形范围内则满足要求。
top_left:矩形左上角点位
bottom_right:矩形右下角点位
5.2 附近查询
也叫做距离查询(geo_distance
):查询到指定中心点小于某个距离值的所有文档。
就是在地图上找一个点作为圆心,以指定距离为半径,画一个圆,落在圆内的坐标都算符合条件。
geo_distance查询语法示例:
GET /indexName/_search
{
"query": {
"geo_distance": {
"distance": "15km", // 半径
"location": "31.21,121.5" // 圆心
}
}
}
distance是固定的,代表圆的半径
location是字段名称,代表圆心
这也是更常用的查询,一般更习惯用圆圈去搜索,而不是用矩形范围去检索。
六、布尔查询
布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询。
子查询的组合方式有:
must
:必须匹配每个子查询,类似“与”(参与算分)should
:选择性匹配子查询,类似“或”(参与算分)must_not
:必须不匹配,类似“非”(不参与算分)filter
:必须匹配(不参与算分)
需要注意的是,搜索时,参与打分的字段越多,查询的性能也越差。因此这种多条件查询时,建议这样做:
- 搜索框的关键字搜索,是全文检索查询,使用must查询,参与算分
- 其它过滤条件,采用filter查询。不参与算分
布尔查询语法示例:
GET /indexName/_search
{
"query": {
"bool": {
"must": [
{"term": {"city": "广州" }}
],
"should": [
{"term": {"brand": "皇冠假日" }},
{"term": {"brand": "华美达" }}
],
"must_not": [
{ "range": { "price": { "lte": 500 } }}
],
"filter": [
{ "range": {"score": { "gte": 45 } }}
]
}
}
}
七、搜索结果处理
7.1 排序
elasticsearch
默认是根据相关度算分(_score
)来排序,但是也支持自定义方式对搜索结果排序。
可以排序字段类型有:keyword
类型、数值类型、地理坐标类型、日期类型等。
文档说明:
https://www.elastic.co/guide/en/elasticsearch/reference/current/sort-search-results.html
7.1.1 普通字段排序
keyword
、数值、日期类型排序的语法基本一致。
布尔查询语法示例:
GET /indexName/_search
{
"query": {
"match_all": {} // 查询全部
},
"sort": [
{
"FIELD": "desc" // 排序字段、排序方式ASC、DESC
}
]
}
7.1.2 地理坐标排序
GET /indexName/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"_geo_distance" : {
"location": {
"lat": 31.03,
"lon": 121.61
}, // 位置字段:文档中geo_point类型的字段名、目标坐标点
"order" : "asc", // 排序方式
"unit" : "km" // 排序的距离单位
}
}
]
}
7.1.3 可以获取某个位置经纬度的网站
https://lbs.amap.com/demo/jsapi-v2/example/map/click-to-get-lnglat/
只要在地图上点击想要获取坐标的位置就可以得到该位置的经纬度
7.2 .分页
elasticsearch
默认情况下只返回top10
的数据。如果要查询更多数据就需要修改分页参数了。
elasticsearch
中通过修改from
、size
参数来控制要返回的分页结果:
from
:从第几个文档开始size
:总共查询几个文档
类似于mysql
中的limit ?, ?
7.2.1 基础分页
GET /indexName/_search
{
"query": {
"match_all": {}
},
"from": 0, // 分页开始的位置,默认为0
"size": 10, // 期望获取的文档总数
"sort": [
{"price": "asc"} // 升序排列
]
}
7.2.2 深度分页
假设需要查询990~1000
的数据,可以这么写DSL
语句:
GET /indexName/_search
{
"query": {
"match_all": {}
},
"from": 990, // 分页开始的位置990
"size": 10, // 期望获取的文档总数10
"sort": [
{"price": "asc"} // 升序排列
]
}
但是elasticsearch
内部分页时,必须先查询 0~1000
条,然后截取其中的990 ~ 1000
的这10
文档数据条。
查询前1000
,如果es是单点模式,则没有任何问题。
但是elasticsearch
将来一定是集群,例如集群有5
个节点,要查询TOP1000
的数据,并不是每个节点查询200
条就可以了。
要想获取整个集群的TOP1000
,必须先查询出每个节点的TOP1000
,汇总结果后,重新排名,重新截取TOP1000
。
当查询分页深度较大时,汇总数据过多,对内存和CPU
会产生非常大的压力,因此elasticsearch
会禁止from+ size
超过10000
的请求。
针对深度分页,ES主要提供了两种解决方案:
search after
:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。官方推荐使用的方式。scroll
:原理将排序后的文档id
形成快照,保存在内存。官方已经不推荐使用。
官方文档位置:
https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html
一般来说,业务层面就会避免超出10000条的请求。
如果实在有这种需求,再按照官方文档来解决
7.3 结果高亮
百度,京东搜索时,关键字会变成红色,比较醒目,这叫搜索结果高亮显示。
搜索结果高亮显示的实现分为两步:
- 给文档中的所有关键字都添加一个自定义标签(或者其它约定好的名称),例如
<em>
标签 - 给
<em>
标签编写CSS
样式
结果高亮的语法示例:
GET /indexName/_search
{
"query": {
"match": {
"all": "如家" // 查询条件,高亮一定要使用全文检索查询
}
},
"highlight": {
"fields": {
"name": { // 指定要高亮的字段的名字
"pre_tags": "<em>", // 用来标记高亮字段的前置标签
"post_tags": "</em>" // 用来标记高亮字段的后置标签
}
}
}
}
注意:
- 高亮是对关键字高亮,因此搜索条件必须带有关键字,而不能是范围这样的查询。
- 默认情况下,高亮的字段,必须与搜索指定的字段一致,否则无法高亮
- 如果要对非搜索字段高亮,则需要添加一个属性:
required_field_match=false
这样的话返回的数据会自带高亮标签包裹,前端可以直接进行渲染
八、聚合
聚合(aggregations
)可以让我们极其方便的实现对数据的统计、分析、运算。
elasticsearch
实现统计功能比数据库的sql
要方便的多,而且查询速度非常快,可以实现近实时搜索效果。
8.1 聚合的种类
-
桶(Bucket) 聚合:用来对文档做分组
TermAggregation
:按照文档字段值分组,例如按照品牌值分组、按照国家分组Date Histogram
:按照日期阶梯分组,例如一周为一组,或者一月为一组
-
度量(Metric) 聚合:用以计算一些值,比如:最大值、最小值、平均值等
Avg
:求平均值Max
:求最大值Min
:求最小值Stats
:同时求max
、min
、avg
、sum
等
-
管道(pipeline) 聚合:其它聚合的结果为基础做聚合,并不对文档中的字段进行聚合
能使用聚合的字段一定是不分词的,可以是任何数据类型,但是不可以是
text
类型
8.2 Bucket聚合语法
8.2.1 Bucket聚合语法
GET /indexName/_search
{
"size": 0, // 设置size为0,结果中不包含文档,只包含聚合结果
"aggs": { // 定义聚合
"brandAgg": { // 给聚合起个名字
"terms": { // 聚合的类型,按照品牌值聚合,所以选择term,精确查询
"field": "brand", // 参与聚合的字段
"size": 20 // 希望获取的聚合结果数量
}
}
}
}
8.2.2 限定聚合范围
默认情况下,Bucket
聚合是对索引库的所有文档做聚合,
真实场景下,用户会输入搜索条件,因此聚合必须是对搜索结果聚合。那么聚合必须添加限定条件。
我们可以限定要聚合的文档范围,只要添加query
条件即可:
GET /hotel/_search
{
"query": {
"range": {
"price": {
"lte": 200 // 只对200元以下的文档聚合
}
}
},
"size": 0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 20,
"order": {
"_count": "asc"
}
}
}
}
}
8.3 Metric聚合语法
当要获取每个桶内min
、max
、avg
等值的时候,可以使用Metric
聚合。
GET /hotel/_search
{
"size": 0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 20,
"order": {
"scoreAgg.avg": "desc" // 给聚合结果排序
}
},
"aggs": { // 是brandAgg聚合的子聚合,也就是分组后对每组分别计算
"score_stats": { // 聚合名称
"stats": { // 聚合类型,这里stats可以计算min、max、avg等
"field": "score" // 聚合字段,这里是score
}
}
}
}
}
}
小结:
- 聚合三要素
- 聚合名称
- 聚合类型
- 聚合字段
- 聚合可配置属性
- size:指定聚合结果数量
- order:指定聚合结果排序方式
- field:指定聚合字段
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/116465.html