Elasticsearch 数据查询实践

国破山河在,城春草木深。

1 前言

Elasticsearch 是一个开源的分布式搜索和分析引擎,其强大的查询能力支撑了复杂的业务查询。其领域特定语言DSL(Domain Specific Language)是通过 JSON 构建的,非常灵活的支持多种方式的查询。本文将结合业务实践,总结使用 ES 查询 DSL 的结构和常用的查询方法,巩固使用 ES 查询数据的基础知识。

2 DSL 介绍

DSL 常用的搜索操作包括:

  • 全文搜索 对文本字段执行搜索,支持短语搜索和模糊匹配。
  • 结构化搜索 对结构化的数据字段进行搜索,比如日期、数字、枚举等。
  • 复合查询 多种查询条件的组合,比如 bool 查询,通常用来执行复杂的搜索条件。
  • 数据聚合 对数据进行聚合查询,用于执行统计信息。
  • 结果排序 对查询的结果根据一个或者多个字段进行排序后展示。
  • 数据分页 使用 from 和 size 来控制数据的返回数量和偏移量,实现查询的分页。

ES 中,查询主要分为两大类:

  • Leaf Query(叶查询) : 应用于简单的数据查询,直接与文档中的字段进行匹配。例如 matchmulti_matchidstermrange 等查询。

  • Compound Query(复合查询) :结合多个叶查询或其他复合查询,构建复杂的查询逻辑。例如 bool 查询、dis_max 查询等。

2 ES 叶查询

通常情况下,ES 的查询是以 GET 请求的方式,起手式如下所示:

json 复制代码
GET /index_name/_search
{
  "query": {
    // 查询结构
  }
}

一些简单的查询语句构建如下所示:

go 复制代码
`match` 查询是最常用的查询之一,它会对查询字段进行分词并匹配分词后的内容。
{ "query": { "match": { "field_name": "查询参数" } } }

`multi_match` 通常用于指定多个查询字段,查询的内容都是相同的。
{ "query": { "multi_match": { "query": "查询参数", "fields": ["brand","name"] }} }

`term` 查询用于精确匹配字段值,不会进行分词。适合用于关键字 `keyword`类型字段查询。
{ "query": { "term": { "field_name": "查询参数" } } }

`terms` 查询多个精确匹配字段,同 `term` 类似 。
{ "query": { "terms": { "field_name": ["查询参数", "查询参数2"] } } }

`range` 查询用于范围查询,适用于日期、数字等范围类型的数据,查询的条件如(gte lte gt lt 等)
{ "query": { "range": { "age": { "gte": 10, "lte": 20 } } } }
range 查询也可以为如下格式,include_lower 和 include_upper 表示为是否包含上下界
{ "query":{ "range" : { "id" : { "from" : 150, "to" : 180, "include_lower" : true,"include_upper" : true } }}}

`exists` 查询用于检查某个字段是否存在。
{ "query": { "exists": { "field": "字段名称" } } }

`wildcard` 查询支持使用通配符进行模糊匹配。它允许使用 `*` 表示任意数量的字符,`?` 表示单个字符。适用于 `keyword` 类型字段。
{ "query": { "wildcard": { "field_name": "val*" } } }

`prefix` 查询用于查找以某个前缀开始的值,适用于 `keyword` 类型字段。
{ "query": { "fuzzy": { "field_name": { "value": "quikc", "fuzziness": "AUTO" } } } }

`fuzzy` 查询用于查找与指定的词语相近的匹配,支持拼写错误等模糊匹配。可通过 `fuzziness` 参数指定允许的编辑距离。`fuzziness` 可以设置为 `AUTO`、1 或 2,表示允许的编辑距离。
{ "query": { "fuzzy": { "field_name": { "value": "quikc", "fuzziness": "AUTO" } } } }

`ids` 查询用于通过文档的 `_id` 字段查询。
{ "query": { "ids": { "values": ["1", "2", "3"] } } }

3 ES 复合查询

复合查询用于将多个叶查询组合在一起。复合查询可以通过布尔逻辑(例如 ANDOR)或评分(boost)等方式来组合查询。在 bool 查询语句中,是按照子句的顺序进行执行,每个子句中可以放置单独的逻辑。

go 复制代码
`must`: 所有的子查询必须匹配,相当于逻辑 AND。
`should`: 至少有一个子查询匹配,相当于逻辑 OR。
`must_not`: 子查询不匹配,相当于逻辑 NOT。
`filter`: 和 `must` 类似,但不会影响相关性评分。

