Как исключить поле из поиска с помощью elasticsearch 6.1?

У меня есть индекс с несколькими полями. Я хочу отфильтровать на основе наличия строки поиска во всех полях, кроме одного - user_comments. Поиск по запросу, который я выполняю,

{
    "from": offset,
    "size": limit,
    "_source": [
      "document_title"
    ],
    "query": {
      "function_score": {
        "query": {
          "bool": {
            "must":
            {
              "query_string": {
                "query": "#{query}"
              }
            }
          }
        }
      }
    }
  }

Хотя строка запроса выполняет поиск по всем полям и дает мне документы с соответствующей строкой в ​​поле user_comments. Но я хочу запросить его по всем полям, исключив поле user_comments. Белый список - это очень большой список, а также имена полей являются динамическими, поэтому невозможно упомянуть список полей из белого списка с помощью параметра полей, например.

"query_string": {
                    "query": "#{query}",
                    "fields": [
                      "document_title",
                      "field2"
                    ]
                  }

Кто-нибудь может предложить идею о том, как исключить поле из поиска?


person Richa Sinha    schedule 11.10.2018    source источник
comment
Я думаю, что с ElasticSearch 1.4 можно было использовать partial_fields. Нечто похожее на то, что упомянуто в этом ответе - stackoverflow.com/a/31713773/4066118   -  person Richa Sinha    schedule 11.10.2018


Ответы (2)


Есть способ заставить его работать, это некрасиво, но сработает. Вы можете достичь своей цели, используя boost и многополевые параметры query_string, _ 2_ запрос для объединения оценок и настройки _ 3_:

POST my-query-string/doc/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "query_string": {
            "query": "#{query}",
            "type": "most_fields",
            "boost": 1
          }
        },
        {
          "query_string": {
            "fields": [
              "comments"
            ],
            "query": "#{query}",
            "boost": -1
          }
        }
      ]
    }
  },
  "min_score": 0.00001
}

Так что же происходит под капотом?

Предположим, у вас есть следующий набор документов:

PUT my-query-string/doc/1
{
  "title": "Prodigy in Bristol",
  "text": "Prodigy in Bristol",
  "comments": "Prodigy in Bristol"
}
PUT my-query-string/doc/2
{
  "title": "Prodigy in Birmigham",
  "text": "Prodigy in Birmigham",
  "comments": "And also in Bristol"
}
PUT my-query-string/doc/3
{
  "title": "Prodigy in Birmigham",
  "text": "Prodigy in Birmigham and Bristol",
  "comments": "And also in Cardiff"
}
PUT my-query-string/doc/4
{
  "title": "Prodigy in Birmigham",
  "text": "Prodigy in Birmigham",
  "comments": "And also in Cardiff"
}

В вашем поисковом запросе вы хотели бы видеть только документы 1 и 3, но ваш исходный запрос вернет 1, 2 и 3.

В Elasticsearch результаты поиска сортируются по релевантности _6 _, чем больше балл, тем лучше.

Итак, давайте попробуем сдвиньте вниз поле "comments", чтобы не повлиять на оценку релевантности. Мы можем сделать это, объединив два запроса с should и используя отрицательный boost:

POST my-query-string/doc/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "query_string": {
            "query": "Bristol"
          }
        },
        {
          "query_string": {
            "fields": [
              "comments"
            ],
            "query": "Bristol",
            "boost": -1
          }
        }
      ]
    }
  }
}

Это даст нам следующий результат:

{
  "hits": {
    "total": 3,
    "max_score": 0.2876821,
    "hits": [
      {
        "_index": "my-query-string",
        "_type": "doc",
        "_id": "3",
        "_score": 0.2876821,
        "_source": {
          "title": "Prodigy in Birmigham",
          "text": "Prodigy in Birmigham and Bristol",
          "comments": "And also in Cardiff"
        }
      },
      {
        "_index": "my-query-string",
        "_type": "doc",
        "_id": "2",
        "_score": 0,
        "_source": {
          "title": "Prodigy in Birmigham",
          "text": "Prodigy in Birmigham",
          "comments": "And also in Bristol"
        }
      },
      {
        "_index": "my-query-string",
        "_type": "doc",
        "_id": "1",
        "_score": 0,
        "_source": {
          "title": "Prodigy in Bristol",
          "text": "Prodigy in Bristol",
          "comments": "Prodigy in Bristol",
          "discount_percent": 10
        }
      }
    ]
  }
}

