ES搜索

  • 1 搜索结果解析
  • 2 多索引和多类别
  • 3 分页
  • 在集群系统中深度分页(为什么搜索引擎很难做到大量结果排序搜索)
  • 4 字符串查询
  • 4.1 简易搜索
  • _all字段
  • TIP
  • 更复杂的语句
  • TIP
  • (下面部分的内容留待后续整理充实)
  • 4.2 DSL语句
  • 4.3 全文搜索(相比于短语搜索更为广泛、内容按相关性排序)
  • 4.4 短语搜索
  • 4.5 高亮支持
  • 4.6 聚合


1 搜索结果解析

{
   "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:匹配到的文档数 , 以及前十条数据
took 请求时间 ms
shards 参与的分片数及成败情况(即便失败了也会返回剩余的结果,但将报告分片failed)
timeout 可自行设置 GET /_search?timeout=10ms , 
(超时时间设置:
非断路器模式 ,在超时时返回已查询到的结果,但不会中断还在进行的查询结果)

2 多索引和多类别

你注意到空搜索的结果中不同类型的文档—— user和 tweet——来自于不同的索引—— us和 gb。

通过限制搜索的不同索引或类型,我们可以在集群中跨所有文档搜索。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中搜索 search types user and tweet in all indices

当你搜索包含单一索引时,Elasticsearch转发搜索请求到这个索引的主分片或每个分片的复制分片上,然后聚集每个分片的结果。搜索包含多个索引也是同样的方式——只不过或有更多的分片被关联。

重点:
搜索一个索引有5个主分片和5个索引各有一个分片事实上是一样的。
接下来,你将看到这些简单的情况如何灵活的扩展以适应你需求的变更。

3 分页

和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个主分片的索引中搜索。当我们请求结果的第一页(结果1到10)时,每个分片产生自己最顶端10个结果然后返回它们给请求节点(requesting node),它再排序这所有的50个结果以选出顶端的10个结果。

现在假设我们请求第1000页——结果10001到10010。工作方式都相同,不同的是每个分片都必须产生顶端的10010个结果。然后请求节点排序这50050个结果并丢弃50040个!

你可以看到在分布式系统中,排序结果的花费随着分页的深入而成倍增长。这也是为什么网络搜索引擎中任何语句不能返回多于1000个结果的原因。(搜索的性能会随着搜索结果条数或页数急剧下降)

TIP

在《重建索引》章节我们将阐述如何能高效的检索大量文档

4 字符串查询

4.1 简易搜索

search API有两种表单:一种是**“简易版”的查询字符串(query string)将所有参数通过查询字符串定义,另一种版本使用JSON完整的表示请求体(request body),这种富搜索语言叫做结构化查询语句(DSL)**

查询字符串搜索对于在命令行下运行点对点(ad hoc)查询特别有用。例如这个语句查询所有类型为 tweet并在 tweet字段中包含 elasticsearch字符的文档:

GET /_all/tweet/_search?q=tweet:elasticsearch

下一个语句查找 name字段中包含 "john"和 tweet字段包含 "mary"的结果。实际的查询只需要:

+name:john +tweet:mary

但是百分比编码(percent encoding)(译者注:就是url编码)需要将查询字符串参数变得更加神秘:

GET /_search?q=%2Bname%3Ajohn+%2Btweet%3Amary

"+"前缀表示语句匹配条件必须被满足。类似的 "-"前缀表示条件必须不被满足。所有条件如果没有 +或 -表示是可选的——匹配越多,相关的文档就越多。

_all字段

返回包含 "mary"字符的所有文档的简单搜索:

GET /_search?q=mary

在前一个例子中,我们搜索 tweet或 name字段中包含某个字符的结果。然而,这个语句返回的结果在三个不同的字段中包含 “mary”:

用户的名字是“Mary”
“Mary”发的六个推文
针对“@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"

若没有指定字段,查询字符串搜索(即q=xxx)使用 _all字段搜索。

TIP

_all字段对于开始一个新应用时是一个有用的特性。之后,如果你定义字段来代替 _all字段,你的搜索结果将更加可控。当 _all字段不再使用,你可以停用它.

更复杂的语句

下一个搜索推特的语句:

_all field

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)

就像你上面看到的例子,简单(lite)查询字符串搜索惊人的强大。它的查询语法,会在《查询字符串语法》章节阐述。参考文档允许我们简洁明快的表示复杂的查询。这对于命令行下一次性查询或者开发模式下非常有用。

然而,你可以看到简洁带来了隐晦和调试困难。而且它很脆弱——查询字符串中一个细小的语法错误,像 -、 :、 /或 "错位就会导致返回错误而不是结果。

最后,查询字符串搜索允许任意用户在索引中任何一个字段上运行潜在的慢查询语句,可能暴露私有信息甚至使你的集群瘫痪。

TIP

因为这些原因,我们不建议直接暴露查询字符串搜索给用户,除非这些用户对于你的数据和集群可信。

取而代之的,生产环境我们一般依赖全功能的请求体搜索API,它能完成前面所有的事情,甚至更多。在了解它们之前,我们首先需要看看数据是如何在Elasticsearch中被索引的。

(下面部分的内容留待后续整理充实)

4.2 DSL语句

GET /megacorp/employee/_search
{
    "query" : {
        "match" : {
            "last_name" : "Smith"
        }
    }
}
GET /megacorp/employee/_search
{
    "query" : {
        "filtered" : {
            "filter" : {
                "range" : {
                    "age" : { "gt" : 30 } <1>
                }
            },
            "query" : {
                "match" : {
                    "last_name" : "smith" <2>
                }
            }
        }
    }
}
<1> 这部分查询属于区间过滤器(range filter),它用于查找所有年龄大于30岁的数据——gt为"greater than"的缩写。
<2> 这部分查询与之前的match语句(query)一致。

4.3 全文搜索(相比于短语搜索更为广泛、内容按相关性排序)

GET /megacorp/employee/_search
 {
 “query” : {
 “match” : {
 “about” : “rock climbing”
 }
 }
 }

4.4 短语搜索

GET /megacorp/employee/_search
 {
 “query” : {
 “match_phrase” : {
 “about” : “rock climbing”
 }
 }
 }

4.5 高亮支持

GET /megacorp/employee/_search
 {
 “query” : {
 “match_phrase” : {
 “about” : “rock climbing”
 }
 },
 “highlight”: {
 “fields” : {
 “about” : {}
 }
 }
 }

4.6 聚合