ElasticSearch权威指南学习(文档)
什么是文档
- 在Elasticsearch中,文档(document)这个术语有着特殊含义。它特指最顶层结构或者根对象(root object)序列化成的JSON数据(以唯一ID标识并存储于Elasticsearch中)
- 文档元数据
节点 | 说明 |
---|---|
_index | 文档存储的地方 |
_type | 文档代表的对象的类 |
_id | 文档的唯一标识 |
_index
- 索引(index)类似于关系型数据库里的“数据库”——它是我们存储和索引关联数据的地方。
_type
- 在应用中,我们使用对象表示一些“事物”,例如一个用户、一篇博客、一个评论,或者一封邮件。每个对象都属于一个类(class),这个类定义了属性或与对象关联的数据。user类的对象可能包含姓名、性别、年龄和Email地址。
- 每个类型(type)都有自己的映射(mapping)或者结构定义,就像传统数据库表中的列一样。
- _type的名字可以是大写或小写,不能包含下划线或逗号。
_id
- id仅仅是一个字符串,它与_index和_type组合时,就可以在Elasticsearch中唯一标识一个文档。
- 当创建一个文档,你可以自定义_id,也可以让Elasticsearch帮你自动生成。
索引一个文档
- 使用自己的ID
PUT /{index}/{type}/{id}
{
"field": "value",
...
}
- 例如我们的索引叫做“website”,类型叫做“blog”,我们选择的ID是“123”
PUT /website/blog/123
{
"title": "My first blog entry",
"text": "Just trying this out...",
"date": "2014/01/01"
}
响应
{
"_index": "website",
"_type": "blog",
"_id": "123",
"_version": 1,
"created": true
}
- 自增ID
POST /website/blog/
{
"title": "My second blog entry",
"text": "Still trying this out...",
"date": "2014/01/01"
}
响应
{
"_index": "website",
"_type": "blog",
"_id": "wM0OSFhDQXGZAWDf0-drSA",
"_version": 1,
"created": true
}
自动生成的ID有22个字符长
检索文档
- GET /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"
}
}
pretty
在任意的查询字符串中增加pretty参数,类似于上面的例子。会让Elasticsearch美化输出(pretty-print)JSON响应以便更加容易阅读
- 检索文档的一部分 GET /website/blog/123?_source=title,text
- _source字段现在只包含我们请求的字段
{
"_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"
}
检查文档是否存在
- 只是检查文档是否存在——你对内容完全不感兴趣——使用HEAD方法来代替GET。HEAD请求不会返回响应体,只有HTTP头:
curl -i -XHEAD http://localhost:9200/website/blog/123
Elasticsearch将会返回200 OK状态如果你的文档存在:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 0
如果不存在返回404 Not Found:
curl -i -XHEAD http://localhost:9200/website/blog/124
更新整个文档
- 文档在Elasticsearch中是不可变的——我们不能修改他们。如果需要更新已存在的文档,我们可以使用《索引文档》章节提到的index API 重建索引(reindex) 或者替换掉它
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增加了。
{
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 2,
"created": false
}
created标识为false因为同索引、同类型下已经存在同ID的文档
2. 在内部,Elasticsearch已经标记旧文档为删除并添加了一个完整的新文档。旧版本文档不会立即消失,但你也不能去访问它。Elasticsearch会在你继续索引更多数据时清理被删除的文档。
创建一个新文档
POST /website/blog/
{ ... }
- 如果想使用自定义的_id
第一种方法使用op_type查询参数
PUT /website/blog/123?op_type=create
{ ... }
第二种方法是在URL后加/_create做为端点:
PUT /website/blog/123/_create
{ ... }
- 请求成功响应状态码是201 Created
- 如果包含相同的_index、_type和_id的文档已经存在,Elasticsearch将返回409 Conflict响应状态码
{
"error" : "DocumentAlreadyExistsException[[website][4] [blog][123]:
document already exists]",
"status" : 409
}
删除文档
- DELETE /website/blog/123
成功,返回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
}
处理冲突
- 当使用index API更新文档的时候,我们读取原始文档,做修改,然后将整个文档(whole document)一次性重新索引。最近的索引请求会生效——Elasticsearch中只存储最后被索引的任何文档。如果其他人同时也修改了这个文档,他们的修改将会丢失。
- 乐观并发控制
- Elasticsearch使用_version保证所有修改都被正确排序。当一个旧版本出现在新版本之后,它会被简单的忽略。
- 比如,创建一个新的博文,响应体告诉我们这是一个新建的文档,它的_version是1
PUT /website/blog/1/_create
{
"title": "My first blog entry",
"text": "Just trying this out..."
}
- 当我们通过重新索引文档保存修改时,我们这样指定了version参数,我们只希望文档的_version是1时更新才生效
PUT /website/blog/1?version=1
{
"title": "My first blog entry",
"text": "Starting to get the hang of this..."
}
- 成功
{
"_index": "website",
"_type": "blog",
"_id": "1",
"_version": 2 //成功版本+1
"created": false
}
- 失败
{
"error" : "VersionConflictEngineException[[website][2] [blog][1]:
version conflict, current [2], provided [1]]",
"status" : 409
}
- 使用外部版本控制系统
- 外部版本号与之前说的内部版本号在处理的时候有些不同。它不再检查_version是否与请求中指定的一致,而是检查是否小于指定的版本。如果请求成功,外部版本号就会被存储到_version中。
- 创建一个包含外部版本号5的新博客
PUT /website/blog/2?version=5&version_type=external
{
"title": "My first external blog entry",
"text": "Starting to get the hang of this..."
}
响应
{
"_index": "website",
"_type": "blog",
"_id": "2",
"_version": 5,
"created": true
}
文档局部更新
- 文档是不可变的——它们不能被更改,只能被替换。update API必须遵循相同的规则。表面看来,我们似乎是局部更新了文档的位置,内部却是像我们之前说的一样简单的使用update API处理相同的检索-修改-重建索引流程,我们也减少了其他进程可能导致冲突的修改。
- 为博客添加一个tags字段和一个views字段:
POST /website/blog/1/_update
{
"doc" : {
"tags" : [ "testing" ],
"views": 0
}
}
使用脚本局部更新
- Elasticsearch允许你使用脚本实现自己的逻辑。脚本支持非常多的API,例如搜索、排序、聚合和文档更新。脚本可以通过请求的一部分、检索特殊的.scripts索引或者从磁盘加载方式执行。
- 默认的脚本语言是Groovy,一个快速且功能丰富的脚本语言,语法类似于Javascript。它在一个沙盒(sandbox)中运行,以防止恶意用户毁坏Elasticsearch或攻击服务器。
- 例如,我们可以使用脚本增加博客的views数量:
POST /website/blog/1/_update
{
"script" : "ctx._source.views+=1"
}
更新可能不存在的文档
- 想象我们要在Elasticsearch中存储浏览量计数器。每当有用户访问页面,我们增加这个页面的浏览量。但如果这是个新页面,我们并不确定这个计数器存在与否。当我们试图更新一个不存在的文档,更新将失败。
在这种情况下,我们可以使用upsert参数定义文档来使其不存在时被创建。
POST /website/pageviews/1/_update
{
"script" : "ctx._source.views+=1",
"upsert": {
"views": 1
}
}
第一次执行这个请求,upsert值被索引为一个新文档,初始化views字段为1.接下来文档已经存在,所以script被更新代替,增加views数量。
- 想象我们要在Elasticsearch中存储浏览量计数器。每当有用户访问页面,我们增加这个页面的浏览量。但如果这是个新页面,我们并不确定这个计数器存在与否。当我们试图更新一个不存在的文档,更新将失败。
更新和冲突
- 对于多用户的局部更新,文档被修改了并不要紧。例如,两个进程都要增加页面浏览量,增加的顺序我们并不关心——如果冲突发生,我们唯一要做的仅仅是重新尝试更新既可。
- 这些可以通过retry_on_conflict参数设置重试次数来自动完成,这样update操作将会在发生错误前重试——这个值默认为0。
POST /website/pageviews/1/_update?retry_on_conflict=5 //在错误发生前重试更新5次
{
"script" : "ctx._source.views+=1",
"upsert": {
"views": 0
}
}
- 这适用于像增加计数这种顺序无关的操作
检索多个文档
- 像Elasticsearch一样,检索多个文档依旧非常快。合并多个请求可以避免每个请求单独的网络开销。如果你需要从Elasticsearch中检索多个文档,相对于一个一个的检索,更快的方式是在一个请求中使用multi-get或者mget API。
POST /_mget
{
"docs" : [
{
"_index" : "website",
"_type" : "blog",
"_id" : 2
},
{
"_index" : "website",
"_type" : "pageviews",
"_id" : 1,
"_source": "views"
}
]
}
响应体也包含一个docs数组,每个文档还包含一个响应,它们按照请求定义的顺序排列。每个这样的响应与单独使用get request响应体相同:
{
"docs" : [
{
"_index" : "website",
"_id" : "2",
"_type" : "blog",
"found" : true,
"_source" : {
"text" : "This is a piece of cake...",
"title" : "My first external blog entry"
},
"_version" : 10
},
{
"_index" : "website",
"_id" : "1",
"_type" : "pageviews",
"found" : true,
"_version" : 2,
"_source" : {
"views" : 2
}
}
]
}
- 如果你想检索的文档在同一个_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", "1" ]
}
我们请求的第二个文档并不存在。我们定义了类型为blog,但是ID为1的文档类型为pageviews。这个不存在的文档会在响应体中被告知。
{
"docs" : [
{
"_index" : "website",
"_type" : "blog",
"_id" : "2",
"_version" : 10,
"found" : true,
"_source" : {
"title": "My first external blog entry",
"text": "This is a piece of cake..."
}
},
{
"_index" : "website",
"_type" : "blog",
"_id" : "1",
"found" : false //没被找到
}
]
}
事实上第二个文档不存在并不影响第一个文档的检索。每个文档的检索和报告都是独立的
更新时的批量操作
- 就像mget允许我们一次性检索多个文档一样,bulk API允许我们使用单一请求来实现多个文档的create、index、update或delete。这对索引类似于日志活动这样的数据流非常有用,它们可以以成百上千的数据为一个批次按序进行索引。
- bulk请求体如下
{ action: { metadata }}\n
{ request body }\n
{ action: { metadata }}\n
{ request body }\n
...
这种格式类似于用"\n"符号连接起来的一行一行的JSON文档流(stream)。两个重要的点需要注意:
- 每行必须以"\n"符号结尾,包括最后一行。这些都是作为每行有效的分离而做的标记。
- 每一行的数据不能包含未被转义的换行符,它们会干扰分析——这意味着JSON不能被美化打印。
action/metadata这一行定义了文档行为(what action)发生在哪个文档(which document)之上。
行为(action)必须是以下几种:
行为 | 解释 |
---|---|
create | 当文档不存在时创建之 |
index | 创建新文档或替换已有文档 |
update | 局部更新文档 |
delete | 删除一个文档 |
- 例如删除请求看起来像这样:
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}
- bulk请求表单是这样的
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, <1>
"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,然后错误的细节将在相应的请求中被报告
- 演示错误情况
POST /_bulk
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title": "Cannot create - it already exists" }
{ "index": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title": "But we can update it" }
响应中我们将看到create文档123失败了,因为文档已经存在,但是后来的在123上执行的index请求成功了:
{
"took": 3,
"errors": true, //错误标识
"items": [
{ "create": {
"_index": "website",
"_type": "blog",
"_id": "123",
"status": 409,//错误状态
"error": "DocumentAlreadyExistsException //错误信息
[[website][4] [blog][123]:
document already exists]"
}},
{ "index": {
"_index": "website",
"_type": "blog",
"_id": "123",
"_version": 5,
"status": 200 //正确状态
}}
]
}
- 多大才算太大?
- 整个批量请求需要被加载到接受我们请求节点的内存里,所以请求越大,给其它请求可用的内存就越小。有一个最佳的bulk请求大小。超过这个大小,性能不再提升而且可能降低
- 最佳大小,当然并不是一个固定的数字。它完全取决于你的硬件、你文档的大小和复杂度以及索引和搜索的负载
- 试着批量索引标准的文档,随着大小的增长,当性能开始降低,说明你每个批次的大小太大了。开始的数量可以在1000~5000个文档之间,如果你的文档非常大,可以使用较小的批次。
- 通常着眼于你请求批次的物理大小是非常有用的。一千个1kB的文档和一千个1MB的文档大不相同。一个好的批次最好保持在5-15MB大小间。
ElasticSearch权威指南学习(文档)的更多相关文章
- ElasticSearch权威指南学习(分布式文档存储)
路由文档到分片 当你索引一个文档,它被存储在单独一个主分片上.Elasticsearch是如何知道文档属于哪个分片的呢?当你创建一个新文档,它是如何知道是应该存储在分片1还是分片2上的呢? 进程不能是 ...
- ElasticSearch权威指南学习(排序)
排序方式 相关性排序 默认情况下,结果集会按照相关性进行排序 -- 相关性越高,排名越靠前. 相关性分值会用_score字段来给出一个浮点型的数值,所以默认情况下,结果集以_score进行倒序排列. ...
- ElasticSearch权威指南学习(结构化查询)
请求体查询 简单查询语句(lite)是一种有效的命令行adhoc查询.但是,如果你想要善用搜索,你必须使用请求体查询(request body search)API. 空查询 我们以最简单的 sear ...
- ElasticSearch权威指南学习(映射和分析)
概念 映射(mapping)机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型(string, number, booleans, date等).+ 分析(analysis)机制用于进行全文 ...
- ElasticSearch权威指南学习(分布式集群)
空集群 只有一个空节点的集群 一个节点(node)就是一个Elasticsearch实例,而一个集群(cluster)由一个或多个节点组成,它们具有相同的cluster.name,它们协同工作,分享数 ...
- ElasticSearch权威指南学习(索引管理)
创建索引 当我们需要确保索引被创建在适当数量的分片上,在索引数据之前设置好分析器和类型映射. 手动创建索引,在请求中加入所有设置和类型映射,如下所示: PUT /my_index { "se ...
- ElasticSearch权威指南学习(分布式搜索)
查询阶段 在初始化查询阶段(query phase),查询被向索引中的每个分片副本(原本或副本)广播. 每个分片在本地执行搜索并且建立了匹配document的优先队列(priority queue). ...
- HTTP权威指南-学习笔记
目录 HTTP权威指南-学习笔记 HTTP: Web的基础 URL与资源 HTTP报文 连接管理 HTTP结构 Web服务器 代理 缓存 集成点: 网关,隧道及中继 Web机器人 识别,认证与安全 客 ...
- 初识Elastic search—附《Elasticsearch权威指南—官方guide的译文》
本文作为Elastic search系列的开篇之作,简要介绍其简要历史.安装及基本概念和核心模块. 简史 Elastic search基于Lucene(信息检索引擎,ES里一个index—索引,一个索 ...
随机推荐
- ExecutorService——shutdown方法和awaitTermination方法
ExecutorService的关闭shutdown和awaitTermination为接口ExecutorService定义的两个方法,一般情况配合使用来关闭线程池. 方法简介shutdown方法: ...
- Servlet中获取Spring管理的bean
描述: 在Servlet中调用Spring管理的接口,可以使Dao/Service/ServiceImpl. 前提是在调用的bean中有注解: @Repository("beanName&q ...
- c++中被忽视的隐藏
稍微懂得点oop的人都知道重载,那是多态性的重要体现!可是在c++中你能分清成员函数的重载.覆盖吗?这个好像也不难,重载存在与同一个类中,而覆盖存在于派生类于基类中!可是如果再加上隐藏呢?说实话,以前 ...
- Mac Eclipse 配置 SDK Manager Proxy (代理)
默认的下载地址非常慢,可以换成东软的代理. 顶部任务栏中选择SDK Manager -> 偏好设置 : 可以看到下载速度快了很多,出现类很多安装选项: 安装好后,在偏好设置窗口中,选择Clear ...
- FortiGate日志中session clash
1.出现于:FortiGate v5.0和v5.2 2.出现原因 Session clash messages appear in the logs when a new session is cre ...
- Ueditor 前后端分离实现文件上传到独立服务器
关于Ueditor 前后端分离实现文件上传到独立服务器,在网上搜索确实遇到大坑,不过还好遇到了 虚若影 最终实现了,在此感谢!虚若影的原文博客网址:http://www.cnblogs.com/hpn ...
- 获取CNVD的cookie
def getCookie(self): #获取cookie spiderFirefox = webdriver.Firefox() spiderFirefox.get("http://ww ...
- jmeter 如何发送上传文件接口请求
1.上传图片接口,通过抓包工具获取接口相关信息,然后在信息头里添加Content-Disposition:form-data; name="imgType" 2.在请求中MIME类 ...
- SQL里执行CLR c#代码
这里只说一个重点: 1.直接在sql里执行clr代码的时候,sql还是会报错 说没有启用 clr 执行以下代码才会起作用 EXEC sp_configure 'clr enabled', 1; RE ...
- Spring 框架下 事务的配置(复杂)
//db.properties配置 src下的文件 jdbc.jdbcUrl=jdbc:mysql:///day43jdbc.driverClass=com.mysql.jdbc.Driverjdb ...