Elasticsearch基本语法
ES结构化搜索
结构化搜索(Structured search) 是指有关探询那些具有内在结构数据的过程。比如日期、时间和数字都是结构化的:它们有精确的格式,我们可以对这些格式进行逻辑操作。比较常见的操作包括比较数字或时间的范围,或判定两个值的大小。
文本也可以是结构化的。如彩色笔可以有离散的颜色集合: 红(red) 、 绿(green) 、 蓝(blue) 。一个博客可能被标记了关键词 分布式(distributed) 和 搜索(search) 。电商网站上的商品都有 UPCs(通用产品码 Universal Product Codes)或其他的唯一标识,它们都需要遵从严格规定的、结构化的格式。
在结构化查询中,我们得到的结果 总是 非是即否,要么存于集合之中,要么存在集合之外。结构化查询不关心文件的相关度或评分;它简单的对文档包括或排除处理。
这在逻辑上是能说通的,因为一个数字不能比其他数字 更 适合存于某个相同范围。结果只能是:存于范围之中,抑或反之。同样,对于结构化文本来说,一个值要么相等,要么不等。没有 更似 这种概念。
空搜索
GET /_search
返回:
{ "hits" : { "total" : 14, "hits" : [ { "_index": "us", "_type": "tweet", "_id": "7", "_score": 1, "_source": { "date": "2014-09-17", "name": "John Smith", "tweet": "The Query DSL is really powerful and flexible", "user_id": 2 } }, ... 9 RESULTS REMOVED ... ], "max_score" : 1 }, "took" : 4, "_shards" : { "failed" : 0, "successful" : 10, "total" : 10 }, "timed_out" : false }
说明:
hits
返回结果中最重要的部分是 hits ,它 包含 total 字段来表示匹配到的文档总数,并且一个 hits 数组包含所查询结果的前十个文档。在 hits 数组中每个结果包含文档的 _index 、 _type 、 _id ,加上 _source 字段。这意味着我们可以直接从返回的搜索结果中使用整个文档。这不像其他的搜索引擎,仅仅返回文档的ID,需要你单独去获取文档。每个结果还有一个 _score ,它衡量了文档与查询的匹配程度。默认情况下,首先返回最相关的文档结果,就是说,返回的文档是按照 _score 降序排列的。在这个例子中,我们没有指定任何查询,故所有的文档具有相同的相关性,因此对所有的结果而言 1 是中性的 _score 。max_score 值是与查询所匹配文档的 _score 的最大值。
took
took 值告诉我们执行整个搜索请求耗费了多少毫秒。
shards
_shards 部分 告诉我们在查询中参与分片的总数,以及这些分片成功了多少个失败了多少个。正常情况下我们不希望分片失败,但是分片失败是可能发生的。如果我们遭遇到一种灾难级别的故障,在这个故障中丢失了相同分片的原始数据和副本,那么对这个分片将没有可用副本来对搜索请求作出响应。假若这样,Elasticsearch 将报告这个分片是失败的,但是会继续返回剩余分片的结果。
timeout
timed_out 值告诉我们查询是否超时。默认情况下,搜索请求不会超时。 如果低响应时间比完成结果更重要,你可以指定 timeout 为 10 或者 10ms(10毫秒),或者 1s(1秒):
GET /_search?timeout=10ms
在请求超时之前,Elasticsearch 将会返回已经成功从每个分片获取的结果。
精确值查找
当进行精确值查找时, 我们会使用过滤器(filters)。过滤器很重要,因为它们执行速度非常快,不会计算相关度(直接跳过了整个评分阶段)而且很容易被缓存。
term 查询数字
term 查询, 可以用它处理数字(numbers)、布尔值(Booleans)、日期(dates)以及文本(text)。
PS:中文慎用!!!中文匹配用 match_phrase,constant_score 查询以非评分模式来执行 term 查询并以一作为统一评分
sql: SELECT * FROM table WHERE column=value; es: GET /X/Y/_search { "query" : { "constant_score" : { "filter" : { "term" : { "column" : value } } } } }
#X是索引名 Y是类型
过滤器
布尔过滤器bool
一个 bool 过滤器由三部分组成:
{ "bool" : { "must" : [], "should" : [], "must_not" : [], } }
# must
# 所有的语句都 必须(must) 匹配,与 AND 等价。
# must_not
# 所有的语句都 不能(must not) 匹配,与 NOT 等价。
# should
# 至少有一个语句要匹配,与 OR 等价。
eg1:
sql: SELECT * FROM table WHERE (A= a OR B= b) AND (C!= c); es: GET /X/Y/_search { "query" : { "filtered" : { "filter" : { "bool" : { "should" : [ { "term" : {"A" : a}}, { "term" : {"B" : b}} ], "must_not" : { "term" : {"C" : c} } } } } } }
eg2:
sql: SELECT * FROM table WHERE A= a OR ( B=b AND C=c); eg: GET /X/Y/_search { "query" : { "filtered" : { "filter" : { "bool" : { "should" : [ { "term" : {"A" : a}}, { "bool" : { "must" : [ { "term" : {"B" : b}}, { "term" : {"C" : c}} ] }} ] } } } } }
查找多个精确值
terms 查找
sql: select * from table where A in (a1,a2); es: GET /X/Y/_search { "query" : { "constant_score" : { "filter" : { "terms" : { "A" : [a1, a2] } } } } }
PS:term 和 terms 包含,而不是相等
一定要了解 term 和 terms 是 包含(contains) 操作,而非 等值(equals) (判断)! 精确相等,如果一定期望得到前面说的那种行为(即整个字段完全相等),最好的方式是增加并索引另一个字段
范围
使用 range 查询数字范围的数据
gt: > 大于(greater than) lt: < 小于(less than) gte: >= 大于或等于(greater than or equal to) lte: <= 小于或等于(less than or equal to)
sql: select * from table where A between a1 and a2; es : GET /X/Y/_search { "query" : { "constant_score" : { "filter" : { "range" : { "A" : { "gte" : a1, "lt" : a2 } } } } } }
日期范围
range 查询同样可以应用在日期字段上
# 准确的时间范围 "range" : { "timestamp" : { "gt" : "2019-09-01 00:00:00", "lt" : "2019-09-07 00:00:00" } } # 当前时间一小时内 "range" : { "timestamp" : { "gt" : "now-1h" } } # 查询某个指定的区间,跨度一个月 "range" : { "timestamp" : { "gt" : "2019-09-01 00:00:00", "lt" : "2019-09-01 00:00:00||+1M" } }
字符串范围
range 查询同样可以处理字符串字段, 字符串范围可采用 字典顺序(lexicographically) 或字母顺序(alphabetically)。
"range" : { "string" : { "gte" : "a", "lt" : "b" } }
注意基数,数字和日期字段的索引方式使高效地范围计算成为可能。 但字符串却并非如此,要想对其使用范围过滤,Elasticsearch 实际上是在为范围内的每个词项都执行 term 过滤器,这会比日期或数字的范围过滤慢许多。字符串范围在过滤 低基数(low cardinality) 字段(即只有少量唯一词项)时可以正常工作,但是唯一词项越多,字符串范围的计算会越慢。
处理Null值
null, [] (空数组)和 [null] 所有这些都是等价的
存在查询
使用 exists 查询
sql: select * from table where A is NOT NULL; es: GET /X/Y/_search { "query" : { "constant_score" : { "filter" : { "exists" : { "field" : "A" } } } } }
缺失查询
missing 查询本质上与 exists 恰好相反: 它返回某个特定 _无_ 值字段的文档
sql: select * from table where A is NULL; es: GET /X/Y/_search { "query" : { "constant_score" : { "filter": { "missing" : { "field" : "A" } } } } }
当 null 的意思是 null
有时候我们需要区分一个字段是没有值,还是它已被显式的设置成了 null 。在之前例子中,我们看到的默认的行为是无法做到这点的;数据被丢失了。不过幸运的是,我们可以选择将显式的 null 值替换成我们指定 占位符(placeholder) 。在为字符串(string)、数字(numeric)、布尔值(Boolean)或日期(date)字段指定映射时,同样可以为之设置 null_value 空值,用以处理显式 null 值的情况。不过即使如此,还是会将一个没有值的字段从倒排索引中排除。
当选择合适的 null_value 空值的时候,需要保证以下几点:
1、它会匹配字段的类型,我们不能为一个 date 日期字段设置字符串类型的 null_value 。
2、它必须与普通值不一样,这可以避免把实际值当成 null 空的情况。
对象上缺失和存在
exists and missing 查询 还可以处理一个对象的内部字段
eg1: { "name" : { "first" : "John", "last" : "Smith" } } eg2: { "name.first" : "John", "name.last" : "Smith" } # 存在检查 { "exists" : { "field" : "name" } } # 等价于 { "bool": { "should": [ { "exists": { "field": "name.first" }}, { "exists": { "field": "name.last" }} ] } } # 只有 name.first 和 name.last 都同时为空的时候才有返回
匹配查询
匹配查询 match 是个 核心 查询。无论需要查询什么字段, match 查询都应该会是首选的查询方式。 它是一个高级 全文查询 ,这表示它既能处理全文字段,又能处理精确字段。
单个词查询
GET /X/Y/_search { "query": { "match": { "title": "QUICK!" } } }
Elasticsearch 执行上面这个 match 查询的步骤是:
检查字段类型
标题 title 字段是一个 string 类型( analyzed )已分析的全文字段,这意味着查询字符串本身也应该被分析。
分析查询字符串
将查询的字符串 QUICK! 传入标准分析器中,输出的结果是单个项 quick 。因为只有一个单词项,所以 match 查询执行的是单个底层 term 查询。
查找匹配文档
用 term 查询在倒排索引中查找 quick 然后获取一组包含该项的文档,本例的结果是文档:1、2 和 3 。
为每个文档评分
用 term 查询计算每个文档相关度评分 _score ,这是种将 词频(term frequency,即词 quick 在相关文档的 title 字段中出现的频率)和反向文档频率(inverse document frequency,即词 quick 在所有文档的 title 字段中出现的频率),以及字段的长度(即字段越短相关度越高)相结合的计算方式。
多词查询
使用 match 进行多词查询
GET /X/Y/_search { "query": { "match": { "title": "WORD1 WORD2" } } }
提高精度
不去匹配 WORD1 OR WORD2 ,而通过匹配 WORD1 AND WORD2 找到所有文档。
GET /X/Y/_search { "query": { "match": { "title": { "query": "WORD1 WORD2", "operator": "and" } } } }
控制精度
按百分比精度查找,match 查询支持 minimum_should_match 最小匹配参数, 这让我们可以指定必须匹配的词项数用来表示一个文档是否相关。我们可以将其设置为某个具体数字,更常用的做法是将其设置为一个百分数,因为我们无法控制用户搜索时输入的单词数量
GET /X/Y/_search { "query": { "match": { "title": { "query": "WORD1 WORD2 WORD2", "minimum_should_match": "75%" } } } }
组合查询
与过滤器一样, bool 查询也可以接受 must 、 must_not 和 should 参数下的多个查询语句
# 以下的查询结果返回 A 字段包含词项 WORD1 但不包含 WORD2 的任意文档。目前为止,这与 bool 过滤器的工作方式非常相似,区别就在于两个 should 语句,也就是说:一个文档不必包含 WORD3 或 WORD4 这两个词项,但如果一旦包含,我们就认为它们更相关
GET /X/Y/_search { "query": { "bool": { "must": { "match": { "A": "WORD1" }}, "must_not": { "match": { "A": "WORD2" }}, "should": [ { "match": { "A": "WORD3" }}, { "match": { "A": "WORD4" }} ] } } }
评分计算
bool 查询会为每个文档计算相关度评分 _score , 再将所有匹配的 must 和 should 语句的分数 _score 求和,最后除以 must 和 should 语句的总数。
must_not 语句不会影响评分; 它的作用只是将不相关的文档排除。
控制精度
就像能控制 match 查询的精度 一样,我们可以通过 minimum_should_match 参数控制需要匹配的 should 语句的数量, 它既可以是一个绝对的数字,又可以是个百分比
GET /X/Y/_search { "query": { "bool": { "should": [ { "match": { "A": "WORD1" }}, { "match": { "A": "WORD2" }}, { "match": { "A": "WORD3" }} ], "minimum_should_match": 2 } } }
匹配和布尔匹配
多词 match 查询只是简单地将生成的 term 查询包裹 在一个 bool 查询中。如果使用默认的 or 操作符,每个 term 查询都被当作 should 语句,这样就要求必须至少匹配一条语句。以下两个查询是等价的:
{ "match": { "A": "WORD1 WORD2"} } # 等价于 { "bool": { "should": [ { "term": { "A": "WORD1" }}, { "term": { "A": "WORD2" }} ] } }
如果使用 and 操作符,所有的 term 查询都被当作 must 语句,所以 所有(all) 语句都必须匹配。以下两个查询是等价的:
{ "match": { "A": { "query": "WORD1 WORD2", "operator": "and" } } } #等价于 { "bool": { "must": [ { "term": { "A": "WORD1" }}, { "term": { "A": "WORD2" }} ] } }
如果指定参数 minimum_should_match ,它可以通过 bool 查询直接传递,使以下两个查询等价:
{ "match": { "A": { "query":"WORD1 WORD2 WORD3", "minimum_should_match": "75%" } } } # 等价于 { "bool": { "should": [ { "term": { "A": "WORD1" }}, { "term": { "A": "WORD2" }}, { "term": { "A": "WORD3" }} ], "minimum_should_match": 2 } }
查询语句提示权重
假设想要查询关于 “full-text search(全文搜索)” 的文档, 但我们希望为提及 “Elasticsearch” 或 “Lucene” 的文档给予更高的权重 ,这里更高权重是指如果文档中出现 “Elasticsearch” 或 “Lucene” ,它们会比没有的出现这些词的文档获得更高的相关度评分 _score ,也就是说,它们会出现在结果集的更上面。
一个简单的 bool 查询 允许我们写出如下这种非常复杂的逻辑:
GET /_search { "query": { "bool": { "must": { "match": { "content": { "query": "full text search", "operator": "and" } } }, "should": [ { "match": { "content": "Elasticsearch" }}, { "match": { "content": "Lucene" }} ] } } }
# content 字段必须包含 full 、 text 和 search 所有三个词。
# 如果 content 字段也包含 Elasticsearch 或 Lucene ,文档会获得更高的评分 _score 。
should 语句匹配得越多表示文档的相关度越高。目前为止还挺好。
但是如果我们想让包含 Lucene 的有更高的权重,并且包含 Elasticsearch 的语句比 Lucene 的权重更高,该如何处理?
我们可以通过指定 boost 来控制任何查询语句的相对的权重, boost 的默认值为 1 ,大于 1 会提升一个语句的相对权重。所以下面重写之前的查询:
GET /_search { "query": { "bool": { "must": { "match": { "content": { "query": "full text search", "operator": "and" } } }, "should": [ { "match": { "content": { "query": "Elasticsearch", "boost": 3 } }}, { "match": { "content": { "query": "Lucene", "boost": 2 } }} ] } } }
# 这些语句使用默认的 boost 值 1 。
# Elasticsearch 这条语句更为重要,因为它有最高的 boost 值。
# 这条语句比使用默认值的更重要,但它的重要性不及 Elasticsearch 语句。
dis_max查询
不使用 bool 查询,可以使用 dis_max 即分离 最大化查询(Disjunction Max Query) 。分离(Disjunction)的意思是 或(or) ,这与可以把结合(conjunction)理解成 与(and) 相对应。分离最大化查询(Disjunction Max Query)指的是: 将任何与任一查询匹配的文档作为结果返回,但只将最佳匹配的评分作为查询的评分结果返回 :
{ "query": { "dis_max": { "queries": [ { "match": { "title": "WORD1 WORD2" }}, { "match": { "body": "WORD1 WORD2" }} ] } } }
tie_breaker参数
{ "query": { "dis_max": { "queries": [ { "match": { "title": "WORD1 WORD2" }}, { "match": { "body": "WORD1 WORD2" }} ], "tie_breaker": 0.3 } } }
tie_breaker 参数提供了一种 dis_max 和 bool 之间的折中选择,它的评分方式如下:
1、获得最佳匹配语句的评分 _score 。
2、将其他匹配语句的评分结果与 tie_breaker 相乘。
3、对以上评分求和并规范化。
有了 tie_breaker ,会考虑所有匹配语句,但最佳匹配语句依然占最终结果里的很大一部分。
Tips:tie_breaker 可以是 0 到 1 之间的浮点数,其中 0 代表使用 dis_max 最佳匹配语句的普通逻辑, 1 表示所有匹配语句同等重要。最佳的精确值需要根据数据与查询调试得出,但是合理值应该与零接近(处于 0.1 - 0.4 之间),这样就不会颠覆 dis_max 最佳匹配性质的根本。
多匹配查询
multi_match 查询为能在多个字段上反复执行相同查询提供了一种便捷方式。
{ "dis_max": { "queries": [ { "match": { "title": { "query": "Quick brown fox", "minimum_should_match": "30%" } } }, { "match": { "body": { "query": "Quick brown fox", "minimum_should_match": "30%" } } }, ], "tie_breaker": 0.3 } } # 等价于 { "multi_match": { "query": "Quick brown fox", "type": "best_fields", "fields": [ "title", "body" ], "tie_breaker": 0.3, "minimum_should_match": "30%" } }
模糊匹配
字段名称可以用模糊匹配的方式给出:任何与模糊模式正则匹配的字段都会被包括在搜索条件中, 例如可以使用以下方式同时匹配 book_title 、 chapter_title 和 section_title (书名、章名、节名)这三个字段:
{ "multi_match": { "query": "Quick brown fox", "fields": "*_title" } }
提升单个字段的权重
可以使用 ^ 字符语法为单个字段提升权重,在字段名称的末尾添加 ^boost , 其中 boost 是一个浮点数:
{ "multi_match": { "query": "Quick brown fox", "fields": [ "*_title", "chapter_title^2" ] } }
# chapter_title 这个字段的 boost 值为 2 ,而其他两个字段 book_title 和 section_title 字段的默认 boost 值为 1 。
短语匹配
就像 match 查询对于标准全文检索是一种最常用的查询一样,当你想找到彼此邻近搜索词的查询方法时,就会想到 match_phrase 查询 。
GET /X/Y/_search { "query": { "match_phrase": { "title": "quick brown fox" } } }
# 等价于
"match": {
"title": {
"query": "quick brown fox",
"type": "phrase"
}
}
类似 match 查询, match_phrase 查询首先将查询字符串解析成一个词项列表,然后对这些词项进行搜索,但只保留那些包含 全部 搜索词项,且 位置 与搜索词项相同的文档。 比如对于 quick fox 的短语搜索可能不会匹配到任何文档,因为没有文档包含的 quick 词之后紧跟着 fox 。
混合
精确短语匹配 或许是过于严格了。也许我们想要包含 “quick brown fox” 的文档也能够匹配 “quick fox,” , 尽管情形不完全相同。
我们能够通过使用 slop 参数将灵活度引入短语匹配中:
GET /X/Y/_search { "query": { "match_phrase": { "title": { "query": "quick fox", "slop": 1 } } } }
# slop 参数告诉 match_phrase 查询词条相隔多远时仍然能将文档视为匹配 。 相隔多远的意思是为了让查询和文档匹配你需要移动词条多少次
前缀查询
为了找到所有以 W1 开始的数据,可以使用简单的 prefix 查询:
GET /X/Y/_search { "query": { "prefix": { "column": "W1" } } }
通配符正则查询
与 prefix 前缀查询的特性类似, wildcard 通配符查询也是一种底层基于词的查询, 与前缀查询不同的是它允许指定匹配的正则式。它使用标准的 shell 通配符查询: ? 匹配任意字符, * 匹配 0 或多个字符。
GET /X/Y/_search { "query": { "wildcard": { "column": "W?F*HW" } } }
参考:https://www.elastic.co/guide/cn/elasticsearch/guide/cn/index.html
可视化工具推荐:Kibana