《Elasticsearch检索引擎学习指南》第五章 搜索——最基本的工具

本文档介绍了Elasticsearch(ES)的搜索功能,包括空搜索、多索引多类型搜索、分页以及在分布式系统中的深度分页问题。ES作为一个强大的搜索引擎,支持结构化查询、全文检索以及灵活的查询DSL。分页在ES中通过from和size参数实现,但在深度分页时由于需要跨多个分片排序,效率会显著下降。此外,文档提到了_all字段用于方便的全文搜索,以及轻量级查询字符串搜索的使用和局限性。
摘要由CSDN通过智能技术生成

《Elasticsearch检索引擎学习指南》第五章 搜索——最基本的工具

参考资料

前言

ES作为一个简单的NoSQL风格的分布式文档存储系统,除去存储信息和检索信息,ES的强大之处还在于可以从无规律的数据中找到有意义的信息——从“大数据”到“大信息”。

ES的搜索可以做到:

  • 在类似于gender或者age这样的字段上使用结构化查询,join_data这样的字段上使用排序,就像SQL的结构化查询一样。
  • 全文检索,找出所有匹配关键字的文档并按照相关性(relevance)排序后返回结果
  • 上面兼而有之

很多搜索都是开箱即用,为了充分挖掘ES的潜力,需要理解以下三个概念:

  • 映射(Mapping)
    描述数据在每个字段内如何存储
  • 分析(Analysis)
    全文是如何处理使之可以被搜索的
  • 领域特定查询语言(Query DSL)
    ES的查询语言

本章节将介绍以上三点的一些基本概念–大致了解搜索是如何工作的

空搜索

搜索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
}
  • htis
    返回结果中最重要的部分是hits,它包含total字段来表示匹配到的文档总数,并且一个hits数组包含所查询结果的前十个文档。
    在hits数组中每个结果包含文档的元数据(_index,_type,_id,_source)。这意味着我们可以直接从返回的搜索结果中使用整个文档。而不像其他的搜索引擎,仅仅返回文档的ID,需要单独去获取文档。
    每个结果还有一个_scord,它衡量了文档与查询的匹配程度。默认情况下,返回的文档是按照_score降序排列的。
    max_score 值是与查询所匹配文档的 _score 的最大值。
  • took
    took 值告诉我们执行整个搜索请求耗费了多少毫秒。
  • shards
    _shards 部分告诉我们在查询中参与分片的总数,以及这些分片成功了多少个失败了多少个。正常情况下我们不希望分片失败,但是分片失败是可能发生的。如果我们遭遇到一种灾难级别的故障,在这个故障中丢失了相同分片的原始数据和副本,那么对这个分片将没有可用副本来对搜索请求作出响应。假若这样,Elasticsearch 将报告这个分片是失败的,但是会继续返回剩余分片的结果。
  • timeout
    timed_out 值告诉我们查询是否超时。默认情况下,搜索请求不会超时。如果低响应时间比完成结果更重要,你可以指定 timeout 为 10 或者 10ms(10毫秒),或者 1s(1秒):
GET /_search?timeout=10ms

在请求超时之前,Elasticsearch 将会返回已经成功从每个分片获取的结果。
WARNING 应当注意的是 timeout 不是停止执行查询,它仅仅是告知正在协调的节点返回到目前为止收集的结果并且关闭连接。在后台,其他的分片可能仍在执行查询即使是结果已经被发送了。
使用超时是因为 SLA(服务等级协议)对你是很重要的,而不是因为想去中止长时间运行的查询。

多索引 多类型

如果不对某一特殊的索引或者类型做限制,就会搜索集群中的所有文档。ES转发搜索请求到每一个主分片或者副本分片,汇集查询出的前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 类型

在单一的索引下进行搜索的时候,ES转发请求到索引的每个分片中,可以是主分片也可以是副本分片,然后从每个分片中收集结果。多索引搜索恰好也是用相同的方式工作,只是会涉及到更多的分片。
TIP 搜索一个索引有5个分片和搜索五个索引各有一个分片准确来说是等价的。

分页

在之前空搜索内容中,说明了集群中有14个文档匹配了查询。但是在hits数组中只有10个文档。如何才能看到其他的文档。
和SQL使用LIMIT关键字返回单个page结果的方法相同,ES接受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页,所有都以相同的方式工作除了每个分片不得不产生前10010个结果以外。然后协调节点对全部的50050个结果排序最后丢弃掉这些结果中的50040个结果。
可以知道,在分布式系统中,对结果排序的成本随着分页的深度成指数上升。这就是web搜索引擎对任何查询都不要返回超过1000个结果的原因。

轻量搜索

有两种形式的搜索API:一种是轻量的查询字符串版本,要求在查询字符串中传递所有的参数,另一种是更完整的请求体版本,要求使用JSON格式和更丰富的查询表达式作为搜索语言。
查询字符串搜索非常适用于通过命令行做查询。例如,查询在 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

当索引一个文档的时候,ES取出所有字段的值拼接成一个大的字符串,作为_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字段不再有用的时候,可以将它设为无效。

更复杂的查询

下面的查询针对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 中是如何被索引的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值