当前位置:   article > 正文

理解Es的DSL语法(一):查询_es dsl

es dsl

目录

DSL相关联的基本概念

ElasticSearch的字段类型

ElasticSearch的查询类型

DSL语法

query

普通查询

布尔查询 

 字符串构建查询

_source 

from和size

sort 

关于sort和查询的相关性评分

关于sort的性能


DSL相关联的基本概念

Es的DSL并不是独立的知识点,是需要和Es的存储类型和查询类型相关联,所以在进行DSL语法的解析之前,需要先硬性理解或者说记录这两个内容

ElasticSearch的字段类型

es中的文档是以Json的格式存在,像关系型数据库一样,在使用时需要通过Mapping进行内容的预定义(虽然Es本身可以根据插入的数据进行自动的类型识别,但是最好还是在写入数据之前进行数据建模为好,毕竟自动识别是存在误差的),而Mapping定义中除了定义索引相关的信息外,最重要的就是索引内存储文档的各个字段类型(像关系型数据库中定义表结构时,需要指名每个 cloum的字段类型是varchar还是date)。

例如一个student索引,我们存储的内容包括,学号、姓名、年龄、性别、年级、宣言,那么一个学生的信息在es中的文档应该是:

  1. {
  2. "stu_no":"sadfas213213312",
  3. "name":"张三",
  4. "level":"3"
  5. "age":9,
  6. "sex":"男",
  7. "desc":"阳光向上,积极进取"
  8. }

 这么一种Json结构,在进行Es存储以及后续的检索时,各个字段的数据类型和性能以及查询语句的写法都息息相关,例如年纪,使用中可能需要经常被聚合统计,设置为keyword会更好,desc这种可能需要被快速查询,就需要用到可分析类型(text)……

Es的常用的存储类型有:

es的字段类型
字段类型使用场景是否分词是否纳入倒排索引其他
Text适用于需要进行全文搜索的字段,可存放内容性质、描述性质的数据,如文章内容唯一的可分析字段,Es会对该类型的字段内容进行分析、分词,并把词项纳入倒排索引中
keyword类似于varchar,用于存放简短的字符串性质内容,例如状态码、姓名、账号等keyword不进行分词,但是内容会整个被倒排索引纳入作为一个词项,适用于精确匹配、聚合
Integer, Short, Long, Byte数值类型,用于存储整数注:对于数字类型的字段,Elasticsearch 会为它们构建一个数值索引(Numeric Index),这种索引允许高效的数值范围查询和排序操作。数值索引不包含文本分析和分词,因此它们不适合用于搜索包含文本的查询。
Double, Float, Half_Float数值类型,用于存放浮点数
Date存储日期和时间

1. 日期字段存储为时间戳,查询时和数值类型一样使用数值索引来优化查询

2.在聚合和排序时,通过DocValues、fielddata来提高性能

3.针对日期类型,在查询、聚合时,Es都提供方便的计算和处理

Boolean存放布尔值
Object存储复杂数据结构,例如嵌套JSONObject类型虽然不会被纳入倒排索引,但是内容中可以包含倒排索引的字段
Nested存储嵌套对象数组慎用,对于此类型在查询、聚合时都较为麻烦
Ip专门用于存储 IP 地址使用该类型,Es可以提供一些针对Ip的操作和运算较为方便

ElasticSearch的查询类型

Es中的查询类型又叫查询句柄,如果类比SQL语法,这里的查询类型就类似于SQL中WHERE部分的内容,例如= 、< 、>、LIKE、NOT之类。

Es的查询类型较多,在这里就放一些常用的,对于特殊需求的,可以查阅详细官方文档。

Es常用查询类型
查询类型使用场景适用字段类型缓存情况
Match Query

用于全文搜索,可以进行分词和分析,类似于 SQL 中的 LIKE 查询。

