Elasticsearch `DSL`:用 Python 对象构建复杂 Elasticsearch 查询

好的,各位观众老爷们,欢迎来到今天的“Elasticsearch DSL:用Python对象构建复杂查询”专场!今天咱们不整虚的,直接上干货,用最接地气的方式,把Elasticsearch DSL这玩意儿给扒个精光。

开场白:Elasticsearch 和 DSL 的那些事儿

Elasticsearch,这货现在可是搜索界响当当的人物,谁要没听说过它,都不好意思说自己是搞技术的。它就像一个超级强大的数据库,专门用来存储和搜索海量的数据。但是,光有数据库还不行,你还得告诉它你想搜啥,怎么搜。

这时候,Elasticsearch 的查询语言(Query DSL)就闪亮登场了。它就是你和 Elasticsearch 交流的桥梁,你通过它告诉 Elasticsearch 你想找什么,它再吭哧吭哧地给你找出来。

但是!原始的 Query DSL 是 JSON 格式的,就像这样:

{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "Elasticsearch" }},
        { "match": { "content": "Python" }}
      ],
      "filter": [
        { "range": { "date": { "gte": "2023-01-01" }}}
      ]
    }
  }
}

看着是不是有点眼晕?尤其是当你的查询越来越复杂的时候,这 JSON 嵌套的层数简直能让你怀疑人生。而且,手写 JSON 容易出错不说,代码可读性也差得要命。

Python DSL:英雄登场,拯救世界

这时候,Python DSL 就如同救世主一般出现了!它允许你用 Python 对象来构建 Elasticsearch 查询,而不是直接写 JSON。这意味着你可以用更简洁、更易读的代码来表达你的查询意图。

简单来说,Python DSL 就是把 Elasticsearch 的 Query DSL 变成了一堆 Python 类和对象。你可以像搭积木一样,把这些类和对象组合起来,最终拼出一个完整的查询。

安装和配置

废话不多说,先装个 elasticsearch-dsl 库:

pip install elasticsearch-dsl

装好之后,咱们得先连接到 Elasticsearch 服务器。假设你的 Elasticsearch 跑在本地的 9200 端口,你可以这样连接:

from elasticsearch import Elasticsearch
from elasticsearch_dsl import Search

# 连接到 Elasticsearch
es = Elasticsearch([{'host': 'localhost', 'port': 9200}])

# 检查连接是否成功
if es.ping():
    print("Successfully connected to Elasticsearch!")
else:
    print("Failed to connect to Elasticsearch.")

DSL 的核心概念:查询、过滤器和布尔查询

在深入代码之前,咱们得先搞清楚几个核心概念:

  • 查询(Query): 用于查找与查询条件匹配的文档,并根据相关性进行排序。
  • 过滤器(Filter): 用于筛选出满足特定条件的文档,但不参与相关性评分。
  • 布尔查询(Bool Query): 用于组合多个查询和过滤器,实现更复杂的查询逻辑。

你可以把查询想象成“大海捞针”,它会尽力找到所有可能符合你要求的“针”,然后按照“针”的闪亮程度(相关性)排序。而过滤器则像一个筛子,它只会让你想要的“针”通过,其他的全部过滤掉。布尔查询则像一个指挥官,它告诉你如何组合不同的“大海捞针”和“筛子”,最终找到你想要的“针”。

常用查询类型:一个一个盘它

接下来,咱们来认识一下几种常用的查询类型,并用 Python DSL 来实现它们:

  1. Match Query(匹配查询): 这是最常用的查询之一,用于查找包含指定关键词的文档。

    from elasticsearch_dsl import Q
    
    # 查找 title 字段包含 "Elasticsearch" 的文档
    q = Q("match", title="Elasticsearch")
    
    # 构建 Search 对象
    s = Search(using=es, index="my-index").query(q)
    
    # 执行查询并打印结果
    response = s.execute()
    for hit in response:
        print(hit.title, hit.content)
  2. Term Query(精确查询): 用于查找指定字段包含精确值的文档。

    # 查找 category 字段的值为 "python" 的文档
    q = Q("term", category="python")
    
    s = Search(using=es, index="my-index").query(q)
    
    response = s.execute()
    for hit in response:
        print(hit.title, hit.category)
  3. Range Query(范围查询): 用于查找指定字段的值在指定范围内的文档。

    # 查找 date 字段的值在 2023-01-01 到 2023-01-31 之间的文档
    q = Q("range", date={"gte": "2023-01-01", "lte": "2023-01-31"})
    
    s = Search(using=es, index="my-index").query(q)
    
    response = s.execute()
    for hit in response:
        print(hit.title, hit.date)
  4. Bool Query(布尔查询): 用于组合多个查询和过滤器,实现更复杂的查询逻辑。

    # 查找 title 字段包含 "Elasticsearch" 且 content 字段包含 "Python" 的文档
    q = Q(
        "bool",
        must=[
            Q("match", title="Elasticsearch"),
            Q("match", content="Python")
        ],
        filter=[
            Q("range", date={"gte": "2023-01-01"})
        ]
    )
    
    s = Search(using=es, index="my-index").query(q)
    
    response = s.execute()
    for hit in response:
        print(hit.title, hit.content, hit.date)

    在 Bool Query 中,有三个重要的子句:

    • must: 文档必须匹配所有 must 子句中的查询。
    • should: 文档应该匹配 should 子句中的查询,但不是必须的。
    • filter: 文档必须匹配所有 filter 子句中的查询,但不参与相关性评分。
    • must_not: 文档必须不匹配所有 must_not 子句中的查询。
  5. Multi Match Query(多字段匹配查询): 用于在多个字段中查找包含指定关键词的文档。

    # 在 title 和 content 字段中查找包含 "Elasticsearch" 的文档
    q = Q("multi_match", query="Elasticsearch", fields=["title", "content"])
    
    s = Search(using=es, index="my-index").query(q)
    
    response = s.execute()
    for hit in response:
        print(hit.title, hit.content)

