Подтвердить что ты не робот

Точные соответствия Elasticsearch на анализируемых полях

Есть ли способ, чтобы ElasticSearch идентифицировал точные совпадения в анализируемых полях? В идеале я хотел бы ввести строчный, токенизирующий, стебель и, возможно, даже фонетизировать мои документы, а затем запросить "точные" совпадения.

Я имею в виду, что если я проиндексирую "Гамбургские булочки" и "Гамбургеры", они будут проанализированы как [ "гамбургер" , "булочка" ] и [ "гамбургер" ]. Если я буду искать "Гамбургер", он вернет только документ "гамбургер" , так как это "точное" совпадение.

Я пробовал использовать ключевое слово tokenizer, но это не приведет к возникновению отдельных токенов. Нужно ли мне что-то делать, чтобы количество токенов было одинаковым?

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

4b9b3361

Ответ 1

Используйте токенизатор черепицы вместе со стеблем и все, что вам нужно. Добавьте подполе типа token_count, которое будет подсчитывать количество токенов в поле.

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

Попытка для этого, просто дать вам идею:

{
  "settings": {
    "analysis": {
      "filter": {
        "filter_shingle": {
          "type": "shingle",
          "max_shingle_size": 10,
          "min_shingle_size": 2,
          "output_unigrams": true
        },
        "filter_stemmer": {
          "type": "porter_stem",
          "language": "_english_"
        }
      },
      "analyzer": {
        "ShingleAnalyzer": {
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "snowball",
            "filter_stemmer",
            "filter_shingle"
          ]
        }
      }
    }
  },
  "mappings": {
    "test": {
      "properties": {
        "text": {
          "type": "string",
          "analyzer": "ShingleAnalyzer",
          "fields": {
            "word_count": {
              "type": "token_count",
              "store": "yes",
              "analyzer": "ShingleAnalyzer"
            }
          }
        }
      }
    }
  }
}

И запрос:

{
  "query": {
    "filtered": {
      "query": {
        "match_phrase": {
          "text": {
            "query": "HaMbUrGeRs BUN"
          }
        }
      },
      "filter": {
        "term": {
          "text.word_count": "2"
        }
      }
    }
  }
}

Фильтр shingles важен здесь, потому что он может создавать комбинации токенов. И более того, это комбинации, которые поддерживают порядок или жетоны. Имо, самым сложным требованием для выполнения здесь является изменение жетонов (сокращение, нижняя шкала и т.д.), А также сборка исходного текста. Если вы не определили свой собственный фильтр "конкатенации", я не думаю, что есть другой способ, чем использовать фильтр shingles.

Но с shingles возникает другая проблема: он создает комбинации, которые не нужны. Для текста, такого как "Hamburgers buns in Los Angeles", вы получите длинный список черепицы:

          "angeles",
          "buns",
          "buns in",
          "buns in los",
          "buns in los angeles",
          "hamburgers",
          "hamburgers buns",
          "hamburgers buns in",
          "hamburgers buns in los",
          "hamburgers buns in los angeles",
          "in",
          "in los",
          "in los angeles",
          "los",
          "los angeles"