Text使用queryCache,但由于全文搜索的复杂性,缓存效果可能不如 Term Query。
Prefix Query用于查找以特定前缀开头的词项Keyword、Text(不进行分词的情况下)使用queryCache,尤其是当字段是 Keyword 类型时效果更佳
Multi-Match Query允许在多个字段上执行全文搜索Text使用queryCache,但可能因跨多个字段而降低缓存效率
Term Query用于查找与指定词项(term)完全匹配的文档Keyword、Boolean、Integer、Short、Long、Byte、Double、Float、Date、IP通常使用普通的queryCache,如果字段是 Keyword 或 Date 类型,并且查询是精确匹配,也可能使用字段数据缓存(fielddata cache)
Range Query用于查找在指定数值或日期范围内的文档Integer、Short、Long、Byte、Double、Float、Date不使用查询缓存
Wildcard Query

支持使用通配符(如 * 和 ?)进行搜索

Keyword、Text通常不使用查询缓存,因为通配符查询可能涉及大量文档
Fuzzy Query允许进行模糊搜索,可以容忍一定数量的拼写错误Keyword、Text不使用查询缓存
Bool Query

允许组合多个查询条件,使用 AND、OR 和 NOT 逻辑

因为其内部可以嵌套其他查询类型,所以适配所有字段类型使用QueryCache,但缓存效率取决于子查询的复杂性和可缓存性
Nested Query用于在嵌套对象中执行查询Nested不使用查询缓存
Script Query允许使用脚本语言(如 Painless)执行自定义查询根据脚本内容决定适配的字段类型不使用查询缓存

关于缓存内容可以参考上一篇文章,详细的查询类型使用方法会在下文中提及

DSL语法

先看一下DSL的语法,DSL常用的有查询、返回内容、排序、条数、以及聚合

  1. {
  2. "query":{}, // 查询
  3. "_source":[], //返回内容
  4. "sort":[], //排序
  5. "from":0, // 起始位置
  6. "size":10, // 返回条数
  7. }

以常用的SQL进行对照理解大约是这样的:

DSLSQL解释
querySELECT

name,age

FROM  student

WHERE

home = '饭都花园'

ORDER BY

stu_no DESC

LIMIT

0, 10
相当于SQL中的WHERE起手,此次内容为查询的条件
_sourceSELECT
name,age
FROM  student
WHERE
home = '饭都花园'
ORDER BY
stu_no DESC
LIMIT
0, 10
相当于SQL中SELECT关键字后指定此次查询需要的字段,DSL中通过_source来指定一次查询中需要返回json文档中的哪些字段的内容
from、sizeSELECT
name,age
FROM
student
WHERE
home = '饭都花园'
ORDER BY
stu_no DESC
LIMIT
0, 10
相当于SQL中的返回条数limit,指定多少条数据开始,返回多少个数据
sortSELECT
name,age
FROM
student
WHERE
home = '饭都花园'
ORDER BY stu_no DESC
LIMIT 0, 10
相当于SQL中的ORDER BY排序字段,即查询出的数据是以JSON文档中哪个字段以何种方式排序
aggsSELECT
stu_id
FROM student
WHERE
home = '饭都花园'
GROUP BY stu_id
相当于SQL中的Group BY操作,即对某个字段内容进行聚合

query

其中query关键字是查询中最核心的部分,它是上文中提到的查询类型的集中区,不论是查询功能方面,还是查询性能方面,query的操作性是最多的;从查询类型上来看query又可以分为普通查询、过滤查询、布尔查询、字符串构建查询:

普通查询

普通查询一般使用match查询类型,是最常见的查询语句,只针对类型为text的字段才生效,此查询为全文检索;全文检索会影响返回结果的相关性得分,这意味着Elasticsearch会计算每个匹配文档的相关性,并按照得分排序返回结果

  1. //一个普通的全文查询
  2. { "query": { "match": { "context": "this is a boy" } } }
  3. // 包含顺序的词短语搜索
  4. { "query": { "match_phrase": { "context": "搜索引擎" } } }

过滤查询其实也可以算在简单查询里,即都是通过query下一层直接使用查询句柄(查询类型),不同的是使用的查询类型,过滤查询一般使用的查询句柄为 term、range这种查询类型

  1. //等于逻辑筛选 name = 张三
  2. { "query": { "term": { "name": "张三" } } }
  3. //范围筛选 , age >= 10 ,age =< 20
  4. { "query": { "range": { "age": { "gte": 10,"lte":20 } } } }

