赞
踩
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。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。