在ES中最重要的操作就是查询,上篇文章"ES中文档的基本操作"中,我们讨论了ES中对文档的一些常用操作,对于查询只是简单的描述了使用文档id进行查询的场景,然而在工作过程中,更多的场景是一些复杂的条件检索。接下来的这篇文章,我们就一起学习一下ES中的一些常用的检索方式。
什么是全文
ES相比其他数据库,比较擅长的是全文检索,在介绍全文检索前,我们有必要了解一下什么是全文,以及和全文相对应的精确值。
精确值:如它们听起来那样精确。例如日期或者用户 ID,但字符串也可以表示精确值,例如用户名或邮箱地址。对于精确值来说,Foo 和 foo 是不同的,2014 和 2014-09-15 也是不同的。
全文:是指文本数据(通常以人类容易识别的语言书写),例如一个推文的内容或一封邮件的内容。
查询分类
在ES中根据查询对象的类型可以分为:全文检索 和 精确值查询。
全文检索: 主要针对字段类型为String的数据进行检索。String类型的数据,在写入到ES中的过程中,会被分析器分析,分词,然后建立倒排索引,在进行查询的时候,也会对查询关键语句进行分词,然后对满足任意分词的文档进行返回。
精确查询: 主要针对非String类型的数据进行查询,也就是等值查询,只有完全包含被查询关键词的文档文档才会被返回。
按照查询条件和查询返回结果的匹配程度,查询可以分为:带评分的查询和不带评分的查询:
过滤: 不带评分的查询,又称为过滤,当使用过滤查询时,查询被设置成一个“不评分”或者“过滤”查询。即,这个查询只是简单的问一个问题:“这篇文档是否匹配?”。回答也是非常的简单,yes 或者 no ,二者必居其一。
查询: 在进行全文查询时,和不评分的查询类似,也要去判断这个文档是否匹配,同时它还需要判断这个文档匹配的有多好(匹配程度如何)。匹配程度使用score值作为评判依据。 此查询的典型用法是用于查找以下文档:查找与 full text search 这个词语最佳匹配的文档,包含 run 这个词,也能匹配 runs 、 running 、 jog 或者 sprint。
查询常用语法
match: 可以进行全文检索也可以进行精确查询,因为此次查询是全文检索还是精确查询,取决于被查询的字段的类型。如果被查询的字段是全文字段,则进行全文查询,也即使对查询字符串进行分词处理后,在进行查询。如果被查询的字段是精确字段,则进行精确查询。
term: 查询只进行精确查询,也即是不对查询字符串进行分词处理,将查询字符串作为一个整体进行查询。
terms: 是对term进行了扩展,terms可以对一个字段进行多值查询,也就是查询字符串是一个数组,可以实现一次查询多个查询字符串的效果,而term一次只能查询一个查询字符串。
复杂查询,多条件联合查询:
must: 文档 必须 匹配这些条件才能被包含进来。形如:
"must": { "match": { "title": "how to make millions" }}
must_not: 文档 必须不 匹配这些条件才能被包含进来。形如:
"must_not": { "match": { "tag": "spam" }}
should: 如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分。也就是说,should 的条件不会影响查询返回的结果的数量,只会影响返回结果的score值。形如:
"should": [
{ "match": { "tag": "starred" }},
{ "range": { "date": { "gte": "2014-01-01" }}}
]
filter: 必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。形如:
"filter": {
"range": { "date": { "gte": "2014-01-01" }}
}
bool:使用bool可以组合多个查询,被组合的条件之间是与的关系。
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }},
{ "range": { "date": { "gte": "2014-01-01" }}}
]
}
}
总结来说,查询可以按照两个维度进行划分:是否进行分词,是否支持评分。
match 对于字符串域进行查询,支持分词和评分。而对于非字符串域,支持评分,不支持分词。
term 对于字符串域进行查询,不支持分词,支持评分,对于非字符串域,支持评分,不支持分词。
filter 对于字符串域进行查询,支持分词不支持评分,对于非字符串域,不支持评分,不支持分词。
查询验证
对于比较复杂的查询,查询语句可能会比较复杂,对于其中可能存在的语法错误,查询执行流程可能会比较难以理解,对于这个问题,ES提供了两个辅助校验的api:_validate 和 explain。
_validate 主要用来验证查询是否合法
GET /megacorp/employee/_validate/query
{
"query":{
"bool" : {
"must":{"match" : {"about" : "rock climbing"}},
"should": [
{"match":{"age":15}},
{"match": {"first_name": "John"}}
]
}
}
}
响应结果如下:
{
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"valid" : true
}
字段 vaild用来标识 验证是否通过。
_explain: 主要用来洞察内部查询流程,类似于mysql的explain。
如下,对about进行全文检索,会将"rock climbing"进行分词处理,分成"rock"和"climbing",然后对两个词进行查询。
GET /megacorp/employee/_validate/query?explain
{
"query":{
"match" : {"about" : "rock climbing"}
}
}
响应结果如下:
{
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"valid" : true,
"explanations" : [
{
"index" : "megacorp",
"valid" : true,
"explanation" : "+(about:rock about:climbing) #*:*"
}
]
}
2.使用term进行精确查找,不对"rock climbing"进行分词处理,而是作为一个整体进行查询:
GET /megacorp/employee/_validate/query?explain
{
"query":{
"term": {"about" : "rock climbing"}
}
}
响应结果如下:
{
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"valid" : true,
"explanations" : [
{
"index" : "megacorp",
"valid" : true,
"explanation" : "+about:rock climbing #*:*"
}
]
}
3.filter查询不对查询返回结果进行评分优化,但是会对查询字符串进行分词处理。
GET /megacorp/employee/_validate/query?explain
{
"query":{
"bool": {
"filter": {"match": {"about" : "rock climbing"}}
}
}
}
响应结果如下:
{
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"valid" : true,
"explanations" : [
{
"index" : "megacorp",
"valid" : true,
"explanation" : "+(#(about:rock about:climbing)) #*:*"
}
]
}
从返回结果可以看出和1的差异在于filter的返回结果带有"#"。