Elasticsearch - фильтр, где (один из вложенного массива) и (весь вложенный массив)
TL; DR. Как проверить, соответствуют ли одному из и всем вложенным массивом определенные критерии?
У меня есть document
. Каждый document
имеет массив вложенных outer
объектов, которые сами имеют список вложенных объектов inner
. Мне нужно выполнить фильтр для всех документов, где соответствует хотя бы один из документов outer
вложенных объектов. Когда я говорю совпадение, я имею в виду, что все объекты <вложенные объекты outer
outer
совпадают. Вот пример отображения для ссылки;
{ "document" : {
"properties" : {
"name" : {
"type" : "string"
},
"outer" : {
"type" : "nested",
"properties" : {
"inner" : {
"type" : "nested",
"properties" : {
"match" : {
"type" : "string",
"index" : "not_analyzed"
},
"type" : {
"type" : "string",
"index" : "not_analyzed"
}
}}}}}}
}
Если в документе нет объектов outer
/inner
, считается, что он соответствует. Но для того, чтобы ухудшить положение, внутренние объекты должны рассматриваться как соответствующие друг другу в зависимости от type
в виде условной логики (например, CASE
в SQL). Например, если type
был термином "Country"
, то объект inner
считался бы соответствующим, если match
был указанным кодом страны, например ES
. Документ может иметь inner
объекты переменной type
, и нет гарантии, что будут существовать определенные типы.
Исходя из императивного (Java) программирования, у меня возникают невероятные проблемы с тем, как реализовать такую фильтрацию. Ничто, о котором я не могу думать, даже смутно соответствует этому поведению. До сих пор все, что у меня есть, - это отфильтрованный запрос;
"filtered" : {
"query" : {
"match_all" : { }
},
"filter" : {
"bool" : {
"should" : {
"missing" : {
"field" : "outer.inner.type"
}
}}}}
}
Итак, вопрос в том, что...
Как фильтровать документы, у которых есть хотя бы один outer
объект, который имеет все inner
объекты, соответствующие на основе type
inner
объект?
Подробнее по запросу -
Пример документа JSON
{
"name":"First",
"outer":[
{
"inner":[
{"match":"ES","type":"Country"},
{"match":"Elite","type":"Market"}
]
},{
"inner":[
{"match":"GBR","type":"Country"},
{"match":"1st Class","type":"Market"},
{"match":"Admin","type":"Role"}
]
}
],
"lockVersion":0,"sourceId":"1"
}
Приведенный выше пример должен пройти через фильтр, если мы предоставим "1st Class"
market и страну "GRB"
, потому что второй из двух объектов outer
будет считаться совпадением, поскольку оба inner
соответствуют объекты. Если бы мы предоставили стране страны "GRB"
и на рынке "Elite"
, то мы бы не вернули этот документ, потому что ни один из объектов outer
не беспокоился бы о своих объектах inner
в целом. Если бы мы хотели, чтобы второй объект outer
соответствовал, то все три inner
должны были бы совпадать. Обратите внимание, что в третьем inner
есть дополнительный type
. Это приводит к ситуации, когда , если существует тип , тогда он должен иметь соответствие для него else, он не должен совпадать, потому что он отсутствует.
Ответы
Ответ 1
Один из вложенных массивов
Имея один из, вложенный массив, соответствующий некоторым критериям, оказывается очень простым. A вложенный фильтр оценивает соответствие /true, если любой из массивов вложенных объектов соответствует указанным внутренним фильтрам. Например, для массива объектов outer
, где один из этих объектов имеет поле match
со значением "matching"
, следующее считается истинным.
"nested": {
"path": "outer",
"filter": {
"term" : { "match" : "matching" }
}
}
Вышеупомянутое будет считаться истинным/совпадающим, если один из вложенных объектов outer
имеет поле с именем match
со значением "matching"
.
Все вложенные массивы
Наличие вложенного фильтра будет считаться совпадающим, если все более вложенные объекты в сопоставлении массива. На самом деле это невозможно. Но учитывая, что считается совпадением, если только один из вложенных объектов соответствует фильтру, мы можем отменить логику и сказать: "Если none вложенных объектов не соответствует для достижения того, что нам нужно. Например, заданный массив вложенных объектов outer.inner
, где все эти объекты имеют поле match
со значением "matching"
, следующее считается истинным.
"not" : {
"nested": {
"path": "outer.inner",
"filter": {
"not" : {
"term" : { "match" : "matching" }
}
}
}
}
Вышеупомянутое будет считаться истинным/сопоставлением, потому что none вложенных outer.inner
объектов не (двойной отрицательный) имеет поле с именем match
с значение "matching"
. Это, конечно, такое же, как все вложенные inner
объекты, имеющие поле match
со значением "matching"
.
Отсутствие вложенных объектов
Вы не можете проверить, отсутствует ли поле, содержащее вложенные объекты, с помощью традиционного отсутствующего фильтра. Это связано с тем, что вложенные объекты фактически не являются в документе, они хранятся где-то в другом месте. Как таковые отсутствующие фильтры всегда будут считаться истинными. Однако вы можете проверить, что фильтр match_all
не возвращает таких результатов;
"not": {
"nested": {
"path": "outer",
"filter": {
"match_all": {}
}
}
}
Это считается истинным/совпадающим, если match_all
не находит результатов.
Ответ 2
Ну, это doozy, но этот запрос, кажется, делает то, что вы хотите:
POST /test_index/_search
{
"query": {
"filtered": {
"filter": {
"nested": {
"path": "outer",
"filter": {
"bool": {
"must": [
{
"nested": {
"path": "outer.inner",
"filter": {
"bool": {
"must": [
{ "term": { "outer.inner.type": "Market" } },
{ "term": { "outer.inner.match": "1st Class" } }
]
}
}
}
},
{
"nested": {
"path": "outer.inner",
"filter": {
"bool": {
"must": [
{ "term": { "outer.inner.type": "Country" } },
{ "term": { "outer.inner.match": "GBR" } }
]
}
}
}
}
]
}
}
}
}
}
}
}
Вот какой код я использовал для его проверки:
http://sense.qbox.io/gist/f554c2ad2ef2c7e6f5b94b1ddb907813370f4edc
Сообщите мне, если вам нужно объяснение логики; это своего рода участие.