es版本发布相当快,从1.x到2.x,再直接到5.x,6.x
索引这个词在es中有多重意思:
索引(名词):一个索引类似于传统数据库中的一个表,用于存储关系型文档。索引的复数为indexes或indices。
索引(动词):索引一个文档就是存储一个文档到一个索引中以便它可以被检索和查询到,相当于SQL中的upsert。
倒排索引:关系型数据库通过增加一个索引比如B-tree索引到指定列上,以便提升数据检索速度。es使用了一个叫做倒排索引的结构来达到相同的目的。默认情况下,一个文档中的每一个属性都是被索引的(有一个倒排索引的)和可搜索的。一个没有倒排索引的属性是不能被搜索到的。
es是利用分片将数据分发到集群各处的。分片是数据的容器,文档保存在分片内,分片又被分配到集群内的各个节点里。当集群扩容时,es会自动在各节点中迁移分片,使得数据依然均匀分布在集群里。一个分片可以是主分片或者副本分片。副本分片是主分片的拷贝。技术上说,一个主分片最多能够存储Integer.MAX_VALUE - 128个文档。在索引建立时需要指定主分片数(number_of_shards,默认5个主分片),索引创建好后,主分片数就不能修改了,因为es是按照hash(routing)/number_of_shards进行路由文档的,number_of_shards假如有变动的话,那么存进去的数据就查不到了。如果想扩容分片,那么只能reindex。routing默认是文档的id,也可以显式指定。副本分片数(number_of_replicas)可以随时修改。
在es中每个文档都有一个版本号,当每次对文档进行修改时(包括删除),_version 的值都会递增。
本次学习的是es6.3.1版本,看的是6.3版本的官方文档。
新建一个没有任何setting和mapping的索引:
put /website
新建一个3个分片、1个副本的索引:
put /website
{
"settings":{
"number_of_shards":3,
"number_of_replicas":1
}
}
新建一个3个分片、1个副本,且有一个keyword类型的id字段的索引:
put /website
{
"settings":{
"number_of_shards":3,
"number_of_replicas":1
},
"mappings":{
"blog":{
"properties":{
"id":{
"type":"keyword"
}
}
}
}
}
blog是索引的type。properties是关键字。
查看索引的aliases、settings、mappings:
get /website
更改索引的副本数:
put /website/_settings
{
"settings":{
"number_of_replicas":0
}
}
给索引添加新映射:
put /website/_mapping/blog
{
"properties":{
"age":{
"type":"integer"
}
}
}
查看某index的alias:
get /${index}/_alias
如 get /posts/_alias
往某索引添加数据,指定id,put、post都可以:
put /website/blog/1
{
"id":"1",
"name":"zhangsan"
}
往某索引添加数据,不指定id,只能用post:
post /website/blog
{
"name":"lisi",
"password":"lisi"
}
返回文档的一部分:
get /website/blog/1?_source=id,name
只返回_source数据,不要_index、_type、_id等元数据:
get /website/blog/1/_source
更新整个文档,再次put该文档即可:
put /website/blog/1
{
"password":"lisi",
"city":"henan"
}
在内部,es将旧文档标记为已删除,并增加一个全新的文档。尽管不能再对旧版本文档进行访问,但它并不会立即消失,当继续索引更多数据时,es会在后台清理这些已删除的文档。
部分更新文档,用_update关键字。将接收到的文档与现有文档合并,覆盖现有字段,增加新字段。有doc和script两种方式。doc更简单,但script能支持更多功能。
doc方式:
post /website/blog/1/_update
{
"doc":{
"password":"abc123",
"title":"woshititle"
}
}
此时id=1的文档数据为
{
"password": "abc123",
"city": "henan",
"title": "woshititle"
}
script方式:
post /website/blog/1/_update
{
"script": {
"source": "ctx._source.title='haizeiwang';ctx._source.birthday='1993/04/25';"
}
}
此时id=1的文档数据为
{
"password": "abc123",
"city": "henan",
"title": "haizeiwang",
"birthday": "1993/04/25"
}
一次取回多个文档,用_mget。既可以一次从单个索引中取多个文档,也可以一次从多个索引中取多个文档。即使某一个文档不存在,也不会报错:
一次从单个索引中取多个文档:
get /website/blog/_mget
{
"docs": [{
"_id": 1
},{
"_id": 123
},{
"_id": 1234
}]
}
_id指定文档的id。也可以省略docs数组,用ids数组,如下
get /website/blog/_mget
{
"ids": [1,123,1234]
}
一次从多个索引中取多个文档:因为多个文档在多个索引中,所以需要指定每个文档所属的索引
get /_mget
{
"docs": [{
"_index": "website",
"_type": "blog",
"_id": 1
},{
"_index": "spark",
"_type": "index",
"_id": 1
}]
}
如上,docs 数组包含要返回的多个文档信息,主要是用于指定index的_index、用于指定type的_type、用于指定id的_id。
批量操作,用_bulk。允许在单次请求中依次执行多个index、update、delete操作:
post _bulk
{"index":{"_index":"spark","_type":"index","_id" :"2"}}
{"name":"nimabi"}
{"index":{"_index":"spark","_type":"index","_id":"3"}}
{"age":"18"}
{"update":{"_index":"spark","_type":"index","_id":"2"}}
{"doc":{"age":"25"}}
{"delete":{"_index":"spark","_type":"index","_id":"3"}}
多索引查询:
在所有索引中搜索:get /_search
在spark索引中搜索:get /spark/_search
在spark索引中搜索index类型:get /spark/index/_search
在spark和website索引中搜索:get /spark,website/_search
在以s开头的索引中搜索:get /s*/_search
在spark和website索引中搜索index和blog类型:get /spark,website/index,blog/_search
在所有索引中搜索index和blog类型:get /_all/index,blog/_search
text和keyword的区别:
text和keyword都表示字符串类型(从5.x版本开始,废弃了string类型,拆分为text和keyword)
text类型的值会被分析器处理(默认分析器是standard,中文的话需要引入ik分析器),并放入倒排索引。不能用于排序、聚合(即根据此字段排序会报错),除非显式指定 fielddata=true。
keyword类型的值不会被分析器处理,可用于排序、聚合
某字符串会被某分析器处理成什么样子:
post /_analyze
{
"analyzer": "standard",
"text": "I want to be a hero"
}
post /_analyze
{
"analyzer": "ik_max_word",
"text": "我是需要测试的字符串"
}
其中analyzer的值是要使用的分析器,text值是要测试的字符串。ik分词器不是预置的,需要自己安装。
某字符串值已经post进某index某type,如何查看分词情况:
get /${index}/${type}/${id}/_termvectors?fields=${fields_name}
如 get /posts/doc/1/_termvectors?fields=message,其中posts是index,doc是type,1是id,message是对应的字段名
在es中,如果想对一个字符串类型的字段既执行match query,又执行term query,则可以设置该字段type属性为text,同时需要设置其fields属性,值是一个json对象,json对象中有一个key,名称通常是keyword或者raw,值同样是一个json对象,key是type,值是keyword。
示例如下,既想match query name字段,又想term query name字段:
put /my_index/_mapping/_doc
{
"properties":{
"name":{
"type":"text",
"fields":{
"keyword":{
"type":"keyword"
}
}
}
}
}
text类型字段默认是不能用来做排序字段的,如果必须依此字段排序,有2种解决方案:
假如字段名是name,
第一种方案:在put mapping时设置该字段的fields属性。我们就可以通过name.keyword来排序了。这种方案比较好,因为不仅可以排序了,还可以term query了,一举两得。
第二种方案:在put mapping时设置该字段的fielddata属性值为true。我们就可以通过name字段排序了。