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" : "<IDEOGRAPHIC>",
      "position" : 0
    },
    {
      "token" : "试",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "<IDEOGRAPHIC>",
      "position" : 1
    },
    {
      "token" : "产",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "<IDEOGRAPHIC>",
      "position" : 2
    },
    {
      "token" : "品",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "<IDEOGRAPHIC>",
      "position" : 3
    },
    {
      "token" : "4",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "<NUM>",
      "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. 大功告成

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容