Эластичная фильтрация по размеру поля, являющегося массивом

Как я могу отфильтровать документы, у которых есть поле, которое является массивом и имеет больше чем N элементов?

Как я могу отфильтровать документы, которые имеют поле, которое является пустым массивом?

Является ли грань решением? Если так, то как?

12 ответов

Решение

Я бы взглянул на фильтр скриптов. Следующий фильтр должен возвращать только те документы, которые содержат не менее 10 элементов в fieldname поле, которое является массивом. Имейте в виду, что это может быть дорого в зависимости от того, сколько документов у вас есть в вашем индексе.

"filter" : {
    "script" : {
        "script" : "doc['fieldname'].values.length > 10"
    }
}

Что касается второго вопроса: у вас действительно есть пустой массив? Или это просто поле массива без значения? Вы можете использовать отсутствующий фильтр для получения документов, которые не имеют значения для определенного поля:

"filter" : {
    "missing" : { "field" : "user" }
}

В противном случае, я думаю, вам нужно снова использовать сценарии, аналогично тому, что я предложил выше, просто с другой длиной ввода. Если длина постоянна, я бы положил ее в params section, так что сценарий будет кешированручным и использован повторно, так как он всегда один и тот же:

"filter" : {
    "script" : {
        "script" : "doc['fieldname'].values.length > param1"
        "params" : {
            "param1" : 10
        }
    }
}

Ответ javanna верен в Elasticsearch 1.3.x и более ранних версиях, поскольку в 1.4 модуль сценариев по умолчанию был изменен на groovy (был mvel).

Чтобы ответить на вопрос ОП.

На Elasticsearch 1.3.x и ранее используйте этот код:

"filter" : {
    "script" : {
        "script" : "doc['fieldname'].values.length > 10"
    }
}

На Elasticsearch 1.4.x и выше используйте этот код:

"filter" : {
    "script" : {
        "script" : "doc['fieldname'].values.size() > 10"
    }
}

Кроме того, в Elasticsearch 1.4.3 и более поздних версиях вам потребуется включить динамический сценарий, поскольку он отключен по умолчанию из-за проблем безопасности. См.: https://www.elastic.co/guide/en/elasticsearch/reference/1.4/modules-scripting.html

Все еще отправляю сюда для того, кто застрял в той же ситуации со мной. Допустим, ваши данные выглядят так:

{
    "_source": {
        "fieldName" : [
            {
                "f1": "value 11",
                "f2": "value 21"
            },
            {
                "f1": "value 12",
                "f2": "value 22"
            }
        ]
    }
}

Затем отфильтровать fieldName с длиной> 1, например:

"query": {
    "bool" : {
        "must" : {
            "script" : {
                "script" : {
                    "inline": "doc['fieldName.f1'].values.length > 1",
                    "lang": "painless"
                 }
            }
        }
    }
}

Синтаксис сценария приведен в виде документации по ES 5.4 https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-script-query.html.

Если у вас есть массив объектов, которые не отображаются какnested, имейте в виду, что Elastic превратит их в:

attachments: [{size: 123}, {size: 456}] --> attachments.size: [123, 456]

Итак, вы хотите указать свое поле как doc['attachments.size'].lengthне doc['attachments'].length, что очень противоречит интуиции.

То же самое для doc.containsKey(attachments.size).

В .values часть устарела и больше не нужна.

Imho правильный способ фильтрации массивов по размеру с помощью сценариев:

"filter" : {
    "script" : {
        "script" : "_source.fieldName.size() > 1"
    }
}

Если я сделаю это, как подсказывает @javanna, это вызовет исключение groovy.lang.MissingPropertyException: No such property: length for class: java.lang.String

На основании этого: https://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/collect/RegularImmutableList.java?r=707f3a276d4ea8e9d53621d137febb00cd2128da

И на ответ Лисака здесь.

Есть функция size(), которая возвращает длину списка:

"filter" : {
    "script" : {
        "script" : "doc['fieldname'].values.size() > 10"
    }
}

Самый простой способ сделать это - "денормализовать" ваши данные, чтобы у вас было свойство, которое содержит счетчик и логическое значение, если оно существует или нет. Тогда вы можете просто искать по этим свойствам.

Например:

{
   "id": 31939,
   "hasAttachments": true,
   "attachmentCount": 2,
   "attachments": [
      {
         "type": "Attachment",
         "name": "txt.txt",
         "mimeType": "text/plain"
      },
      {
         "type": "Inline",
         "name": "jpg.jpg",
         "mimeType": "image/jpeg"
      }
   ]  
}

Вот что сработало для меня:

      GET index/search {
    "query": {
       "bool": {
          "filter" : {
             "script" : {
                "script" : "doc['FieldName'].length > 10"
                }
            }
         }
       }
}

Когда вам нужно найти документы, которые содержат какое-то поле, размер / длина которого должен быть больше нуля, @javanna дала правильный ответ. Я только хотел добавить, если ваше поле является текстовым полем, и вы хотите найти документы, которые содержат текст в этом поле, вы не можете использовать тот же запрос. Вам нужно будет сделать что-то вроде этого:

GET index/_search 
{
    "query": {
        "bool": {
            "must": [
                {
                    "range": {
                        "FIELD_NAME": {
                            "gt": 0
                        }
                    }
                }
            ]
        }
    }
}

Это не точный ответ на этот вопрос, потому что ответ уже существует, но решение для аналогичной проблемы, которое у меня было, так что, возможно, кто-то найдет его полезным.

Для версии 7+:

      "filter": {
    "script": {
        "script": {
          "source": "doc['fieldName.keyword'].length > 10",
          "lang": "painless"
        }
    }
}

Ссылка https://medium.com/@felipegirotti/elasticsearch-filter-field-array-more-than-zero-8d52d067d3a0

Предположим, наш массив представляет собой объект типа:

      user: {
id: "01"
name: "Shikhil"
}
{
id: "02"
name: "Shankar"
}

Мы можем агрегировать длину, используя безболезненные скрипты.

      GET org-teams/_search
{
  "aggs": {
    "anyId": {
      "terms": {
        "field": "id.keyword",
        "size": 1000
      },
      "aggs": {
        "count_requirements": {
          "sum": {
            "script": {
              "inline": "params._source.containsKey('users') && params._source['users'] != null  ? params._source.users.size() : 0"
            }
          }
        }
      }
    }
  }
}```

предложение по второму вопросу:

Как я могу фильтровать документы, в которых есть поле, представляющее собой пустой массив?

      {
  "query": {
    "bool": {
      "must_not": {
        "exists": {
          "field": "fieldname"
        }
      }
    }
  }
}

вернет документы с пустым fieldname: []массивы. must(скорее, чем must_notвернет обратное).

Другие вопросы по тегам