Документ 2 был оштрафован, но также и документ 1, хотя для нас это желаемое совпадение. Почему так случилось?

Вот как Elasticsearch вычислил _score в этом случае:

_score = max (название: Bristol, текст: Bristol, комментарии: Bristol) - комментарии: Bristol

Документ 1 соответствует части comments:"Bristol" и также является лучшим результатом. По нашей формуле результат равен 0.

На самом деле мы хотели бы усилить первое предложение (со всеми полями) больше, если совпало больше полей.

Можем ли мы увеличить query_string соответствие большему количеству полей?

Мы можем query_string в многополевой режим имеет параметр type, который делает именно это. Запрос будет выглядеть так:

POST my-query-string/doc/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "query_string": {
            "type": "most_fields",
            "query": "Bristol"
          }
        },
        {
          "query_string": {
            "fields": [
              "comments"
            ],
            "query": "Bristol",
            "boost": -1
          }
        }
      ]
    }
  }
}

Это даст нам следующий результат:

{
  "hits": {
    "total": 3,
    "max_score": 0.57536423,
    "hits": [
      {
        "_index": "my-query-string",
        "_type": "doc",
        "_id": "1",
        "_score": 0.57536423,
        "_source": {
          "title": "Prodigy in Bristol",
          "text": "Prodigy in Bristol",
          "comments": "Prodigy in Bristol",
          "discount_percent": 10
        }
      },
      {
        "_index": "my-query-string",
        "_type": "doc",
        "_id": "3",
        "_score": 0.2876821,
        "_source": {
          "title": "Prodigy in Birmigham",
          "text": "Prodigy in Birmigham and Bristol",
          "comments": "And also in Cardiff"
        }
      },
      {
        "_index": "my-query-string",
        "_type": "doc",
        "_id": "2",
        "_score": 0,
        "_source": {
          "title": "Prodigy in Birmigham",
          "text": "Prodigy in Birmigham",
          "comments": "And also in Bristol"
        }
      }
    ]
  }
}

Как видите, нежелательный документ 2 находится внизу и имеет оценку 0. На этот раз оценка была рассчитана следующим образом:

_score = sum (название: Bristol, текст: Bristol, комментарии: Bristol) - комментарии: Bristol

Итак, были выбраны документы, соответствующие "Bristol" в любом поле. Оценка релевантности для comments:"Bristol" была исключена, и только документы, соответствующие title:"Bristol" или text:"Bristol", получили _score ›0.

Можем ли мы отфильтровать эти результаты с нежелательной оценкой?

Да, мы можем, используя _ 24_:

POST my-query-string/doc/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "query_string": {
            "query": "Bristol",
            "type": "most_fields",
            "boost": 1
          }
        },
        {
          "query_string": {
            "fields": [
              "comments"
            ],
            "query": "Bristol",
            "boost": -1
          }
        }
      ]
    }
  },
  "min_score": 0.00001
}

Это будет работать (в нашем случае), поскольку оценка документов будет равна 0 тогда и только тогда, когда "Bristol" был сопоставлен только с полем "comments" и не соответствовал никакому другому полю.

Результатом будет:

{
  "hits": {
    "total": 2,
    "max_score": 0.57536423,
    "hits": [
      {
        "_index": "my-query-string",
        "_type": "doc",
        "_id": "1",
        "_score": 0.57536423,
        "_source": {
          "title": "Prodigy in Bristol",
          "text": "Prodigy in Bristol",
          "comments": "Prodigy in Bristol",
          "discount_percent": 10
        }
      },
      {
        "_index": "my-query-string",
        "_type": "doc",
        "_id": "3",
        "_score": 0.2876821,
        "_source": {
          "title": "Prodigy in Birmigham",
          "text": "Prodigy in Birmigham and Bristol",
          "comments": "And also in Cardiff"
        }
      }
    ]
  }
}

Можно ли это сделать по-другому?

Конечно. Я бы не советовал заниматься _score настройкой, поскольку это довольно сложный вопрос.

Я бы посоветовал сделать выборку существующего сопоставления и заранее составить список полей для запуска запроса, это сделает код намного проще и понятнее.

Оригинальное решение, предложенное в ответе (сохранено для истории)

Первоначально предлагалось использовать этот тип запроса с той же целью, что и в приведенном выше решении:

