主要内容
在elasticsearch中搜索数据
保存数据是为了以后可以方便查询,ES提供了强大的搜索API,配合查询表达式,让ES的数据搜索更加简单
search API
_search API提供易用且强大的搜索接口,让数据查询变得简单。
- 查询数据库中的所有数据:
1 2 |
curl -XGET 192.168.11.3:9200/_search?pretty |
如果不指定Index,那么_search会返回所有Index中的数据
- 查询指定Index中的数据
假如数据库中存在名为bank的Index,那么下面的查询会返回所有bank中的数据
1 2 |
curl -XGET 192.168.11.3:9200/bank/_search?pretty |
很明显,_search放置于哪一个Index下面,就返回该Index的数据。
- 多Index查询
假如存在名为megacorp和bank的索引,下面的查询会同时返回两个索引下的数据
1 2 |
curl -XGET 192.168.11.3:9200/megacorp,bank/_search?pretty |
多个索引使用逗号(,
)分隔,索引名称还可以使用通配符(*
),例如
1 2 |
curl -XGET 192.168.11.3:9200/mega*,bank/_search?pretty |
查询表达式
_search API 配合查询表达式使用,可以对返回的数据进行模糊匹配或者精确匹配。查询表达式作为请求体发送给ES。模糊查询使用match
查询,而精确查询使用term
查询。
match_all查询
match_all是最简单的查询表达式, 它匹配所有数据:
1 2 3 4 5 6 |
{ "query": { "match_all": {} } } |
具体使用方法
1 2 3 4 5 6 7 |
[root@centos7-server ~]# curl -XGET 192.168.11.3:9200/bank/_search?pretty -H 'Content-Type:application/json' -d ' { "query": { "match_all": {} } }' |
当然,match_all是最没用的查询条件,它返回的结果和不用查询表达式一样,都是返回全部的数据。
match查询
match只返回和给定条件匹配的数据
1 2 3 4 5 6 7 8 |
{ "query": { "match": { "about": "rock climbing" } } } |
上面的查询条件可能会令人产生怀疑:因为只要about字段中含有rock和climbing两者之一,该数据就会被返回,而不是两者同时存在才被返回。
match_phrase查询
短语匹配,如果要多个短语同时存在才返回数据,那么就需要使用match_phrase查询。下面的查询返回含有rock climbing
短语的数据,且只有它们两者同时存在才返回
1 2 3 4 5 6 7 8 |
{ "query": { "match_phrase": { "about": "rock climbing" } } } |
match_phrase也可以用另一种方式编写
1 2 3 4 5 6 7 8 9 10 |
{ "query": { "match_phrase": { "about": { "query": "rock climbing" } } } } |
multi_match查询
无论是match还是match_phrase,它们都只能针对一个字段进行搜索,如果要从多个字段中进行数据搜索,就要使用multi_match。
1 2 3 4 5 6 7 8 9 10 |
POST /bank/_search { "query": { "multi_match": { "query": "Bennett", "fields": ["employer", "city"] } } } |
上面的查询在employer
和city
中搜索内容Bennett
,只要两个字段其中之一匹配到数据,即可返回。
term/terms查询
这两个查询用于精确查询,查询的值通常是数字、时间和布尔值。
注意:如果对文本类型的字段值不作特殊处理(特殊处理是把把字符串值标记为not_analyzed
),那么term/terms查询对这些文本值无效
我们可以更新一个映射来添加一个新域,但不能将一个存在的域从
analyzed
改为not_analyzed
,如果一个域的映射已经存在,那么该域的数据可能已经被索引。如果你意图修改这个域的映射,索引的数据可能会出错,不能被正常的搜索
not_analyzed
只能在5.0之前的版本中使用,新版本中已经使用keyword
代替。
例如,旧版本:
1 2 3 4 5 6 7 |
{ "foo": { "type": "string", "index": "not_analyzed" } } |
新版本则是:
1 2 3 4 5 6 7 |
{ "foo": { "type": "keyword", "index": true } } |
term用于对单一值进行查询:
返回bank索引中年龄为20的客户
1 2 3 4 5 6 7 8 9 |
POST /bank/_search { "query": { "term":{ "age": 20 } } } |
terms是term的复数形式,因此terms查询可以提交多个值:
下面的查询返回bank索引中年龄为20和30的客户
1 2 3 4 5 6 7 8 9 |
POST /bank/_search { "query": { "terms": { "age": ["20", "25"] } } } |
range查询
range用于查找某个范围内的数据,例如查找bank索引中年龄介于20~30之间的客户资料
1 2 3 4 5 6 7 8 9 10 11 12 |
POST /bank/_search { "query":{ "range": { "age": { "gte": 20, # grater than or equel 20 "lte": 30 #less then or equal 30 } } } } |
组合多查询
elasticsearch允许组合多个条件进行查询,这是通过bool查询来实现的,bool查询接收以下参数:
- must 返回的文档必须匹配这些条件
- must_not 返回的文档必须不匹配这些条件
- should 对返回的结果增加_score,用于修正文档的相关性得分
- filter 返回的文档必须匹配条件
下面的查询用于查找 title
字段匹配 how to make millions
并且不被标识为 spam
的文档。那些被标识为 starred
或在2014之后的文档,将比另外那些文档拥有更高的排名。如果 两者 都满足,那么它排名将更高:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "query": { "bool": { "must": { "match": { "title": "how to make millions" }}, "must_not": { "match": { "tag": "spam" }}, "should": [ { "match": { "tag": "starred" }}, { "range": { "date": { "gte": "2014-01-01" }}} ] } } } |
过滤器
如果我们不想因为文档的时间而影响得分,可以用 filter
语句来重写前面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "query": { "bool": { "must": { "match": { "title": "how to make millions" }}, "must_not": { "match": { "tag": "spam" }}, "should": [ { "match": { "tag": "starred" }} ], "filter": { "range": { "date": { "gte": "2014-01-01" }} } } } } |
通过将 range 查询移到 filter
语句中,我们将它转成不评分的查询,将不再影响文档的相关性排名。由于它现在是一个不评分的查询,可以使用各种对 filter 查询有效的优化手段来提升性能。
如果你需要通过多个不同的标准来过滤你的文档,bool
查询本身也可以被用做不评分的查询。简单地将它放置到 filter
语句中并在内部构建布尔逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
{ "query": { "bool": { "must": { "match": { "title": "how to make millions" }}, "must_not": { "match": { "tag": "spam" }}, "should": [ { "match": { "tag": "starred" }} ], "filter": { "bool": { "must": [ { "range": { "date": { "gte": "2014-01-01" }}}, { "range": { "price": { "lte": 29.99 }}} ], "must_not": [ { "term": { "category": "ebooks" }} ] } } } } } |
可以看到,filter和term查询一样,作用域基本上是数字、时间和布尔值,还有那些被标记为not_analyzed
的字段
过滤器并非必须和must、must_not、should等一起使用,也可以单独
使用:
1 2 3 4 5 6 7 8 9 10 11 |
{ "query": { "bool": { "filter":{ "term": {"price":20} } } } } |
constant_score查询和过滤器
constant_score用于把一个评分查询转换为非评分查询,constant_score下必须包含filter。
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "query" : { "constant_score" : { "filter" : { "term" : { "price" : 20 } } } } } |
使用boost提升权重
我们可以通过指定 boost
来控制任何查询语句的相对的权重, boost
的默认值为 1
,大于 1
会提升一个语句的相对权重。
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 |
{ "query": { "bool": { "must": { "match": { # 默认权重为1 "content": { "query": "full text search", "operator": "and" } } }, "should": [ { "match": { "content": { "query": "Elasticsearch", "boost": 3 # 权重最高 } }}, { "match": { "content": { "query": "Lucene", "boost": 2 # 权重比boost为3的低 } }} ] } } } |
查询(query)和过滤(filtering)
查询和过滤的相同之处在于,它们都会对数据进行匹配。不同的是,查询会对结果产生一个匹配程度,可以通过结果中的_score
字段查看这个值;过滤则只有匹配和不匹配,没有权重的概念。
除此之外,它们之间的性能会有一定的差距,以下内容摘自官方文档
过滤查询(Filtering queries)只是简单的检查包含或者排除,这就使得计算起来非常快。考虑到至少有一个过滤查询(filtering query)的结果是 “稀少的”(很少匹配的文档),并且经常使用不评分查询(non-scoring queries),结果会被缓存到内存中以便快速读取,所以有各种各样的手段来优化查询结果。
相反,评分查询(scoring queries)不仅仅要找出 匹配的文档,还要计算每个匹配文档的相关性,计算相关性使得它们比不评分查询费力的多。同时,查询结果并不缓存。
多亏倒排索引(inverted index),一个简单的评分查询在匹配少量文档时可能与一个涵盖百万文档的filter表现的一样好,甚至会更好。但是在一般情况下,一个filter 会比一个评分的query性能更优异,并且每次都表现的很稳定。
过滤(filtering)的目标是减少那些需要通过评分查询(scoring queries)进行检查的文档。
查询和过滤的选择
下面的内容同样摘自官方文档
通常的规则是,使用 查询(query)语句来进行 全文 搜索或者其它任何需要影响 相关性得分 的搜索。除此以外的情况都使用过滤(filters)。
查询通常都是对结果的模糊匹配,过滤的目的是对查询返回的结果进行更精确的筛选。
举个例子:当我们使用搜索引擎的时候,会输入特定的关键字,然后搜索引擎会根据相关性返回结果,这些返回的结果中我们可以根据需要选择搜索类型是图片、视频还是新闻等等,后面这个过程就类似过滤。