Bootstrap

ElasticSearch学习笔记七:ES查询(二)

一、前言

在前面的文章我们学习了ES的一些基本查询,同时用Java全部实现了一遍,今天我们继续深入学习一下ES的查询。

二、Term级别查询

在 Elasticsearch 中,Term 级别查询用于精确匹配字段值。与全文搜索不同,Term 级别查询不会进行分词处理,而是直接匹配字段的完整值。这使得 Term 级别查询非常适合用于精确匹配、过滤和聚合操作。

1、Term查询

Term是ES中用于精确匹配的关键字,可以理解为Mysql中的等于,例如我现在想搜索城市是“北京”的酒店就可以这么写:

GET /hotel/_search
{
  "query": {
    "term": {
      "city": {
        "value": "北京"
      }
    }
  }
}
这段DSL翻译成Mysql就是 select * from hotel where `city` = '北京'

需要注意的是,尽量避免使用Term去查询Text类型的数据,因为Text会被分词,这会导致精准匹配变得困难,如果想搜索Text类型的数据可以使用Match(后面也会讲)

2、Terms查询

和Term类型,Terms也适用于精准匹配,但从名字上我们可以看出Terms是复数形式,意味着他可以支持多个条件(当然Term结合bool查询也能实现),例如我们现在想查询城市是青岛或者北京的酒店就可以这么写

GET /hotel/_search
{
  "query": {
    "terms": {
      "city": [
        "青岛",
        "北京"
      ]
    }
  }
}
这段DSL翻译成Mysql就是 select * from hotel where `city` in ('北京','青岛')

terms默认最多支持65,536个值,如果要修改的话可以修改该参数 index.max_terms_count

3、Terms Set

terms_set 是 Elasticsearch 中的一种查询类型,用于执行基于集合的条件查询。与普通的 terms 查询不同,terms_set 查询允许你定义更复杂的条件,例如集合中元素的数量必须满足某些条件。

PUT /programmer
{
  "mappings": {
    "properties": {
      "name":{
        "type": "keyword"
      },
      "skill":{
        "type": "keyword"
      },
      "required_matches":{
        "type":"Integer"
      }
    }
  }
}

我们的程序员索引有两个字段,姓名和技能,同时我们写入数据
POST /programmer/_bulk
{"index":{"_index":"programmer","_id":"1"}}
{"name":"张三","skill":["Java","C++","Python","JavaScript"],"required_matches":2}
{"index":{"_index":"programmer","_id":"2"}}
{"name":"李四","skill":"Python","required_matches":1}
{"index":{"_index":"programmer","_id":"3"}}
{"name":"王五","skill":["C++","Python"],"required_matches":2}

现在我们查询会C++、Python的程序员且至少会其中的几种的,在上述的文档里,张三我们设置的需要匹配(required_matches字段)等于2,李四=1,王五=2,此时我们的DSL如下

GET /programmer/_search
{
  "query": {
    "terms_set": {
      "skill": {
        "terms": [ "C++", "Python"],
        "minimum_should_match_field":"required_matches"
      }
    }
  }
}

结果如下
#! Elasticsearch built-in security features are not enabled. Without authentication, your cluster could be accessible to anyone. See https://www.elastic.co/guide/en/elasticsearch/reference/7.17/security-minimal-setup.html to enable security.
{
  "took" : 878,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 0.7876643,
    "hits" : [
      {
        "_index" : "programmer",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.7876643,
        "_source" : {
          "name" : "张三",
          "skill" : [
            "Java",
            "C++",
            "Python",
            "JavaScript"
          ],
          "required_matches" : 2
        }
      },
      {
        "_index" : "programmer",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 0.7876643,
        "_source" : {
          "name" : "王五",
          "skill" : [
            "C++",
            "Python"
          ],
          "required_matches" : 2
        }
      },
      {
        "_index" : "programmer",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.17426977,
        "_source" : {
          "name" : "李四",
          "skill" : "Python",
          "required_matches" : 1
        }
      }
    ]
  }
}


可以看到所有的文档都返回了,我们来分析一下为什么结果是这样的。

(1)首先我们的查询条件是跟据skill字段terms精准匹配

(2)minimum_should_match_field使用的是 required_matches字段

(3)terms的值为C++和Python,张三会 “Java”,“C++”,“Python”,“JavaScript”,同时只需要满足两个就算匹配上,很显然是符合条件的,所以张三返回

(4)李四,虽然李四只会Python但是他设置的是只要满足一个条件就算匹配上,所以李四也匹配上了。

(5)最后王五,也是同样的道理。所以这次返回了张三、李四、王五。

4、exists

exists不是用来查询文档的,而是用来查询索引的,比如我们想看那些索引包含指定的字段,例如我想查看包含“skill”字段的索引就可以用exists

GET /_search
{
  "query": {
    "exists": {
      "field": "skill"
    }
  }
}

5、Range

Rang,用于范围匹配,例如我们想查询价格300~500的酒店,DSL可以这么写

GET /hotel/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 300,
        "lte": 500
      }
    }
  }
}
这个DSL翻译成SQL 就是 select * from hotel where price >= 300 and price <=500

range可用的参数有

gt(greater than ):大于

gte(greater than or equal):大于等于

lt(less than):小于

lte(less than or equal):小于等于

format:格式化,用于日期类型比较

6、prefix

前缀匹配,有点类似于Mysql中的前缀模糊匹配,需要注意的是prefix 查询默认是不适用于 text 类型字段的。这是因为 text 字段在索引时会被分析器(analyzer)拆分成多个词项(tokens),而 prefix 查询则需要匹配完整的前缀。比如我们想搜索张开头程序员,DSL就可以这么写

GET /programmer/_search
{
  "query": {
    "prefix": {
      "name": {
        "value": "张"
      }
    }
  }
}
7、IDs

IDs查询,就是根据文档的ID查询,这个没有太多东西可以说

GET /hotel/_search
{
  "query": {
    "ids" : {
      "values" : ["001","002"]
    }
  }
}

8、Fuzzy

Fuzzy查询,模糊查询或者说近似匹配,例如电商的里的搜索场景

-  用户输入的查询词可能存在拼写错误,Fuzzy 查询可以帮助返回与拼写错误相似的结果。 
-  例如,用户搜索 "aple" 时,Fuzzy 查询可以返回 "apple" 相关的结果。 

    这里先举个例子,后面再详细学习
GET /_search
{
  "query": {
    "fuzzy": {
      "user.id": {
        "value": "ki",
        "fuzziness": "AUTO",
        "max_expansions": 50,
        "prefix_length": 0,
        "transpositions": true,
        "rewrite": "constant_score"
      }
    }
  }
}

三、结束语

今天学习了一下ES中的 Term级别查询,ES中还有其他类型的搜索后面将继续,同时下一篇文章将用Java把上述的DSL全部实现一遍,希望对有所帮助。

;