Elasticsearch 7.x 深入【4】DSL查询【一】term级别的查询

1. 借鉴

极客时间 阮一鸣老师的Elasticsearch核心技术与实战
Elasticsearch搜索引擎第十篇-Query DSL详解
如何使Elasticsearch的前缀查询(prefix query)效果同sql的like 'prefix%'
官方文档 term-level-queries
官方文档 regexp语法
Elasticsearch:fuzzy 搜索 (模糊搜索)
盘点Elasticsearch中的查询套路
官方文档 rewrite

2. 开始

基于term的查询

    1. term级别的查询有以下几种:term/terms/terms set,range,exists,prefix,wildcard,regexp,fuzzy,ids。
    1. 对输入不做分词,这点要注意

接下来,我们以下列数据为例,来介绍以下基于term的查询.

POST /product/_bulk
{"index": { "_id": 1 }}
{"produceId":"HKXL-1234-SKOX", "name": "测试产品1", "date": "2019-05-20", "price": 10}
{"index": { "_id": 2 }}
{"produceId":"UJSK-1234-BSUA", "name": "测试产品2", "price": 20, "date": "2020-04-15"}
{"index": { "_id": 3 }}
{"produceId":"HSYA-1234-MLBS", "name": "测试产品3", "price": 40, "date": "2020-01-20"}
{"index": { "_id": 4 }}
{"produceId":"HSYA-1234-MLBS", "name": "测试产品4", "price": 40, "date": "2020-01-20", "tag": ["机械"]}

term

GET /product/_search
{
  "query": {
    "term": {
      "name": {
        "value": "测试产品4"
      }
    }
  }
}
  • 查询结果如下:
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}
  • 嗯,啥也没有,是不是觉得很奇怪,不是不对输入进行分词吗?为啥查不到呢?我们前有说过索引,对text类型,es会进行分词,默认使用standard分词器进行分词,我们来看一下standard分词器对”测试产品4“的分词结果
GET /_analyze
{
  "text": "测试产品4",
  "analyzer": "standard"
}
  • 分词结果
{
  "tokens" : [
    {
      "token" : "测",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "",
      "position" : 0
    },
    {
      "token" : "试",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "",
      "position" : 1
    },
    {
      "token" : "产",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "",
      "position" : 2
    },
    {
      "token" : "品",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "",
      "position" : 3
    },
    {
      "token" : "4",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "",
      "position" : 4
    }
  ]
}
  • 可以看到是每个字是单独成词,term对输入不进行分词,索引没有匹配上的关键字,自然也就没有文档被检索出来,那我们该如何查询出来呢?可以使用keyword属性进行查询,但必须是精确的【啥意思?就是你要搜索”测试产品4“,必须是”测试产品4“这5个字符全部】
GET /product/_search
{
  "query": {
    "term": {
      "name.keyword": {
        "value": "测试产品4"
      }
    }
  }
}

terms

了解了term,我们来看下terms查询。term是单值匹配,terms就是多值。

GET /product/_search
{
  "query": {
    "terms": {
      "name": [
        "好优肯",
        "4"
      ]
    }
  }
}

terms set

  • 这里直接翻译官网的话了,更容易理解
terms_set查询与terms查询相同,只是您可以定义返回文档所需的匹配术语的数量。例如:
- 字段programming_languages包含一系列已知的编程语言,如c++、java或php,供求职者使用。您可以使用terms_set查询来返回至少匹配这两种语言的文档。
- 一个名为permissions的字段包含应用程序的可能用户权限列表。您可以使用terms_set查询来返回匹配这些权限子集的文档。

在大多数情况下,需要在索引中包含一个数字字段映射来使用terms_set查询。此数字字段包含返回文档所需的匹配项的数目。

# 创建索引
PUT /job-candidates
{
    "mappings": {
        "properties": {
            "name": {
                "type": "keyword"
            },
            "programming_languages": {
                "type": "keyword"
            },
            "required_matches": {
                "type": "long"
            }
        }
    }
}

