🥖ElasticSearch-05-DSL高级查询和关键字说明
✨DSL概述
DSL:Domain Specific Language 领域专用语言,DSL由叶子查询子句和复合查询子句两种子句组成。

✨无条件查询
无查询条件是查询所有,默认是查询所有的,或者使用match_all表示所有
GET /ralph_index/_doc/_search{"query": {"match_all": {}}}
基本不用
✨有条件查询
模糊匹配
模糊匹配主要是针对文本类型的字段,文本类型的字段会对内容进行分词,对查询时,也会对搜索条件进行分词,然后通过倒排索引查找到匹配的数据,模糊匹配主要通过match等参数来实现
match : 通过match关键词模糊匹配条件内容
- query : 指定匹配的值
operator : 匹配条件类型
- and : 条件分词后都要匹配
- or : 条件分词后有一个匹配即可(默认)
- minmum_should_match : 指定最小匹配的数量
- prefix : 前缀匹配
GET /ralph_index/_doc/_search{"query": {"prefix": {"address": {"value": "880"}}}}

- regexp : 通过正则表达式来匹配数据
通过正则表达式查询
以”502”开头的address字段

组合条件查询
组合条件查询是将叶子条件查询语句进行组合而形成的一个完整的查询条件
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
✨queries 和 filter的区别

