【问题标题】:Elasticsearch-dsl with nested filters and AND and OR conditions with exact match具有嵌套过滤器和 AND 和 OR 条件的 Elasticsearch-dsl 完全匹配
【发布时间】:2020-10-07 14:49:54
【问题描述】:

三个参数来自前端:

  1. State - 字符串
  2. Categories - 一个字符串数组。字符串可以由多个单词组成。
  3. Tags - 类似于类别。

所有参数都是可选的。

如果是多个传输,需要通过AND来实现它们的bundle(和statecategorytag的巧合)。 如果提交了多个categoriestags,则会为其中至少一个进行匹配。

也就是说,如果一个带有参数的请求到达

{"state": "Alaska", "categories": ["category 1", "category 2"]}

答案将是

  • state = Alaskacategories = category 1
  • state = Alaska, categories = category 2;
  • state = Alaska, categories = [category 1, category 2];
  • state = Alaska, categories = [category 1, category 3]至少有一个要求的类别)。

不适合

  • state = Alabama, categories = category 1
  • state = Alaska, categories = 3
  • state = Alaska, categories = 1 category类别名称应该是 1-in-1"category 1" != "1 category"

elastikserch 我从python (3.7) 发送请求。拿了一个图书馆 elasticsearch-dsl

通过Q 对象收集三个过滤器(在其中使用match)。

combined_filter = state_filter & categories_filter & tags_filter

categoriestags的列表分为subfilters through OR

query = queries.pop()
for item in queries:
    query |= item

这样的请求是为elasticsearch 创建的。

Bool(minimum_should_match=1, 
    must=[Match(state='Alaska'), MatchAll()], 
    should=[Match(categories='category 1'), Match(categories='category 2')]
)

为什么这个逻辑通过不准确 category / tag 名称查找条目?

from typing import List

from elasticsearch import Elasticsearch
from elasticsearch_dsl import Q, Search
from flask import request
from flask.views import MethodView


es = Elasticsearch()


class ArticleSearchAPIView(MethodView):
    """
    Search articles using ElasticSearch
    """

    @staticmethod
    def filter_create(queries: List[Q]) -> Q:
        """
        Creates Q.OR filter
        """
        query = queries.pop()
        for item in queries:
            query |= item
        return query

    def get(self) -> dict:
        """
        Search article
        First request - with empty params
        """
        search = Search(using=es, index=ArticleModel.__tablename__)
        state_filter = categories_filter = tags_filter = Q()
        result = "Articles not found."

        data = request.get_json()
        categories = data.get("categories")
        tags = data.get("tags")
        state = data.get("state")

        if state:
            state_filter = Q("match", state=state)

        if categories:
            queries = [Q("match", categories=value) for value in categories]
            categories_filter = self.filter_create(queries)

        if tags:
            queries = [Q("match", tags=value) for value in tags]
            tags_filter = self.filter_create(queries)

        combined_filter = state_filter & categories_filter & tags_filter
        found = (
            search.filter(combined_filter)
            .execute()
            .to_dict()["hits"]
            .get("hits")
        )

        if found:
            result = [article["_source"] for article in found]
        return {"response": result}

更新


Article and CategoryArticle and Tag - MTM 之间的关系

映射

{
  "articles": {
    "mappings": {
      "properties": {
        ...
        "categories": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "state": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "tags": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        }
        ...
      }
    }
  }
}

【问题讨论】:

    标签: python python-3.x elasticsearch flask elasticsearch-dsl


    【解决方案1】:

    您可以使用布尔查询。

    在布尔查询ElasticSearch Boolean Query

    你有 'must' 相当于 'AND' 运算符。和 'should' 充当 'OR' 运算符。

    {
      "query": {
        "bool" : {
          "must" : {
            "term" : { "user" : "kimchy" }
          },
          "should" : [
            { "term" : { "tag" : "wow" } },
            { "term" : { "tag" : "elasticsearch" } }
          ],
        }
      }
    }

    【讨论】:

    • 谢谢。但我不明白,如何从我的代码中运行此查询?(
    • 不确定您正在使用的组件。但通常也有一个运行原始查询的选项..所以您可以像上面一样格式化原始弹性搜索查询,如果您的组件允许,您可以将此原始查询和任何其他必需参数传递给组件,您可以获得类似的结果使用抽象层后你会得到。
    • 我稍后试试。
    • 这不起作用。也许是因为Article and CategoryArticle and TagMTM 关系?此外,我尝试仅使用state 运行query。但结果为 0。es.search(index='articles', body={"query": {"term": {"state": "Alabama"}}})
    • 你的索引映射是什么?
    【解决方案2】:

    我认为这里不需要使用elasticsearch-dsl

    这是我的决定。

    from typing import Dict, List, Tuple, Union
    
    from elasticsearch import Elasticsearch
    from flask import request
    from flask.views import MethodView
    
    from .models import AticleModel  # ArticleModel.__tablename__ == "articles"
    
    
    es = Elasticsearch()
    
    
    class ArticleSearchAPIView(MethodView):
        """
        Search articles using ElasticSearch
        """
    
        def get(
            self,
        ) -> Union[
            Dict[str, Union[list, List[str]]],
            Tuple[Dict[str, str], int],
            Dict[str, Union[list, str]],
        ]:
            """
            Search articles
            """
            data = request.get_json()
            categories = data.get("categories")
            tags = data.get("tags")
            state = data.get("state")
            result = "Articles not found."
    
            query = {"bool": {"must": []}}
            if state:
                query["bool"]["must"].append({"term": {"state.keyword": state}})
            if categories:
                query["bool"]["must"].append(
                    {"terms": {"categories.keyword": categories}}
                )
            if tags:
                query["bool"]["must"].append({"terms": {"tags.keyword": tags}})
    
            found = es.search(
                index=ArticleModel.__tablename__, body={"query": query},
            )["hits"].get("hits")
    
            if found:
                result = [article["_source"] for article in found]
            return {"response": result}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-19
      • 1970-01-01
      • 2017-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多