首页 > 代码库 > ElasticSearch基础(2) - hello world

ElasticSearch基础(2) - hello world

上一篇 ES基础(1)

官网原地址:https://www.elastic.co/guide/en/elasticsearch/reference/1.7/_cluster_health.html

ES权威指南: http://es.xiaoleilu.com/

 

一、Exploring Your Cluster

1.1、Cluster Health

 要检测集群的健康状况,需要使用_cat API

curl localhost:9200/_cat/health?v

 

技术分享

 

 我们可以发现集群ysz的状态是green

  green: 集群中所有主分片和从分片都可用

  yellow: 所有主分片都可用,但是存在不可用的从分片

  red: 存在不可用的主要分片

 

也可以查看集群的节点情况:

  

curl localhost:9200/_cat/nodes?v

查看集群的索引情况: 

curl localhost:9200/_cat/indices?v

 

1.2、Create an Index

 创建一个索引,setting部分可以省略

curl -XPUT ‘http://localhost:9200/mydb/‘ -d ‘{  "settings" : {    "index" : {    "number_of_shards" : 5,    "number_of_replicas" : 2  }}}

 

使用_cat列出所有索引

curl http://localhost:9200/_cat/indices?v

技术分享

使用head查看该索引,有5个shard,2个replica和设置的一样

 

1.3、Index and Query a Document

 

curl -XPUT ‘http://localhost:9200/mydb/students/p1‘ -d {"name" : "Jing Guo","age" : 30,"description" : "she diao da xia","wugong": [ "9 yin zhen jing", "jiang long 18 zhang"]}

 

说明: 插入了一个Docment,其id是p1,内容是郭靖

1. 请记住_index、_type、_id三者唯一确定一个文档,所以要想保证文档是新加入的,最简单的方式是使用POST方法让ES自动生成唯一_id
2. 如果想使用自定义的_id,必须告诉ES应该在_index、_type、_id三者都不同时才接受请求,可以指明就是创建操作而不是更新操作:
(1)第一种方法使用op_type查询参数:

PUT /website/blog/123?op_type=create{ ... }

(2)第二种方法是在URL后加/_create做为端点:

PUT /website/blog/123/_create{ ... }

3. 关于响应
  如果请求成功的创建了一个新文档,ES将返回正常的元数据且响应状态码是201 Created。
  如果包含相同的_index、_type和_id的文档已经存在,ES将返回409 Conflict响应状态码

4. 当创建文档的时候,如果索引不存在,则会自动创建该索引。自动创建的索引会自动映射每个字段的类型。
  这种行为可以在配置文件中配置:
    设置action.auto_create_index为false在所有节点的配置文件中禁用。
    自动映射的字段类型可以通过配置文件设置index.mapper.dynamic为false禁用。
    自动创建索引可以通过模板设置索引名称,例如:
      可以设置action.auto_create_index为 +aaa*,-bbb*,+ccc*,-* (+表示准许,-表示禁止)。

1.4、Delete an Index

curl -XDELETE localhost:9200/customer?prettycurl localhost:9200/_cat/indices?v

二、Modifying Your Data

2.1 查询数据

curl http://localhost:9200/mydb/students/p1?pretty

关于查询数据的方式多种多样,这里介绍的是最简单的一种,即通过_id去查找。

即使是这种简单的方式,es也提供了非常丰富的用法,比如选取字段,你可以用各种方式达到自己的目的,下面演示几种

1. 通常,GET请求将返回文档的全部,存储在_source参数中。但是可能你感兴趣的字段只是title。请求个别字段可以使用_source参数。多个字段可以使用逗号分隔:

curl -XGET http://localhost:9200/mydb/students/p1?_source=name,age\&pretty

 

2:如果你想做的只是检查文档是否存在,对内容完全不感兴趣,使用HEAD方法来代替GET。
  HEAD请求不会返回响应体,只有HTTP头

3: 你也可以禁掉source,只要设置_source=false即可

curl -XGET http://localhost:9200/mydb/students/p1?_source=false\&pretty

 

4:如果不写type,可以用_all来代替,表示在所有的type中选取

5:如果你只想获取source中的一部分内容,还可以用_source_include或者_source_exclude来包含或者过滤其中的某些字段

curl -XGET http://localhost:9200/mydb/students/p1?_source_exclude=name,age\&pretty

 

6:也可以使用fields来选择source中的字段,如:

curl -XGET http://localhost:9200/mydb/students/p1?fields=name,age\&pretty

 

注意:从返回值可以看出,返回的字段是数组类型的,因此只有基本类型的字段可以从fields中进行查询,对象数据是不生效的。

 

7:如果只想获取文档的内容,可以直接指定_source,例如:

curl -XGET http://localhost:9200/mytest/product/p1/_source?pretty

 

 

2.2 Updating Documents

 

