Статьи

Устали от MongoDB Полнотекстовый указатель

Мы использовали MongoDB в качестве основного источника данных в Likeastore и пошли по пути полнотекстового поиска MongoDB. Сначала все было хорошо, но через некоторое время стало очевидно, что MongoDB FTS больше не может меня удовлетворять.

Я знал о поисковых хранилищах ElasticSearch , Sphinx , Solr и т. Д., Но процесс миграции всегда казался мне немного пугающим. После небольшой проработки я решил пойти с Elastic, и миграция была довольно веселой и не такой сложной, как я первоначально ожидал.

Что не так с MongoDB?

Мы используем MongoDB 2.4, и полнотекстовый поиск там является экспериментальной функцией. По умолчанию он отключен на производственных экземплярах MongoHQ. В любом случае, его очень легко включить и запустить textпротив него команды. Но мы встретили узкое место, когда индексная коллекция начинает расти (> 3 млн документов).

Мы делаем много, insertsи если у вас есть полнотекстовый индекс, каждый insertзапускает пересчет индекса. Несколько недель назад я заметил огромные проблемы с производительностью нашей БД. После первого взгляда на информацию о мониторинге стало ясно, что Lockedиндекс составляет> 110%, и для многих запросов истекло время ожидания. Приложение стало мало отзывчивым.

Это может быть потенциально решено путем перехода на более мощный сервер (больше ЦП и ОЗУ) или путем разделения базы данных на несколько экземпляров. Оба стоят денег. Затраты — это то, чего я бы хотел избежать, если это возможно. Пришло время перейти к специализированному решению.

Это причина для развертывания ElasticSearch — меньше напрягать MongoDB и использовать в качестве асинхронного индекса.

План миграции

Прежде чем начать, я проконсультировался с моим гуру Виная Сахни и его опытом ElasticSeach в SupportFu . Общие отзывы о ElasticSeach были действительно хорошими. Они не используют никаких рек, вместо этого используют after_saveхук их ORM, поэтому каждый раз, когда создается новый документ, он помещается в индекс ElasticSearch.

Я решил пойти аналогичным путем, поэтому мой план миграции был:

  1. Экспортируйте все существующие документы в ElasticSearch.
  2. Исправьте код и после каждой новой вставки отправляйте обновление в ElasticSearch.

В моей ситуации это немного проще, к тому же у меня есть только одна коллекция для индексации, а основная часть операций с БД — insertsнет updates, в противном случае я считаю, что использование of riverимеет больше смысла (и больше усилий для настройки).

Развернуть экземпляр на Digital Ocean

Я раскрутил дроплеты Digital Ocean и использовал эти замечательные инструкции по установке (кстати, я рекомендую установить из пакета Debian, так как проще работать с эластичным init.dсервисом).

Всего за 10 минут у вас есть работающий кластер ElasticSeach, готовый к работе.

Перенос данных MongoDB в индекс ElasticSearch

Затем мне нужно было перенести около 12 ГБ данных из моего производственного экземпляра MongoDB в Elastic. Для этого я создал действительно простой инструмент — эластер . Elaster направляет коллекцию MongoDB в индекс ElasticSearch на основе предоставленной вами конфигурации .

Я установил nodejsна сервере ElasticSeach, клонировал Elaster там и запустить,

elaster

Как в elaster, так и в коде приложения используется эластичный пакет npm

Весь процесс занял ~ 2 часа.

Обновите код приложения

Сразу после вставки документов в MongoDB и инициализации с помощью _idони готовы для сохранения в индекс. Я использую массовую операцию для этого,

index: function (items, state, callback) {
  if (!items || items.length === 0) {
      return callback(null, items);
  }

  var commands = [];
  items.forEach(function (item) {
      commands.push({'index': {'_index': 'items', '_type': 'item', '_id': item._id.toString()}});
      commands.push(item);
  });

  elastic.bulk({body: commands}, callback);
}

Простой поисковый запрос

ElasticSearch поставляется с очень хорошим Query DSL . Я очень плохо знаком с этой технологией, поэтому мне удалось создать действительно простой запрос, который уже дает довольно хорошие результаты.

function fullTextItemSearch (user, query, paging, callback) {
  if (!query) {
      return callback(null, { data: [], nextPage: false });
  }

  var page = paging.page || 1;

  elastic.search({
      index: 'items',
      from: (page - 1) * paging.pageSize,
      size: paging.pageSize,
      body: {
          query: {
              filtered: {
                  query: {
                      'query_string': {
                          query: query
                      },
                  },
                  filter: {
                      term: {
                          user: user.email
                      }
                  }
              }
          }
      }
  }, function (err, resp) {
      if (err) {
          return callback(err);
      }

      var items = resp.hits.hits.map(function (hit) {
          return hit._source;
      });

      callback(null, {data: items, nextPage: items.length === paging.pageSize});
  });
}

It just uses query_string and filter term, to filter out results for given user. The interface of Search API remained the same, just instead of querying MongoDB, now we query ElasticSearch.

Summary

The main conclusion is – ElasticSearch is kind of technology that works out of the box. It’s very complex inside, but have great interface that hides all the complexity. First and good enough results can be archived very quickly.

In the same time, the technology requires time and effort to understand it and to use it right. I have absolutely no experience with full text search, so it’s really journey for me.