{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "查询内容" } }
      ],
      "filter": [
        { "term": { "status": "状态信息" } }
      ],
      "must_not": [
        { "range": { "publish_date": { "lt": "2022-01-01" } } }
      ],
      "should": [
        { "match": { "author": "作者信息" } },
        { "match": { "tags": "标签信息" } }
      ],
      "minimum_should_match": 1
    }
  }
}

minimum_should_match 是用来控制查询的经度,如果 bool 查询中仅包含 mustmust_notfilter ,没有 should 子句,那么该值为0,也不起作用。如果包含有 should 子句,那么该值就是用来标识匹配的子句配置,该项配置可以为数字或者百分比,混用(2<-25%, 匹配两个或者25%,两者取最大值)

4 查询实践

4.1 按照分页的方式查询固定的字段并返回
json 复制代码
GET /index_name/_search
{
  "query": {
   "match_all": {}  //查询条件 
  },
  "_source":["id","age","name"], // 查询返回的字段
  "sort": [  // sort 排序,根据 id 进行顺序排列
   { "id": {"order": "asc" } }
   ], 
   "from": 0,
   "size": 500 
}
4.2 将返回的时间戳时间转换为字符串格式
json 复制代码
# 查询单条数据  id = 226, _source 设置为 * 可以用于返回所有字段
GET /index_name/_search
{
  "query":{
    "term":{  "id": "226" }
  },
  "_source":["*"],
  "script_fields": {
    "formDateTime": {
      "script": {
        "lang": "painless",
        "source": """
          ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.ofEpochMilli(doc['pushTime'].value.toInstant().toEpochMilli()), ZoneId.of('UTC+8'));
          DateTimeFormatter formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd HH:mm:ss.SSS');
          return zdt.format(formatter);
        """
      }
    }
  }
}

这里使用了 script_fields 的 painless 脚本,将文档中的 pushTime 转换为 UTC+8 的时间格式输出, 返回内容多了 formDateTime 字段。如果是低版本的ES,可以将source替换为如下命令,更为简洁。 doc['pushTime'].value.toString('yyyy-MM-dd HH:mm:ss')

4.3 嵌套结构数据查询

对于嵌套的数据结构,需要先进行外层的条件判断,然后在进行内部结构的判断。

json 复制代码
GET /index_name/_search
{
    "query": {
        "bool": {
            "must": [
                {
                    "term": {"age": 20}  //最外层内容匹配年龄为 20 
                },
                {
                    "bool": {
                        "must": [
                            {
                                "exists": { "field": "hobbyList" } // 存在 hobbyList list 
                            },
                            {
                                "range": {
                                    "hobbyList.showEndDate": {"gt": '2020-10-10' } // 爱好开始时间过滤
                                }
                            },
                            {
                                "term": { "hobbyList.status": 1 } // 爱好的状态信息过滤
                            }
                        ],
                        "must_not": [
                            {
                                "wildcard": { "hobbyList.name": "足球*" } // 不是足球的爱好
                            }
                        ]
                    }
                }
            ]
        }
    }
}

5 总结

在本文中重要介绍了常用的 DSL 查询语法,以及对应的操作实践,在实际的业务处理和项目运维中可能还会继续有更加复杂的查询需求,会陆续更新到本文中,用于技术的储备和操作的记录。

相关推荐
java1234_小锋3 小时前
Elasticsearch是如何实现Master选举的?
大数据·elasticsearch·搜索引擎
梦幻通灵9 小时前
ES分词环境实战
大数据·elasticsearch·搜索引擎
Elastic 中国社区官方博客9 小时前
Elasticsearch 中的热点以及如何使用 AutoOps 解决它们
大数据·运维·elasticsearch·搜索引擎·全文检索
小黑屋说YYDS15 小时前
ElasticSearch7.x入门教程之索引概念和基础操作(三)
elasticsearch
Java 第一深情17 小时前
Linux上安装单机版ElasticSearch6.8.1
linux·elasticsearch·全文检索
KevinAha1 天前
Elasticsearch 6.8 分析器
elasticsearch
wuxingge1 天前
elasticsearch7.10.2集群部署带认证
运维·elasticsearch
Elastic 中国社区官方博客2 天前
Elasticsearch:如何部署文本嵌入模型并将其用于语义搜索
大数据·人工智能·elasticsearch·搜索引擎·ai·全文检索
Dreams°1232 天前
【大数据测试 Elasticsearch 的标准--超详细篇】
大数据·elasticsearch·jenkins
鸠摩智首席音效师2 天前
如何在 Elasticsearch 中配置 SSL / TLS ?
elasticsearch·ssl