Note though that Elaticsearch does not actually do in-place updates under the hood. Whenever we do an update, Elasticsearch deletes the old document and then indexes a new document with the update applied to it in one slot.

文档在ES中是不可变的,如果需要更新已存在的文档,可以替换掉它。

在内部,ES已经标记旧文档为删除并添加了一个完整的新文档。旧版本文档不会立即消失,但你也不能去访问它。ES会在你继续索引更多数据时清理被删除的文档。

修改description字段:

curl -XPOST http://localhost:9200/mydb/students/p1/_update -d {"doc":{  "description" : "huang rong zhang fu"}}

用doc说明要修改的是文档

1. 指定version

ES支持乐观锁这样的机制,利用version,ES可以在操作中指定version参数,如:

curl -XPOST http://localhost:9200/mydb/students/p1/_update?version=1&pretty -d {"doc":{"description" : "she diao da xia"}}

结果为:

{  "error" : "RemoteTransportException[[ysz213][inet[/192.168.1.213:9300]][indices:data/write/update]]; nested: VersionConflictEngineException[[mydb][4] [students][p1]: version conflict, current [2], provided [1]]; ",  "status" : 409}

 

2. 是否强制更新索引

检查是否需要更新索引,如果设置detect_noop为false,那么不管这个字段是否发生了改变,都会重新索引,设置为:
"doc" : { "name" : "new_name" },
"detect_noop": false

3:insert or update

mysql中经常碰见的操作:insert or update
upsert:如果要修改的文档不存在,就执行upsert的部分,形如:

curl -XPOST http://localhost:9200/mydb/students/p10/_update?pretty -d {"doc":{"name":"Yang Guo","age": 18,"description" : "shen diao da xia","wugong": ["9 yin zhen jing","an ran xiao hun zhang"]},"upsert": {"name":"Yang Guo1","age": 18,"class": "Gu mu pai","description" : "shen diao da xia1","wugong": ["9 yin zhen jing1","an ran xiao hun zhang1"]}}

 

假如修改的操作和插入的操作数据相同,还可以直接设置“doc_as_upsert” : true,直接把doc部分当作新的文档插入。

4.  脚本操作

还支持通过脚本修改,不过依赖于groovy这样的动态语言,详情参考scripting

 

2.2 Deleting Dcouments

 

curl -XDELETE http://localhost:9200/mydb/students/p10?prettycurl -XDELETE http://localhost:9200/mydb/students/_query?pretty -d {  "query":{"match":{"name":"Yang Guo"}}}

 

删除一个文档也不会立即从磁盘上移除,它只是被标记成已删除。ES将会在你之后添加更多索引的时候才会在后台进行删除内容的清理。

 

2.3 Batch Processing

 ES还支持_bulk API 以进行批处理操作
 例如批量增加:
 

curl -XPOST localhost:9200/customer/external/_bulk?pretty -d {"index":{"_id":"1"}}{"name": "John Doe" }{"index":{"_id":"2"}}{"name": "Jane Doe" }

 例如批量操作:

curl -XPOST localhost:9200/customer/external/_bulk?pretty -d {"update":{"_id":"1"}}{"doc": { "name": "John Doe becomes Jane Doe" } }{"delete":{"_id":"2"}}

 

三、Exploring Your Data

https://www.elastic.co/guide/en/elasticsearch/reference/1.7/_exploring_your_data.html

先批量导入部分数据,根据官网地址下载..导入,这里略过了

3.1 The Search API

1. 基本查询

有2种最基本的方式可以用来search,一种通过REST request URI ,另一种通过REST request body

curl localhost:9200/bank/_search?q=*&pretty

 

返回结果说明,源自官网,都非常好理解

  • took – time in milliseconds for Elasticsearch to execute the search
  • timed_out – tells us if the search timed out or not
  • _shards – tells us how many shards were searched, as well as a count of the successful/failed searched shards
  • hits – search results
  • hits.total – total number of documents matching our search criteria
  • hits.hits – actual array of search results (defaults to first 10 documents)
  • _score and max_score - ignore these fields for now

默认返回前10条值,uri中支持的params大致上有以下内容:

NameDescription

q

The query string (maps to the query_string query, seeQuery String Query for more details). 查询字符串

df

The default field to use when no field prefix is defined within the query. 当查询没有定义前缀时,默认使用该字段

analyzer

The analyzer name to be used when analyzing the query string. 定义查询使用的分词器

lowercase_expanded_terms

Should terms be automatically lowercased or not. Defaults to true. 搜索的时候忽略大小写

analyze_wildcard

Should wildcard and prefix queries be analyzed or not. Defaults to false. 是否分析通配符或者查询前缀

default_operator

The default operator to be used, can be AND or OR. Defaults to OR

lenient