Если вас интересуют только те документы, которые соответствуют значению точно, указанные выше документы совпадают только при поиске "булочек с гамбургерами в лос-анджелесах" (и не соответствует чему-то вроде "любых гамбургеров" булочки в лос-ангелах "), тогда вам нужен способ фильтрации этого длинного списка черепицы. Как я вижу, это использовать word_count.

Ответ 2

Вы можете сохранить анализатор так, как вы ожидали (строчный, токенизирующий, стебель,...) и использовать query_string в качестве основного запроса match_phrase в качестве ускоряющего запроса для поиска. Что-то вроде этого:

{
   "bool" : {
      "should" : [
         {
            "query_string" : {
               "default_field" : "your_field",
               "default_operator" : "OR",
               "phrase_slop" : 1,
               "query" : "Hamburger"
            }
         },
         {
            "match_phrase": {
               "your_field": {
                  "query": "Hamburger"
               }
            }
         }
      ]
   }
}

Он будет соответствовать обоим документам, а точное совпадение (match_phrase) будет сверху, так как запрос соответствует как should предложениям (и получает более высокий балл)

default_operator установлено в OR, это поможет запросу "Гамбургские булочки" (совпадение hamburger ИЛИ bun) также соответствовать документу "Гамбургер". phrase_slop устанавливается в 1 для соответствия только с расстоянием = 1, например. поиск Hamburger Buns не будет соответствовать документу Hamburger Big Buns. Вы можете настроить это в соответствии с вашими требованиями.

Вы можете ссылаться Ближе лучше, Строка запроса для более подробной информации.

Ответ 3

Вы можете использовать multi-fields для этой цели и иметь под-поле not_analyzed в вашем поле analyzed (позвольте ему позвонить item в этом примере). Ваше сопоставление должно выглядеть следующим образом:

{
  "yourtype": {
    "properties": {
      "item": {
        "type": "string",
        "fields": {
          "raw": {
            "type": "string",
            "index": "not_analyzed"
          }
        }
      }
    }
  }
}

При таком отображении вы можете проверить, как каждый из значений Hamburgers и Hamburger Buns "просматривается" анализатором по отношению к вашим многополюсным item и item.raw

Для Hamburger:

curl -XGET 'localhost:9200/yourtypes/_analyze?field=item&pretty' -d 'Hamburger'
{
  "tokens" : [ {
    "token" : "hamburger",
    "start_offset" : 0,
    "end_offset" : 10,
    "type" : "<ALPHANUM>",
    "position" : 1
  } ]
}
curl -XGET 'localhost:9200/yourtypes/_analyze?field=item.raw&pretty' -d 'Hamburger'
{
  "tokens" : [ {
    "token" : "Hamburger",
    "start_offset" : 0,
    "end_offset" : 10,
    "type" : "word",
    "position" : 1
  } ]
}

Для Hamburger Buns:

curl -XGET 'localhost:9200/yourtypes/_analyze?field=item&pretty' -d 'Hamburger Buns'
{
  "tokens" : [ {
    "token" : "hamburger",
    "start_offset" : 0,
    "end_offset" : 10,
    "type" : "<ALPHANUM>",
    "position" : 1
  }, {
    "token" : "buns",
    "start_offset" : 11,
    "end_offset" : 15,
    "type" : "<ALPHANUM>",
    "position" : 2
  } ]
}
curl -XGET 'localhost:9200/yourtypes/_analyze?field=item.raw&pretty' -d 'Hamburger Buns'
{
  "tokens" : [ {
    "token" : "Hamburger Buns",
    "start_offset" : 0,
    "end_offset" : 15,
    "type" : "word",
    "position" : 1
  } ]
}

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

Теперь давайте индексируем два примера документов, чтобы проиллюстрировать это:

curl -XPOST localhost:9200/yourtypes/_bulk -d '
{"index": {"_type": "yourtype", "_id": 1}}
{"item": "Hamburger"}
{"index": {"_type": "yourtype", "_id": 2}}
{"item": "Hamburger Buns"}
'

И, наконец, чтобы ответить на ваш вопрос, если вы хотите иметь точное соответствие на Hamburger, вы можете искать в своем подполе item.raw, как это (обратите внимание, что случай тоже должен совпадать):

curl -XPOST localhost:9200/yourtypes/yourtype/_search -d '{
  "query": {
    "term": {
      "item.raw": "Hamburger"
    }
  }
}'

И вы получите:

{
  ...
  "hits" : {
    "total" : 1,
    "max_score" : 0.30685282,
    "hits" : [ {
      "_index" : "yourtypes",
      "_type" : "yourtype",
      "_id" : "1",
      "_score" : 0.30685282,
      "_source":{"item": "Hamburger"}
    } ]
  }
}

ОБНОВЛЕНИЕ (см. комментарии/обсуждение ниже и повторное редактирование вопроса)

Взяв ваш пример из комментариев и попробовав Hamburger Buns match Hamburger Buns, вы могли бы просто достичь этого с помощью запроса match, подобного этому.

curl -XPOST localhost:9200/yourtypes/yourtype/_search?pretty -d '{
  "query": {
    "match": {
      "item": {
        "query": "HaMbUrGeR BuNs",
        "operator": "and"
      }
    }
  }
}'

На основе того же двух индексированных документов выше будет

{
  ...
  "hits" : {
    "total" : 1,
    "max_score" : 0.2712221,
    "hits" : [ {
      "_index" : "yourtypes",
      "_type" : "yourtype",
      "_id" : "2",
      "_score" : 0.2712221,
      "_source":{"item": "Hamburger Buns"}
    } ]
  }
}