Elastic Search, Настройка синонимов

20 февр. 2018 г.

Это вторая статья из цикла про Elastic Search. Речь пойдет о настройках синонимов Elastic Search. Синонимы нужны в том случае, если у нас есть несколько слов, которые морфологически не похожи друг на друга, но при этом имеют сходное значение. Или это может быть профессиональный жаргон. Примеры: Углошлифовальная машина - болгарка, Отвертка - шуруповерт и т.д.

Стартовая проверка

Все настройки проводились на Elastic Search 6.1.2. Запускалось в Docker.

docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.1.0-->

Проверка версии:

http://localhost:9200/

{
  "name": "kPe0CUN",
  "cluster_name": "docker-cluster",
  "cluster_uuid": "htl0xwdDTgSzipAJgSfLmw",
  "version": {
    "number": "6.1.2",
    "build_hash": "5b1fea5",
    "build_date": "2018-01-10T02:35:59.208Z",
    "build_snapshot": false,
    "lucene_version": "7.1.0",
    "minimum_wire_compatibility_version": "5.6.0",
    "minimum_index_compatibility_version": "5.0.0"
  },
  "tagline": "You Know, for Search"
}

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

Создание синонимов

Синонимы включаются в настройки индекса. Также есть возможность указать текстовый файл. Но мне этот вариант показался не таким удобным. Хотя, возможно, он имеет свои преимущества.

Создаем индекс:

curl -X PUT \
http://localhost:9200/product \
-H 'Content-Type: application/json' \
-d '{
  "settings": {
    "analysis": {
      "filter": {
        "my_synonym_filter": {
          "type": "synonym",
          "synonyms": [
            "шуруповерт, отвертка"
          ]
        },
        "ru_stop": {
          "type": "stop",
          "stopwords": "_russian_"
        },
        "ru_stemmer": {
          "type": "stemmer",
          "language": "russian"
        }
      },
      "analyzer": {
        "my_synonyms": {
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "my_synonym_filter",
            "ru_stop",
            "ru_stemmer"
          ]
        }
      }
    }
  }
}'

Создаем маппинг

curl -X PUT \
http://localhost:9200/product/_mapping/type \
-H 'Cache-Control: no-cache' \
-H 'Content-Type: application/json' \
-H 'Postman-Token: 98d28f1a-665b-3e26-948b-10b789a6ffce' \
-d '{
  "properties": {
    "id": {
      "type": "integer"
    },
    "sku": {
      "type": "text",
      "index": true,
      "search_analyzer": "my_synonyms",
      "analyzer": "my_synonyms",
      "term_vector": "with_positions_offsets_payloads"
    },
    "name": {
      "type": "text",
      "index": true,
      "search_analyzer": "my_synonyms",
      "analyzer": "my_synonyms",
      "term_vector": "with_positions_offsets_payloads"
    },
    "description": {
      "type": "text",
      "index": true,
      "search_analyzer": "my_synonyms",
      "analyzer": "my_synonyms",
      "term_vector": "with_positions_offsets_payloads"
    },
    "price": {
      "type": "double"
    },
    "created_at": {
      "type": "text",
      "index": true,
      "search_analyzer": "my_synonyms",
      "analyzer": "my_synonyms",
      "term_vector": "with_positions_offsets_payloads"
    },
    "updated_at": {
      "type": "text",
      "index": true,
      "search_analyzer": "my_synonyms",
      "analyzer": "my_synonyms",
      "term_vector": "with_positions_offsets_payloads"
    }
  }
}'

Добавление данных

Добавляем продукты:

curl -X PUT \
http://localhost:9200/product/type/1 \
-H 'Cache-Control: no-cache' \
-H 'Content-Type: application/json' \
-H 'Postman-Token: c99dcbcc-5f1f-4806-b941-db54d7fc2dfb' \
-d '{
  "id": 1,
  "sku": "ИНТЕРСКОЛ ОА-3,6Ф",
  "name": "Отвертка аккумуляторная ИНТЕРСКОЛ ОА-3,6Ф блистер (433.0.2.00)",
  "description": "Отвертка аккумуляторная ИНТЕРСКОЛ ОА-3,6Ф блистер (433.0.2.00) li-ion Номинальное напряжение, В 3,6 Частота вращения, об/мин 210 Макс. Крутящий момент, Нм 5 Число ступеней регулировки крутящего момента 15+1 Масса, кг 0,5 Особенности: Технология Li-ion, Редуктор с металлическими пластинами, компактность, светодиодный фонарь, индикатор заряда, LED-подсветка.",
  "attribute_set_id": 4,
  "price": 100,
  "created_at": "2017-12-04 10:08:12",
  "updated_at": "2017-12-27 10:28:36"
}'
curl -X PUT \
http://localhost:9200/product/type/2 \
-H 'Cache-Control: no-cache' \
-H 'Content-Type: application/json' \
-H 'Postman-Token: 93c262a5-6acc-b48c-4547-1596f2484534' \
-d '{
  "id": 2,
  "sku": "Шуруповерт HAMMER",
  "name": "Шуруповерт HAMMER",
  "description": "Шуруповерт HAMMER",
  "attribute_set_id": 4,
  "price": 100,
  "created_at": "2017-12-04 10:08:12",
  "updated_at": "2017-12-27 10:28:36"
}'

Поиск

И так, у нас есть 2 продукта с разными названиями. Совпадений нет. Зато у нас есть настроенные синонимы. Пробуем искать по синонимам:

curl -X POST \
http://localhost:9200/product/type/_search \
-H 'Content-Type: application/json' \
-d '{
  "query": {
    "multi_match": {
      "query": "Отвертка"
    }
  }
}'
curl -X POST \
http://localhost:9200/product/type/_search \
-H 'Content-Type: application/json' \
-d '{
  "query": {
    "multi_match": {
      "query": "Шуруповерт"
    }
  }
}'

В обоих случаях возвращаются оба продукта. Значит синонимы работают корректно.

Обновление синонимов

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

Правда все же придется остановить индекс. Добавить синонимы, потом запустить снова. Но, к счастью, это происходит почти моментально.

Закрываем индекс:

curl -X POST \
http://localhost:9200/product/_close \
-H 'Content-Type: application/json' \

Обновляем синонимы

curl -X PUT \
http://localhost:9200/product/_settings \
-H 'Content-Type: application/json' \
-d '{
  "settings": {
    "analysis": {
      "filter": {
        "my_synonym_filter": {
          "type": "synonym",
          "synonyms": [
            "шуруповерт, шурик, отвертка"
          ]
        }
      }
    }
  }
}'

Открываем индекс

curl -X POST \
http://localhost:9200/product/_open \
-H 'Content-Type: application/json' \

Проверка

Мы добавили новый синоним "шурик" - жаргон. Теперь пробуем искать по нему.

curl -X POST \
http://localhost:9200/product/type/_search \
-H 'Cache-Control: no-cache' \
-H 'Content-Type: application/json' \
-H 'Postman-Token: d9d21a6d-07ff-9b29-e080-87f95c5ecddf' \
-d '{
  "query": {
    "multi_match": {
      "query": "шурик"
    }
  }
}'

Также возвращаются оба продукта. Обновление синонимов прошло успешно.

Все запросы из статьи оформлены в виде Postman коллекции. Скачать можно тут: elastic_synonyms.postman_collection.json

На этом пока все. Спасибо за внимание!