目 录CONTENT

文章目录

【入门篇】9. Elasticsearch查询之DSL复合查询详解

冰点
2023-05-20 / 0 评论 / 0 点赞 / 24 阅读 / 16012 字

Elasticsearch DSL 查询之复合查询详解

在实际的搜索应用场景中,单一的查询条件往往无法满足我们的需求,我们经常需要将多个查询条件进行组合,以获取更精准的结果。在 ElasticSearch 中,这种多种条件组合的查询被称为复合查询。ElasticSearch 为我们提供了 5 种强大的复合查询方式,分别是 bool query (布尔查询)、boosting query (提高查询)、constant_score(固定分数查询)、dis_max (最佳匹配查询)以及 function_score (函数查询)。接下来,我们将从构造数据开始,深入探究这五种复合查询的具体用法。

一、构造模拟数据

为了更好地演示复合查询,我先构造一批模拟数据,大家可以直接使用我的示例数据

假设我们有一个电商商品索引,每个商品文档包含title(商品标题)、description(商品描述)、price(商品价格)、category(商品类别)等字段。

ElasticSearch 中批量插入数据:

PUT /taobao_products/_bulk
{"index":{"_id":1}}
{"title":"华为 Mate 60 Pro","description":"麒麟芯片5G全网通智能手机","price":6999,"category":"手机","sales":12800}
{"index":{"_id":2}}
{"title":"小米 14 Ultra","description":"徕卡影像旗舰手机","price":5999,"category":"手机","sales":9650}
{"index":{"_id":3}}
{"title":"李宁跑步鞋男2025春季新款","description":"减震防滑透气网面运动鞋","price":399,"category":"运动鞋","sales":56000}
{"index":{"_id":4}}
{"title":"优衣库春季薄款羽绒服","description":"轻便保暖情侣款外套","price":499,"category":"服装","sales":89200}
{"index":{"_id":5}}
{"title":"Apple AirPods Pro 2","description":"主动降噪无线蓝牙耳机","price":1799,"category":"数码配件","sales":32500}
{"index":{"_id":6}}
{"title":"大疆 Mini 4 Pro","description":"可折叠航拍无人机","price":4788,"category":"摄影器材","sales":15600}
{"index":{"_id":7}}
{"title":"任天堂Switch OLED","description":"掌上游戏机国行续航版","price":2099,"category":"游戏机","sales":42100}
{"index":{"_id":8}}
{"title":"联想小新Pro16 2025","description":"12代酷睿高性能轻薄本","price":6299,"category":"笔记本电脑","sales":28700}

批量插入接口_bulk,一次性向名为products的索引中插入了 5 条商品数据。

二、bool query (布尔查询)

概念

布尔查询是 ElasticSearch 中最常用的复合查询方式,它基于布尔逻辑来组合多个查询子句。布尔查询包含四个重要的子句:must、must_not、should和filter。

  • must:必须满足的条件,相当于逻辑与(AND),满足must条件的文档会被包含在结果集中,并且会影响文档的相关性得分。

  • must_not:必须不满足的条件,相当于逻辑非(NOT),满足must_not条件的文档会被排除在结果集之外,不影响文档的相关性得分。

  • should:应该满足的条件,相当于逻辑或(OR),满足should条件的文档会增加相关性得分。如果must和filter子句没有匹配的文档,那么should子句中至少有一个条件匹配的文档才会被返回。

  • filter:过滤条件,与must类似,满足filter条件的文档会被包含在结果集中,但不计算相关性得分,因此在性能上会比must更好,常用于对查询结果进行条件过滤。

例子

查询价格在 3000 元以上的数码类商品(手机、数码配件、摄影器材),同时排除华为品牌的商品:

must子句确保返回的文档价格大于等于 3000 且类别属于数码类商品,must_not子句则将华为品牌的商品排除在外。

GET /taobao_products/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "price": {
              "gte": 3000
            }
          }
        },
        {
          "terms": {
            "category": ["手机", "数码配件", "摄影器材"]
          }
        }
      ],
      "must_not": [
        {
          "match_phrase": {
            "title": "华为"
          }
        }
      ]
    }
  }
}

此处因为我们没有定义字段的mapping ,category字段可能已经被分词,所以我们指定查询的方式为keyword. 或者可以修改一下这个索引mapping。


PUT /taobao_products/_mapping
{
  "properties": {
    "category": {
      "type": "keyword"
    },
    "description": {
      "type": "text",
      "fields": {
        "keyword": {
          "type": "keyword",
          "ignore_above": 256
        }
      }
    },
    "price": {
      "type": "long"
    },
    "sales": {
      "type": "long"
    },
    "title": {
      "type": "text",
      "fields": {
        "keyword": {
          "type": "keyword",
          "ignore_above": 256
        }
      }
    }
  }
}

三、boosting query (提高查询)

概念

提高查询用于控制文档的相关性得分,它可以提升满足某个查询条件的文档的相关性得分,同时降低不满足另一个查询条件的文档的相关性得分。提高查询包含两个重要的子句:positive和negative,以及一个negative_boost参数。

  • positive:需要提升得分的查询条件。

  • negative:需要降低得分的查询条件。

  • negative_boost:降低得分的权重,取值范围在 0 到 1 之间,数值越小,降低的程度越大。

