如何正确使用 ES 搜索引擎——查询
Elasticsearch 是一个使用 Java 编写的开源搜索引擎,建立在全文搜索引擎库 Apache Lucene™ 基础之上,它的内部使用 Lucene 做索引与搜索,但隐藏了 Lucene 的复杂性,取而代之的是将所有功能打包成一个单独的服务,可以通过程序与它提供的一套简单的 RESTful API 进行通信。
查询表达式(query)
查询表达式(Query DSL)是一种非常灵活的查询语言,es 使用它以简单的 json 接口来展现 Lucene 的绝大部分功能,要使用这种查询表达式,只需将查询语句传递给 query
参数:
1 |
|
query DSL
在查询上下文中,查询会回答这个问题——“这个文档匹不匹配这个查询,它的相关度高吗?”
如何验证匹配很好理解,如何计算相关度呢?es 中索引的数据都会存储一个 _score 分值,分值越高就代表越匹配。另外关于某个搜索的分值计算还是很复杂的,因此也需要一定的时间。
filter DSL
在过滤器上下文中,查询会回答这个问题——“这个文档匹不匹配?”
答案很简单,是或者不是。它不会去计算任何分值,也不会关心返回的排序问题,因此效率会高一点。另外,经常使用过滤器,es 会自动的缓存过滤器的内容,会提高很多查询的性能。
查询请求结果
hits
返回结果中最重要的部分是 hits
,它包含 total
字段来表示匹配到的文档总数,并且一个 hits
数组包含所查询结果的前十个文档。在 hits
数组中每个结果包含文档的 _index
、 _type
、 _id
,加上 _source
字段,这意味着我们可以直接从返回的搜索结果中使用整个文档,而不像其他的搜索引擎,仅仅返回文档的 ID。每个结果还有一个 _score
,它衡量了文档与查询的匹配程度,max_score
表示与查询所匹配文档的 _score
的最大值。
took
took
值告诉我们执行整个搜索请求耗费了多少 毫秒。
timeout
timed_out
值告诉我们查询是否超时。默认情况下,搜索请求不会超时。如果低响应时间比完成结果更重要,你可以指定 timeout
为 10ms 或者 1s。在请求超时之前,es 将会返回已经成功从每个分片获取的结果。
shards
_shards
部分告诉我们在查询中参与分片的总数,以及这些分片成功了多少个失败了多少个。
1 |
|
多索引,多类型
/_search
在所有的索引中搜索所有的类型
/gb/_search
在
gb
索引中搜索所有的类型/gb,us/_search
在
gb
和us
索引中搜索所有的文档/g*,u*/_search
在任何以
g
或者u
开头的索引中搜索所有的类型/gb/user/_search
在
gb
索引中搜索user
类型/gb,us/user,tweet/_search
在
gb
和us
索引中搜索user
和tweet
类型/_all/user,tweet/_search
在所有的索引中搜索
user
和tweet
类型
分页(from、size)
from
显示应该跳过的初始结果数量,默认是
0
size
显示应该返回的结果数量,默认是
10
可以同时使用 from
和 size
参数来分页:
1 |
|
精确值查找(term,terms)
查找单个精确值
可以用 term
处理数字、布尔值、日期、以及文本。
1 |
|
查找多个精确值
term
查询对于查找单个值非常有用,但如果我们想要搜索多个值该如何处理呢?不需要使用多个 term
查询,我们只要用单个 terms
查询,terms
允许指定多个匹配条件,除此之外它几乎与 term
的使用方式一模一样,我们只要将 term
字段的值改为数组即可:
它几乎与 term
的使用方式一模一样,与指定单个价格不同,我们只要将 term
字段的值改为数组即可:
1 |
|
当进行精确值查找时,请尽可能多的使用 过滤式查询(filters),过滤器很重要,因为它们执行速度非常快(不会计算相关度,而且可以被缓存)。
当我们不关心 TF/IDF 对搜索结果排序的影响,只想知道一个词是否在某个字段中出现过时,可以使用 constant_score
将 query
查询语句或者 filter
过滤语句包装起来,同时可以使用 boost
指定权重:
1 |
|
范围(range)
range
过滤允许我们按照指定范围查找某个文档,适用于数字、日期和字符串:
1 |
|
可供组合的选项如下:
gt
:>
大于(greater than)lt
:<
小于(less than)gte
:>=
大于等于(greater than or equal to)lte
:<=
小于等于(less than or equal to)
组合过滤器(bool)
布尔过滤器
一个 bool
过滤器可以由三部分组成:
must
所有的语句都必须匹配,与
AND
等价。must_not
所有的语句都不能匹配,与
NOT
等价。should
至少有一个语句要匹配,与
OR
等价。
使用组合过滤器进行查询:
1 |
|
嵌套布尔过滤器
尽管 bool
是一个复合过滤器,可以接受多个子过滤器,但它本身仍然还只是一个过滤器。因此我们可以将一个 bool
过滤器嵌套在其他 bool
过滤器内部,这为我们提供了处理任意复杂布尔逻辑的能力。
全文匹配(match、match_all、multi_match)
match 查询
匹配查询 match
是一个 标准 查询,无论需要查询什么字段, match
查询都应该会是首选的查询方式。同时它是一个高级 全文查询 ,这表示它既能处理全文字段,又能处理精确字段。
我们用一个示例来解释使用 match
搜索全文字段中的单个词:
1 |
|
可以使用
operator
参数来提高精度,默认情况下该操作符是or
,可以修改成and
让所有指定词项都必须匹配。可以使用
minimum_should_match
最小匹配参数来控制精度,以排除那些不太相关的文档。
match_all 查询
查询所有文档,是没有查询条件下的默认语句:
1 |
|
multi_match 查询
multi_match查询允许你在 match 查询的基础上同时搜索多个字段:
1 |
|
短语匹配(match_phrase
)
就像 match
查询对于标准全文检索是一种最常用的查询一样,当你想寻找邻近的几个单词时,就会使用 match_phrase
查询:
1 |
|
和 match
查询类似,match_phrase
查询首先将查询字符串解析成一个词项列表,然后对这些词项进行搜索,但只保留那些包含 全部 搜索词项,且 位置 与搜索词项相同的文档。比如对于 quick fox
的短语搜索可能不会匹配到任何文档,因为没有文档包含的 quick
词之后紧跟着 fox
。
近似匹配(prefix、wildcards、regexp)
prefix 前缀查询
prefix
查询是一个词级别的底层的查询,它不会在搜索之前分析查询字符串,而是假定传入前缀就正是要查找的前缀。为了找到所有以 W1
开始的邮编,可以使用简单的 prefix
查询:
1 |
|
wildcard 通配符查询
与 prefix
前缀查询的特性类似,wildcard
通配符查询也是一种底层基于词的查询,与前缀查询不同的是它使用标准的 shell
通配符查询,允许匹配指定的正则式,其中:
*
匹配多个任意字符(包括零个或一个)?
匹配一个任意字符(不包括零个)
这个查询会匹配包含 W1F 7HW
和 W2F 8HW
的文档:
1 |
|
regexp 正则表达式查询
假如现在只想匹配 W
区域的所有邮编(只以 W
开始并跟随一个数字),prefix
前缀匹配可能会包括以 WC
开头的所有邮编,wildcard
通配符匹配也可能会遇到同样的问题,如果想匹配,这时可以使用 regexp
正则表达式查询写出更复杂的模式:
1 |
|
处理 Null 值(exists、missing)
存在查询
exists
查询可以用于查找文档中是否包含指定字段:
1 |
|
缺失查询
missing
查询本质上与 exists
恰好相反,它返回没有某个字段的文档:
1 |
|
聚合和过滤(aggs、filter)
agg 聚合
聚合操作被置于顶层参数 aggs
之下,可以为聚合指定一个我们想要名称,然后定义单个桶的类型 terms
。比如汽车经销商可能会想知道哪个颜色的汽车销量最好,用聚合可以轻易得到结果:
1 |
|
在本例中,我们使用 popular_colors
作为聚合的名字,并定义了一个 terms
桶,这个 terms
桶会使用 color
字段为每个颜色动态创建新桶。
filter 过滤
聚合范围限定还有一个自然的扩展就是过滤,因为聚合是在查询结果范围内操作的,任何可以适用于查询的过滤器也可以应用在聚合上。如果我们想找到售价在 $10,000 美元之上的所有汽车同时为这些车计算平均售价,可以简单地使用一个 constant_score
查询和 filter
约束:
1 |
|
avg
为均值度量,min
为最小度量,max
为最大度量。
排序(sort)
简单排序
我们可以简单的使用 sort
参数进行实现按照字段的值排序:
1 |
|
多级排序
假定我们想要结合使用 date
和 _score
进行查询,并且匹配的结果首先按照日期排序,然后按照相关性排序:
1 |
|
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!