Elasticsearch 不只会存储(stores) 文档,为了能被搜索到也会为文档添加索引(indexes) ,这也是为什么我们使用结构化的 JSON 文档,而不是无结构的二进制数据。
文档中的每个字段都将被索引并且可以被查询 。全文检索,可以找出所有匹配关键字的文档并按照相关性(relevance) 排序后返回结果。
1、空搜索
搜索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
数组包含所查询结果的前十个文档。
在 hits
数组中每个结果包含文档的 _index
、 _type
、 _id
,加上 _source
字段。这意味着我们可以直接从返回的搜索结果中使用整个文档。这不像其他的搜索引擎,仅仅返回文档的ID,需要你单独去获取文档。
每个结果还有一个 _score
,它衡量了文档与查询的匹配程度。默认情况下,首先返回最相关的文档结果,就是说,返回的文档是按照 _score
降序排列的。在这个例子中,我们没有指定任何查询,故所有的文档具有相同的相关性,因此对所有的结果而言 1
是中性的 _score
。
max_score
值是与查询所匹配文档的 _score
的最大值。
took
took
值告诉我们执行整个搜索请求耗费了多少毫秒。
shards
_shards
部分 告诉我们在查询中参与分片的总数,以及这些分片成功了多少个失败了多少个。正常情况下我们不希望分片失败,但是分片失败是可能发生的。如果我们遭遇到一种灾难级别的故障,在这个故障中丢失了相同分片的原始数据和副本,那么对这个分片将没有可用副本来对搜索请求作出响应。假若这样,Elasticsearch 将报告这个分片是失败的,但是会继续返回剩余分片的结果。
timeout
timed_out
值告诉我们查询是否超时。默认情况下,搜索请求不会超时。 如果低响应时间比完成结果更重要,你可以指定 timeout
为 10 或者 10ms(10毫秒),或者 1s(1秒):
GET /_search?timeout=10ms
在请求超时之前,Elasticsearch 将会返回已经成功从每个分片获取的结果。
2、多索引 多类型
如果不对某一特殊的索引或者类型做限制,就会搜索集群中的所有文档。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
类型
tips:搜索一个索引有五个主分片和搜索五个索引各有一个分片准确来说是等价的。
3、分页
在之前的 空搜索 中说明了集群中有 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
考虑到分页过深以及一次请求太多结果的情况,结果集在返回之前先进行排序。 但请记住一个请求经常跨越多个分片,每个分片都产生自己的排序结果,这些结果需要进行集中排序以保证整体顺序是正确的。
tips:在分布式系统中,对结果排序的成本随分页的深度成指数上升。这就是 web 搜索引擎对任何查询都不要返回超过 1000 个结果的原因。
4、轻量搜索
有两种形式的 搜索
API:一种是 “轻量的” 查询字符串 版本,要求在查询字符串中传递所有的参数,另一种是更完整的 请求体 版本,要求使用 JSON 格式和更丰富的查询表达式作为搜索语言。
查询字符串搜索非常适用于通过命令行做即席查询。例如,查询在 tweet
类型中 tweet
字段包含 elasticsearch
单词的所有文档:
GET /_all/tweet/_search?q=tweet:elasticsearch
即席查询(Ad Hoc)是用户根据自己的需求,灵活的选择查询条件,系统能够根据用户的选择生成相应的统计报表。 即席查询与普通应用查询最大的不同是普通的应用查询是定制开发的,而即席查询是由用户自定义查询条件的。以单独的SQL语句的形式执行的查询就是即席查询,比如说:在C#程序里嵌入的SQL语句,或者在SSMS里的新建查询窗口,自己键入的SQL代码就是即席查询。而将SQL代码放入存储过程里面,以存储过程或者函数或者触发器来执行的查询就不是即席查询,即席:当场,就是当场去查询。
_all 字段
这个简单搜索返回包含 mary
的所有文档:
GET /_search?q=mary
当索引一个文档的时候,Elasticsearch 取出所有字段的值拼接成一个大的字符串,作为 _all
字段进行索引。除非设置特定字段,否则查询字符串就使用 _all
字段进行搜索。
更复杂的查询
下面的查询针对tweents类型,并使用以下的条件:
name
字段中包含mary
或者john
date
值大于2014-09-10
_all
字段包含aggregations
或者geo
+name:(mary john) +date:>2014-09-10 +(aggregations geo)
查询字符串搜索允许任何用户在索引的任意字段上执行可能较慢且重量级的查询,这可能会暴露隐私信息,甚至将集群拖垮。并且一些查询字符串中很小的语法错误就将导致无法得到查询结果,所以不推荐直接向用户暴露查询字符串搜索功能,除非对于集群和数据来说非常信任他们。
相反,我们经常在生产环境中更多地使用功能全面的 request body 查询API,除了能完成以上所有功能,还有一些附加功能。但在到达那个阶段之前,首先需要了解数据在 Elasticsearch 中是如何被索引的。