ElasticSearch添加、检索数据等常用操作
程序中大多的实体或对象能够被序列化为包含键值对的JSON对象,键(key)是字段(field)或属性(property)的名字,值(value)可以是字符串、数字、波尔类型、另一个对象、值数组或者其他特殊类型,比如表示日期的字符串或者表示地理位置的对象。
文档元数据(Document MetaData):
一个文档不只有数据。它还包含了元数据(metadata)——关于文档的信息。三个必须的元数据节点是:
节点 | 说明 |
---|---|
_index |
文档存储的地方 |
_type |
文档代表的对象的类 |
_id |
文档的唯一标识 |
_index
索引(index)类似于关系型数据库里的“数据库”——它是我们存储和索引关联数据的地方。
事实上,我们的数据被存储和索引在分片(shards)中,索引只是一个把一个或多个分片分组在一起的逻辑空间。然而,这只是一些内部细节——我们的程序完全不用关心分片。对于我们的程序而言,文档存储在索引(index)中。剩下的细节由Elasticsearch关心既可。
后面会继续探讨如何创建并管理索引,但现在,我们将让Elasticsearch为我们创建索引。我们唯一需要做的仅仅是选择一个索引名。这个名字必须是全部小写,不能以下划线开头,不能包含逗号。让我们使用website
做为索引名。
_type
在应用中,我们使用对象表示一些“事物”,例如一个用户、一篇博客、一个评论,或者一封邮件。每个对象都属于一个类(class),这个类定义了属性或与对象关联的数据。user
类的对象可能包含姓名、性别、年龄和Email地址。
在关系型数据库中,我们经常将相同类的对象存储在一个表里,因为它们有着相同的结构。同理,在Elasticsearch中,我们使用相同类型(type)的文档表示相同的“事物”,因为他们的数据结构也是相同的。
每个类型(type)都有自己的映射(mapping)或者结构定义,就像传统数据库表中的列一样。所有类型下的文档被存储在同一个索引下,但是类型的映射(mapping)会告诉Elasticsearch不同的文档如何被索引。 我们将会在《映射》章节探讨如何定义和管理映射,但是现在我们将依赖ELasticsearch去自动处理数据结构。
_type
的名字可以是大写或小写,不能包含下划线或逗号。我们将使用blog
做为类型名。
_id
id仅仅是一个字符串,它与_index
和_type
组合时,就可以在ELasticsearch中唯一标识一个文档。当创建一个文档,你可以自定义_id
,也可以让Elasticsearch帮你自动生成。
ps:还有其它部分其它元数据,后续再介绍。
使用自己的ID
如果你的文档有自然的标识符(例如user_account
字段或者其他值表示文档),你就可以提供自己的_id
,使用这种形式的index
API:
PUT /{index}/{type}/{id}
{"key": "value"...}
如,PUT /website/blog/123
{
"title": "My blog entry",
"text": "汉语你可以。。 ",
"date": "2015/07/16"
}
{
"_index": "website",
"_type": "blog",
"_id": "123",
"_version": 5,
"created": false
}
Elasticsearch中每个文档都有版本号,每当文档变化(包括删除)都会使_version
增加。后续我们将探讨如何使用_version
号确保你程序的一部分不会覆盖掉另一部分所做的更改。
自增ID
如果我们的数据没有自然ID,我们可以让Elasticsearch自动为我们生成。请求结构发生了变化:PUT
方法——“在这个URL中存储文档”
变成了POST
方法——"在这个文档下存储文档"
。(注:原来是把文档存储到某个ID对应的空间,现在是把这个文档添加到某个_type
下)。
URL现在只包含_index
和_type
两个字段:
POST /website/blog/
{
"title": "My second blog entry",
"text": "Still trying this out...",
"date": "2015/07/16"
}
响应内容与刚才类似,只有_id
字段变成了自动生成的值:
{
"_index": "website",
"_type": "blog",
"_id": "AU6Vi9GsUzILmCnC2hkX",
"_version": 1,
"created": true
}
更新整个文档
文档在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 <1>
}
- <1>
created
标识为false
因为同索引、同类型下已经存在同ID的文档。
在内部,Elasticsearch已经标记旧文档为删除并添加了一个完整的新文档。旧版本文档不会立即消失,但你也不能去访问它。Elasticsearch会在你继续索引更多数据时清理被删除的文档。
在后面探讨update
API,这个API 似乎 允许你修改文档的局部,但事实上Elasticsearch遵循与之前所说完全相同的过程,这个过程如下:
- 从旧文档中检索JSON
- 修改它
- 删除旧文档
- 索引新文档
唯一的不同是update
API完成这一过程只需要一个客户端请求既可,不再需要get
和index
请求了。
删除文档
删除文档的语法模式与之前基本一致,只不过要使用DELETE
方法:
DELETE /website/blog/1234
如果文档被找到,Elasticsearch将返回200 OK
状态码和以下响应体。注意_version
数字已经增加了。
{
"found" : true,
"_index" : "website",
"_type" : "blog",
"_id" : "1234",
"_version" : 3
}
如果文档未找到,我们将得到一个404 Not Found
状态码,响应体是这样的:
{
"found" : false,
"_index" : "website",
"_type" : "blog",
"_id" : "1234",
"_version" : 4
}
尽管文档不存在——"found"的值是false——_version依旧增加了。这是内部记录的一部分,它确保在多节点间不同操作可以有正确的顺序。删除一个文档也不会立即从磁盘上移除,它只是被标记成已删除。Elasticsearch将会在你之后添加更多索引的时候才会在后台进行删除内容的清理。
==========常用操作说明======
基础入门版
快速检查集群的健康状况
1 |
GET /_cat/health?v |
说明:如何快速了解集群的健康状况?green、yellow、red?
green:每个索引的primary shard和replica shard都是active状态的
yellow:每个索引的primary shard都是active状态的,但是部分replica shard不是active状态,处于不可用的状态
red:不是所有索引的primary shard都是active状态的,部分索引有数据丢失了
快速查看集群中有哪些索引
1 |
GET /_cat/indices?v |
索引的CRUD操作,以商品为例
新增商品(建立索引)
(1)手动指定document id(用于其他库倒进来时本身就含有id的情况)
1 2 3 4 5 6 7 8 |
PUT /ecommerce/product/1 { "name" : "gaolujie yagao", "desc" : "gaoxiao meibai", "price" : 30, "producer" : "gaolujie producer", "tags": [ "meibai", "fangzhu" ] } |
说明:若已经有/ecommerce/product/1该数据,此时“新增”操作变成“全量替换”,旧数据被deleted。因此如果我们要需要创建,而不允许替换数据(逻辑:若已经有该条数据,则不进行任何操作(报错回滚))
(2)自动生成document id(自动生成的id,长度为20个字符,URL安全,base64编码,GUID,分布式系统并行生成时不可能会发生冲突)
1 2 3 4 5 6 7 8 |
POST /ecommerce/product { "name" : "gaolujie yagao", "desc" : "gaoxiao meibai", "price" : 30, "producer" : "gaolujie producer", "tags": [ "meibai", "fangzhu" ] } |
查询商品(参见查询专题详解)
1 |
GET /ecommerce/product/1 |
修改商品name
全量替换:
1 2 3 4 5 6 7 8 |
PUT /ecommerce/product/1 { "name" : "jiaqiangban gaolujie yagao", "desc" : "gaoxiao meibai", "price" : 30, "producer" : "gaolujie producer", "tags": [ "meibai", "fangzhu" ] } |
部分修改:
1 2 3 4 5 6 |
POST /ecommerce/product/1/_update { "doc": { "name": "jiaqiangban gaolujie yagao" } } |
删除商品
1 |
DELETE /ecommerce/product/1 |
说明:不会理解物理删除,只会将其标记为deleted,当数据越来越多的时候,在后台自动删除
查询专题版
query string search(不常用)
1、搜索全部商品:
1 |
GET /ecommerce/product/_search |
查询结果部分字段说明:
took:耗费了几毫秒。
timed_out:是否超时,这里是没有。
_shards:数据拆成了5个分片,所以对于搜索请求,会打到所有的primary shard(或者是它的某个replica shard也可以)。
hits.total:查询结果的数量,3个document。
hits.max_score:score的含义,就是document对于一个search的相关度的匹配分数,越相关,就越匹配,分数也高。
hits.hits:包含了匹配搜索的document的详细数据。
2、搜索商品名称中包含yagao的商品,而且按照售价降序排序
1 |
GET /ecommerce/product/_search?q=name:yagao&sort=price:desc |
3、指定要查询出来商品的名称和价格
1 |
GET /ecommerce/product/_search?_source=name,price |
query DSL(Domain Specified Language,特定领域的语言)
1、查询所有的商品(match_all)
1 2 3 4 |
GET /ecommerce/product/_search { "query": { "match_all": {} } } |
2、查询名称包含yagao的商品,同时按照价格降序排序(sort)
1 2 3 4 5 6 7 8 9 10 11 |
GET /ecommerce/product/_search { "query" : { "match" : { "name" : "yagao" } }, "sort": [ { "price": "desc" } ] } |
3、分页查询商品(from,size)
例如:总共3条商品,假设每页就显示1条商品,现在显示第2页,所以就查出来第2个商品
1 2 3 4 5 6 |
GET /ecommerce/product/_search { "query": { "match_all": {} }, "from": 1, "size": 1 } |
4、指定要查询出来商品的名称和价格(source)
1 2 3 4 5 |
GET /ecommerce/product/_search { "query": { "match_all": {} }, "_source": ["name", "price"] } |
5、查询name和desc都含yagao的商品(multi_match)
1 2 3 4 5 6 7 8 9 |
GET /ecommerce/product/_search { "query": { "multi_match": { "query": "yagao", "fields": ["name", "desc"] } } } |
6、查询价格大于等于30的商品(range)
1 2 3 4 5 6 7 8 9 10 |
GET /ecommerce/product/_search { "query": { "range": { "price": { "gte": 30 } } } } |
7、查询name是yagao的商品(term 或者 terms数组指定多个)
1 2 3 4 5 6 7 8 |
GET /test_index/test_type/_search { "query": { "term": { "name ": "yagao" } } } |
说明:不常用。若想用term查询,则被查询的字段需要定义为不分词。
query filter
1、搜索商品名称包含yagao,而且售价大于25元的商品
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
GET /ecommerce/product/_search { "query" : { "bool" : { "must" : { "match" : { "name" : "yagao" } }, "filter" : { "range" : { "price" : { "gt" : 25 } } } } } } |
说明:bool 多条件查询,含以下参数:
must:返回的doc必须满足must子句的条件,并且参与计算score
must_not:返回的文档必须不满足must_not定义的条件
should:返回的文档可能满足should子句的条件。在一个Bool查询中,如果没有must或者filter,有一个或者多个should子句,那么只要满足一个就可以返回。minimum_should_match参数定义了至少满足几个子句。
filter:返回的doc必须满足filter子句的条件,不参与计算sorce
full-text search(全文检索)
1 2 3 4 5 6 7 8 |
GET /ecommerce/product/_search { "query" : { "match" : { "producer" : "yagao producer" } } } |
phrase search(短语搜索)
跟全文检索相对应,相反,全文检索会将输入的搜索串拆解开来,去倒排索引里面去一一匹配,只要能匹配上任意一个拆解后的单词,就可以作为结果返回。
phrase search,要求输入的搜索串,必须在指定的字段文本中,完全包含一模一样的,才可以算匹配,才能作为结果返回。
1 2 3 4 5 6 7 8 |
GET /ecommerce/product/_search { "query" : { "match_phrase" : { "producer" : "yagao producer" } } } |
highlight search(高亮搜索结果)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
GET /ecommerce/product/_search { "query" : { "match" : { "producer" : "producer" } }, "highlight": { "fields" : { "producer" : {} } } } |
批量操作
批量查询mget
获取商品id为1和2的商品信息(假设在同一个index和type下)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
GET /_mget { "docs" : [ { "_index" : "ecommerce", "_type" : "product", "_id" : 1 }, { "_index" : "ecommerce", "_type" : "product", "_id" : 2 } ] } 或者 GET /ecommerce/_mget { "docs" : [ { "_type" : "product", "_id" : 1 }, { "_type" : "product", "_id" : 2 } ] } 或者 GET /ecommerce/product/_mget { "ids": [1, 2] } |
批量操作bulk
需求:
1、删除id为1的商品
2、创建id为3的商品(name为“zhonghua yagao”)
3、修改商品2的name为“replaced zhonghua yagao”(采用全量替换的方式)
4、修改商品3的name为“bulk zhonghua yagao”(采用部分更新的方式)
1 2 3 4 5 6 7 8 |
POST /_bulk { "delete": { "_index": "ecommerce", "_type": "product", "_id": "1" }} { "create": { "_index": "ecommerce", "_type": "product", "_id": "3" }} { "name": "zhonghua yagao" } { "index": { "_index": "ecommerce", "_type": "product", "_id": "2" }} { "name": "replaced zhonghua yagao" } { "update": { "_index": "ecommerce", "_type": "product", "_id": "3", "_retry_on_conflict" : 3} } { "doc" : {"name" : "bulk zhonghua yagao"} } |
说明:
(1)delete:删除一个文档,只要1个json串就可以了
(2)create:PUT /index/type/id/_create,强制创建
(3)index:普通的put操作,可以是创建文档,也可以是全量替换文档
(4)update:执行的partial update操作
bulk api对json的语法,有严格的要求,每个json串不能换行,只能放一行,同时一个json串和一个json串之间,必须有一个换行
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~