ElasticSearch
ElasticSearch进阶教程
文档批量操作
多个文档是指,批量操作多个文档。
批量获取文档数据
通过_mget的API来实现的;
- 在URL中不指定index和type
请求方式:GET;
请求地址:_mget;
功能说明 : 可以通过ID批量获取不同index和type的数据;
请求参数:
1、 docs:文档数组参数
_index:指定index;
_type:指定type;
_id:指定id;
_source:指定要查询的手段;
GET _mget
{
"docs": [
{
"_index": "es_db",
"_type": "_doc",
"_id": 1
},
{
"_index": "es_db",
"_type": "_doc",
"_id": 2
}
]
}
- 在URL中指定index
请求方式:GET
请求地址:/{{indexName}}/_mget
功能说明 : 可以通过ID批量获取相同ndex索引库不同type的数据
请求参数:docs 文档数组参数;
_type : 指定type
_id : 指定id
_source : 指定要查询的字段
GET /es_db/_mget
{
"docs": [
{
"_type": "_doc",
"_id": 3
},
{
"_type": "_doc",
"_id": 4
}
]
}
查找es_db索引库下,type为_doc中,id=3,4的文档
- 在URL中指定index和type
请求方式:GET
请求地址:/{{indexName}}/{{typeName}}/_mget
功能说明 : 可以通过ID批量获取相同index和type的数据
请求参数:docs : 文档数组参数
_id : 指定id
_source : 指定要查询的字段
GET /es_db/_doc/_mget
{
"docs": [{
"_id" : 1,
"_source":"name"
},
{
"_id": 3 ,
"_source":"name"
}
]
}
批量操作文档数据
批量对文档进行写操作是通过_bulk的API来实现的
请求方式:POST
请求地址:_bulk
请求参数:通过_bulk操作文档,一般至少有两行参数(或偶数行参数)
第一行参数为指定操作的类型及操作的对象(index,type和id);
第二行参数才是操作的数据
{"actionName":{"_index":"indexName", "_type":"typeName","_id":"id"}}
{"field1":"value1", "field2":"value2"}
actionName:表示操作类型,主要有create,index,delete和update
- 批量创建文档create
POST _bulk
{ "create": {"_index": "article","_type": "_doc", "_id": 3 }}
{ "id": 3,"title": "白起","content": "无敌","tags": [ "java", "面向对象" ],"create_time": "1222222"}
{ "create": { "_index": "article", "_type": "_doc", "_id": 4 }}
{ "id": 4, "title": "玄霄", "content": "无敌","tags": [ "C", "面向对象"], "create_time": "1333333"}
- 普通创建或者全量替换index
如果原文档不存在,则是创建
如果原文档存在,则是替换(全量修改原文档)
POST _bulk
{"index":{"_index":"article","_type":"_doc","_id":3}}
{"id":3,"title":"白起2","content":"无敌2","tags":["java2","面向对象"],"create_time":"12222224"}
{"index":{"_index":"article","_type":"_doc","_id":4}}
{"id":4,"title":"玄霄2","content":"无敌2","tags":["C2","面向对象"],"create_time":"13333334"}
修改操作:
POST _bulk
{"index":{"_index":"article","_type":"_doc","_id":5}}
{"id":5,"title":"白起5","content":"无敌2","tags":["java2","面向对象"],"create_time":"12222224"}
{"index":{"_index":"article","_type":"_doc","_id":6}}
{"id":6,"title":"玄霄6","content":"无敌2","tags":["C2","面向对象"],"create_time":"13333334"}
id不存在,则新创建;
- 批量删除delete
POST _bulk
{"delete":{"_index":"article","_type":"_doc","_id":5}}
{"delete":{"_index":"article","_type":"_doc","_id":6}}
- 批量修改update
POST _bulk
{"update":{"_index":"article","_type":"_doc","_id":3}}
{"doc":{"content":"不灭天宫"}}
DSL语言高级查询
- Domain Specific Language:领域专用语言
- Elasticsearch provides a ful1 Query DSL based on JSON to define
- queries:Elasticsearch提供了基于JSON的DSL来定义查询。
DSL由叶子查询子句和复合查询子句两种子句组成
无条件查询
无查询条件是查询所有,默认是查询所有的,或者使用match_all表示所
有
GET /article/_doc/_search
{
"query":{
"match_all":{}
}
}
//相当于select * from article/_doc ;
有查询条件
叶子条件查询(单字段查询条件)
-
模糊匹配
模糊匹配主要是针对文本类型的字段,文本类型的字段会对内容进行分
词,对查询时,也会对搜索条件进行分词,然后通过倒排索引查找到匹
配的数据,模糊匹配主要通过match等参数来实现;
1)match : 通过match关键词模糊匹配条件内容
2)prefix : 前缀匹配
3)regexp : 通过正则表达式来匹配数据; -
match的复杂用法
match条件还支持以下参数:
query : 指定匹配的值
operator : 匹配条件类型。and : 条件分词后都要匹配;or : 条件分词后有一个匹配即可(默认)
minmum_should_match : 指定最小匹配的数量 -
精确匹配
term : 单个条件相等
terms : 单个字段属于某个值数组内的值
range : 字段属于某个范围内的值
exists : 某个字段的值是否存在
ids : 通过ID批量查询
组合条件查询(多条件查询)
组合条件查询是将叶子条件查询语句进行组合而形成的一个完整的查询
条件;
- bool : 各条件之间有and,or或not的关系;
must : 各个条件都必须满足,即各条件是and的
关系
should : 各个条件有一个满足即可,即各条件
是or的关系
must_not : 不满足所有条件,即各条件是not的
关系
filter : 不计算相关度评分,它不计算_score
即相关度评分,效率更高
constant_score : 不计算相关度评分
must/filter/shoud/must_not 等的子条件是通
过 term/terms/range/ids/exists/match 等叶子条件为参数的
注:以上参数,当只有一个搜索条件时,must等对应的是一个对象,当
是多个条件时,对应的是一个数组
连接查询(多文档合并查询)
父子文档查询:parent/child
嵌套文档查询: nested
查询DSL(query DSL)和过滤DSL(filter DSL)
它们两个的区别如下图
- query DSL
在查询上下文中,查询会回答这个问题——“这个文档匹不匹配这个查询,它的
相关度高么?”
如何验证匹配很好理解,如何计算相关度呢?ES中索引的数据都会存储一个
_score分值,分值越高就代表越匹配。另外关于某个搜索的分值计算还是很复杂
的,因此也需要一定的时间。 - filter DSL
在过滤器上下文中,查询会回答这个问题——“这个文档匹不匹配?”
答案很简单,是或者不是。它不会去计算任何分值,也不会关心返回的排序问
题,因此效率会高一点。
过滤上下文 是在使用filter参数时候的执行环境,比如在bool查询中使用
must_not或者filter
另外,经常使用过滤器,ES会自动的缓存过滤器的内容,这对于查询来说,会提
高很多性能。
Query方式查询案例
- 根据名称精确查询姓名 term, term查询不会对字段进行分词查询,会
采用精确匹配
注意: 采用term精确查询, 查询字段映射类型应为keyword
.
POST /es_db/_doc/_search
{
"query":{
"term":{
"name":"yaoxian"
}
}
}
// select * from student where name = 'admin'
- 根据备注信息模糊查询 match, match会根据该字段的分词器,进行分
词查询
POST /es_db/_doc/_search
{
"from":0,
"size":2,
"query":{
"match":{
"address":"广州"
}
}
}
//select * from user where address like '%广州%' limit 0, 2
- 多字段模糊匹配查询与精准查询 multi_match
POST /es_db/_doc/_search
{
"query":{
"multi_match":{
"query":"yaoxian",
"fields":["address","name"]
}
}
}
//SQL: select * from student where name like '%张三%' or address like '%张
三%
- 未指定字段条件查询 query_string , 含 AND 与 OR 条件
POST /es_db/_doc/_search
{
"from":0,
"size":2,
"query":{
"query_string":{
"query":"yaoxian OR yaoguai"
}
}
}
- 指定字段条件查询 query_string , 含 AND 与 OR 条件
POST /es_db/_doc/_search
{
"from":0,
"size":2,
"query":{
"query_string":{
"query":"yaoxian OR yaoguai",
"fields":["name","address"]
}
}
}
- 范围查询
json请求字符串中部分字段的含义:
range:范围关键字;
gte 大于等于;
lte 小于等于;
gt 大于;
lt 小于;
now 当前时间;
POST /es_db/_doc/_search
{
"from":0,
"size":2,
"query":{
"range":{
"age":{
"gte":16,
"lte":50
}
}
}
}
// select * from user where age between 25 and 2
在这里插入代码片
- 分页、输出字段、排序综合查询
POST /es_db/_doc/_search
{
"from":0,
"size":2,
"_source": ["name","age"],
"sort":{
"age":"desc"
},
"query":{
"range":{
"age":{
"gte":0,
"lte":50
}
}
}
}
- Filter过滤器方式查询,它的查询不会计算相关性分值,也不会对结果进
行排序, 因此效率会高一点,查询的结果可以被缓存。
Filter Context 对数据进行过滤
POST /es_db/_doc/_search
{
"from":0,
"size":2,
"query":{
"bool":{
"filter":{
"term":{
"name":"yaoxian"
}
}
}
}
}
// 查看名字是否为yaoxain
POST /es_db/_doc/_search
{
"from":0,
"size":2,
"query":{
"bool":{
"filter":{
"query_string":{
"query":"yaoxian OR yaoguai"
}
}
}
}
}
总结
- match
match:模糊匹配,需要指定字段名,但是输入会进行分词,比如"hello
world"会进行拆分为hello和world,然后匹配,如果字段中包含hello或者
world,或者都包含的结果都会被查询出来,也就是说match是一个部分匹配的模
糊查询。查询条件相对来说比较宽松。 - term
term: 这种查询和match在有些时候是等价的,比如我们查询单个的词hello,那
么会和match查询结果一样,但是如果查询"hello world",结果就相差很大,因
为这个输入不会进行分词,就是说查询的时候,是查询字段分词结果中是否
有"hello world"的字样,而不是查询字段中包含"hello world"的字样。当保存
文本text数据"hello world"时,elasticsearch会对字段内容进行分词,“hello world"会被分成hello和world,不存在"hello world”,因此这里的查询结果会为空。这也是term查询和match的区别。 - match_phase
match_phase:会对输入做分词,但是需要结果中也包含所有的分词,而且顺序要求一样。以"hello world"为例,要求结果中必须包含hello和world,而且还要求
他们是连着的,顺序也是固定的,hello that word不满足,world hello也不满
足条件。 - query_string
query_string:和match类似,但是match需要指定字段名,query_string是在所有字段中搜索范围更广泛。
文档映射
ES中映射可以分为动态映射和静态映射
动态映射
在关系数据库中,需要事先创建数据库,然后在该数据库下创建数据表,并创建
表字段、类型、长度、主键等,最后才能基于表插入数据。而Elasticsearch中不
需要定义Mapping映射(即关系型数据库的表、字段等),在文档写入
Elasticsearch时,会根据文档字段自动识别类型,这种机制称之为动态映射。
动态映射规则如下:
- 静态映射
静态映射是在Elasticsearch中也可以事先定义好映射,包含文档的各字段类
型、分词器等,这种方式称之为静态映射。
动态映射
- 删除原创建的索引
DELETE /users - 创建索引
PUT /users - 创建文档(ES根据数据类型, 会自动创建映射)
PUT /users/_doc/1
{
"name":"tom",
"age":20,
"sex":1,
"book":"java",
"address":"广州"
}
- 获取文档映射
Get /users/_mapping
静态映射
- 删除原创建的索引
DELETE /users
-
创建索引
PUT /users2 -
设置文档映射
PUT /users2
{
"mappings": {
"properties": {
"name":{"type":"keyword","index": true,"store": true},
"address":{"type":"text","index": true,"store": true},
"book":{"type":"text","index": true,"store": true},
"age":{"type": "integer","index": true,"store": true},
"sex":{"type": "integer","index": true,"store": true}
}
}
}
- 根据静态映射创建文档
PUT /users2/_doc/1
{
"name":"tom",
"age":20,
"sex":1,
"book":"java",
"address":"广州"
}
- 获取文档映射
GET /users2/_mapping
核心数据类型
- 字符串:string,string类型包含 text 和 keyword。
text:该类型被用来索引长文本,在创建索引前会将这些文本进行分词,转化为
词的组合,建立索引;允许es来检索这些词,text类型不能用来排序和聚合。
keyword:该类型不能分词,可以被用来检索过滤、排序和聚合,keyword类型不
可用text进行分词模糊检索。 - 数值型:long、integer、short、byte、double、float
- 日期型:date
- 布尔型:boolean
keyword 与 text 映射类型的区别
name字段为 keyword 映射 (只能精准查询, 不能分词查询,能聚合、
排序)
POST /users2/_doc/_search
{
"from":0,
"size":2,
"query":{
"term":{
"name":"xian"
}
}
}
book 字段为 text 映射能模糊查询, 能分词查询,不能聚合、排序)
POST /users2/_doc/_search
{
"query":{
"match":{
"book":"泛型 java"
}
}
}
对已存在的mapping映射进行修改
具体方法
1)如果要推倒现有的映射, 你得重新建立一个静态索引
2)然后把之前索引里的数据导入到新的索引里
3)删除原创建的索引
4)为新索引起个别名, 为原索引名
POST _reindex
{
"source": {
"index": "users2"
},
"dest": {
"index": "users"
}
}
我们需要先创建一个新的索引映射users,再将users2的数据转移到users中。
通过这几个步骤就实现了索引的平滑过渡,并且是零停机,不需要去重启服务器,只需要在客户端进行命令操作即可。
Elasticsearch乐观并发控制
在数据库领域中,有两种方法来确保并发更新,不会丢失数据:
- 悲观并发控制
这种方法被关系型数据库广泛使用,它假定有变更冲突可能发生,因此阻塞访问
资源以防止冲突。 一个典型的例子是读取一行数据之前先将其锁住,确保只有放
置锁的线程能够对这行数据进行修改。 - 乐观并发控制
Elasticsearch 中使用的这种方法假定冲突是不可能发生的,并且不会阻塞正在
尝试的操作。 然而,如果源数据在读写当中被修改,更新将会失败。应用程序接
下来将决定该如何解决冲突。 例如,可以重试更新、使用新的数据、或者将相关
情况报告给用户。
3、再以创建一个文档为例 - ES老版本(7.0之前)
PUT /users2/_doc/4
{
"name":"daxia",
"age":20,
"sex":1,
"book":"java进阶线程",
"address":"广州"
}
实现_version乐观锁更新文档
PUT /users2/_doc/4?version=1
{
"name":"daxia",
"age":20,
"sex":1,
"book":"java进阶线程",
"address":"广州"
}
- ES新版本不使用version进行并发版本控制 if_seq_no=版本值
&if_primary_term=文档位置
_seq_no:文档版本号,作用同_version
_primary_term:文档所在位置
PUT /users2/_doc/4
{
"name":"dalao",
"age":20,
"sex":1,
"book":"java进阶线程",
"address":"广州"
}
//先修改一下数据,使版本号+1,
POST /users2/_doc/4/?if_seq_no=6&if_primary_term=1
{
"doc":{
"book":"C++"
}
}
//如果版本号等于6,则修改成功,如果不等于6,则修改失败
//再执行同样操作,更新失败;