Elasticsearch 搜索

Elasticsearch真正强大之处在于可以从混乱的数据中找出有意义的信息——从大数据到全面的信息。这也是为什么我们使用结构化的JSON文档,而不是无结构的二进制数据。Elasticsearch不只会存储(store)文档,也会索引(indexes)文档内容来使之可以被搜索。每个文档里的字段都会被索引并被查询。而且不仅如此。在简单查询时,Elasticsearch可以使用所有的索引,以非常快的速度返回结果。这让你永远不必考虑传统数据库的一些东西。

A search can be: 搜索(search)可以:

在类似于gender或者age这样的字段上使用结构化查询,join_date这样的字段上使用排序,就像SQL的结构化查询一样。
全文检索,可以使用所有字段来匹配关键字,然后按照关联性(relevance)排序返回结果。
或者结合以上两条。

很多搜索都是开箱即用的,为了充分挖掘Elasticsearch的潜力,你需要理解以下三个概念:
概念 解释
映射(Mapping) 数据在每个字段中的解释说明
分析(Analysis) 全文是如何处理的可以被搜索的
领域特定语言查询(Query DSL) Elasticsearch使用的灵活的、强大的查询语言

空白搜索

搜索API最常用的一种形式就是空白搜索,也就是不加任何查询条件的,只是返回集群中所有文档的搜索。

GET /_search

curl -XGET "http://localhost:9200/_search?pretty"

返回内容如下:

{
  "took" : 14,
  "timed_out" : false,
  "_shards" : {
    "total" : 10,
    "successful" : 10,
    "failed" : 0
  },
  "hits" : {
    "total" : 8,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "megacorp",
        "_type" : "employee",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "first_name" : "Jane",
          "last_name" : "Smith",
          "age" : 32,
          "about" : "I like to collect rock albums",
          "interests" : [
            "music"
          ]
        }
      },
      {
        "_index" : "megacorp",
        "_type" : "employee",
        "_id" : "AVpkks-zosJrEdXVKk5N",
        "_score" : 1.0,
        "_source" : {
          "first_name" : "Douglas",
          "last_name" : "Fir",
          "age" : 35,
          "about" : "I like to build cabinets",
          "interests" : [
            "forestry"
          ]
        }
      },
      {
        "_index" : "megacorp",
        "_type" : "employee",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "first_name" : "John",
          "last_name" : "Aires",
          "age" : 24,
          "about" : "I love to go rock climbing",
          "interests" : [
            "sports",
            "food"
          ],
          "views" : 0,
          "tags" : [
            "testing"
          ]
        }
      },
      {
        "_index" : "megacorp",
        "_type" : "employee",
        "_id" : "AVpk5HclosJrEdXVKk5V",
        "_score" : 1.0,
        "_source" : {
          "title" : "My second bulk demo"
        }
      },
      {
        "_index" : "megacorp",
        "_type" : "employee",
        "_id" : "AVpk5cudosJrEdXVKk5W",
        "_score" : 1.0,
        "_source" : {
          "title" : "My second bulk demo"
        }
      },
      {
        "_index" : "music",
        "_type" : "songs",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "name" : "Deck the Halls",
          "year" : 1885,
          "lyrics" : "Fa la la la la"
        }
      },
      {
        "_index" : "music",
        "_type" : "lyrics",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "name" : "Deck the Halls",
          "year" : 1886,
          "lyrics" : "Fa fa fa faf a "
        }
      },
      {
        "_index" : "megacorp",
        "_type" : "employee",
        "_id" : "11",
        "_score" : 1.0,
        "_source" : {
          "first_name" : "John",
          "last_name" : "Aires",
          "age" : 24,
          "about" : "I love to go rock climbing",
          "interests" : [
            "sports",
            "music"
          ]
        }
      }
    ]
  }
}

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秒钟)。

多索引,多类型

当我们没有特别指定一个索引或者类型的时候,我们将会搜索整个集群中的所有文档。Elasticsearch会把搜索请求转发给集群中的每一个主从分片,然后按照结果的相关性得到前十名,并将它们返回给我们。

然 而,往往我们只需要在某一个特定的索引的几个类型中进行搜索。我们可以通过在URL中定义它来实现这个功能:
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内的所有文档
当你在一个索引中搜索的时候,Elasticsearch或将你的搜索请求转发给相应索引中的所有主从分片,然后收集每一个分片的结果。在多个索引中搜索也是相同的流程,只不过是增加了一些参与分片。

分页