例子

查询手机类商品,同时降低销量低于 10000 的商品的相关性得分:

positive子句筛选出类别为手机的商品,negative子句找出销量低于 10000 的商品,并通过negative_boost参数将其相关性得分降低到原来的 30%。

GET /taobao_products/_search
{
  "query": {
    "boosting": {
      "positive": {
        "term": {
          "category.keyword": "手机"
        }
      },
      "negative": {
        "range": {
          "sales": {
            "lt": 10000
          }
        }
      },
      "negative_boost": 0.3
    }
  }
}

需要理解boosting查询的工作机制:它不会过滤文档,只会调整分数。具体来说:

  1. positive 查询:匹配所有类别为 "手机" 的商品(在你的数据中是华为 Mate 60 Pro 和小米 14 Ultra)

  2. negative 查询:检查这些商品的销量是否低于 10000

  3. negative_boost=0.3:如果商品同时满足 negative 条件(销量 < 10000),则将其相关性分数乘以 0.3

数据情况:

  • 华为 Mate 60 Pro:销量 12800(不满足 negative 条件,分数不变)

  • 小米 14 Ultra:销量 9650(满足 negative 条件,分数 ×0.3)

预期结果:

  • 两个商品都会返回,但小米 14 Ultra 的分数会被降低

  • 如果按分数排序,华为 Mate 60 Pro 会排在前面

验证方法:

执行查询并查看返回结果的_score字段:

GET /taobao_products/_search
{
  "query": {
    "boosting": {
      "positive": {
        "term": {
          "category.keyword": "手机"
        }
      },
      "negative": {
        "range": {
          "sales": {
            "lt": 10000
          }
        }
      },
      "negative_boost": 0.3
    }
  },
  "_source": ["title", "sales"],
  "explain": true  // 显示分数计算详情
}

误解:

如果你期望销量 < 10000 的商品被完全过滤掉,需要使用bool查询而非boosting

GET /taobao_products/_search
{
  "query": {
    "bool": {
      "must": [
        { "term": { "category.keyword": "手机" } }
      ],
      "filter": [
        { "range": { "sales": { "gte": 10000 } } }
      ]
    }
  }
}

boosting查询语法正确,它会保留所有手机类商品,但降低销量低的商品的排名。如果这不是想要的效果,可以改用bool+filter组合。

四、constant_score(固定分数查询)

概念

固定分数查询会为所有匹配的文档分配一个固定的相关性得分,而不考虑文档与查询条件的实际匹配程度。它常用于在不关心文档相关性得分,只需要根据特定条件过滤文档的场景。固定分数查询通常与其他查询条件结合使用,通过filter子句来实现。

例子

查询所有销量超过 50000 的商品,并为这些商品分配固定的相关性得分 1.0:

GET /taobao_products/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "sales": {
            "gt": 50000
          }
        }
      },
      "boost": 1.0
    }
  }
}

在这个filter子句筛选出销量超过 50000 的商品,boost参数为这些匹配的文档设置固定的相关性得分为 1.0。

五、dis_max (最佳匹配查询)

概念

最佳匹配查询会返回与任何一个查询条件匹配度最高的文档,它只考虑最佳匹配的查询子句,而忽略其他子句的匹配情况,并且会对最佳匹配的子句的相关性得分进行提升。dis_max 查询包含一个queries参数,用于指定多个查询子句。

查询标题或描述中包含 “轻薄” 或 “便携” 的商品,只返回匹配度最高的文档:

GET /taobao_products/_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "match": {
            "title": "轻薄 便携"
          }
        },
        {
          "match": {
            "description": "轻薄 便携"
          }
        }
      ]
    }
  }
}

queries参数包含了两个match查询,分别在商品的标题和描述中搜索 “轻薄” 和 “便携”,最终只会返回与这两个查询条件中匹配度最高的文档。

六、function_score (函数查询)

概念

函数查询允许我们根据自定义的函数来调整文档的相关性得分。它可以结合多种函数,如weight(固定权重)、random_score(随机得分)、field_value_factor(根据字段值计算得分)等,对文档的相关性得分进行灵活的控制。函数查询包含query子句用于指定基础查询条件,以及functions数组用于定义调整得分的函数。

例子

查询所有价格在 1000-5000 元之间的商品,并根据销量来调整相关性得分,销量越高,得分越高:

GET /taobao_products/_search
{
  "query": {
    "function_score": {
      "query": {
        "range": {
          "price": {
            "gte": 1000,
            "lte": 5000
          }
        }
      },
      "functions": [
        {
          "field_value_factor": {
            "field": "sales",
            "modifier": "log1p",
            "factor": 0.5
          }
        }
      ]
    }
  }
}

query子句先筛选出价格在 1000-5000 元之间的商品,functions数组中的field_value_factor函数根据商品的sales字段值来计算相关性得分,通过modifier参数指定使用log1p函数对销量进行转换,使得销量越高,相关性得分提升得越明显。

通过以上对 ElasticSearch 中五种复合查询方式的详细介绍和实例演示,相信你已经对复合查询有了更深入的理解。在实际应用中,根据不同的业务需求灵活选择和组合这些复合查询方式,能够帮助我们实现更加精准和高效的搜索功能。

0

评论区