Как сделать вложенные фильтры AND и OR в ElasticSearch?

Мои фильтры сгруппированы в категории. Я хотел бы получить документы, где документ может соответствовать любому фильтру в категории, но если установлены две (или более) категории, то документ должен соответствовать любому из фильтров во ВСЕХ категориях.

Если он написан в псевдо-SQL, он будет выглядеть следующим образом:

SELECT * FROM Documents WHERE (CategoryA = 'A') AND (CategoryB = 'B' OR CategoryB = 'C')

Я пробовал вложенные фильтры следующим образом:

{
    "sort": [{
        "orderDate": "desc"
    }],
    "size": 25,
    "query": {
        "match_all": {}
    },
    "filter": {
        "and": [{
            "nested": {
                "path":"hits._source",
                "filter": {
                    "or": [{
                        "term": {
                            "progress": "incomplete"
                        }
                    }, {
                        "term": {
                            "progress": "completed"
                        }
                    }]
                }
            }
        }, {
            "nested": {
                "path":"hits._source",
                "filter": {
                    "or": [{
                        "term": {
                            "paid": "yes"
                        }
                    }, {
                        "term": {
                            "paid": "no"
                        }
                    }]
                }
            }
        }]
    }
}

Но, очевидно, я не совсем понимаю синтаксис ES. Это на правильном пути или мне нужно использовать другой фильтр?

Ответ 1

Это должно быть оно (переведено с данного псевдо-SQL)

{
   "sort": [
      {
        "orderDate": "desc"
      }
    ],
    "size": 25,
    "query":
    {
        "filtered":
        {
            "filter":
            {
                "and":
                [
                    { "term": { "CategoryA":"A" } },
                    {
                        "or":
                        [
                            { "term": { "CategoryB":"B" } },
                            { "term": { "CategoryB":"C" } }
                        ]
                    }
                ]
            }
        }
    }
}

Я понимаю, что вы не упоминаете грани, а только ради полноты:

Вы также можете использовать filter в качестве основы (как и вы) вместо filtered query (как и я). Полученный json почти идентичен разнице:

  • отфильтрованный запрос будет фильтровать как основные результаты, так и грани
  • фильтр будет фильтровать только основные результаты NOT facets.

Наконец, вложенные фильтры (которые вы пытались использовать) не связаны с "фильтрами вложенности", как вы, казалось, верили, но связаны с фильтрацией на вложенных документах (parent-child)

Ответ 2

Хотя я полностью не понимаю вашу структуру, это может быть то, что вам нужно.

Вы должны думать по-деревенски. Вы создаете bool, где вы должны (= и) выполнять встроенные bools. Каждый встроенный проверяет, не существует ли поле или нет (вместо этого нужно использовать здесь здесь) поле должно (условия здесь) быть одним из значений в списке.

Не уверен, есть ли лучший способ и не знает производительности.

{
    "sort": [
        {
            "orderDate": "desc"
        }
    ],
    "size": 25,
    "query": {
        "query": {           #
            "match_all": {}  # These three lines are not necessary
        },                   #
        "filtered": {
            "filter": {
                "bool": {
                    "must": [
                        {
                            "bool": {
                                "should": [
                                    {
                                        "not": {
                                            "exists": {
                                                "field": "progress"
                                            }
                                        }
                                    },
                                    {
                                        "terms": {
                                            "progress": [
                                                "incomplete",
                                                "complete"
                                            ]
                                        }
                                    }
                                ]
                            }
                        },
                        {
                            "bool": {
                                "should": [
                                    {
                                        "not": {
                                            "exists": {
                                                "field": "paid"
                                            }
                                        }
                                    },
                                    {
                                        "terms": {
                                            "paid": [
                                                "yes",
                                                "no"
                                            ]
                                        }
                                    }
                                ]
                            }
                        }
                    ]
                }
            }
        }
    }
}