简单写一个ES系列,希望对NewSql、检索有兴趣的同学可以快速入手,争取在7月份完成该系列文章
ES入门系列
# 查询在 tweet 类型中 tweet 字段包含 elasticsearch 单词的所有文档
GET /_all/tweet/_search?q=tweet:elasticsearch
# 上面等同于
GET /_all/tweet/_search?q=+name:john +tweet:mary
# 下面语句搜索返回包含 mary 的所有文档。对 _all 字段进行查找,除非设置特定字段,否则查询字符串就使# # 用 _all 字段进行搜索
GET GET /_search?q=mary
当索引一个文档的时候,Elasticsearch 取出所有字段的值拼接成一个大的字符串,作为 _all
字段进行索引。例如,当索引这个文档时:
{
"tweet": "However did I manage before Elasticsearch?",
"date": "2014-09-14",
"name": "Mary Jones",
"user_id": 1
}
这就好似增加了一个名叫 _all
的额外字段:
"However did I manage before Elasticsearch? 2014-09-14 Mary Jones 1"
注意:刚开始开发一个应用时,_all
字段是一个很实用的特性。之后,你会发现如果搜索时用指定字段来代替 _all
字段,将会更好控制搜索结果。当 _all
字段不再有用的时候,可以将它置为失效
查询字符串搜索允许任何用户在索引的任意字段上执行可能较慢且重量级的查询,这可能会暴露隐私信息,甚至将集群拖垮。因为这些原因,不推荐直接向用户暴露查询字符串搜索功能,除非对于集群和数据来说非常信任他们。相反,我们经常在生产环境中更多地使用功能全面的 request body 查询API,除了能完成以上所有功能,还有一些附加功能。
Elasticsearch 使用的查询语言(DSL)拥有一套查询组件,这些组件可以以无限组合的方式进行搭配。包括查询模式和过滤模式
查询模式:查询就变成了一个“评分”的查询。和不评分的查询类似,也要去判断这个文档是否匹配,同时它还需要判断这个文档匹配的有 多好(匹配程度如何)。匹配程度越好评分越高,评分查询计算每一个文档与此查询的 相关程度,同时将这个相关程度分配给表示相关性的字段 _score
,并且按照相关性对匹配到的文档进行排序。
过滤模式:查询被设置成一个“不评分”或者“过滤”查询。即,这个查询只是简单的问一个问题:“这篇文档是否匹配?”。回答也是非常的简单,yes 或者 no ,二者必居其一。
一旦组成了词项列表,这个查询会对每个词项逐一执行底层的查询,再将结果合并,然后为每个文档生成一个最终的相关度评分。
GET /_search
{
}
# 等同于下面查询
GET /_search
{
"query": {
"match_all": {}
}
}
# 1、典型结构
{
QUERY_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
# 2、如果针对某个字段
{
QUERY_NAME: {
FIELD_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
}
eg :
GET /_search
{
"match": {
"tweet": "elasticsearch"
}
}
# 上面查询完整结构如下:
GET /_search
{
"query": {
"match": {
"tweet": "elasticsearch"
}
}
}
查询语句(Query clauses) 就像一些简单的组合块,这些组合块可以彼此之间合并组成更复杂的查询。这些语句可以是如下形式:
match
语句) 被用于将查询字符串和一个字段(或者多个字段)对比。bool
语句 允许在你需要的时候组合其它语句,无论是 must
匹配、 must_not
匹配还是 should
匹配,同时它可以包含不评分的过滤器(filters)复合查询列子:
Get /_search
{
"bool": {
"must": { "match": { "tweet": "elasticsearch" }},
"must_not": { "match": { "name": "mary" }},
"should": { "match": { "tweet": "full text" }},
"filter": { "range": { "age" : { "gt" : 30 }} }
}
}
所有查询会或多或少的执行相关度计算,但不是所有查询都有分析阶段。和一些特殊的完全不会对文本进行操作的查询(如 bool
或 function_score
)不同,文本查询可以划分成两大家族:
基于词项的查询
如 term
或 fuzzy
这样的底层查询不需要分析阶段,它们对单个词项进行操作。用 term
查询词项 Foo
只要在倒排索引中查找 准确词项 ,并且用 TF/IDF 算法为每个包含该词项的文档计算相关度评分 _score
。
记住 term
查询只对倒排索引的词项精确匹配,这点很重要,它不会对词的多样性进行处理(如, foo
或 FOO
)。这里,无须考虑词项是如何存入索引的。如果是将 ["Foo","Bar"]
索引存入一个不分析的( not_analyzed
)包含精确值的字段,或者将 Foo Bar
索引到一个带有 whitespace
空格分析器的字段,两者的结果都会是在倒排索引中有 Foo
和 Bar
这两个词。
基于全文的查询
像 match
或 query_string
这样的查询是高层查询,它们了解字段映射的信息:
日期(date)
或 整数(integer)
字段,它们会将查询字符串分别作为日期或整数对待。not_analyzed
)未分析的精确值字符串字段,它们会将整个查询字符串作为单个词项对待。analyzed
)已分析的全文字段,它们会先将查询字符串传递到一个合适的分析器,然后生成一个供查询的词项列表
match_all 查询简单的匹配所有文档,在没有指定查询方式时,它是默认的查询
{
"match_all": { }
}
match
查询是你可用的标准查询。如果你在一个全文字段上使用 match
查询,在执行查询前,它将用正确的分析器去分析查询字符串;如果在一个精确值的字段上使用它,例如数字、日期、布尔或者一个 not_analyzed
字符串字段,那么它将会精确匹配给定的值。
# 全文字符串
{ "match": { "tweet": "About Search" }}
# 精确值,例如数字、日期、布尔或者一个 not_analyzed 字符串字段
{ "match": { "age": 26 }}
{ "match": { "date": "2014-09-01" }}
{ "match": { "public": true }}
{ "match": { "tag": "full_text" }}
match_all 查询可以在多个字段上执行相同的 match 查询
{
"multi_match": {
"query": "full text search",
"fields": [ "title", "body" ]
}
}
range
查询找出那些落在指定区间内的数字或者时间
gt
:大于gte
:大于等于lt
:小于lte
:小于等于{
"range": {
"age": {
"gte": 20,
"lt": 30
}
}
}
term 查询被用于精确值匹配,这些精确值可能是数字、时间、布尔或者那些 not_analyzed 的字符串
{ "term": { "age": 26 }}
{ "term": { "date": "2014-09-01" }}
{ "term": { "public": true }}
{ "term": { "tag": "full_text" }}
注意:
term
和terms
是 包含(contains) 操作,而非 等值(equals) (判断)。说明一下:terms
查询好比是term
查询的复数形式,多个term查询词项,或关系,只有命中其中一个term查询项,及满足
#比如下面查询体
{ "term" : { "tags" : "search" } }
#可以匹配到下面两个文档,因为term或者terms是查询是基于包含关系
{ "tags" : ["search"] }
{ "tags" : ["search", "open_source"] }
exists
查询和 missing
查询被用于查找那些指定字段中有值 (exists
) 或无值 (missing
) 的文档。这与SQL中的 IS_NULL
(missing
) 和 NOT IS_NULL
(exists
) 在本质上具有共性
{
"exists": {
"field": "title"
}
}
你可以用 bool
查询来实现你的需求。这种查询将多查询组合在一起,成为用户自己想要的布尔查询。它接收以下参数:
must
:文档 必须 匹配这些条件才能被包含进来。must_not
:文档 必须不 匹配这些条件才能被包含进来。should
:如果满足这些语句中的任意语句,将增加 _score
,否则,无任何影响。它们主要用于修正每个文档的相关性得分。filter
:必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。记住:如果没有
must
语句,那么至少需要能够匹配其中的一条should
语句。但如果存在至少一条must
语句,则对should
语句的匹配没有要求。
下面的查询用于查找 title
字段匹配 how to make millions
并且tag不为 spam
的文档。那些tag为 starred
或在2014之后的文档,将比另外那些文档拥有更高的排名。如果 两者 都满足,那么它排名将更高
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }},
{ "range": { "date": { "gte": "2014-01-01" }}}
]
}
}
上面例子,如果我们不想因为文档的时间而影响得分,可以用 filter
语句来重写上面的例子
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }}
],
"filter": {
"range": { "date": { "gte": "2014-01-01" }}
}
}
}
上面例子,如果你需要通过多个不同的标准来过滤你的文档,bool
查询本身也可以被用做不评分的查询。简单地将它放置到 filter
语句中并在内部构建布尔逻辑
{
"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" }}
]
}
}
}
}
constant_score
查询也是你工具箱里有用的查询工具。它将一个不变的常量评分应用于所有匹配的文档。它被经常用于你只需要执行一个 filter 而没有其它查询(例如,评分查询)的情况下。可以使用它来取代只有 filter 语句的 bool
查询。在性能上是完全相同的,但对于提高查询简洁性和清晰度有很大帮助
# term 查询被放置在 constant_score 中,转成不评分的 filter。这种方式可以用来取代只有 filter 语句的 bool 查询
{
"constant_score": {
"filter": {
"term": { "category": "ebooks" }
}
}
}
operator
match
查询还可以接受 operator
操作符作为输入参数,默认情况下该操作符是 or,也可以改成and
#下面查询必须同时命中brown、dog才会命中查询
GET /my_index/my_type/_search
{
"query": {
"match": {
"title": {
"query": "BROWN DOG!",
"operator": "and"
}
}
}
}
minimum_should_match
match
查询支持 minimum_should_match
最小匹配参数,这让我们可以指定必须匹配的词项数用来表示一个文档是否相关。我们可以将其设置为某个具体数字,更常用的做法是将其设置为一个百分数。minimum_should_match是向下取整,eg: minimum_should_match=75%,查询词项3个,则需要命中 3 * 75% = 2.25 ==> 向下取整等于2,既需要命中2个此项
#下面查询需要命中2个词项才认为命中
GET /my_index/my_type/_search
{
"query": {
"match": {
"title": {
"query": "quick brown dog",
"minimum_should_match": "75%"
}
}
}
}
boost
可以用来控制任何查询语句的相对的权重,默认值1。boost
参数被用来提升一个语句的相对权重( boost
值大于 1
)或降低相对权重( boost
值处于 0
到 1
之间),但是这种提升或降低并不是线性的,换句话说,如果一个 boost
值为 2
,并不能获得两倍的评分 _score
。
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
}
}}
]
}
}
}
#假设mytype类型的title字段,使用默认的 standard 标准分析器,返回词项 foxes
GET /my_index/_analyze
{
"field": "my_type.title",
"text": "Foxes"
}
#假设mytype类型的english_title字段,使用默认的 english 标准分析器,返回词项 fox
GET /my_index/_analyze
{
"field": "my_type.english_title",
"text": "Foxes"
}
req:
GET /gb/tweet/_validate/query
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
resp:
{
"valid" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
}
}
对于不合法查询,explanations 给出不合法的具体信息;对于合法查询,使用 explain
参数将返回可读的描述,我们查询的每一个 index 都会返回对应的 explanation
,因为每一个 index 都有自己的映射和分析器。
eg1 不合法查询:
GET /gb/tweet/_validate/query?explain
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
# 下面返回告诉:我们将查询类型(match)与字段名称 (tweet)搞混了
{
"valid" : false,
"_shards" : { ... },
"explanations" : [ {
"index" : "gb",
"valid" : false,
"error" : "org.elasticsearch.index.query.QueryParsingException:
[gb] No query registered for [tweet]"
} ]
}
eg2 合法查询:
GET /_validate/query?explain
{
"query": {
"match" : {
"tweet" : "really powerful"
}
}
}
# 下面返回,从 explanation 中可以看出,匹配 really powerful 的 match 查询被重写为两个针对 tweet # 字段的 single-term 查询,一个single-term查询对应查询字符串分出来的一个term。
# 当然,对于索引 us ,这两个 term 分别是 really 和 powerful ,而对于索引 gb ,term 则分别是 # realli 和 power 。之所以出现这个情况,是由于我们将索引 gb 中 tweet 字段的分析器修改为 english # 分析器。
{
"valid" : true,
"_shards" : { ... },
"explanations" : [ {
"index" : "us",
"valid" : true,
"explanation" : "tweet:really tweet:powerful"
}, {
"index" : "gb",
"valid" : true,
"explanation" : "tweet:realli tweet:power"
} ]
}
==========
待更新
==========