# 索引两篇文档
PUT /job-candidates/_doc/1?refresh
{
    "name": "Jane Smith",
    "programming_languages": ["c++", "java"],
    "required_matches": 2
}

PUT /job-candidates/_doc/2?refresh
{
    "name": "Jason Response",
    "programming_languages": ["java", "php"],
    "required_matches": 2
}
  • 可以使用required_matches字段值作为返回terms_set查询中的文档所需的匹配项数量。
GET /job-candidates/_search
{
    "query": {
        "terms_set": {
            "programming_languages": {
                "terms": ["c++", "java", "php"],
                "minimum_should_match_field": "required_matches"
            }
        }
    }
}
  • 我们也可以使用script脚本来查询
GET /job-candidates/_search
{
    "query": {
        "terms_set": {
            "programming_languages": {
                "terms": ["c++", "java", "php"],
                "minimum_should_match_script": {
                   "source": "Math.min(params.num_terms, doc['required_matches'].value)"
                }
            }
        }
    }
}

range

# 对数字进行区间查询
GET /product/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 10,
        "lte": 20
      }
    }
  }
}

# 对时间进行区间查询1
GET /product/_search
{
  "query": {
    "range": {
      "date": {
        "lte": "2020-04-20",
        "gte": "2019-01-01"
      }
    }
  }
}

#对时间进行区间查询2
# 查找一年以前的数据
GET /product/_search
{
  "query": {
    "range": {
      "date": {
        "gte": "now-1y"
      }
    }
  }
}
  • 时间表达式
表达式 释义
y
M
w
d
H/h 小时
m 分钟
s
  • 比较表达式
表达式 释义
gt 大于
gte 大于等于
lt 小于
lte 小于等于

exists

GET /product/_search
{
  "query": {
    "exists": {
      "field": "tag"
    }
  }
}

prefix

前缀查询

GET /product/_search
{
  "query": {
    "prefix": {
      "name": {
        "value": "产"
      }
    }
  }
}
  • 查询结果
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 4,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "product",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "produceId" : "HKXL-1234-SKOX",
          "name" : "测试产品1",
          "date" : "2019-05-20",
          "price" : 10
        }
      },
      {
        "_index" : "product",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "produceId" : "UJSK-1234-BSUA",
          "name" : "测试产品2",
          "price" : 20,
          "date" : "2020-04-15"
        }
      },
      {
        "_index" : "product",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 1.0,
        "_source" : {
          "produceId" : "HSYA-1234-MLBS",
          "name" : "测试产品3",
          "price" : 40,
          "date" : "2020-01-20"
        }
      },
      {
        "_index" : "product",
        "_type" : "_doc",
        "_id" : "4",
        "_score" : 1.0,
        "_source" : {
          "produceId" : "HSYA-1234-MLBS",
          "name" : "测试产品4",
          "price" : 40,
          "date" : "2020-01-20",
          "tag" : [
            "机械"
          ]
        }
      }
    ]
  }
}
  • 等等,不是说前缀查询吗,您这也不是前缀查询啊,"测试产品","产"是第三个词啊。
  • 嗯,对于es中的查询,不能忽略的就是他的分词结果,我们在上面确认了它的分词结果,是每个字,单独成词,所以对于每个字来说,他就是自己的前缀,所以,你搜”测试产品“中的任意字都是前缀,那如何来解决呢?大家可以修改分词器,可以参照我借鉴的文章,有关分词器的部分,后续也会更新一篇文章// TODO

wildcard

通配符查询

  • “*”,它匹配任何字符序列(包括空字符)
  • “?”,它匹配任何单个字符。
  • 为了防止极慢的通配符查询,通配符项不应以通配符 “*” 或 “?” 开头
GET /product/_search
{
  "query": {
    "wildcard": {
      "name.keyword": {
        "value": "测试产品?"
      }
    }
  }
}

regexp

正则查询

GET /product/_search
{
  "query": {
    "wildcard": {
      "name.keyword": {
        "value": "测试产品?"
      }
    }
  }
}

我们来看下它都有哪些操作符【官方直译】

