空搜索
搜索API的最基础的形式是没有指定任何查询的空搜索 ,它简单地返回集群中所有索引下的所有文档:
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 数组包含所查询结果的前十个文档。
-
took
took 值告诉我们执行整个搜索请求耗费了多少毫秒。
-
shards
_shards 部分 告诉我们在查询中参与分片的总数,以及这些分片成功了多少个失败了多少个。
-
timeout
timed_out 值告诉我们查询是否超时。默认情况下,搜索请求不会超时。 如果低响应时间比完成结果更重要,你可以指定 timeout 为 10 或者 10ms(10毫秒),或者 1s(1秒)
GET /_search?timeout=10ms
在请求超时之前,Elasticsearch 将会返回已经成功从每个分片获取的结果。应当注意的是 timeout 不是停止执行查询,它仅仅是告知正在协调的节点返回到目前为止收集的结果并且关闭连接。在后台,其他的分片可能仍在执行查询即使是结果已经被发送了。
多索引,多类型
如果不对某一特殊的索引或者类型做限制,就会搜索集群中的所有文档。Elasticsearch 转发搜索请求到每一个主分片或者副本分片,汇集查询出的前10个结果,并且返回给我们。
然而,经常的情况下,你 想在一个或多个特殊的索引并且在一个或者多个特殊的类型中进行搜索。我们可以通过在URL中指定特殊的索引和类型达到这种效果,如下所示:
-
/_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 类型
分页
在之前的 空搜索 中说明了集群中有 14 个文档匹配了(empty)query 。 但是在 hits 数组中只有 10 个文档。如何才能看到其他的文档?
和 SQL 使用 LIMIT 关键字返回单个 page 结果的方法相同,Elasticsearch 接受 from 和 size 参数:
-
size
显示应该返回的结果数量,默认是 10
-
from
显示应该跳过的初始结果数量,默认是 0
如果每页展示 5 条结果,可以用下面方式请求得到 1 到 3 页的结果:
GET /_search?size=5
GET /_search?size=5&from=5
GET /_search?size=5&from=10
-
在分布式系统中深度分页
理解为什么深度分页是有问题的,我们可以假设在一个有 5 个主分片的索引中搜索。 当我们请求结果的第一页(结果从 1 到 10 ),每一个分片产生前 10 的结果,并且返回给 协调节点 ,协调节点对 50 个结果排序得到全部结果的前 10 个。
现在假设我们请求第 1000 页--结果从 10001 到 10010 。所有都以相同的方式工作除了每个分片不得不产生前10010个结果以外。 然后协调节点对全部 50050 个结果排序最后丢弃掉这些结果中的 50040 个结果。
可以看到,在分布式系统中,对结果排序的成本随分页的深度成指数上升。这就是 web 搜索引擎对任何查询都不要返回超过 1000 个结果的原因。
轻量搜索
一种是 “轻量的” 查询字符串 版本,要求在查询字符串中传递所有的 参数。
查询字符串搜索非常适用于通过命令行做即时查询。例如,查询在 tweet 类型中 tweet 字段包含 elasticsearch 单词的所有文档:
GET /_all/tweet/_search?q=tweet:elasticsearch
下一个查询在 name 字段中包含 john 并且在 tweet 字段中包含 mary 的文档。实际的查询就是这样
+name:john +tweet:mary
但是查询字符串参数所需要的 URL编码,实际上更加难懂:
GET /_search?q=%2Bname%3Ajohn+%2Btweet%3Amary
+ 前缀表示必须与查询条件匹配。类似地, - 前缀表示一定不与查询条件匹配。没有 + 或者 - 的所有其他条件都是可选的——匹配的越多,文档就越相关。
- _all字段
这个简单搜索返回包含 mary 的所有文档:
GET /_search?q=mary
之前的例子中,我们在 tweet 和 name 字段中搜索内容。然而,这个查询的结果在三个地方提到了 mary。
Elasticsearch 是如何在三个不同的字段中查找到结果的呢?
当索引一个文档的时候,Elasticsearch 取出所有字段的值拼接成一个大的字符串,作为 _all 字段进行索引。
- 更复杂的查询
下面的查询针对tweents类型,并使用以下的条件:
- name 字段中包含 mary 或者 john
- date 值大于 2014-09-10
- all 字段包含 aggregations 或者 geo
+name:(mary john) +date:>2014-09-10 +(aggregations geo)
查询字符串在做了适当的编码后,可读性很差:
?q=%2Bname%3A(mary+john)+%2Bdate%3A%3E2014-09-10+%2B(aggregations+geo)
不推荐直接向用户暴露查询字符串搜索功能,除非对于集群和数据来说非常信任他们。在生产环境中更多地使用功能全面的 request body 查询API。
参考资料
Elasticsearch: 权威指南