与SQL使用LIMIT来控制单“页”数量类似,Elasticsearch使用的是from以及size两个参数:
参数 说明
size 每次返回多少个结果,默认值为10
from 忽略最初的几条结果,默认值为0
假设每页显示5条结果,那么1至3页的请求就是:
curl -XGET "http://localhost:9200/_search?size=5&from=5"
{
    "took": 7,
    "timed_out": false,
    "_shards": {
        "total": 10,
        "successful": 10,
        "failed": 0
    },
    "hits": {
        "total": 8,
        "max_score": 1,
        "hits": [
            {
                "_index": "music",
                "_type": "songs",
                "_id": "1",
                "_score": 1,
                "_source": {
                    "name": "Deck the Halls",
                    "year": 1885,
                    "lyrics": "Fa la la la la"
                }
            },
            {
                "_index": "music",
                "_type": "lyrics",
                "_id": "1",
                "_score": 1,
                "_source": {
                    "name": "Deck the Halls",
                    "year": 1886,
                    "lyrics": "Fa fa fa faf a "
                }
            },
            {
                "_index": "megacorp",
                "_type": "employee",
                "_id": "11",
                "_score": 1,
                "_source": {
                    "first_name": "John",
                    "last_name": "Aires",
                    "age": 24,
                    "about": "I love to go rock climbing",
                    "interests": [
                        "sports",
                        "music"
                    ]
                }
            }
        ]
    }
}
当心不要一次请求过多或者页码过大的结果。它们会在返回前排序。一个请求会经过多个分片。每个分片都会生成自己的排序结果。然后再进行集中整理,以确保最终结果的正确性。

分布式系统中的大页码页面

为了说明白为什么页码过大的请求会产生问题,我们就先预想一下我们在搜索一个拥有5个主分片的索引。当我们请求第一页搜索的时候,每个分片产生自己前十名,然后将它们返回给请求节点,然后这个节点会将50条结果重新排序以产生最终的前十名。

现在想想一下我们想获得第1,000页,也就是第10,001到第10,010条结果,与之前同理,每一个分片都会先产生自己的前10,010名,然后请求节点统一处理这50,050条结果,然后再丢弃掉其中的50,040条!

现在你应该明白了,在分布式系统中,大页码请求所消耗的系统资源是呈指数式增长的。这也是为什么网络搜索引擎不会提供超过1,000条搜索结果的原因。

精简 搜索

搜索的API分为两种:其一是通过参数来传递查询的“精简版”查询语句(query string),还有一种是通过JSON来传达丰富的查询的完整版请求体(request body),这种搜索语言被称为查询DSL。

查询语句在行命令中运行点对点查询的时候非常实用。比如我想要查询所有employee类型中,所有first_name字段为"John"的文档:
curl -XGET "http://localhost:9200/megacorp/employee/_search?q=first_name:John+last_name:Aires"
{
    "took": 8,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 2,
        "max_score": 0.6931472,
        "hits": [
            {
                "_index": "megacorp",
                "_type": "employee",
                "_id": "1",
                "_score": 0.6931472,
                "_source": {
                    "first_name": "John",
                    "last_name": "Aires",
                    "age": 24,
                    "about": "I love to go rock climbing",
                    "interests": [
                        "sports",
                        "food"
                    ],
                    "views": 0,
                    "tags": [
                        "testing"
                    ]
                }
            },
            {
                "_index": "megacorp",
                "_type": "employee",
                "_id": "11",
                "_score": 0.2876821,
                "_source": {
                    "first_name": "John",
                    "last_name": "Aires",
                    "age": 24,
                    "about": "I love to go rock climbing",
                    "interests": [
                        "sports",
                        "music"
                    ]
                }
            }
        ]
    }
}
下一个查询是想要寻找first_name字段为"john"且last_name字段为"Aires"的文档,实际的查询就是:
curl -XGET "http://localhost:9200/megacorp/employee/_search?q=first_name:John+last_name:sherry"
{
    "took": 8,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 2,
        "max_score": 0.6931472,
        "hits": [
            {
                "_index": "megacorp",
                "_type": "employee",
                "_id": "1",
                "_score": 0.6931472,
                "_source": {
                    "first_name": "John",
                    "last_name": "sherry'",
                    "age": 24,
                    "about": "I love to go rock climbing",
                    "interests": [
                        "sports",
                        "food"
                    ],
                    "views": 0,
                    "tags": [
                        "testing"
                    ]
                }
            },


        ]
    }
}
但是经过百分号编码(percent encoding)处理后,会让它看起来稍显神秘:

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

前缀"+"表示必须要满足我们的查询匹配条件,而前缀"-"则表示绝对不能匹配条件。没有+或者-的表示可选条件。匹配的越多,文档的相关性就越大。

字段_all

   下面这条简单的搜索将会返回所有包含"aires"字符的文档:
curl -XGET "http://localhost:9200/megacorp/employee/_search?q=aires"
 在之前的例子中,我们搜索first_name或者last_name中的文字。然而,搜索的结果显示"aires"在三个不同的字段中:

用户的名字为"aires"
6个"aires"发送的推文
1个"@aires"

 那么Elasticsearch是如何找到三个不同字段中的内容呢?

当我们在索引一个文档的时候,Elasticsearch会将所有字段的数值都汇总到一个大的字符串中,并将它索引成一个特殊的字段_all:


 除非指定了字段名,不然查询语句就会搜索字段_all。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值