空白搜索
搜索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 个结果被隐藏 ... ], "max_score" : 1 }, "took" : 4, "_shards" : { "failed" : 0, "successful" : 10, "total" : 10 }, "timed_out" : false }
hits
返回内容中最重要的内容就是 hits ,它指明了匹配查询的文档的 总数 , hits 数组里则会包 含前十个匹配文档——也就是搜索结果。
hits 数组中的每一条结果都包含了文档的 _index , _type 以及 _id 信息,以及 _source 字 段。这也就意味着你可以直接从搜索结果中获取到整个文档的内容。这与其他搜索引擎只返 回给你文档编号,还需要自己去获取文档是截然不同的。
每一个元素还拥有一个 _score 字段。这个是相关性评分,这个数值表示当前文档与查询的匹 配程度。通常来说,搜索结果会先返回最匹配的文档,也就是说它们会按照 _score 由高至低进行排列。在这个例子中,我们并没有声明任何查询,因此 _score 就都会返回 1
max_score 数值会显示所有匹配文档中的 _score 的最大值。
took
took 数值告诉我们执行这次搜索请求所耗费的时间有多少毫秒。
shards
_shards 告诉了我们参与查询分片的总数,以及有多少 successful 和 failed 。通常情况下 我们是不会得到失败的反馈,但是有的时候它会发生。如果我们的服务器突然出现了重大事 故,然后我们丢失了同一个分片中主从两个版本的数据。在查询请求中,无法提供可用的备 份。这种情况下,Elasticsearch就会返回`failed提示,但是它还会继续返回剩下的内容。
timeout
timed_out 数值告诉了我们查询是否超时。通常,搜索请求不会超时。如果相比完整的结果 你更需要的是快速的响应时间,这是你可以指定 timeout 值,例如 10 、 "10ms" (10毫秒) 或者 "1s" (1秒钟):
GET /_search?timeout=10ms
Elasticsearch会尽可能地返回你指定时间内它所查到的内容
Timeout并不是终止者
这里应该强调一下 timeout 并不会终止查询,它只是会在你指定的时间内返回当时已经查询 到的数据,然后关闭连接。在后台,其他的查询可能会依旧继续,尽管查询结果已经被返回 了。 使用超时是因为你要保障你的品质,并不是因为你需要终止你的查询。
多索引,多类型
当我们没有特别指定一个索引或者类型的时候,我们将会搜索整个集群中的所有文档。 Elasticsearch会把搜索请求转发给集群中的每一个主从分片,然后按照结果的相关性得到前 十名,并将它们返回给我们。
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 内的所有文档 |
搜索一个拥有五个主分片的索引与搜索五个都只拥有一个主分片是完全一样的。
分页
与SQL使用 LIMIT 来控制单“页”数量类似,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个主 分片的索引。当我们请求第一页搜索的时候,每个分片产生自己前十名,然后将它们返回给 请求节点,然后这个节点会将50条结果重新排序以产生最终的前十名。 现在想想一下我们想获得第1,000页,也就是第10,001到第10,010条结果,与之前同理,每一 个分片都会先产生自己的前10,010名,然后请求节点统一处理这50,050条结果,然后再丢弃 掉其中的50,040条!
现在你应该明白了,在分布式系统中,大页码请求所消耗的系统资源是呈指数式增长的。这 也是为什么网络搜索引擎不会提供超过1,000条搜索结果的原因。
精简 搜索
搜索的API分为两种:其一是通过参数来传递查询的“精简版”查询语句(query string),还有 一种是通过JSON来传达丰富的查询的完整版请求体(request body),这种搜索语言被称为 查询DSL。
查询语句在行命令中运行点对点查询的时候非常实用。比如我想要查询所有 tweet 类型中, 所有 tweet 字段为 "elasticsearch" 的文档:
GET /_all/tweet/_search?q=tweet:elasticsearch
下一个查询是想要寻找 name 字段为 "john" 且 tweet 字段为 "mary" 的文档,实际的查询就 是:
+name:john +tweet:mary
但是经过百分号编码(percent encoding)处理后,会让它看起来稍显神秘:
GET /_search?q=%2Bname%3Ajohn+%2Btweet%3Amary
前缀 "+" 表示必须要满足我们的查询匹配条件,而前缀 "-" 则表示绝对不能匹配条件。没 有 + 或者 - 的表示可选条件。匹配的越多,文档的相关性就越大。
字段 _all
下面这条简单的搜索将会返回所有包含 "mary" 字符的文档:
GET /_search?q=mary
在之前的例子中,我们搜索 tweet 或者 name 中的文字。然而,搜索的结果显示 "mary" 在三 个不同的字段中:
- 用户的名字为"Mary"
- 6个"Mary"发送的推文
- 1个"@mary“
那么Elasticsearch是如何找到三个不同字段中的内容呢?
当我们在索引一个文档的时候,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 。
TIP: 在你刚开始创建程序的时候你可能会经常使用 _all 这个字段。但是慢慢的,你可能就会 在请求中指定字段。当字段 _all 已经没有使用价值的时候,那就可以将它关掉。之后的《字 段all》一节中将会有介绍
更加复杂的查询
再实现一个查询:
- 字段 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)
最后要提一句,任何用户都可以通过查询语句来访问臃肿的查询,或许会得到一些私人的信息,或许会通过大量的运算将你的集群压垮!
TIP 出于以上原因,我们不建议你将查询语句直接暴露给用户,除非是你信任的可以访问数据与 集群的权限用户。
映射与统计
当我们在进行搜索的事情,我们会发现有一些奇怪的事情。比如有一些内容似乎是被打破 了:在我们的索引中有12条推文,中有一个包含了 2014-09-15 这个日期,但是看看下面的查 询结果中的总数量:
GET /_search?q=2014 # 12 results GET /_search?q=2014-09-15 # 12 results ! GET /_search?q=date:2014-09-15 # 1 result GET /_search?q=date:2014 # 0 results !
为什么我们使用字段 _all 搜索全年就会返回所有推文,而使用字段 date 搜索年份却没有结 果呢?为什么使用两者所得到的结果是不同的?
推测大概是因为我们的数据在 _all 和 date 在索引时没有被相同处理。我们来看看 Elasticsearch是如何处理我们的文档结构的。我们可以对 gb 的 tweet 使用mapping请求:
GET /gb/_mapping/tweet
{ "gb": { "mappings": { "tweet": { "properties": { "date": { "type": "date", "format": "dateOptionalTime" }, "name": { "type": "string" }, "tweet": { "type": "string" }, "user_id": { "type": "long" } } } } } }
Elasticsearch会根据系统自动判断字段类型并生成一个映射。返回结果告诉我们 date 字段被 识别成了 date 类型。 _all 没有出现是因为他是默认字段,但是我们知道字段 _all 实际上 是 string 类型的。