最重要的查询过滤语句
之前的那篇博客,主要是针对elasticsearch的整理。下面我个人觉得会非常实用。
term过滤
term 主要用于精确匹配哪些值,比如数字,日期,布尔值或not_analyzed的字符串
{“term”:{“age”:26}}
{“term”:{“date”:”2014-09-01”}}
{“term”:{“public”:true}}
{“term”:{“tag”:”full_text”}}
terms过滤
terms跟term有点类似,但terms允许指定多个匹配条件。如果某个字段指定了多个值,那么稳定需要一起去匹配:
{
“terms”:{
“tag”:[“search”,”full_text”,”nosql”]
}
}
range过滤
range过滤允许我们按照指定方位查找一批数据:
{
“range”:{
“age”:{
“gte”:20,
“lt”:30
}
}
}
操作符 | 意思 |
---|---|
gt | 大于 |
gte | 大于等于 |
lt | 小于 |
lte | 小于等于 |
exists和missing过滤
exists和missing过滤可以用于查找稳定中是否包含指定字段或没有某个字段,类似于sql语句中is_null条件
{
“exists”:{
“field”:”titile”
}
}
bool过滤
bool 过滤可以用来合并多个过滤条件查询结果的布尔逻辑,它包含以下操作符:
操作符 | 意思 |
---|---|
must | 多个查询天剑的完全匹配 相当于and |
must_not | 多个查询条件的相反匹配,相当于not |
should | 至少有一个查询条件匹配,相当于or |
{
“bool”:{
“must”:{“term”:{“folder”:”inbox”}},
“must_not”:{“term”:{“tag”:”spam”}},
“should”:[
{“term”:{“starred”:true}},
{“term”:{“unread”:true}}
]
}
}
match_all 查询
使用match_all 可以查询到所有文档,是没有查询条件下的默认语句。
match 查询
match查询是一个标准查询,不管你需要权文博查询还是精确查询基本上都要用到它。
如果你使用match查询一个全文档字段,它会在站长查询之前用分析器先分析match一下查询字符:
{
“match”:{
“tweet”:”About Search”
}
}
multi_match查询
multi_match 查询允许你做match查询的基础上同时搜索多个字段:
{
“mulit_match”:{
“query”:”full text search”,
“fields”:[“title”,”body”]
}
}
bool 查询
bool查询与bool过滤相似,用于合并多个查询子句。不同的是,bool过滤可以直接给出是否匹配成功,而bool查询要计算每一个查询子句的_score(相关性分值)
查询与过滤条件的合并
查询语句和过滤语句可以放在各自的上下文中,在elasticsearch api中我们会看到很多带有query或filter的语句。这些语句既可以包含单挑query语句,也可以包含一条filter子句。换句话说,这些语句需要首先创建一个query或filter的上下文关系。
符合查询语句可以加入其它查询子句,符合过滤语句也可以加入其它过滤子句。通常情况下,一条查询语句需要过滤语句的辅助。全文搜索除外。
所以说,查询语句可以包含过滤子句,反之亦然。以便于我们切换query或filter的上下文。这就要求我们在读懂需求的同时构造正确有效的语句。
带过滤的查询语句
过滤一条查询语句
{
“match”:{
“email”:”business opportunity”
}
}
然后我们让这条语句加入term过滤,在收信箱中匹配邮件:
{
“term”:{
“folder”:”inbox”
}
}
search API中只能保护query语句,所以我们需要用filtered来同时包含”query“和”filter“子句:
{
”filter“:{
“query”:{“match”:{“email”:”business opportunity”}},
“filter”:{“term”:{“filder”:”index”}}
}
}
我们在外层再加入query的上下文:
{
”query“:{
...
}
}
单条过滤语句
在query上下文中,如果你只需要一条过滤语句,比如在匹配全部邮件的时候,你可以省略query子句
GET /_search
{
“query”:{
“filtered”:{
“filter”:{“term”:{“foler”:”inbox”}}
}
}
}
如果一条查询语句没有指定查询方位,那么它默认使用match_all查询,所以上面语句的完整结果如下:
GET /_search
{
“query”:{
“filtered”:{
“query”:{“match_all”:{}},
“filter”:{“term”:{“folder”:”inbox”}}
}
}
}
查询语句中的过滤
GET /_search
{
“query”:{
“filtered”:{
“filter”:{
“bool”:{
“must”:{“term”:{“folder”:”inbox”}},
“must_not”:{
“query”:{
“match”:{“email”:”urgent business proposal”}
}
}
}
}
}
}
}
验证查询
查询语句可以变得非常复杂,特别是与不同的分析器与字段映射相结合后,就会有些难度。
validate API 可以验证一条查询语句是否合法。
GET /gb/tweet/_validate/query
{
“query”:{
.....
}
}
以上请求的返回值告诉我们这条语句的是非法的
{
“valid”:false,
“_shards”:{
“total”:1,
“successful”:1,
“failed”:0
}
}
理解错误信息
相纸的语句非法的具体错误信息,需要加上explain参数:
GET /gb/tweet/_validate/query?explain
....
理解查询语句
如果合法语句的话,使用explain参数可以防护一个带有查询语句的可阅读描述,可以帮助了解查询语句在es中是如何执行的:
{
“valid”:true,
“_shards”:{...},
“explanations”:[{
“index”:”us”,
“valid”:true,
“explanation”:”tweet:really tweet:powerful”
},{
“index”:”gb”,
“valid”:true,
“explanation”:”tweet:really tweet:power”
}]
}
从返回的explanation你会看到match是如何为查询字符串”really powerful“进行查询的,首先,它被拆分成两个独立的分词分别在tweet字段中进行查询。
而且在索引us中这个两个词为really和powerful,在索引gb中被拆分成really和power。这时因为我们在索引gb中使用了english分析器。
相关性排序
默认情况下,结果集会按照相关性进行排序–相关性越高,排名越靠前。
排序方式
为了使结果可以按照相关性进行排序,我们需要一个相关性的值。在elasticsearch的查询结果中,相关性分值会使用_score字段来给出一个浮点型的数值,所以默认情况下,结果集以_score进行排序。
过滤语句与_score 没有关系,但是有隐含的查询条件match_all为所有的文档的_score设值为1。也就相当于所有的文档相关性是相同的。
字段值排序
GET /_search
{
“query”:{
“filter”:{
”filter“:{“term”:{“userid”:1}}
}
},
“sort”:{“date”:{“order”:”desc”}}
}
首先,在每个结果中增加了一个sort字段,它所包含的值是用来排序的。在这个例子中date字段在内部被转为毫秒,即长整型数字1411516800000等同于日期字符串2014-09-24.
其次就是_score和max_score字段都为null。计算_score是比较消耗性能的,而且通过主要用作排序–我们不是用相关性进行排序的时候,即不要统计其相关性。如果你想强制计算其相关性,可以设置track_scores为true。
默认排序
多级排序
如果我们想要合并一个查询语句,并且展示所有匹配的结果集使用第一排序是date,第二排序是_score:
GET /_search
{
“query”:{
“filtered”:{
“query”:{“match”:{“tweet”:”manage text search”}},
“filter”:{“term”:{“user_id”:2}}
}
},
“sort”:[
{“date”:{“order”:”desc}},
{“_score”:{“order”:”desc”}}
]
}
排序是很重要的。结果集会先用第一排序字段来排序,当用用作第一字段排序的值相同的时候,然后在用第二字段对第一排序值相同的文档进行排序,以此类推。
多级排序不需要包含_score–你可以使用几个不同的字段,如位置距离或者自定义数值。
为多值字段排序
在为一个字段的多个值进行排序的时候,其实这些值本来是没有固定排序的–一个拥有多值的字段就是一个集合,你准备以哪一个作为排序依据呢?
对于数字和日期,你可以从多个值中取出一个来进行排序,你可以使用min,max,avg或sum这些模式。比如你可以在dates字段中用最早的日期来进行排序:
“sorts”:{
“dates”:{
“order”:”asc”,
“mode”:”min”
}
}
多值字段字符串排序
被分析器处理过的字符串称为analyzed filed,analyzed字符串同时也是多值字段,在这些字段上排序往往得不到你想要的值。比如你分析一个字符“fine old art”,你最终会得到三个值。例如我们想要按照第一个词首字母排序,如果第一个单词相同的话,再用第二个词的首字母排序,以此类推,可惜elasticsearch在进行排序时 是得不到这些信息的。
当然你可以使用min和max模式来排但它是依据art或者old排序,而不是我们所期望的那样。
为了使一个string字段可以进行排序,它必须只包含一个词:即完整的not_analzyed字符串。当然我们需要对字段进行全文搜索的时候还必须使用被analyzed标记的字段。
在_source 下相同的字符串上排序两次会造成不必要的资源浪费。而我们想要的是同一个字段中同时包含这两种索引方式,我们呢只需要改变索引的mapping即可。
“tweet”:{
“type”:”string”,
“analyzer”:”english”
}
“tweet”:{
“type”:”string”,
“analzyer”:”english”,
“fileds”:{
“raw”:{
“type”:”String”,
“index”:”not_analzyed”
}
}
}
tweet字段用于全文本的analyzed索引方式不变。
新增的tweet.raw子字段索引方式是not_analyzed
现在,再给数据重建索引后,我们既可以使用tweet字段进行全文搜索,也可以用tweet.raw字段进行排序:
GET /_search
{
“query”:{
“match”:{
“tweet”:”elasticsearch”
}
},
“sort”:”tweet.raw”
}
相关性简介
查询语句会为每个文档添加一个_score字段。评分的计算方式取决于不同的查询类型–不同的查询语句用于不同的目的:fuzzy查询会计算与关键词的拼写相似程度,term是查询会计算找到的内容与关键词组成的部分匹配的百分比,但是一般意义上我们说的全文本搜索是指计算内容与关键词的类似程度。
elasticsearch的相似度算法被定义为TF/IDE,即检索词频率/反向文档频率,包括一下内容:
检索词频率:
检索词在该字段出现的频率?出现频率越高,相关性也越高。字段中出现过5次要比只出现过1次的相关性高。
反向文档频率:
每个检索词在索引中出现的频率?频率越高,相关性越低。检索词出现在多数文档中会比出现在少数文档中的权重更低,即检验一个检索词在文档中的普遍重要性。
字段长度准则:
字段的长度是多少?长度越长,相关性越低。检索词出现在一个短的title要比同样的词出现在一个长的content字段。
单个查询可以使用TF/IDF评分标准或其他方式,比如短语查询中检索词的距离或模糊查询里的检索词相似度。
相关性并不只是全文本检索的专利。也适用于yes|no的子句,匹配的子句越多,相关性评分越高。
如果多条查询子句被合并为一条符合查询语句,比如bool查询,则每个查询子句计算得出的评分会被合并到总的相关性评分中。
理解评分标准
当调试一条复制的查询语句时,想要理解相关性评分_score是比较困难的。elasticsearch在每个查询语句中都有一个explain参数,将explain设为true就可以得到更详细的信息。
GET /_search?explain
{
“query”:{“match”:{“tweet”:”honeymoon”}}
}