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遵循与之前所说完全相同的过程,这个过程如下:

  1. 从旧文档中检索JSON
  2. 修改它
  3. 删除旧文档
  4. 索引新文档

唯一的不同是update API完成这一过程只需要一个客户端请求既可,不再需要getindex请求了。

 

 

删除文档

 

删除文档的语法模式与之前基本一致,只不过要使用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串之间,必须有一个换行

 

posted @ 2016-08-18 21:19  小天儿  阅读(2941)  评论(0)    收藏  举报