If set to true will cause format based failures (like providing text to a numeric field) to be ignored. Defaults to false. 如果是true,字段类型转换失败会忽略掉

explain

For each hit, contain an explanation of how scoring of the hits was computed. 解释评分机制

_source

Set to false to disable retrieval of the _source field. You can also retrieve part of the document by using_source_include & _source_exclude (see the request body documentation for more details)

fields

The selective stored fields of the document to return for each hit, comma delimited. Not specifying any value will cause no fields to return. 

sort

Sorting to perform. Can either be in the form offieldName, or fieldName:asc/fieldName:desc. The fieldName can either be an actual field within the document, or the special _score name to indicate sorting based on scores. There can be several sortparameters (order is important).

track_scores

When sorting, set to true in order to still track scores and return them as part of each hit. 评分轨迹,即排序时的评分信息

timeout

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. 超时时间

terminate_after

[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 to indicate whether the query execution has actually terminated_early. Defaults to no terminate_after.  

在每个分片中查询的最大条数,如果设置返回结果中会有一个terminated_early字段

from

The starting from index of the hits to return. Defaults to 0. 开始的记录数

size

The number of hits to return. Defaults to 10. 搜索结果中的条数

search_type

The type of the search operation to perform. Can bedfs_query_then_fetchdfs_query_and_fetch,query_then_fetchquery_and_fetchcountscan. Defaults to query_then_fetch. See Search Type for more details on the different types of search that can be performed. 搜索类型

说明:

(1) timeout:

一般情况下,搜索请求不会超时。通常,协调节点会等待接收所有分片的回答。假如有一个节点遇到问题,整个搜索请求必须等待该节点返回。
Timeout参数告诉协调节点最多等待多久,就可以放弃等待而将已有结果返回,但是返回的部分结果
搜索请求的返回将会指出这个搜索是否超时,以及有多少分片成功答复了。
你可以定义timeout参数为10或者10ms(10毫秒),或者1s(1秒),例如:
GET /_search?timeout=10ms 而ES将返回在请求超时前收集到的结果。

注意:timeout不会停止执行查询,它仅仅告诉你目前顺利返回结果的节点然后关闭连接。在后台,其他分片可能依旧执行查询,尽管结果已经被发送。

 

(2) 多索引搜索

 不像传统数据库支持丰富的join操作,但ES可以通过定义URL中的索引或类型来限定搜索的范围:通过限制搜索的索引或类型,可以在集群中跨所有文档搜索。ES转发搜索请求到集群中的主分片或每个分片的复制分片上,收集结果后选择顶部十个返回给我们。


直接看例子:
  /_search:在所有索引的所有类型中搜索
  /mytest/_search:在索引mytest的所有类型中搜索
  /mytest,test2/_search:在索引mytest和test2的所有类型中搜索
  /my*,t*/_search:在以my或t开头的索引的所有类型中搜索
  /mytest/product/_search:在索引mytest的类型product中搜索
  /mytest,test2/product,user/_search:在索引mytest和test2的类型为product和user中

多类型搜索:
  /_all/product,user/_search:在所有索引的product和user类型中搜索,这里的_all指所有索引,而product,user是指的type

 大致流程:当你搜索包含单一索引时,ES转发搜索请求到这个索引的主分片或每个分片的复制分片上,然后聚集每个分片的结果。而搜索包含多个索引也是同样的方式——只不过或有更多的分片被关联。

 注意:搜索一个索引有5个主分片和5个索引各有一个分片是一样的,都是5个lucene实例.

 

(3) 分页

 ES接受from和size参数:

  size: 结果数,默认10
  from: 跳过开始的结果数,默认0
如果你想每页显示5个结果,页码从1到2,那请求如下:
  GET /_search?size=5
  GET /_search?size=5&from=5

 

(4) 深度分页-不建议的做法

 为什么深度分页有问题?假设在一个有5个主分片的索引中搜索,当请求结果的第一页(结果1到10)时,每个分片产生自己最顶端10个结果然后返回它们给请求节点,它再排序这所有的50个结果以选出顶端的10个结果。

现在假设请求第1000页——结果10001到10010,工作方式都相同,不同的是每个分片都必须产生顶端的10010个结果,然后请求节点排序这50050个结果并丢弃50040个!

可以看到在分布式系统中,排序结果的花费随着分页的深入而成倍增长。这也是为什么网络搜索引擎中任何语句不能返回多于1000个结果的原因。

 

(5) 关于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‘

 

(6) 关于+/-号

 搜索中还可以使用加减号,“+”前缀表示语句匹配条件必须被满足。类似的“-”前缀表示条件必须不被满足。所有条件如果没有+或-表示是可选的——匹配越多,相关的文档就越多,比如:

curl -XGET http://localhost:9200/bank/account/_search?q=+gender:F&pretty

curl -XGET http://localhost:9200/bank/account/_search?q=-gender:F&pretty

 

2. 大致查询流程


技术分享

 

 

 

 

 

3.2 Introduing the Query Language

Query DSL

上个例子中我们使用了

{  "query": { "match_all": {} }}

the query part tell us what our query definition is and the match_all part is simple the type of query that we want to run.

出了query这个参数,还可以继续在json中增加参数,例如size表示结果集大小,默认是10,

比如想要分页

 

curl -XPOST localhost:9200/bank/_search?pretty -d {  "query": { "match_all": {} },  "from": 10,  "size": 10}

 

比如想要排序:

curl -XPOST localhost:9200/bank/_search?pretty -d {  "query": { "match_all": {} },  "sort": { "balance": { "order": "desc" } }}

3.3 Executing Searches.

  现在让我们更深入一点,比如我们可以投影

curl -XPOST localhost:9200/bank/_search?pretty -d {  "query": { "match_all": {} },  "_source": ["account_number", "balance"]}

这里只选择了字段account_number和balance

我们还可以使用match query ,这是种full text的方式

 

curl -XPOST localhost:9200/bank/_search?pretty -d {  "query": { "match": { "address": "mill" } }}

 

This example returns all accounts containing the term "mill" or "lane" in the address:

curl -XPOST localhost:9200/bank/_search?pretty -d {  "query": { "match": { "address": "mill lane" } }}

This example is a variant of match (match_phrase) that returns all accounts containing the phrase "mill lane" in the address:(即mill lane是一个短语,必须是一个短语才返回)

 

curl -XPOST localhost:9200/bank/_search?pretty -d {  "query": { "match_phrase": { "address": "mill lane" } }}

 

下面介绍一些bool query 的例子 ,逻辑运算

curl -XPOST localhost:9200/bank/_search?pretty -d {  "query": {    "bool": {      "must": [        { "match": { "address": "mill" } },        { "match": { "address": "lane" } }      ]    }  }}

上面的例子中,address中 must be true。即同时包含

curl -XPOST localhost:9200/bank/_search?pretty -d {  "query": {    "bool": {      "should": [        { "match": { "address": "mill" } },        { "match": { "address": "lane" } }      ]    }  }}

should则表示应该包含,即or的意思

curl -XPOST localhost:9200/bank/_search?pretty -d {  "query": {    "bool": {      "must_not": [        { "match": { "address": "mill" } },        { "match": { "address": "lane" } }      ]    }  }}

must not 表示非的意思

下面看一个更复杂的例子

 

curl -XPOST localhost:9200/bank/_search?pretty -d {  "query": {    "bool": {      "must": [        { "match": { "age": "40" } }      ],      "must_not": [        { "match": { "state": "ID" } }      ]    }  }}

 

This example returns all accounts of anybody who is 40 years old but don’t live in ID(aho):

 

3.3 Excuting Filters

在以上的内容中,我们有意忽略了一些细节,比如说文档评分 (the document score: _score) .这是一个数值型的值,用来描述文档和我们搜索query的相关度。相关度越高则越靠前。

所有的queries都和相关度有关,如果我们不关心这个问题,ES提供了更加高效的方式,我们称为filter

Filter和queries很相似,但是更高效,以下是选择fitler的最重要原因:

(1) Fitlers do not score so they are faster to execute than queries

(2) Fitlers can be cached in memory allowing repeated search executions to be significantly faster than queries.

我们首先介绍filterd query.

 

curl -XPOST localhost:9200/bank/_search?pretty -d {  "query": {    "filtered": {      "query": { "match_all": {} },      "filter": {        "range": {          "balance": {            "gte": 20000,            "lte": 30000          }        }      }    }  }}

 

这个演示中同时使用了query和fitler

 

3.4 Exeuting Aggregations

ES同样支持聚合操作,最基本的聚合操作,通过state分组,然后返回最上面10个,默认通过count排序

curl -XPOST localhost:9200/bank/_search?pretty -d {  "size": 0,  "aggs": {    "group_by_state": {      "terms": {        "field": "state"      }    }  }}

可以用SQL语句来理解:

SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC

 

继续往下深入:分组且求每组平均数

curl -XPOST ‘localhost:9200/bank/_search?pretty‘ -d ‘{  "size": 0,  "aggs": {    "group_by_state": {      "terms": {        "field": "state"      },      "aggs": {        "average_balance": {          "avg": {            "field": "balance"          }        }      }    }  }}‘

 

继续,求平均数后,通过平均数倒序..

curl -XPOST localhost:9200/bank/_search?pretty -d {  "size": 0,  "aggs": {    "group_by_state": {      "terms": {        "field": "state",        "order": {          "average_balance": "desc"        }      },      "aggs": {        "average_balance": {          "avg": {            "field": "balance"          }        }      }    }  }}

 

ElasticSearch基础(2) - hello world