Сохранение порядка терминов в запросе ElasticSearch

Возможно ли в ElasticSearch сформировать запрос, который бы сохранил порядок терминов?

Простым примером будет индексирование этих документов с использованием стандартного анализатора:

  1. Вы знаете, для поиска
  2. Вы знаете, поиск
  3. Знай поиск для тебя

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

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

Учитывая, что это возможно для фраз, просто цитируя текст: "you know" (получить 1-й и 2-й документы) мне кажется, что должен быть способ сохранить порядок для нескольких терминов, которые не являются смежными.

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

3 ответа

Решение

Вы могли бы использовать span_near запрос, он имеет in_order параметр.

{
    "query": {
        "span_near": {
            "clauses": [
                {
                    "span_term": {
                        "field": "you"
                    }
                },
                {
                    "span_term": {
                        "field": "search"
                    }
                }
            ],
            "slop": 2,
            "in_order": true
        }
    }
}

Соответствие фраз не гарантирует порядок;-). Если вы укажете достаточно склонов, например 2, например, "hello world" будет соответствовать "world hello". Но это не обязательно плохо, потому что обычно поиски более актуальны, если два термина "близки" друг к другу и их порядок не имеет значения. И я не думаю, что авторы этой функции думали о совпадении слов, которые находятся на расстоянии 1000 пометок друг от друга.

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

POST /my_index/my_type/_bulk
{ "index": { "_id": 1 }}
{ "title": "hello world" }
{ "index": { "_id": 2 }}
{ "title": "world hello" }
{ "index": { "_id": 3 }}
{ "title": "hello term1 term2 term3 term4 world" }

POST my_index/_search
{
  "query": {
    "filtered": {
      "query": {
        "match": {
          "title": {
            "query": "hello world",
            "slop": 5,
            "type": "phrase"
          }
        }
      },
      "filter": {
        "script": {
          "script": "term1Pos=0;term2Pos=0;term1Info = _index['title'].get('hello',_POSITIONS);term2Info = _index['title'].get('world',_POSITIONS); for(pos in term1Info){term1Pos=pos.position;}; for(pos in term2Info){term2Pos=pos.position;}; return term1Pos<term2Pos;",
          "params": {}
        }
      }
    }
  }
}

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

term1Pos = 0;
term2Pos = 0;
term1Info = _index['title'].get('hello',_POSITIONS);
term2Info = _index['title'].get('world',_POSITIONS);
for(pos in term1Info) {
  term1Pos = pos.position;
}; 
for(pos in term2Info) {
  term2Pos = pos.position;
}; 
return term1Pos < term2Pos;

Выше приведен запрос, который ищет слово "hello world" со значением 5, которое в приведенных выше документах будет соответствовать всем им. Но скрипт-фильтр гарантирует, что позиция в документе со словом "привет" ниже, чем позиция в документе для слова "мир". Таким образом, независимо от того, сколько слотов мы установили в запросе, тот факт, что позиции располагаются одна за другой, обеспечивает порядок.

Это раздел в документации, который проливает некоторый свет на вещи, используемые в приведенном выше сценарии.

Это именно то, что match_phrase запрос (см. здесь) делает.

Он проверяет положение терминов, помимо их присутствия.

Например, эти документы:

POST test/values
{
  "test": "Hello World"
}

POST test/values
{
  "test": "Hello nice World"
}

POST test/values
{
  "test": "World, I don't say hello"
}

все будет найдено с основным match запрос:

POST test/_search
{
  "query": {
    "match": {
      "test": "Hello World"
    }
  }
}

Но используя match_phrase, будет возвращен только первый документ:

POST test/_search
{
  "query": {
    "match_phrase": {
      "test": "Hello World"
    }
  }
}

{
   ...
   "hits": {
      "total": 1,
      "max_score": 2.3953633,
      "hits": [
         {
            "_index": "test",
            "_type": "values",
            "_id": "qFZAKYOTQh2AuqplLQdHcA",
            "_score": 2.3953633,
            "_source": {
               "test": "Hello World"
            }
         }
      ]
   }
}

В вашем случае вы хотите согласиться на некоторое расстояние между вашими условиями. Это может быть достигнуто с помощью slop параметр, который указывает, насколько далеко вы позволяете вашим терминам быть один от другого:

POST test/_search
{
  "query": {
    "match": {
      "test": {
        "query": "Hello world",
        "slop":1,
        "type": "phrase"
      }
    }
  }
}

С этим последним запросом вы также найдете второй документ:

{
   ...
   "hits": {
      "total": 2,
      "max_score": 0.38356602,
      "hits": [
         {
            "_index": "test",
            "_type": "values",
            "_id": "7mhBJgm5QaO2_aXOrTB_BA",
            "_score": 0.38356602,
            "_source": {
               "test": "Hello World"
            }
         },
         {
            "_index": "test",
            "_type": "values",
            "_id": "VKdUJSZFQNCFrxKk_hWz4A",
            "_score": 0.2169777,
            "_source": {
               "test": "Hello nice World"
            }
         }
      ]
   }
}

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

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