Эластичная фильтрация по размеру поля, являющегося массивом
Как я могу отфильтровать документы, у которых есть поле, которое является массивом и имеет больше чем 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
вернет обратное).