Elasticsearch 是一个实时的分布式搜索分析引擎,它能让你以一个之前从未有过的速度和规模,去探索你的数据。 它被用作全文检索、结构化搜索、分析以及这三个功能的组合
大部分数据库在从你的数据中提取可用知识时出乎意料的低效。 当然,你可以通过时间戳或精确值进行过滤,但是它们能够进行全文检索、处理同义词、通过相关性给文档评分么? 它们从同样的数据中生成分析与聚合数据吗?最重要的是,它们能实时地做到上面的那些而不经过大型批处理的任务么?
Elasticsearch是 面向文档 的,意味着它存储整个对象或 文档_。在 Elasticsearch 中,你 对文档进行索引、检索、排序和过滤--而不是对行列数据。这是一种完全不同的思考数据的方式,也是 Elasticsearch 能支持复杂全文检索的原因。
例:存储雇员数据
这将会以 雇员文档 的形式存储:一个文档代表一个雇员。存储数据到 Elasticsearch 的行为叫做 索引 ,但在索引一个文档之前,需要确定将文档存储在哪里。
一个 Elasticsearch 集群可以 包含多个 索引 ,相应的每个索引可以包含多个 类型 。 这些不同的类型存储着多个 文档 ,每个文档又有 多个 属性 。
索引文档:
对于雇员目录,我们将做如下操作:
每个雇员索引一个文档,包含该雇员的所有信息。
每个文档都将是 employee 类型 。
该类型位于 索引 megacorp 内。
该索引保存在我们的 Elasticsearch 集群中。
具体代码操作如下:
PUT /megacorp/employee/1
{
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
注意,路径 /megacorp/employee/1 包含了三部分的信息:
megacorp: 索引名称
employee: 类型名称
1: 特定雇员的ID
检索文档:
简单地执行 一个 HTTP GET 请求并指定文档的地址——索引库、类型和ID。 使用这三个信息可以返回原始的 JSON 文档:
具体代码:GET /megacorp/employee/1
返回结果:
{
"_index" : "megacorp", //索引
"_type" : "employee", //类型
"_id" : "1", //ID
"_version" : 1,
"found" : true,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
}
将 HTTP 命令由 PUT 改为 GET 可以用来检索文档,同样的,可以使用 DELETE 命令来删除文档,以及使用 HEAD 指令来检查文档是否存在。如果想更新已存在的文档,只需再次 PUT
搜索所有雇员:GET /megacorp/employee/_search
搜索姓氏为 ``Smith`` 的雇员:GET /megacorp/employee/_search?q=last_name:Smith
Elasticsearch 提供一个丰富灵活的查询语言叫做 查询表达式 , 它支持构建更加复杂和健壮的查询。领域特定语言 (DSL), 指定了使用一个 JSON 请求。
GET /megacorp/employee/_search
{
"query" : {
"match" : {
"last_name" : "Smith"
}
}
}
GET /megacorp/employee/_search
{
"query" : {
"bool": {
"must": {
"match" : {
"last_name" : "smith"
}
},
"filter": {
"range" : {
"age" : { "gt" : 30 }
}
}
}
}
}
全文搜索:
GET /megacorp/employee/_search
{
"query" : {
"match" : {
"about" : "rock climbing"
}
}
}
返回结果:curl -X GET "localhost:9200/website/blog/123?pretty"
{
...
"hits": {
"total": 2, //匹配结果数量
"max_score": 0.16273327,
"hits": [
{
...
"_score": 0.16273327, //匹配结果得分
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
},
{
...
"_score": 0.016878016, //匹配结果得分
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": [ "music" ]
}
}
]
}
}
短语搜索:
GET /megacorp/employee/_search
{
"query" : {
"match_phrase" : { //短语搜索,全文搜索为match
"about" : "rock climbing"
}
}
}
返回结果:
{
...
"hits": {
"total": 1,
"max_score": 0.23013961,
"hits": [
{
...
"_score": 0.23013961,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
}
]
}
}
高亮搜素:
GET /megacorp/employee/_search
{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
},
"highlight": {
"fields" : {
"about" : {}
}
}
}
Elasticsearch 有一个功能叫聚合(aggregations),允许我们基于数据生成一些精细的分析结果。聚合与 SQL 中的 GROUP BY 类似但更强大。
分析:挖掘出雇员中最受欢迎的兴趣爱好
具体代码:
GET /megacorp/employee/_search
{
"aggs": {
"all_interests": {
"terms": { "field": "interests" }
}
}
}
可以直接添加适当的查询来组合查询:
GET /megacorp/employee/_search
{
"query": {
"match": {
"last_name": "smith"
}
},
"aggs": {
"all_interests": {
"terms": {
"field": "interests"
}
}
}
}
具体原理跳过.......
文档元数据: 一个文档不仅仅包含它的数据 ,也包含 元数据 —— 有关 文档的信息。 三个必须的元数据元素如下:
_index: 文档在哪存放
_type: 文档表示的对象类别
_id: 文档唯一标识
一个文档的 _index 、 _type 和 _id 唯一标识一个文档。
我们可以提供自定义的 _id 值:
PUT /{index}/{type}/{id}
{
"field": "value",
...
}
或者让 index API 自动生成:
POST /website/blog/
{
"title": "My second blog entry",
"text": "Still trying this out...",
"date": "2014/01/01"
}
Elasticsearch 响应体如下所示:
{
"_index": "website",
"_type": "blog",
"_id": "123",
"_version": 1, //在 Elasticsearch 中每个文档都有一个版本号。当每次对文档进行修改时(包括删除), _version 的值会递增
"created": true
}
取回一个文档;
1. GET /website/blog/123?pretty //在请求的查询串参数中加上 pretty 参数,使得 JSON 响应体更加可读
2. GET /website/blog/123?_source=title,text //取回一个文档的title和text字段
3. GET /website/blog/123/_source //只得到_socure字段
检查文档是否存在:
1. HEAD /website/blog/123 //如果文档存在, Elasticsearch 将返回一个 200 ok 的状态码
//若文档不存在, Elasticsearch 将返回一个 404 Not Found 的状态码
更新整个文档:
1. PUT /website/blog/123 //ES中的文档是不可改变的,如果要修改他,只能用相同的索引去替换他,但是旧的文档不会立即消失,会在后台保留一段时间
{
"title": "My first blog entry",
"text": "I am starting to get the hang of this...",
"date": "2014/01/02"
}
创建新文档:
1. _index 、 _type 和 _id 的组合可以唯一标识一个文档,所以,确保创建一个新文档的最简单办法是,使用索引请求的 POST 形式让 Elasticsearch 自动生成唯一 _id
POST /website/blog/
{ ... }
2. 如果已经有自己的 _id ,那么我们必须告诉 Elasticsearch ,只有在相同的 _index 、 _type 和 _id 不存在时才接受我们的索引请求
PUT /website/blog/123?op_type=create
{ ... }
或者:
PUT /website/blog/123/_create
{ ... }
如果创建新文档的请求成功执行,Elasticsearch 会返回元数据和一个 201 Created 的 HTTP 响应码。
另一方面,如果具有相同的 _index 、 _type 和 _id 的文档已经存在,Elasticsearch 将会返回 409 Conflict 响应码,以及一些错误信息
删除文档:
1. DELETE /website/blog/123
处理冲突:
1. 悲观并发控制,阻塞访问资源以防止冲突
2. 乐观并发控制,不做阻塞处理,而是重新更新,使用新的数据或者将相关情况反馈给用户
文档的部分更新:
1. 请求最简单的一种形式是接收文档的一部分作为 doc 的参数, 它只是与现有的文档进行合并。对象被合并到一起,覆盖现有的字段,增加新的字段
POST /website/blog/1/_update
{
"doc" : {
"tags" : [ "testing" ],
"views": 0
}
}
取回多个文档:
将多个请求合并成一个,避免单独处理每个请求花费的网络延时和开销。
代价较小的批量操作:
bulk API 允许在单个步骤中进行多次 create 、 index 、 update 或 delete 请求。
如: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"} }
查询全文数据要微妙的多。我们问的不只是“这个文档匹配查询吗”,而是“该文档匹配查询的程度有多大?”换句话说,该文档与给定查询的相关性如何?
我们很少对全文类型的域做精确匹配。相反,我们希望在文本类型的域中搜索。不仅如此,我们还希望搜索能够理解我们的 意图 :
Elasticsearch 使用一种称为 倒排索引 的结构,它适用于快速的全文搜索。一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表。
Quick 可以小写化为 quick 。
foxes 可以 词干提取 --变为词根的格式-- 为 fox 。类似的, dogs 可以为提取为 dog 。
jumped 和 leap 是同义词,可以索引为相同的单词 jump
分词和标准化的过程称为 分析
查询
基本的查询某一条语句
1. itslaw2/itslaw2/1502
返回结果更具有可读性
2. itslaw2/itslaw2/1502?pretty
只返回caseType,paragraphs字段
3. itslaw2/itslaw2/1502?_source=caseType,paragraphs
不需要任何元数据的查询
4.itslaw2/itslaw2/1502/_source
基于第四条只返回部分字段
5.itslaw2/itslaw2/1502/_source?_source=caseType,paragraphs
增加
1. itslaw2/itslaw2/0000000
{
"title": "My first blog entry",
"text": "I am starting to get the hang of this...",
"date": "2014/01/02"
}
创建新文档:
1. _index 、 _type 和 _id 的组合可以唯一标识一个文档,所以,确保创建一个新文档的最简单办法是,使用索引请求的 POST 形式让 Elasticsearch 自动生成唯一 _id
POST /website/blog/
{ ... }
2. 如果已经有自己的 _id ,那么我们必须告诉 Elasticsearch ,只有在相同的 _index 、 _type 和 _id 不存在时才接受我们的索引请求
PUT itslaw2/itslaw2/0000000?op_type=create
{ ... }
或者:
PUT itslaw2/itslaw2/0000000/_create
{ ... }
如果创建新文档的请求成功执行,Elasticsearch 会返回元数据和一个 201 Created 的 HTTP 响应码。
另一方面,如果具有相同的 _index 、 _type 和 _id 的文档已经存在,Elasticsearch 将会返回 409 Conflict 响应码,以及一些错误信息
删除
1. DELETE /website/blog/123
大部分数据库在从你的数据中提取可用知识时出乎意料的低效。 当然,你可以通过时间戳或精确值进行过滤,但是它们能够进行全文检索、处理同义词、通过相关性给文档评分么? 它们从同样的数据中生成分析与聚合数据吗?最重要的是,它们能实时地做到上面的那些而不经过大型批处理的任务么?
Elasticsearch是 面向文档 的,意味着它存储整个对象或 文档_。在 Elasticsearch 中,你 对文档进行索引、检索、排序和过滤--而不是对行列数据。这是一种完全不同的思考数据的方式,也是 Elasticsearch 能支持复杂全文检索的原因。
例:存储雇员数据
这将会以 雇员文档 的形式存储:一个文档代表一个雇员。存储数据到 Elasticsearch 的行为叫做 索引 ,但在索引一个文档之前,需要确定将文档存储在哪里。
一个 Elasticsearch 集群可以 包含多个 索引 ,相应的每个索引可以包含多个 类型 。 这些不同的类型存储着多个 文档 ,每个文档又有 多个 属性 。
索引文档:
对于雇员目录,我们将做如下操作:
每个雇员索引一个文档,包含该雇员的所有信息。
每个文档都将是 employee 类型 。
该类型位于 索引 megacorp 内。
该索引保存在我们的 Elasticsearch 集群中。
具体代码操作如下:
PUT /megacorp/employee/1
{
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
注意,路径 /megacorp/employee/1 包含了三部分的信息:
megacorp: 索引名称
employee: 类型名称
1: 特定雇员的ID
检索文档:
简单地执行 一个 HTTP GET 请求并指定文档的地址——索引库、类型和ID。 使用这三个信息可以返回原始的 JSON 文档:
具体代码:GET /megacorp/employee/1
返回结果:
{
"_index" : "megacorp", //索引
"_type" : "employee", //类型
"_id" : "1", //ID
"_version" : 1,
"found" : true,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
}
将 HTTP 命令由 PUT 改为 GET 可以用来检索文档,同样的,可以使用 DELETE 命令来删除文档,以及使用 HEAD 指令来检查文档是否存在。如果想更新已存在的文档,只需再次 PUT
搜索所有雇员:GET /megacorp/employee/_search
搜索姓氏为 ``Smith`` 的雇员:GET /megacorp/employee/_search?q=last_name:Smith
Elasticsearch 提供一个丰富灵活的查询语言叫做 查询表达式 , 它支持构建更加复杂和健壮的查询。领域特定语言 (DSL), 指定了使用一个 JSON 请求。
GET /megacorp/employee/_search
{
"query" : {
"match" : {
"last_name" : "Smith"
}
}
}
GET /megacorp/employee/_search
{
"query" : {
"bool": {
"must": {
"match" : {
"last_name" : "smith"
}
},
"filter": {
"range" : {
"age" : { "gt" : 30 }
}
}
}
}
}
全文搜索:
GET /megacorp/employee/_search
{
"query" : {
"match" : {
"about" : "rock climbing"
}
}
}
返回结果:curl -X GET "localhost:9200/website/blog/123?pretty"
{
...
"hits": {
"total": 2, //匹配结果数量
"max_score": 0.16273327,
"hits": [
{
...
"_score": 0.16273327, //匹配结果得分
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
},
{
...
"_score": 0.016878016, //匹配结果得分
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": [ "music" ]
}
}
]
}
}
短语搜索:
GET /megacorp/employee/_search
{
"query" : {
"match_phrase" : { //短语搜索,全文搜索为match
"about" : "rock climbing"
}
}
}
返回结果:
{
...
"hits": {
"total": 1,
"max_score": 0.23013961,
"hits": [
{
...
"_score": 0.23013961,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
}
]
}
}
高亮搜素:
GET /megacorp/employee/_search
{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
},
"highlight": {
"fields" : {
"about" : {}
}
}
}
Elasticsearch 有一个功能叫聚合(aggregations),允许我们基于数据生成一些精细的分析结果。聚合与 SQL 中的 GROUP BY 类似但更强大。
分析:挖掘出雇员中最受欢迎的兴趣爱好
具体代码:
GET /megacorp/employee/_search
{
"aggs": {
"all_interests": {
"terms": { "field": "interests" }
}
}
}
可以直接添加适当的查询来组合查询:
GET /megacorp/employee/_search
{
"query": {
"match": {
"last_name": "smith"
}
},
"aggs": {
"all_interests": {
"terms": {
"field": "interests"
}
}
}
}
具体原理跳过.......
文档元数据: 一个文档不仅仅包含它的数据 ,也包含 元数据 —— 有关 文档的信息。 三个必须的元数据元素如下:
_index: 文档在哪存放
_type: 文档表示的对象类别
_id: 文档唯一标识
一个文档的 _index 、 _type 和 _id 唯一标识一个文档。
我们可以提供自定义的 _id 值:
PUT /{index}/{type}/{id}
{
"field": "value",
...
}
或者让 index API 自动生成:
POST /website/blog/
{
"title": "My second blog entry",
"text": "Still trying this out...",
"date": "2014/01/01"
}
Elasticsearch 响应体如下所示:
{
"_index": "website",
"_type": "blog",
"_id": "123",
"_version": 1, //在 Elasticsearch 中每个文档都有一个版本号。当每次对文档进行修改时(包括删除), _version 的值会递增
"created": true
}
取回一个文档;
1. GET /website/blog/123?pretty //在请求的查询串参数中加上 pretty 参数,使得 JSON 响应体更加可读
2. GET /website/blog/123?_source=title,text //取回一个文档的title和text字段
3. GET /website/blog/123/_source //只得到_socure字段
检查文档是否存在:
1. HEAD /website/blog/123 //如果文档存在, Elasticsearch 将返回一个 200 ok 的状态码
//若文档不存在, Elasticsearch 将返回一个 404 Not Found 的状态码
更新整个文档:
1. PUT /website/blog/123 //ES中的文档是不可改变的,如果要修改他,只能用相同的索引去替换他,但是旧的文档不会立即消失,会在后台保留一段时间
{
"title": "My first blog entry",
"text": "I am starting to get the hang of this...",
"date": "2014/01/02"
}
创建新文档:
1. _index 、 _type 和 _id 的组合可以唯一标识一个文档,所以,确保创建一个新文档的最简单办法是,使用索引请求的 POST 形式让 Elasticsearch 自动生成唯一 _id
POST /website/blog/
{ ... }
2. 如果已经有自己的 _id ,那么我们必须告诉 Elasticsearch ,只有在相同的 _index 、 _type 和 _id 不存在时才接受我们的索引请求
PUT /website/blog/123?op_type=create
{ ... }
或者:
PUT /website/blog/123/_create
{ ... }
如果创建新文档的请求成功执行,Elasticsearch 会返回元数据和一个 201 Created 的 HTTP 响应码。
另一方面,如果具有相同的 _index 、 _type 和 _id 的文档已经存在,Elasticsearch 将会返回 409 Conflict 响应码,以及一些错误信息
删除文档:
1. DELETE /website/blog/123
处理冲突:
1. 悲观并发控制,阻塞访问资源以防止冲突
2. 乐观并发控制,不做阻塞处理,而是重新更新,使用新的数据或者将相关情况反馈给用户
文档的部分更新:
1. 请求最简单的一种形式是接收文档的一部分作为 doc 的参数, 它只是与现有的文档进行合并。对象被合并到一起,覆盖现有的字段,增加新的字段
POST /website/blog/1/_update
{
"doc" : {
"tags" : [ "testing" ],
"views": 0
}
}
取回多个文档:
将多个请求合并成一个,避免单独处理每个请求花费的网络延时和开销。
代价较小的批量操作:
bulk API 允许在单个步骤中进行多次 create 、 index 、 update 或 delete 请求。
如: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"} }
查询全文数据要微妙的多。我们问的不只是“这个文档匹配查询吗”,而是“该文档匹配查询的程度有多大?”换句话说,该文档与给定查询的相关性如何?
我们很少对全文类型的域做精确匹配。相反,我们希望在文本类型的域中搜索。不仅如此,我们还希望搜索能够理解我们的 意图 :
Elasticsearch 使用一种称为 倒排索引 的结构,它适用于快速的全文搜索。一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表。
Quick 可以小写化为 quick 。
foxes 可以 词干提取 --变为词根的格式-- 为 fox 。类似的, dogs 可以为提取为 dog 。
jumped 和 leap 是同义词,可以索引为相同的单词 jump
分词和标准化的过程称为 分析
查询
基本的查询某一条语句
1. itslaw2/itslaw2/1502
返回结果更具有可读性
2. itslaw2/itslaw2/1502?pretty
只返回caseType,paragraphs字段
3. itslaw2/itslaw2/1502?_source=caseType,paragraphs
不需要任何元数据的查询
4.itslaw2/itslaw2/1502/_source
基于第四条只返回部分字段
5.itslaw2/itslaw2/1502/_source?_source=caseType,paragraphs
增加
1. itslaw2/itslaw2/0000000
{
"title": "My first blog entry",
"text": "I am starting to get the hang of this...",
"date": "2014/01/02"
}
创建新文档:
1. _index 、 _type 和 _id 的组合可以唯一标识一个文档,所以,确保创建一个新文档的最简单办法是,使用索引请求的 POST 形式让 Elasticsearch 自动生成唯一 _id
POST /website/blog/
{ ... }
2. 如果已经有自己的 _id ,那么我们必须告诉 Elasticsearch ,只有在相同的 _index 、 _type 和 _id 不存在时才接受我们的索引请求
PUT itslaw2/itslaw2/0000000?op_type=create
{ ... }
或者:
PUT itslaw2/itslaw2/0000000/_create
{ ... }
如果创建新文档的请求成功执行,Elasticsearch 会返回元数据和一个 201 Created 的 HTTP 响应码。
另一方面,如果具有相同的 _index 、 _type 和 _id 的文档已经存在,Elasticsearch 将会返回 409 Conflict 响应码,以及一些错误信息
删除
1. DELETE /website/blog/123