POST my-query-string/doc/_search
{
  "query": {
    "function_score": {
      "query": {
        "bool": {
          "must": {
            "query_string": {
              "fields" : ["*", "comments^0"],
              "query": "#{query}"
            }
          }
        }
      }
    }
  },
  "min_score": 0.00001
}

Единственная проблема в том, что если индекс содержит какие-либо числовые значения, эта часть:

"fields": ["*"]

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

person Nikolay Vasiliev    schedule 11.10.2018
comment
^ 0 работает, хотя, когда я добавляю подстановочный знак, создание запроса не выполняется в полях: [*, комментарии ^ 0]. Есть ли другой способ указать все поля. Также я считаю, что min_score должен быть установлен на что-то большее, чем 0, иначе он будет выбирать комментарии, а его оценка будет равна 0. Я сохраняю 0,000001 в качестве минимальной оценки. И я использую оценку функции, потому что в моем фактическом запросе я применил ускорение. - person Richa Sinha; 11.10.2018
comment
@RichaSinha На какой версии ES вы работаете? Я проверил приведенный выше код с помощью 6.3, и он работал. - person Nikolay Vasiliev; 11.10.2018
comment
Я на ES версии 6.1 - person Richa Sinha; 11.10.2018
comment
@RichaSinha > Also I believe the min_score should be set to something greater than 0 - спасибо, в моем ответе была опечатка, конечно, он должен быть что-то маленькое, но больше 0. I am on ES version 6.1 - только что попробовал с ES 6.0, "fields" : ["*", "comments^0"], работает, не могли бы вы показать мне точное сообщение об ошибке? - person Nikolay Vasiliev; 11.10.2018
comment
ошибка говорит, что - Можно использовать префиксные запросы только для ключевых слов и текстовых полей - но не для [object.discount_percent], который имеет тип [double]. Можете ли вы попробовать поместить поле десятичного типа с типом в качестве ключевого слова при помещении в индекс. - person Richa Sinha; 12.10.2018
comment
@RichaSinha Мне удалось воспроизвести проблему, спасибо! Похоже на ошибку ES. Тем временем мне также удалось найти способ заставить его работать, проверьте ответ, я его обновил. - person Nikolay Vasiliev; 12.10.2018
comment
Спасибо, что решили мою проблему. Есть ли эта ошибка и в последней версии, если да, я думаю, что об этом следует сообщить им. - person Richa Sinha; 14.10.2018
comment
еще один запрос, почему мы используем most_fields? он объединяет все оценки вместе, если я не ошибаюсь, как это нам помогает? Также он отлично работает, используя тип по умолчанию, то есть best_fields. - person Richa Sinha; 15.10.2018
comment
@Richa Sinha мы используем most_fields, чтобы попросить ES дать нам сумму всех оценок всех сопоставленных полей, поэтому большее количество сопоставленных полей будет означать более высокую оценку (см. docs). Есть тонкий случай, когда best_fields не сработает: есть совпадение в text со счетом, скажем, 5 и в comments со счетом 6 ... Лучшим будет 6 и он будет исключен (поскольку мы вычитаем оценку comments). - person Nikolay Vasiliev; 15.10.2018
comment
спасибо @Nikolay это объясняет - person Richa Sinha; 16.10.2018

Как вы ищите, ES будет искать совпадения в поле _all. Чтобы исключить одно поле, вы можете отключить _all поле для комментариев пользователей.

Ссылка - https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-all-field.html#enables-all-field

Для ES 6.x это можно реплицировать с помощью copy_to

https://www.elastic.co/guide/en/elasticsearch/reference/current/copy-to.html

person xrage    schedule 11.10.2018
comment
Это правильно, так, как я добавил, поиск будет выполняться по всем полям. И если я не ошибаюсь, copy_to создаст дубликат тех же данных. У меня таких полей около 500, в каждом поле около миллиона значений. Дублирование сделает поиск необязательно медленным. - person Richa Sinha; 11.10.2018
comment
Другой способ - использовать enabled: false для user_comments, я не тестировал его, но на основе документов. Кажется, он просто сохраняет значение в индексе, но не выполняет никаких запросов по этому поводу. elastic.co/guide/en/elasticsearch/reference/6.4/ enabled.html - person xrage; 11.10.2018
comment
Это может сработать, но у меня есть отдельный запрос для поиска только в поле user_comments. Таким образом, если включить: false, я не смогу выполнять поиск в этом поле. поэтому это решение не сработает для меня. - person Richa Sinha; 11.10.2018