Elasticsearch7——DSL中的复合查询

612 阅读5分钟

[这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战]


Elasticsearch 作为一种搜索中间件,也提供了相应的查询语法 DSL(Domain Specific Language)来进行数据查询.由于Elasticsearch提供了REST 风格的API进行查询,所以DSL查询数据也是基于JSON格式的。

什么是DSL

Elasticsearch DSL 是一个高级库,其目的是帮助编写和运行针对 Elasticsearch 的查询。DSL查询可以分为两种:

  • 叶查询子句:用于在检索特定字段的特定值,可以单独使用
  • 复合查询字句:包含了叶查询以及复合查询

这种设计是把查询视为树结构,如下所示

image.png

查询上下文与过滤上下文

在默认情况下,Elasticsearch 按相关性分数对匹配的搜索结果进行排序,相关性分数用于衡量每个文档与查询的匹配程度。分数越高,匹配越高,在返回结果中的排序越靠前。

  • 查询上下文中,查询子句描述了匹配程度。所以除了判断文档是否匹配外,查询子句还会计算_score元数据字段中的相关性分数。
  • 而在过滤上下文中,查询子句只是描述了是否匹配,并不计算分数。Elasticsearch 会自动缓存常用的过滤器,以提高性能。

例如:

GET /_search
{
  "query": { 
    "bool": { 
      "must": [
        { "match": { "name":   "pt"        }}
      ],
      "filter": [ 
        { "range": { "create_date": { "gte": "2021-11-28" }}}
      ]
    }
  }
}

高成本的查询

某些类型的查询通常会因其实现方式而执行缓慢,导致一直处于搜索状态,无法与其它节点交互,从而影响集群的稳定性。例如:

  • 需要进行线性扫描以识别匹配项的查询
    • script
  • 初始阶段查询代价过高
    • fuzzy(wildcard类型的字段除外)
    • regexp(wildcard类型的字段除外)
    • prefix(wildcard类型的字段除外或没有启用index_prefixes参数的)
    • wildcard(wildcard类型的字段除外)
    • range(text和 keyword类型的字段)
  • join查询
  • 高成本的文本数据查询
    • script_score
    • percolate

可以通过将search.allow_expensive_queries的值设置为false(默认为true)来阻止此类查询的执行。

复合查询

复合查询可以包装其它复合查询和叶查询,可以:

  • 组合结果和分数
  • 从查询上下文切换到过滤上下文

复合查询的组成:

  • bool
  • boosting
  • constant_score
  • dis_max
  • function_score

image.png

布尔查询

bool查询用于查询数据是否匹配,由一个或多个布尔字句组成。布尔字句的类型如下:

  • must:用作AND,查询子句必须出现在文档中并且可以增加score数值
  • filter:可用作AND,但是运行在过滤上下文中、忽略分数并且可以被缓存
  • should:用作OR,可以使用 minimum_should_match参数设置匹配的最小子句数
  • must_not:用作NOT

例如:

POST book/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "type": {
              "value": "大学教材"
            }   
          }
        },
        {
          "term": {
            "type": {
              "value": "考试认证"
            }   
          }
        }
      ]
    }
  },
  "size": 2, 
  "aggs": {
    "allTYpe": {
      "terms": {
        "field": "type"
      }
    }
  }
}

Boosting 查询

Boosting查询可以根据字段值改变_score的结果。相关参数如下:

  • positive(必选):返回数据集必须与此查询配置
  • negative(必选):数据集中匹配的文档,_score必须乘以negative_boost参数
  • negative_boost(必选):浮点数

search.png

例如:

POST book/_search?pretty
{
  "size": 2,
  "query": {
    "boosting": {
      "positive": {
        "term": {
          "type": {
            "value": "考试认证"
          }
        }
      },
      "negative": {
        "term": {
          "publish": {
            "value": "高等教育出版社"
          }
        }
      },
      "negative_boost": 0.2
    }
  },
  "_source": ["type","name","publish"]
}

查询结果如下:

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 15,
      "relation" : "eq"
    },
    "max_score" : 0.80781645,
    "hits" : [
      {
        "_index" : "book",
        "_type" : "_doc",
        "_id" : "35",
        "_score" : 0.80781645,
        "_source" : {
          "publish" : "高等教育出版社",
          "name" : "全国计算机等级考试笔试+上机全真模拟:三级网络技术(最新版)",
          "type" : "考试认证"
        }
      },
      {
        "_index" : "book",
        "_type" : "_doc",
        "_id" : "38",
        "_score" : 0.80781645,
        "_ignored" : [
          "info.keyword"
        ],
        "_source" : {
          "publish" : "高等教育出版社",
          "name" : "全国计算机等级考试考点解析例题精解与实战练习:二级C语言程序设计(附光盘)",
          "type" : "考试认证"
        }
      }
    ]
  }
}

固定分数(constant score)

固定分数查询使用了 constant_score参数,具有以下选项:

  • filter 用于过滤查询
  • boost(可选)设置固定分数,默认为1.0

例如:

POST book/_search
{
  "size": 1,
  "from": 6, 
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "type": "生物科学"
        }
      },
      "boost": 1.2
    }
  },
  "_source": ["type","name","publish"]
}

查询结果如下,可以看到分数固定为1.2

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 18,
      "relation" : "eq"
    },
    "max_score" : 1.2,
    "hits" : [
      {
        "_index" : "book",
        "_type" : "_doc",
        "_id" : "222",
        "_score" : 1.2,
        "_source" : {
          "publish" : "高等教育出版社",
          "name" : "生态学实验设计与分析(第2版)(中文版)",
          "type" : "生物科学"
        }
      }
    ]
  }
}

查询最匹配字段(dis-query)

在Elasticsearch中,如果返回的文档与多查询子句匹配,dis_max查询会为匹配文档分配最高相关性分数;并为任何其他匹配子查询添加增量,从而避免出现分数相同的情况。相关参数如下:

  • queries (必选,查询对象数组)包含一个或多个查询子句,返回的文档需要与这些子句中的一个或多个匹配。
  • tie_breaker(必选、浮点数、默认为0.0

例如:

POST book/_search
{
  "size": 20,
  "from": 0,
  "_source": ["type","name","publish"],
  "explain": true,
  "query": {
    "dis_max": {
      "tie_breaker": 0.7,
      "boost": 1.2,
      "queries": [
        {
          "term": {"type": "生物科学"}
        }
      ]
    }
  } 
}

通过函数修改分数

function_score 查询中,可以为匹配文档调用配置好的函数,从而改变文档的原始分数。

支持以下几个函数:

  • script_score:可以包装脚本
  • weight:表示权重,用于乘以分数
  • random_score:生成小于1的浮点数;可以设置seed 和field使分数可以重现
  • field_value_factor:配置后字段值会影响分数
  • decay functions:衰减函数,包括 gauss, linear, exp

例如:

POST book/_search
{
  "query": {
    "function_score": {
      "query": {
        "match_all": {}
      },
      "functions": [
        {
          "filter": {"term": {
            "type": "生物科学"}
          },
          "random_score": {},
          "weight": 1
        },
        {
          "filter": {"term": {
            "publish": "农村读物出版社"}
          },
          "random_score": {},
          "weight": 5
        }
      ]
    }
  },
  "size": 20
}

参考文档

Compound queries