Elasticsearch bool query_string для частичного и полного соответствия текста - PullRequest
1 голос
/ 30 апреля 2020

Я пытаюсь создать запрос Elasticsearch, который выполнит частичное и полнотекстовое сопоставление двух полей name и type в моем индексе и вернет все совпадения, содержащие указанное значение поля c uid. Например, у меня есть следующие записи:

  • { name: "Doug", "type": "Large"}
  • { name: "Doug Small", "type":"Large"}
  • { name: "Smal", "type": "Medium"}
  • { name: "Peter", "type": "Small"}

Я бы хотел, чтобы мой запрос совпадал и возвращал все эти записи. Вот мой запрос:

{
  "query": {
    "bool": {
      "must": [
        {
          "query_string": {
            "fields": [
              "name",
              "type"
            ],
            "query": "*Doug Small*~",
            "default_operator": "AND"
          }
        }
      ],
      "filter": [
        {
          "match": {
            "uid": "123"
          }
        }
      ]
    }
  }
}

Чтобы получить какие-либо результаты, я должен был обернуть запрос в *, а также добавить нечеткий ~ в конце. Это правильный тип запроса для этого варианта использования?

Вот мое отображение:

{
  "test": {
    "mappings": {
      "data": {
        "properties": {
          "uid": {
            "type": "keyword"
          },
          "name": {
            "type": "keyword"
          },
          "type": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

1 Ответ

1 голос
/ 30 апреля 2020

Здесь нужно рассмотреть несколько проблем.

  1. Не используйте query_string , если вы точно не знаете, что делаете. Обратите особое внимание, если ввод поступает от пользователя. Вместо этого предпочитайте использовать simple_query_string .
  2. Я сомневаюсь, что вы хотите, чтобы имя имело тип keyword. Этот тип означает, что строка не будет проанализирована (в нижнем регистре, токены и т. Д. c). Так что, если вы ищете что-то, кроме того же самого ввода, то оно не будет совпадать. например, Doug Small. Можно подумать, что, поскольку вы выполняете поиск с точно таким же вводом, по крайней мере, этот документ вернется, но это не так. Причина в том, что входные данные query_string или simple_query_string анализируются (и, как следствие, токенизируются). Если вы не укажете свои данные как один термин, они не будут совпадать. Для этого вам нужно заключить в термин двойные кавычки (" \" Doug Small \ ""). Но если вы сделаете это, вы потеряете все остальные совпадения.
  3. Я считаю, что вам нужно имя и тип, чтобы иметь тип text. Это означает, что сохраненная строка будет проанализирована (tokenized, в нижнем регистре и т. Д. c, отметьте простой анализатор (который используется по умолчанию, если вы не указали другой) .
  4. У вас есть оператор указано как AND для query_string. Это означает, что все термины запроса должны совпадать либо по имени, либо по типу. Но вы заявляете, что вам нужно, чтобы все документы возвращались вместе с вашим запросом. Только у одного документа есть оба Doug и Small. Если вам это нужно, оператор должен изменить на OR (по умолчанию).

Полный пример

PUT test
{
  "mappings": {
    "properties": {
      "uid": {
        "type": "keyword"
      },
      "name": {
        "type": "text"
      },
      "type": {
        "type": "text"
      }
    }
  }
}
POST test/_bulk
{ "index" : { "_id" : "1" } }
{ "name": "Doug", "type": "Large"}
{ "index" : { "_id" : "2" } }
{ "name": "Doug Small", "type":"Large"}
{ "index" : { "_id" : "3" } }
{ "name": "Smal", "type": "Medium"}
{ "index" : { "_id" : "4" } }
{ "name": "Peter", "type": "Small"}
GET test/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "simple_query_string": {
            "fields": [
              "name",
              "type"
            ],
            "query": "*Doug Small*",
            "default_operator": "OR"
          }
        }
      ]
    }
  }
}

Приведенный выше запрос теперь возвращает все три документа, которые имеют Doug или Small или оба. Более того, регистр не учитывается (поскольку он теперь анализируется), поэтому этот *doug small* даст те же 3 результата.

Поскольку теперь поля анализируются, вам не нужно использовать подстановочный символ, потому что теперь он используется для первого токена и последнего. Значение

  • *Doug Small*: сопоставить все, что имеет <ANYTHING>Dog ИЛИ Small<Anything>

Итак, давайте удалим Подстановочный знак также

GET test/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "simple_query_string": {
            "fields": [
              "name",
              "type"
            ],
            "query": "Doug Small",
            "default_operator": "OR"
          }
        }
      ]
    }
  }
}

Это дает те же самые 3 результата. Вам все еще не хватает Smal. Теперь вам нужно добавить нечеткое соответствие, чтобы включить это.

GET test/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "simple_query_string": {
            "fields": [
              "name",
              "type"
            ],
            "query": "Doug Small~",
            "default_operator": "OR"
          }
        }
      ]
    }
  }
}

Это Doug Small~ означает, что вы можете принести все, что имеет Doug ИЛИ Small, где Small может быть НЕ точным соответствием ,

Нечеткое совпадение для всех ваших терминов

GET test/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "simple_query_string": {
            "fields": [
              "name",
              "type"
            ],
            "query": "Dg~ Small~",
            "default_operator": "OR"
          }
        }
      ]
    }
  }
}

Причина, по которой Dg совпадает с Doug, связана с уровнем нечеткости https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#fuzziness

Максимально допустимое расстояние редактирования Левенштейна (или количество правок)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...