queries
在查询上下文中,查询会回答这个问题——“这个文档匹不匹配这个查询,它的相关度高么?如何验证匹配很好理解,如何计算相关度呢?ES中索引的数据都会存储一个_score分值,分值越高就代表越匹配。另外关于某个搜索的分值计算还是很复杂的,因此也需要一定的时间。
filter
在过滤器上下文中,查询会回答这个问题——“这个文档匹不匹配?答案很简单,是或者不是。它不会去计算任何分值,也不会关心返回的排序问题,因此效率会高一点。过滤上下文 是在使用filter参数时候的执行环境,比如在bool查询中使用must_not或者filter另外,经常使用过滤器,ES会自动的缓存过滤器的内容,这对于查询来说,会提高很多性能。
queries 会在匹配的基础上面拷贝
_score的分值
✨ 其他的关键字
- 数据准备
```sh
PUT /test-dsl-term-level
{
“mappings”: {
“properties”: {
} } }"name": {"type": "keyword"},"programming_languages": {"type": "keyword"},"required_matches": {"type": "long"}
POST /test-dsl-term-level/_bulk {“index”:{“_id”:1}} {“name”:”Jane Smith”,”programming_languages”:[“c++”,”java”],”required_matches”:2} {“index”:{“_id”:2}} {“name”:”Jason Response”,”programming_languages”:[“java”,”php”],”required_matches”:2} {“index”:{“_id”:3}} {“name”:”Dave Pdai”,”programming_languages”:[“java”,”c++”,”php”],”required_matches”:3,”remarks”:”hello world”}
<a name="9c1941c0"></a>### 字段是否存在:exist由于多种原因,文档字段的索引值可能不存在:- 源JSON中的字段是`null`或`[]`- 该字段已"index" : false在映射中设置- 字段值的长度超出`ignore_above`了映射中的设置- 字段值格式错误,并且`ignore_malformed`已在映射中定义所以exist表示查找是否存在字段。```bashGET /ralph_index/_doc/_search{"query": {"exists": {"field":"age"}}}
结果
"hits" : {"total" : {"value" : 1000,"relation" : "eq"},
id查询:ids
GET /ralph_index/_doc/_search{"query": {"ids": {"values": [3, 1]}}}

分词匹配:term
前文最常见的根据分词查询
GET /ralph_index/_doc/_search{"query": {"term": {"age": 30}}}

多个分词匹配:terms
按照读个分词term匹配,它们是or的关系
GET /test-dsl-term-level/_search{"query": {"terms": {"programming_languages": ["php","c++"]}}}

按某个数字字段分词匹配:term set
设计这种方式查询的初衷是用文档中的数字字段动态匹配查询满足term的个数
GET /test-dsl-term-level/_search{"query": {"terms_set": {"programming_languages": {"terms": [ "java", "php" ],"minimum_should_match_field": "required_matches"}}}}

通配符:wildcard
通配符匹配,比如*
GET /test-dsl-term-level/_search{"query": {"wildcard": {"name": {"value": "D*ai","boost": 1.0,"rewrite": "constant_score"}}}}

范围:range
常常被用在数字或者日期范围的查询
GET /test-dsl-term-level/_search{"query": {"range": {"required_matches": {"gte": 3,"lte": 4}}}}

模糊匹配:fuzzy
官方文档对模糊匹配:编辑距离是将一个术语转换为另一个术语所需的一个字符更改的次数。这些更改可以包括:
更改字符(box→ fox)
删除字符(black→ lack)
插入字符(sic→ sick)
转置两个相邻字符(act→ cat)

✨ 文档映射 mapping
ES中映射可以分为
动态映射和静态映射
动态映射:
在关系数据库中,需要事先创建数据库,然后在该数据库下创建数据表,并创建表字段、类型、长度、主键等,最后才能基于表插入数据。而Elasticsearch中不需要定义Mapping映射(即关系型数据库的表、字段等),在文档写入Elasticsearch时,会根据文档字段自动识别类型,这种机制称之为动态映射。

- 静态映射:
静态映射是在Elasticsearch中也可以事先定义好映射,包含文档的各字段类型、分词器等,这种方式称之为静态映射。
动态映射-创建索引
PUT /es_db
动态映射-创建文档(ES根据数据类型, 会自动创建映射)
PUT /es_db/_doc/1{"name": "Jack","sex": 1,"age": 25,"book": "java入门至精通","address": "广州小蛮腰"}
动态映射-获取文档映射
GET /es_db/_mapping

删除原创建的索引
DELETE /es_db
静态映射-创建索引
PUT /es_db
静态映射-设置文档映射
PUT /es_db{"mappings": {"properties": {"name": {"type": "keyword","index": true,"store": true},"sex": {"type": "integer","index": true,"store": true},"age": {"type": "integer","index": true,"store": true},"book": {"type": "text","index": true,"store": true},"address": {"type": "text","index": true,"store": true}}}}
在创建映射的时候有两只类型一个是text、一个是keyword,那么具体作用如下:
text:该类型被用来索引长文本,在创建索引前会将这些文本进行分词,转化为词的组合,
建立索引;允许es来检索这些词,text类型不能用来排序和聚合。
keyword:该类型不能分词,可以被用来检索过滤、排序和聚合,keyword类型不可用text进行分词模糊检索。
数值型:long、integer、short、byte、double、float
日期型:date
布尔型:boolean
| keyword | text | |
|---|---|---|
| 精准查询 | ✓ | ✓ |
| 分词查询 | ✘ | ✓ |
| 聚合 | ✓ | ✘ |
| 排序 | ✓ | ✘ |
修改mapping映射
具体方法
- 如果要推倒现有的映射, 你得重新建立一个静态索引
- 然后把之前索引里的数据导入到新的索引里
- 删除原创建的索引
- 为新索引起个别名, 为原索引名
POST _reindex{"source": {"index": "db_index"},"dest": {"index": "db_index_2"}}DELETE /db_indexPUT /db_index_2/_alias/db_index
通过这几个步骤就实现了索引的平滑过渡,并且是零停机
✨ Elasticsearch乐观并发控制
在数据库领域中,有两种方法来确保并发更新,不会丢失数据:
1、悲观并发控制
这种方法被关系型数据库广泛使用,它假定有变更冲突可能发生,因此阻塞访问资源以防止冲突。 一个典型的例子是读取一行数据之前先将其锁住,确保只有放置锁的线程能够对这行数据进行修改。
2、乐观并发控制
Elasticsearch 中使用的这种方法假定冲突是不可能发生的,并且不会阻塞正在尝试的操作。 然而,如果源数据在读写当中被修改,更新将会失败。应用程序接下来将决定该如何解决冲突。 例如,可以重试更新、使用新的数据、或者将相关情况报告给用户。
ES老版本(7.X以前)使用version进行并发版本控制
PUT /db_index/_doc/1?version=1
ES新版本(7.x) if_seq_no=版本值&if_primary_term=文档位置
- _seq_no :文档版本号,作用同 _version
- _primary_term:文档所在位置
POST /es_sc/_update/1/?if_seq_no=1&if_primary_term=1
✨ 总结
1. match
match:模糊匹配,需要指定字段名,但是输入会进行分词,比如”hello world”会进行拆分为hello和world,然后匹配,如果字段中包含hello或者world,或者都包含的结果都会被查询出来,也就是说match是一个部分匹配的模糊查询。查询条件相对来说比较宽松。
2. term
term: 这种查询和match在有些时候是等价的,比如我们查询单个的词hello,那么会和match查询结果一样,但是如果查询”hello world”,结果就相差很大,因为这个输入不会进行分词,就是说查询的时候,是查询字段分词结果中是否有”hello world”的字样,而不是查询字段中包含”hello world”的字样。当保存数据”hello world”时,elasticsearch会对字段内容进行分词,”hello world”会被分成hello和world,不存在”hello world”,因此这里的查询结果会为空。这也是term查询和match的区别。
| term | match | |
|---|---|---|
| 查询keyword字段 | term不会分词。而keyword字段也不分词。需要完全匹配才可 | match会被分词,而keyword不会被分词,match的需要跟keyword的完全匹配才可。 |
| 查询text字段 | text字段会分词,而term不分词,所以term查询的条件必须是text字段分词后的某一个 | match分词,text也分词,只要match的分词结果和text的分词结果有相同的就匹配 |
3. match_phase
match_phase:会对输入做分词,但是需要结果中也包含所有的分词,而且顺序要求一样。以”hello world”为例,要求结果中必须包含hello和world,而且还要求他们是连着的,顺序也是固定的,hello that world不满足,world hello也不满足条件。
4. query_string
query_string:和match类似,但是match需要指定字段名,query_string是在所有字段中搜索,范围更广泛。
参考文章
https://pdai.tech/md/db/nosql-es/elasticsearch-x-dsl-term.html
