上一篇 ES(1)
官网原地址:https://www.elastic.co/guide/en/elasticsearch/reference/1.7/_cluster_health.html
ES权威指南: http://es.xiaoleilu.com/
Elasticsearch是一个分布式的文档(document)存储引擎。它可以实时存储并检索复杂数据结构——序列化的JSON文档。换言说,一旦文档被存储在Elasticsearch中,它就可以在集群的任一节点上被检索。
当然,我们不仅需要存储数据,还要快速的批量查询。虽然已经有很多NoSQL的解决方案允许我们以文档的形式存储对象,但它们依旧需要考虑如何查询这些数据,以及哪些字段需要被索引以便检索时更加快速。
在Elasticsearch中,每一个字段的数据都是默认被索引的。也就是说,每个字段专门有一个反向索引用于快速检索。而且,与其它数据库不同,它可以在同一个查询中利用所有的这些反向索引,以惊人的速度返回结果。
一、文档和索引
通常,我们可以认为对象(object)和文档(document)是等价相通的。不过,他们还是有所差别:对象(Object)是一个JSON结构体——类似于哈希、hashmap、字典或者关联数组;对象(Object)中还可能包含其他对象(Object)。 在Elasticsearch中,文档(document)这个术语有着特殊含义。它特指最顶层结构或者根对象(root object)序列化成的JSON数据(以唯一ID标识并存储于Elasticsearch中)。
文档通过index
API被索引——使数据可以被存储和搜索。但是首先我们需要决定文档所在。正如我们讨论的,文档通过其_index
、_type
、_id
唯一确定。们可以自己提供一个_id
,或者也使用index
API 为我们生成一个。
(1) 使用自己的ID
我们前面的例子中都是使用自己的id
PUT /{index}/{type}/{id}
{
"field": "value",
...
}
- 使用put请求
- 指定唯一{id},否则是update
(2) 自增ID
如果我们的数据没有自然ID,我们可以让Elasticsearch自动为我们生成。请求结构发生了变化:PUT
方法——“在这个URL中存储文档”
变成了POST
方法——"在这个类型下存储文档"
。(译者注:原来是把文档存储到某个ID对应的空间,现在是把这个文档添加到某个_type
下)。
例如:
POST /website/blog/
{
"title": "My second blog entry",
"text": "Still trying this out...",
"date": "2014/01/01"
}
- 使用POST请求
- 不指定{id}
注意:上面的put方法会自动创建或者更新...
但是有时候我们会希望只能创建,不能更新,为了做到这点有两种方法,它们其实做的是同一件事情。你可以选择适合自己的方式:
PUT /website/blog/123?op_type=create
{ ... }
或者第二种方法是在URL后加/_create
做为端点:
PUT /website/blog/123/_create
{ ... }
如果请求成功的创建了一个新文档,Elasticsearch将返回正常的元数据且响应状态码是201 Created。
另一方面,如果包含相同的_index
、_type
和_id
的文档已经存在,Elasticsearch将返回409 Conflict
响应状态码,错误信息类似如下:
{
"error" : "DocumentAlreadyExistsException[[website][4] [blog][123]:
document already exists]",
"status" : 409
}
二、检索文档
想要从Elasticsearch中获取文档,我们使用同样的_index
、_type
、_id
,但是HTTP方法改为GET
:
curl -XGET 'http://localhost:9200/website/blog/123?pretty'
返回内容如下:
{
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 1,
"found" : true,
"_source" : {
"title" : "My first blog entry",
"text" : "Just trying this out...",
"date" : "2014/01/01"
}
}
GET请求返回的响应内容包括{"found": true}
。这意味着文档已经找到。如果我们请求一个不存在的文档,依旧会得到一个JSON,不过found
值变成了false
。
此外,HTTP响应状态码也会变成'404 Not Found'
代替'200 OK'
。我们可以在curl
后加-i
参数得到响应头:
curl -i -XGET 'http://localhost:9200/website/blog/123?pretty'
检索文档的一部分
通常,GET
请求将返回文档的全部,存储在_source
参数中。但是可能你感兴趣的字段只是title
。请求个别字段可以使用_source
参数。多个字段可以使用逗号分隔:
GET /website/blog/123?_source=title,text
_source
字段现在只包含我们请求的字段,而且过滤了date
字段:
{
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 1,
"exists" : true,
"_source" : {
"title": "My first blog entry" ,
"text": "Just trying this out..."
}
}
或者你只想得到_source
字段而不要其他的元数据,你可以这样请求:
GET /website/blog/123/_source
它仅仅返回:
{
"title": "My first blog entry",
"text": "Just trying this out...",
"date": "2014/01/01"
}
elasticsearch的查询方式也多种多样,下面举一些简单的例子:
(1) 你也可以禁掉source,只要设置_source=false即可
curl -XGET 'http://localhost:9200/website/blog/123?_source=false&pretty'
(2) 如果不写type,可以用_all来代替,表示在所有的type中选取
curl -XGET 'http://localhost:9200/website/_all/123?_source=false&pretty'
(3) 如果你只想获取source中的一部分内容,还可以用_source_include或者_source_exclude来包含或者过滤其中的某些字段
(4) 也可以使用fields来选择source中的字段
curl -XGET 'http://localhost:9200/website/_all/123?fields=title,date&pretty'
注意:从返回值可以看出,返回的字段是数组类型的,因此只有基本类型的字段可以从fields中进行查询,对象数据是不生效的。
三、存在、更新、删除
- 存在可以使用HEAD请求
- 更新直接使用PUT请求
PUT /website/blog/123
{
"title": "My first blog entry",
"text": "I am starting to get the hang of this...",
"date": "2014/01/02"
}
在响应中,我们可以看到Elasticsearch把_version
增加了。
- reated`标识为`false`因为同索引、同类型下已经存在同ID的文档。
在内部,Elasticsearch已经标记旧文档为删除并添加了一个完整的新文档。旧版本文档不会立即消失,但你也不能去访问它。Elasticsearch会在你继续索引更多数据时清理被删除的文档。
在本章的后面,我们将会在《局部更新》中探讨update
API。这个API 似乎 允许你修改文档的局部,但事实上Elasticsearch遵循与之前所说完全相同的过程,这个过程如下:
- 从旧文档中检索JSON
- 修改它
- 删除旧文档
- 索引新文档
唯一的不同是update
API完成这一过程只需要一个客户端请求既可,不再需要get
和index
请求了。
关于删除
删除文档的语法模式与之前基本一致,只不过要使用DELETE
方法:
DELETE /website/blog/123
如果文档被找到,Elasticsearch将返回200 OK
状态码和以下响应体。注意_version
数字已经增加了。
{
"found" : true,
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 3
}
如果文档未找到,我们将得到一个404 Not Found
状态码,响应体是这样的:
{
"found" : false,
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 4
}
尽管文档不存在——"found"
的值是false
——_version
依旧增加了。这是内部记录的一部分,它确保在多节点间不同操作可以有正确的顺序。
正如在《更新文档》一章中提到的,删除一个文档也不会立即从磁盘上移除,它只是被标记成已删除。Elasticsearch将会在你之后添加更多索引的时候才会在后台进行删除内容的清理。
四、版本控制与乐观锁
elasticsearch 支持乐观锁的机制来完成并发控制。
乐观并发控制(Optimistic concurrency control)
Elasticsearch是分布式的。当文档被创建、更新或删除,文档的新版本会被复制到集群的其它节点。Elasticsearch即是同步的又是异步的,意思是这些复制请求都是平行发送的,并无序(out of sequence)的到达目的地。这就需要一种方法确保老版本的文档永远不会覆盖新的版本。
上文我们提到index
、get
、delete
请求时,我们指出每个文档都有一个_version
号码,这个号码在文档被改变时加一。Elasticsearch使用这个_version
保证所有修改都被正确排序。当一个旧版本出现在新版本之后,它会被简单的忽略。
我们利用_version
的这一优点确保数据不会因为修改冲突而丢失。我们可以指定文档的version
来做想要的更改。如果那个版本号不是现在的,我们的请求就失败了。
让我们创建一个新的博文:
PUT /website/blog/1/_create
{
"title": "My first blog entry",
"text": "Just trying this out..."
}
响应体告诉我们这是一个新建的文档,它的_version
是1
。现在假设我们要编辑这个文档:把数据加载到web表单中,修改,然后保存成新版本。
首先我们检索文档,发现响应体包含相同的_version
是1:
{
"_index" : "website",
"_type" : "blog",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"title": "My first blog entry",
"text": "Just trying this out..."
}
}
现在我们通过version进行更新:
PUT /website/blog/1?version=1
{
"title": "My first blog entry",
"text": "Starting to get the hang of this..."
}
请求成功,响应体告诉我们_version
已经增加到2
:
{
"_index": "website",
"_type": "blog",
"_id": "1",
"_version": 2
"created": false
}
然而,如果我们重新运行相同的索引请求,依旧指定version=1
,Elasticsearch将返回409 Conflict
状态的HTTP响应。响应体类似这样:
{
"error" : "VersionConflictEngineException[[website][2] [blog][1]:
version conflict, current [2], provided [1]]",
"status" : 409
}
以上就是es所支持的内部版本系统,同样es还支持外部版本系统。
使用外部版本控制系统
一种常见的结构是使用一些其他的数据库做为主数据库,然后使用Elasticsearch搜索数据,这意味着所有主数据库发生变化,就要将其拷贝到Elasticsearch中。如果有多个进程负责这些数据的同步,就会遇到上面提到的并发问题。
如果主数据库有版本字段——或一些类似于timestamp
等可以用于版本控制的字段——是你就可以在Elasticsearch的查询字符串后面添加version_type=external
来使用这些版本号。版本号必须是整数,大于零小于9.2e+18
——Java中的正的long
。
外部版本号与之前说的内部版本号在处理的时候有些不同。它不再检查_version
是否与请求中指定的一致,而是检查是否小于指定的版本。如果请求成功,外部版本号就会被存储到_version
中。
外部版本号不仅在索引和删除请求中指定,也可以在创建(create)新文档中指定。
例如,创建一个包含外部版本号5
的新博客,我们可以这样做:
PUT /website/blog/2?version=5&version_type=external
{
"title": "My first external blog entry",
"text": "Starting to get the hang of this..."
}
在响应中,我们能看到当前的_version
号码是5
:
{
"_index": "website",
"_type": "blog",
"_id": "2",
"_version": 5,
"created": true
}
现在我们更新这个文档,指定一个新version
号码为10
:
PUT /website/blog/2?version=10&version_type=external
{
"title": "My first external blog entry",
"text": "This is a piece of cake..."
}
请求成功的设置了当前_version
为10
:
{
"_index": "website",
"_type": "blog",
"_id": "2",
"_version": 10,
"created": false
}
如果你重新运行这个请求,就会返回一个像之前一样的冲突错误,因为指定的外部版本号不大于当前在Elasticsearch中的版本。
五、局部更新
使用update
API,我们可以使用一个请求来实现局部更新,例如增加数量的操作。
我们也说过文档是不可变的——它们不能被更改,只能被替换。update
API必须遵循相同的规则。表面看来,我们似乎是局部更新了文档的位置,内部却是像我们之前说的一样简单的使用update
API处理相同的检索-修改-重建索引流程,我们也减少了其他进程可能导致冲突的修改。
最简单的update
请求表单接受一个局部文档参数doc
,它会合并到现有文档中——对象合并在一起,存在的标量字段被覆盖,新字段被添加。举个例子,我们可以使用以下请求为博客添加一个tags
字段和一个views
字段:
POST /website/blog/1/_update
{
"doc" : {
"tags" : [ "testing" ],
"views": 0
}
}
检索文档文档显示被更新的_source
字段:
{
"_index": "website",
"_type": "blog",
"_id": "1",
"_version": 3,
"found": true,
"_source": {
"title": "My first blog entry",
"text": "Starting to get the hang of this...",
"tags": [ "testing" ],
"views": 0
}
}
更新可能不存在的文档
想象我们要在Elasticsearch中存储浏览量计数器。每当有用户访问页面,我们增加这个页面的浏览量。但如果这是个新页面,我们并不确定这个计数器存在与否。当我们试图更新一个不存在的文档,更新将失败。
在这种情况下,我们可以使用upsert
参数定义文档来使其不存在时被创建。
在这种情况下,我们可以使用upsert
参数定义文档来使其不存在时被创建。
POST /website/pageviews/1/_update { "script" : "ctx._source.views+=1", "upsert": { "views": 1 } }
第一次执行这个请求,upsert
值被索引为一个新文档,初始化views
字段为1
.接下来文档已经存在,所以script
被更新代替,增加views
数量。
假如修改的操作和插入的操作数据相同,还可以直接设置“doc_as_upsert” : true,直接把doc部分当作新的文档插入。
更新和冲突
这这一节的介绍中,我们介绍了如何在检索(retrieve)和重建索引(reindex)中保持更小的窗口,如何减少冲突性变更发生的概率,不过这些无法被完全避免,像一个其他进程在update
进行重建索引时修改了文档这种情况依旧可能发生。
为了避免丢失数据,update
API在检索(retrieve)阶段检索文档的当前_version
,然后在重建索引(reindex)阶段通过index
请求提交。如果其他进程在检索(retrieve)和重建索引(reindex)阶段修改了文档,_version
将不能被匹配,然后更新失败。
对于多用户的局部更新,文档被修改了并不要紧。例如,两个进程都要增加页面浏览量,增加的顺序我们并不关心——如果冲突发生,我们唯一要做的仅仅是重新尝试更新既可。
这些可以通过retry_on_conflict
参数设置重试次数来自动完成,这样update
操作将会在发生错误前重试——这个值默认为0。
POST /website/pageviews/1/_update?retry_on_conflict=5
{
"script" : "ctx._source.views+=1",
"upsert": {
"views": 0
}
}
- 在错误发生前重试更新5次
这适用于像增加计数这种顺序无关的操作,但是还有一种顺序非常重要的情况。例如index
API,使用“保留最后更新(last-write-wins)”的update
API,但它依旧接受一个version
参数以允许你使用乐观并发控制(optimistic concurrency control)来指定你要更细文档的版本。
六、Mget
像Elasticsearch一样,检索多个文档依旧非常快。合并多个请求可以避免每个请求单独的网络开销。如果你需要从Elasticsearch中检索多个文档,相对于一个一个的检索,更快的方式是在一个请求中使用multi-get或者mget
API。
mget
API参数是一个docs
数组,数组的每个节点定义一个文档的_index
、_type
、_id
元数据。如果你只想检索一个或几个确定的字段,也可以定义一个_source
参数:
curl -XGET 'http://localhost:9200/_mget?pretty' -d '
{
"docs" : [
{
"_index" : "website",
"_type" : "blog",
"_id" : 2
},
{
"_index" : "website",
"_type" : "blog",
"_id" : 1,
"_source": "views"
}
]
}
'
返回结果如下:
{
"docs" : [ {
"_index" : "website",
"_type" : "blog",
"_id" : "2",
"_version" : 2,
"found" : true,
"_source" : {
"title" : "second",
"text" : "111111111"
}
}, {
"_index" : "website",
"_type" : "blog",
"_id" : "1",
"_version" : 2,
"found" : true,
"_source" : {
"views" : 0
}
} ]
}
如果你想检索的文档在同一个_index
中(甚至在同一个_type
中),你就可以在URL中定义一个默认的/_index
或者/_index/_type
。
你依旧可以在单独的请求中使用这些值:
POST /website/blog/_mget
{
"docs" : [
{ "_id" : 2 },
{ "_type" : "pageviews", "_id" : 1 }
]
}
事实上,如果所有文档具有相同_index
和_type
,你可以通过简单的ids
数组来代替完整的docs
数组:
POST /website/blog/_mget
{
"ids" : [ "2", "3" ]
}
事实上第二个文档(id位3)不存在并不影响第一个文档的检索。每个文档的检索和报告都是独立的。
注意:
尽管前面提到有一个文档没有被找到,但HTTP请求状态码还是
200
。事实上,就算所有文档都找不到,请求也还是返回200
,原因是mget
请求本身成功了。如果想知道每个文档是否都成功了,你需要检查found
标志。
七、批处理
更省时的批量操作
就像mget
允许我们一次性检索多个文档一样,bulk
API允许我们使用单一请求来实现多个文档的create
、index
、update
或delete
。这对索引类似于日志活动这样的数据流非常有用,它们可以以成百上千的数据为一个批次按序进行索引。
bulk
请求体如下,它有一点不同寻常:
{ action: { metadata }}
{ request body }
{ action: { metadata }}
{ request body }
...
这种格式类似于用"
"
符号连接起来的一行一行的JSON文档流(stream)。两个重要的点需要注意:
-
每行必须以
" "
符号结尾,包括最后一行。这些都是作为每行有效的分离而做的标记。 - 每一行的数据不能包含未被转义的换行符,它们会干扰分析——这意味着JSON不能被美化打印。
提示:
在《批量格式》一章我们介绍了为什么
bulk
API使用这种格式。
action/metadata这一行定义了文档行为(what action)发生在哪个文档(which document)之上。
行为(action)必须是以下几种:
行为 | 解释 |
---|---|
create |
当文档不存在时创建之。详见《创建文档》 |
index |
创建新文档或替换已有文档。见《索引文档》和《更新文档》 |
update |
局部更新文档。见《局部更新》 |
delete |
删除一个文档。见《删除文档》 |
在索引、创建、更新或删除时必须指定文档的_index
、_type
、_id
这些元数据(metadata)。
例如删除请求看起来像这样:
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}
请求体(request body)由文档的_source
组成——文档所包含的一些字段以及其值。它被index
和create
操作所必须,这是有道理的:你必须提供文档用来索引。
这些还被update
操作所必需,而且请求体的组成应该与update
API(doc
, upsert
, script
等等)一致。删除操作不需要请求体(request body)。
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title": "My first blog post" }
POST /_bulk
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title": "My first blog post" }
{ "index": { "_index": "website", "_type": "blog" }}
{ "title": "My second blog post" }
{ "update": { "_index": "website", "_type": "blog", "_id": "123", "_retry_on_conflict" : 3} }
{ "doc" : {"title" : "My updated blog post"} }
- 注意`delete`**行为(action)**没有请求体,它紧接着另一个**行为(action)**
- 记得最后一个换行符
Elasticsearch响应包含一个items
数组,它罗列了每一个请求的结果,结果的顺序与我们请求的顺序相同:
{
"took": 4,
"errors": false,
"items": [
{ "delete": {
"_index": "website",
"_type": "blog",
"_id": "123",
"_version": 2,
"status": 200,
"found": true
}},
{ "create": {
"_index": "website",
"_type": "blog",
"_id": "123",
"_version": 3,
"status": 201
}},
{ "create": {
"_index": "website",
"_type": "blog",
"_id": "EiwfApScQiiy7TIKFxRCTw",
"_version": 1,
"status": 201
}},
{ "update": {
"_index": "website",
"_type": "blog",
"_id": "123",
"_version": 4,
"status": 200
}}
]
}}
- 所有子请求都成功完成。
每个子请求都被独立的执行,所以一个子请求的错误并不影响其它请求。如果任何一个请求失败,顶层的error
标记将被设置为true
,然后错误的细节将在相应的请求中被报告。
多大才算太大?
整个批量请求需要被加载到接受我们请求节点的内存里,所以请求越大,给其它请求可用的内存就越小。有一个最佳的bulk
请求大小。超过这个大小,性能不再提升而且可能降低。
最佳大小,当然并不是一个固定的数字。它完全取决于你的硬件、你文档的大小和复杂度以及索引和搜索的负载。幸运的是,这个最佳点(sweetspot)还是容易找到的:
试着批量索引标准的文档,随着大小的增长,当性能开始降低,说明你每个批次的大小太大了。开始的数量可以在1000~5000个文档之间,如果你的文档非常大,可以使用较小的批次。
通常着眼于你请求批次的物理大小是非常有用的。一千个1kB的文档和一千个1MB的文档大不相同。一个好的批次最好保持在5-15MB大小间。
八、搜索
https://www.elastic.co/guide/en/elasticsearch/reference/1.7/_exploring_your_data.html
8.1. 基本查询
有2种最基本的方式可以用来search,一种通过REST request URI ,另一种通过REST request body
curl 'localhost:9200/bank/_search?q=*&pretty'
默认返回前10条值,uri中支持的params大致上有以下内容:
Name | Description |
---|---|
|
The query string (maps to the |
|
The default field to use when no field prefix is defined within the query. 当查询没有定义前缀时,默认使用该字段 |
|
The analyzer name to be used when analyzing the query string. 定义查询使用的分词器 |
|
Should terms be automatically lowercased or not. Defaults to |
|
Should wildcard and prefix queries be analyzed or not. Defaults to |
|
The default operator to be used, can be |
|
If set to true will cause format based failures (like providing text to a numeric field) to be ignored. Defaults to false. 如果是true,字段类型转换失败会忽略掉 |
|
For each hit, contain an explanation of how scoring of the hits was computed. 解释评分机制 |
|
Set to |
|
The selective stored fields of the document to return for each hit, comma delimited. Not specifying any value will cause no fields to return. |
|
Sorting to perform. Can either be in the form of |
|
When sorting, set to |
|
A search timeout, bounding the search request to be executed within the specified time value and bail with the hits accumulated up to that point when expired. Defaults to no timeout. 超时时间 |
|
[experimental] This functionality is experimental and may be changed or removed completely in a future release.The maximum number of documents to collect for each shard, upon reaching which the query execution will terminate early. If set, the response will have a boolean field 在每个分片中查询的最大条数,如果设置返回结果中会有一个terminated_early字段 |
|
The starting from index of the hits to return. Defaults to |
|
The number of hits to return. Defaults to |
|
The type of the search operation to perform. Can be |
hits
响应中最重要的部分是hits
,它包含了total
字段来表示匹配到的文档总数,hits
数组还包含了匹配到的前10条数据。
hits
数组中的每个结果都包含_index
、_type
和文档的_id
字段,被加入到_source
字段中这意味着在搜索结果中我们将可以直接使用全部文档。这不像其他搜索引擎只返回文档ID,需要你单独去获取文档。
每个节点都有一个_score
字段,这是相关性得分(relevance score),它衡量了文档与查询的匹配程度。默认的,返回的结果中关联性最大的文档排在首位;这意味着,它是按照_score
降序排列的。这种情况下,我们没有指定任何查询,所以所有文档的相关性是一样的,因此所有结果的_score
都是取得一个中间值1
max_score
指的是所有文档匹配查询中_score
的最大值。
took
took
告诉我们整个搜索请求花费的毫秒数。
shards
_shards
节点告诉我们参与查询的分片数(total
字段),有多少是成功的(successful
字段),有多少的是失败的(failed
字段)。通常我们不希望分片失败,不过这个有可能发生。如果我们遭受一些重大的故障导致主分片和复制分片都故障,那这个分片的数据将无法响应给搜索请求。这种情况下,Elasticsearch将报告分片failed
,但仍将继续返回剩余分片上的结果。
timeout
time_out
值告诉我们查询超时与否。一般的,搜索请求不会超时。如果响应速度比完整的结果更重要,你可以定义timeout
参数为10
或者10ms
(10毫秒),或者1s
(1秒)
GET /_search?timeout=10ms
Elasticsearch将返回在请求超时前收集到的结果。
超时不是一个断路器(circuit breaker)(译者注:关于断路器的理解请看警告)。
警告
需要注意的是
timeout
不会停止执行查询,它仅仅告诉你目前顺利返回结果的节点然后关闭连接。在后台,其他分片可能依旧执行查询,尽管结果已经被发送。使用超时是因为对于你的业务需求(译者注:SLA,Service-Level Agreement服务等级协议,在此我翻译为业务需求)来说非常重要,而不是因为你想中断执行长时间运行的查询。
8.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个索引各有一个分片事实上是一样的。
8.3 分页
默认只有10个文档在hits
数组中。我们如何看到其他文档?
和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个结果的原因。
8.4 其它
(1) 使用routing
什么是路由?ES寻找document的过程。可以在操作文档的时候,指定用来计算路由的routing值,从而限定操作会落在哪些分片上,如果在新增文档的时候指定了routing,那么后续对这个文档的所有操作,都应该使用同样的routing值,
这个技术在设计非常大的搜索系统时非常有用,例如:
1:新增Document,指定routing:curl -XPUT 'http://localhost:9200/mytest/product/p12?routing=myrouting' -d '{ "name" : "Mac Book 笔记本1212", "price" : 12, "description" : "这是一款笔记本", "cats": [ "3c", "computer"] }'2:查询的时候,也带上相同的routing值
curl -XGET 'http://localhost:9200/mytest/product/p12?pretty=true&routing=myrouting'
(2) 关于+/-号
搜索中还可以使用加减号,“+”前缀表示语句匹配条件必须被满足。类似的“-”前缀表示条件必须不被满足。所有条件如果没有+或-表示是可选的——匹配越多,相关的文档就越多,比如:
curl -XGET 'http://localhost:9200/bank/account/_search?q=+gender:F&pretty'和
curl -XGET 'http://localhost:9200/bank/account/_search?q=-gender:F&pretty'