使用term、range这种查询句柄相较于match、match_phrase来说,不会影响相关性得分,过滤查询的结果不会根据相关性进行排序,它们只是简单地确定文档是否应该被包含在结果集中,减少了排序步骤,性能上会好一些。

布尔查询 

布尔查询就是在query关键字下使用布尔查询类型来进行查询,相比于简单查询,仅能使用一种逻辑查询,布尔查询可以理解为复杂查询,即可以使用逻辑关系组合条件,布尔查询包含四个逻辑组,must(且)must_not(非)should(或)filter(且),组成方式是在query下存放逻辑组,逻辑组中放查询句柄:

关键字含义用法
must”是“条件组,参数类型为[]数组内可配多个查询句柄,各查询句柄之间为且的关系
must_not”否“条件组,参数类型为[]数组内可配多个查询句柄,每个查询句柄为逻辑取反
should”或“条件组 ,参数类型为[]数组内可配多个查询句柄,各查询句柄之间为或的关系
filter筛选器,参数类型为[]数组内可配多个查询句柄,查询句柄的关系也为且关系,且使用filter的可以忽略相关性得分,考虑性能可使用filter替代must
  1. {
  2. "query":{
  3. "bool":{
  4. "must":[], //且
  5. "should":[], // 或
  6. "must_not":[] // 非
  7. }
  8. }
  9. }

例如以下几个例子

  1. # 查询 性别是女的或者 年龄大于30岁且教授语文的老师
  2. SELECT * FROM tb_teach WHERE sex = 1 OR ( age > 30 AND role = '语文' )