DSL 的高级用法:花式玩转 Elasticsearch

除了上面这些基本的查询类型之外,Python DSL 还提供了许多高级用法,让你可以更加灵活地玩转 Elasticsearch。

  1. Fuzzy Query(模糊查询): 用于查找与指定关键词相似的文档,允许一定的拼写错误。

    # 查找 title 字段与 "Elasticsearhc" 相似的文档(允许一个拼写错误)
    q = Q("fuzzy", title="Elasticsearhc")
    
    s = Search(using=es, index="my-index").query(q)
    
    response = s.execute()
    for hit in response:
        print(hit.title)
  2. Prefix Query(前缀查询): 用于查找指定字段以指定前缀开头的文档。

    # 查找 title 字段以 "Elastic" 开头的文档
    q = Q("prefix", title="Elastic")
    
    s = Search(using=es, index="my-index").query(q)
    
    response = s.execute()
    for hit in response:
        print(hit.title)
  3. Wildcard Query(通配符查询): 用于查找指定字段与指定通配符模式匹配的文档。

    # 查找 title 字段与 "Elasti*" 匹配的文档
    q = Q("wildcard", title="Elasti*")
    
    s = Search(using=es, index="my-index").query(q)
    
    response = s.execute()
    for hit in response:
        print(hit.title)

    通配符查询支持两种通配符:

    • *:匹配零个或多个字符。
    • ?:匹配单个字符。
  4. Regexp Query(正则表达式查询): 用于查找指定字段与指定正则表达式匹配的文档。

    # 查找 title 字段与 "Elasti.*" 正则表达式匹配的文档
    q = Q("regexp", title="Elasti.*")
    
    s = Search(using=es, index="my-index").query(q)
    
    response = s.execute()
    for hit in response:
        print(hit.title)
  5. Boosting Query(权重提升查询): 用于提高某些查询的相关性得分。

    # 提高 title 字段包含 "Elasticsearch" 的文档的相关性得分
    q = Q(
        "boosting",
        positive=Q("match", title="Elasticsearch"),
        negative=Q("match", content="Python"),
        negative_boost=0.2
    )
    
    s = Search(using=es, index="my-index").query(q)
    
    response = s.execute()
    for hit in response:
        print(hit.title, hit.content, hit.meta.score)

    Boosting Query 包含三个部分:

    • positive: 匹配 positive 子句中的查询的文档,其相关性得分会提高。
    • negative: 匹配 negative 子句中的查询的文档,其相关性得分会降低。
    • negative_boost: negative 子句的权重,用于控制降低相关性得分的程度。

DSL 的聚合操作:数据统计一把抓

除了查询之外,Elasticsearch 还提供了强大的聚合(Aggregation)功能,用于对数据进行统计分析。Python DSL 也支持聚合操作,让你可以轻松地从 Elasticsearch 中提取有用的信息。

常见的聚合类型:

  • Terms Aggregation(词项聚合): 统计指定字段中每个词项的出现次数。
  • Range Aggregation(范围聚合): 将指定字段的值分成多个范围,并统计每个范围内的文档数量。
  • Date Histogram Aggregation(日期直方图聚合): 将指定日期字段的值分成多个时间间隔,并统计每个时间间隔内的文档数量。
  • Avg Aggregation(平均值聚合): 计算指定字段的平均值。
  • Sum Aggregation(求和聚合): 计算指定字段的总和。
  • Min Aggregation(最小值聚合): 查找指定字段的最小值。
  • Max Aggregation(最大值聚合): 查找指定字段的最大值。

举个例子,咱们可以用 Terms Aggregation 来统计 category 字段中每个词项的出现次数:

from elasticsearch_dsl import A

# 创建一个 Search 对象
s = Search(using=es, index="my-index")

# 添加一个 Terms Aggregation,统计 category 字段中每个词项的出现次数
s.aggs.bucket('categories', A('terms', field='category'))

# 执行查询并打印结果
response = s.execute()

for category in response.aggregations.categories.buckets:
    print(category.key, category.doc_count)

DSL 的最佳实践:让你的代码更上一层楼

  • 保持查询的简洁性: 尽量避免写过于复杂的查询,将复杂的查询拆分成多个简单的查询,并使用 Bool Query 将它们组合起来。
  • 使用过滤器来提高性能: 对于不需要参与相关性评分的条件,尽量使用过滤器而不是查询。
  • 使用缓存来提高性能: 对于经常使用的查询,可以将其缓存起来,避免重复执行。
  • 编写单元测试: 为你的查询编写单元测试,确保它们能够正常工作。

总结:DSL,你值得拥有!

今天咱们一起学习了 Elasticsearch DSL 的基本概念和常用用法,包括查询、过滤器、布尔查询、常用查询类型、高级用法和聚合操作。希望这些知识能够帮助你更好地使用 Elasticsearch,并构建更加强大的搜索应用。

总而言之,Elasticsearch DSL 是一个非常强大的工具,它可以让你用 Python 对象来构建复杂的 Elasticsearch 查询,提高代码的可读性和可维护性。只要你掌握了它的基本概念和常用用法,就可以轻松地玩转 Elasticsearch,并从海量的数据中提取有用的信息。

最后,希望大家在实际应用中多多实践,不断探索 Elasticsearch DSL 的更多可能性。记住,只有不断学习和实践,才能真正掌握一门技术。

感谢各位的观看,下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注