操作符 释义 例子
.                   匹配任意字符 -
? 重复0次或1次 abc?
matches 'ab' and 'abc'                                                                            
+ 重复1次或多次 ab+
matches 'ab', 'abb', 'abbb', ...
* 重复0次或多次 ab*
matches 'a', 'ab', 'abb', 'abbb', ...
{} 可重复的最小,最大次数 a{2}
matches 'aa'

a{2,4}
matches 'aa', 'aaa', and 'aaaa'

a{2,}
matches 'a` repeated two or more times
| 如果左边或右边最长的模式匹配,则匹配将成功 abc|xyz
matches 'abc' and 'xyz'
( … ) 形成一个组。可以使用组将表达式的一部分视为单个字符 abc(def)?
matches 'abc' and 'abcdef' but not 'abcd'
[ … ] 1.匹配括号中的一个字符
2.在方括号内—表示一个范围,除非—是第一个字符或转义字符
3.方括号中的字符前的^将使字符或范围无效。
1.[abc]
matches 'a', 'b', 'c'

2.[a-c]
matches 'a', 'b', or 'c'

[-abc]
'-' is first character. Matches '-', 'a', 'b', or 'c'

[abc-]
Escapes '-'. Matches 'a', 'b', 'c', or '-'

3.[^abc]
matches any character except 'a', 'b', or 'c'

[^a-c]
matches any character except 'a', 'b', or 'c'

[^-abc]
matches any character except '-', 'a', 'b', or 'c'

[^abc-]
matches any character except 'a', 'b', 'c', or '-'

我们可以使用flags参数为Lucene的正则表达式引擎启用更多的可选操作符。要启用多个操作符,使用|分隔符

GET /product/_search
{
    "query": {
        "regexp":{
            "name.keyword": {
                "value": "测.+&.+试.*",
                "flags" : "INTERSECTION"
            }
        }
    }
}

我们看下flags都有哪些类型

操作符 释义 例子
ALL 启用所有可选操作 -
COMPLEMENT 启用~操作 a~bc
matches 'adc' and 'aec' but not 'abc'
INTERVAL 启用<>操作,可以用作匹配一个数字的区间 foo<1-100>
matches 'foo1', 'foo2' ... 'foo99', 'foo100'

foo<01-100>
matches 'foo01', 'foo02' ... 'foo99', 'foo100'
INTERSECTION 启用&操作,相当于AND aaa.+&.+bbb
matches 'aaabbb'
ANYSTRING 启用@操作 @&~(abc.+)
matches everything except terms beginning with 'abc'

fuzzy

模糊查询

# 添加三篇测试文档
PUT /fuzz/_doc/1
{
  "name": "sunruikai 123"
}

PUT /fuzz/_doc/2
{
  "name": "gabriella 456"
}

PUT /fuzz/_doc/3
{
  "name": "test kak"
}
  • 执行查询
GET /fuzz/_search
{
  "query": {
    "fuzzy": {
      "name": {
        "value": "sunruilai"
      }
    }
  }
}

当然,除了value它还有其他属性

属性 释义
fuzziness                                                                         允许匹配的最大编辑距离
max_expansions 查询中的词项可以扩展的数目,默认为50【避免在max_expansions参数中使用高值,特别是当prefix_length参数值为0时。max_expansions参数中的高值会导致性能低下,因为要检查的变量太多。】
prefix_length 指明区分词项的共同前缀长度,默认是0
transpositions 表示编辑是否包含两个相邻字符的移位(ab→ba),默认为true
rewrite 有6中方式,constant_score(默认),constant_score_boolean,scoring_boolean,top_terms_blended_freqs_N,top_terms_boost_N,top_terms_N

ids

见名知意,就是通过一堆id进行查询

GET /product/_search
{
  "query": {
    "ids": {
      "values": [1,2,3]
    }
  }
}

costanct_score

不计算得分

GET /product/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "produceId.keyword": "HKXL-1234-SKOX"
        }
      }
    }
  }
}

3. 大功告成

你可能感兴趣的:(Elasticsearch 7.x 深入【4】DSL查询【一】term级别的查询)