对应使用Es的布尔查询则是:

  1. {
  2. "query":{
  3. "bool":{
  4. "should":[ //对应SQL中最高层的 OR 逻辑
  5. {
  6. "term":{ "sex":1}
  7. },
  8. {
  9. "bool":{ // 对应 SQL中二层里的AND逻辑
  10. "must":[
  11. {"range": { "age":{ "gt":30 } } }, //age > 30
  12. {"term":{ "role":"语文" } // role = 语文
  13. ]// end must
  14. }
  15. }
  16. ] // end should
  17. }
  18. }
  19. }
  1. #查询不教语文,且年龄在20-30之间的老师
  2. SELECT * FROM tb_teach WHERE role != '语文' AND age BETWEEN 20 AND 30
  3. #对应的DSL
  4. {
  5. "query":{
  6. "bool":{
  7. "must":[ //对应SQL中最高层的 AND 逻辑
  8. {
  9. "bool":{ "must_not":[{"term":{ "role":"语文"}}] }
  10. },
  11. {
  12. "range":{
  13. "age":{
  14. "gte":20,
  15. "lte":30
  16. }
  17. }
  18. }
  19. ]
  20. }
  21. }
  22. }
  1. #查询名字为张三且年龄为12岁,或者部门不在人事部且年龄在1020岁之间的人
  2. SELECT
  3. *
  4. FROM
  5. USER
  6. WHERE
  7. (name = "张三" AND age = 12) OR (dept != "人事部" AND age >= 10 AND age<=20 )
  8. #对应的DSL
  9. {
  10. "query":{
  11. "bool":{
  12. "should":[ //对标SQL 中 的 OR
  13. {
  14. "bool":{
  15. "flter":[ //对标SQL 中 OR左侧()中的AND
  16. {
  17. "term":{
  18. "name":"张三"
  19. }
  20. },
  21. {
  22. "term":{
  23. "age":12
  24. }
  25. }
  26. ]
  27. }
  28. }, // ↑ 为OR左侧的() 中的条件
  29. {
  30. "bool":{
  31. "must_not":[ // 对标dept != "人事部"
  32. {
  33. "term":{
  34. "dept":"人事部"
  35. }
  36. }
  37. ],
  38. "filter":[
  39. {
  40. "range":{
  41. "age":{
  42. "gte":10,
  43. "lte":20
  44. }
  45. }
  46. }
  47. ]
  48. }
  49. } ↑ 为OR右侧的() 中的条件
  50. ]
  51. }
  52. }
  53. }

可以看到,布尔查询也可以被当作逻辑组(must、filter、should、must_not)的一个条件 ,逻辑是可以嵌套的,可以根据需求,选择不同的逻辑组,通过不同的嵌套组合就可以完成复杂逻辑的查询。

另外一点就是must和filter,它俩都可以表示且的关系,但是在执行原理和性能上会有不同:

关键字作用缓存
filter用于定义查询的过滤条件,这些条件用于限制返回的文档集合。过滤条件通常用于结构化数据(如数值范围、日期范围、确切的值匹配等)通常会对 filter 子句使用缓存,这意味着如果相同的过滤条件被多次使用,它们的执行结果可以被缓存和重用,从而提高查询性能
must用于定义查询的搜索条件,这些条件用于计算文档的相关性得分。must 子句中的查询通常用于全文搜索,影响返回文档的排序must 子句通常不使用缓存,因为它们影响文档的相关性得分,每次查询可能都会产生不同的结果

对于结构化的查询,建议优先使用filter

 字符串构建查询

字符串构建查询也是Es常用的一种查询,它适用于需要复杂文本搜索的场景,如搜索引擎的查询框。它允许用户以类 SQL 的方式执行复杂的文本搜索,使用一个查询字符串来构建查询,支持多种查询语法和操作符,使用关键字为:query_string

  1. {
  2. "query": {
  3. "query_string": {
  4. "query": "title:guide AND (title OR body):Elasticsearch"
  5. }
  6. }
  7. }

这里主要是query_string中的query内容,它是一个类SQL的语法,表示我们要查询 (titile字段中包含“guide”词项) 且 (title字段或者body字段中包含"Elasticsearch"词项)的文章,这里query支持的形式包含:

  • 布尔逻辑(AND, OR, NOT)
  • 通配符搜索(使用 * 和 ?
  • 模糊搜索(使用 ~ 后跟编辑距离)
  • 正则表达式搜索(使用 / 包围)
  • 短语搜索(使用双引号 " " 包围词组)
  • 字段特定搜索(使用 FIELD: 指定字段)
  • 提升(Boosting)搜索词项(使用 ^ 后跟提升值)

一个完整的query_string还包含字符串查询的一些控制项:

  1. {
  2. "query": {
  3. "query_string": {
  4. "query": "title:guide AND (title OR body):Elasticsearch",
  5. "default_operator": "AND",
  6. "fields": ["title", "body"],
  7. "use_dis_max": true,
  8. "tie_breaker": 0.3,
  9. "minimum_should_match": "2<75%",
  10. "boost": 1.0,
  11. "analyzer": "standard",
  12. "quote_field_suffix": ".exact",
  13. "fuzzy_max_expansions": 50,
  14. "fuzzy_prefix_length": 2,
  15. "phrase_slop": 2,
  16. "escape": false,
  17. "auto_generate_phrase_queries": true
  18. }
  19. }
  20. }

  • default_operator:设置默认的布尔操作符。AND 表示默认使用 AND 逻辑,即所有条件都需要匹配。

  • fields:指定搜索的字段列表。这里我们在 titlebody 字段中搜索。

  • use_dis_max:当设置为 true 时,使用 dis_max 查询来组合多个字段的搜索结果,以提高相关性。

  • tie_breaker:在 use_dis_maxtrue 时,用于控制多字段搜索的相关性得分计算。

  • minimum_should_match:对于使用 OR 操作符的查询,定义至少需要匹配的最小条件数量。这里的 "2<75%" 表示至少匹配两个条件,或者匹配超过 75% 的条件。

  • boost:提升查询的整体相关性得分。

  • analyzer:指定查询时使用的分析器。

  • quote_field_suffix:后缀,用于指定精确匹配的字段版本。

  • fuzzy_max_expansions:模糊查询可以扩展的最大项数。

  • fuzzy_prefix_length:在模糊查询中,前缀的最小字符数。

  • phrase_slop:短语查询中的间隔。

  • escape:是否转义查询字符串中的特殊字符。

  • auto_generate_phrase_queries:是否自动将双引号内的词组转换为短语查询。

_source 

_source在DSL语法中是比较简单的,类比SQL中SELECT 之后的返回内容控制,即这次检索我需要查询的数据需要JSON文档中的哪几个字段,例如索引中数据:

  1. {
  2. "id": "1",
  3. "title": "Elasticsearch Basics",
  4. "content": "Elasticsearch is a powerful search engine based on the Lucene library. It provides full-text search, analysis, and indexing of data. This article will guide you through the basics of Elasticsearch.",
  5. "date": "2024-06-08T08:00:00Z",
  6. "author": "John Doe"
  7. }

查询文章标题为Elstaicsearch的文章:

  1. #查询文章名称为Elasticsearch的文档
  2. {
  3. "query":{
  4. "term":{
  5. "title":"Elasticsearch"
  6. }
  7. }
  8. }
  9. #返回:
  10. {
  11. "took": 10,
  12. "timed_out": false,
  13. "_shards": {
  14. "total": 5,
  15. "successful": 5,
  16. "skipped": 0,
  17. "failed": 0
  18. },
  19. "hits": {
  20. "total": {
  21. "value": 1,
  22. "relation": "eq"
  23. },
  24. "max_score": 1.0,
  25. "hits": [
  26. {
  27. "_index": "articles",
  28. "_type": "_doc",
  29. "_id": "1",
  30. "_score": 1.0,
  31. "_source": { #返回了Json文档全部的字段
  32. "id": "1",
  33. "title": "Elasticsearch Basics",
  34. "content": "Elasticsearch is a powerful search engine based on the Lucene library...",
  35. "date": "2024-06-08T08:00:00Z",
  36. "author": "John Doe"
  37. }
  38. }
  39. ]
  40. }
  41. }

如果只需要其中的author和context,则使用_source进行指定:

  1. #查询文章名称为Elasticsearch的文章,需要文章作者和内容两部分
  2. {
  3. "query":{
  4. "term":{
  5. "title":"Elasticsearch"
  6. }
  7. },
  8. "_source":["author","context"]
  9. }
  10. #返回
  11. {
  12. "took": 5,
  13. "timed_out": false,
  14. "_shards": {
  15. "total": 1,
  16. "successful": 1,
  17. "skipped": 0,
  18. "failed": 0
  19. },
  20. "hits": {
  21. "total": {
  22. "value": 1,
  23. "relation": "eq"
  24. },
  25. "max_score": 1.0,
  26. "hits": [
  27. {
  28. "_index": "articles",
  29. "_type": "_doc",
  30. "_id": "1",
  31. "_score": 1.0,
  32. "_source": { #只根据_source指定的内容进行返回
  33. "author": "John Doe",
  34. "content": "Elasticsearch is a powerful search engine based on the Lucene library. It provides full-text search, analysis, and indexing of data."
  35. }
  36. }
  37. ]
  38. }
  39. }

关于查询文档内容,可以根据需求和性能要求使用stored在Mapping定义阶段就使用,也可以通过_source进行灵活指定,各有利弊,详细可以参考在ElasticSearch性能原理拆解中提过Stored Fields

from和size

fromsize 是用于分页和限制返回结果集的参数,等同于SQL中的Limit语法,

from 参数指定了搜索结果中跳过的文档数量。这可以用于实现分页功能,其中第一页的结果可以跳过0个结果,第二页跳过10个结果(如果每页显示10个结果),以此类推。

size 参数定义了搜索结果中返回的最大文档数量。使用 size 可以限制返回的文档数量,这对于性能和网络带宽管理非常重要,因为它可以防止一次性返回大量数据。

例如:

  1. #执行一个分页查询
  2. {
  3. "from": 2,
  4. "size": 10,
  5. "query": {
  6. "match_all": {}
  7. }
  8. }
  9. #输出:
  10. {
  11. "took": 5,
  12. "timed_out": false,
  13. "_shards": {
  14. "total": 5,
  15. "successful": 5,
  16. "skipped": 0,
  17. "failed": 0
  18. },
  19. "hits": {
  20. "total": {
  21. "value": 1000,
  22. "relation": "eq"
  23. },
  24. "max_score": 1.0,
  25. "hits": [
  26. {
  27. "_index": "my_index",
  28. "_type": "_doc",
  29. "_id": "doc_11",
  30. "_score": 1.0,
  31. "_source": { ... },
  32. },
  33. // ... 其他文档,直到第10个
  34. ]
  35. }
  36. }

但是使用form 和size去实现分页查询存在一个问题,因为对于用户看到的是返回了10条数据,而对于Es而言则是

  • 执行 match_all 查询,匹配 my_index 索引中的所有文档。
  • 计算所有文档的相关性得分。
  • 忽略前30个文档。
  • 选择第31到40个文档。
  • 将这10个文档的相关信息返回给用户。

当不停地滑动页码,from的值越来越大时,会让Es过度计算和内存消耗,必要时会让Es抛出异常。所以,尽管form+size在分页查询的场景下是有效的,但是面对深度分页时,form+size是不推荐的,在面对大批量数据且有分页查询的诉求时,Elasticsearch推荐使用 search_after 参数,因为它可以提供更高效的分页机制。search_after 通过指定一个或多个排序值来检索结果集的一部分,这使得它可以避免重新计算所有文档的得分,从而提高性能。

sort 

sort 用于对搜索结果进行排序。sort 允许你指定一个或多个字段,根据这些字段的值对结果进行排序。以下是 sort 的使用方法:

  1. #对查询结果,根据学号进行正序排序
  2. {
  3. "query": {
  4. "match_all": {}
  5. },
  6. "sort": [
  7. {
  8. "stu_no": {
  9. "order": "asc" //或者desc
  10. }
  11. }
  12. ]
  13. }
也可以指定多个字段进行排序,第一个字段的排序完成后,如果存在相同值,则根据第二个字段排序,以此类推。
  1. {
  2. "query": {
  3. "match_all": {}
  4. },
  5. "sort": [
  6. {
  7. "stu_no": {
  8. "order": "asc"
  9. }
  10. },
  11. {
  12. "age": {
  13. "order": "desc"
  14. }
  15. }
  16. ]
  17. }

还可以通过脚本进行排序

  1. {
  2. "query": {
  3. "match_all": {}
  4. },
  5. "sort": [
  6. {
  7. "_script": {
  8. "type": "number",
  9. "script": {
  10. "lang": "painless",
  11. "source": "doc['age'].value * 2"
  12. },
  13. "order": "asc"
  14. }
  15. }
  16. ]
  17. }

关于sort和查询的相关性评分

使用 sort 语句在 Elasticsearch 中对查询结果进行排序不会直接影响查询的相关性评分(_score)。相关性评分是由查询自身的算法决定的,例如 match 查询或 term 查询等,这些查询根据文档与查询条件的匹配程度来计算每个文档的得分。

sort 语句通常用于在相关性评分的基础上对结果进行进一步的排序。例如,当你使用 match_all 查询时,所有文档的相关性评分可能相同,此时你可以使用 sort 来根据其他标准(如日期、数值字段等)对结果进行排序。

  • 独立性: sort 操作是独立的,它基于你指定的字段和排序顺序对文档进行排序,与文档的相关性评分无关。
  • 组合使用: 你可以在任何类型的查询之后使用 sort,包括那些返回相关性评分的查询。
  • 优先级: 当使用 bool 查询时,可以在 mustshouldfilter 子句中组合多个查询,并使用 sort 来确定最终的排序顺序。
  • 不改变评分: 即使文档经过 sort 操作被重新排序,它们的相关性评分 (_score) 保持不变。
  • 混合使用: 你可以将基于评分的排序与基于其他字段的排序混合使用。例如,首先根据相关性评分降序排序,然后根据日期字段升序排序。

关于sort的性能

使用 sort 可能会对查询性能产生影响,虽然DocValues的存在会对sort有一定的优化,但是DocValues不是适用所有字段:

  • keyword:对于keyword类型的字段,会默认使用 DocValues 存储

  • 数值和日期: 对于数值(如 integerfloatdouble)和日期(date)类型的字段,默认使用 DocValues 存储。

  • Text:文本(text)类型的字段默认不使用 DocValues,它需要经过分析(analysis)来支持全文搜索。但是,可以为 text 字段显式定义一个 keyword 子字段,该子字段将使用 DocValues。

使用 search_after 进行深度分页时,排序字段必须在索引时定义为 docValues,以便高效地检索和排序。

关于聚合部分,放在下一篇介绍:

理解DSL语法(二):聚合

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号