Статьи

Введение в Elasticsearch в PHP

В этом уроке мы рассмотрим Elasticsearch и то, как мы можем использовать его в PHP. Elasticsearch — это поисковый сервер с открытым исходным кодом, основанный на Apache Lucene. Мы можем использовать его для выполнения сверхбыстрых полнотекстовых и других сложных поисков. Он также включает в себя REST API, который позволяет нам легко отправлять запросы на создание, удаление, обновление и получение данных.

ElasticSearch Logo

Установка Elasticsearch

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

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

sudo add-apt-repository ppa:webupd8team/java sudo apt-get update 

Как только это будет сделано, мы можем установить Java.

 sudo apt-get install oracle-java8-installer 

Далее, давайте загрузим Elasticsearch с помощью wget .

 wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.5.2.tar.gz 

В настоящее время самая последняя стабильная версия — 1.5.2, так что мы использовали выше. Если вы хотите убедиться, что вы получили самую последнюю версию, взгляните на страницу загрузок Elasticsearch .

Затем мы извлекаем и устанавливаем.

 mkdir es tar -xf elasticsearch-1.5.2.tar.gz -C es cd es ./bin/elasticsearch 

Когда мы получаем доступ к http://localhost:9200 в браузере, мы получаем что-то похожее на следующее:

 { "status" : 200, "name" : "Rumiko Fujikawa", "cluster_name" : "elasticsearch", "version" : { "number" : "1.5.2", "build_hash" : "62ff9868b4c8a0c45860bebb259e21980778ab1c", "build_timestamp" : "2015-04-27T09:21:06Z", "build_snapshot" : false, "lucene_version" : "4.10.4" }, "tagline" : "You Know, for Search" } 

Использование Elasticsearch

Теперь мы можем начать играть с Elasticsearch. Сначала давайте установим официальный клиент Elasticsearch для PHP.

 composer require elasticsearch/elasticsearch 

Далее, давайте создадим новый php-файл, который мы будем использовать для тестирования и со следующим кодом, чтобы мы могли использовать клиент Elasticsearch.

 <?php require 'vendor/autoload.php'; $client = new Elasticsearch\Client(); 

Индексирование документов

Индексирование новых документов можно выполнить, вызвав метод index на клиенте. Этот метод принимает массив в качестве аргумента. Массив должен содержать body , index и type качестве его ключей. body — это массив, содержащий данные, которые вы хотите проиндексировать. index — это место, где вы хотите проиндексировать конкретный документ (соответствует базе данных в традиционных СУБД). Наконец, type — это тип, который вы хотите присвоить документу, как вы хотите классифицировать документ. Это как таблица в земле СУРБД. Вот пример:

 $params = array(); $params['body'] = array( 'name' => 'Ash Ketchum', 'age' => 10, 'badges' => 8 ); $params['index'] = 'pokemon'; $params['type'] = 'pokemon_trainer'; $result = $client->index($params); 

Если вы распечатаете $result вы получите нечто похожее на следующее:

 Array ( [_index] => pokemon [_type] => pokemon_trainer [_id] => AU1Bn51W5l_vSaLQKPOy [_version] => 1 [created] => 1 ) 

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

 $params = array(); $params['body'] = array( 'name' => 'Brock', 'age' => 15, 'badges' => 0 ); $params['index'] = 'pokemon'; $params['type'] = 'pokemon_trainer'; $params['id'] = '1A-000'; $result = $client->index($params); 

Когда мы печатаем $result :

 Array ( [_index] => pokemon [_type] => pokemon_trainer [_id] => 1A-001 [_version] => 1 [created] => 1 ) 

При индексации документов мы не ограничиваемся одномерным массивом. Мы также можем индексировать многомерные:

 $params = array(); $params['body'] = array( 'name' => 'Misty', 'age' => 13, 'badges' => 0, 'pokemon' => array( 'psyduck' => array( 'type' => 'water', 'moves' => array( 'Water Gun' => array( 'pp' => 25, 'power' => 40 ) ) ) ) ); $params['index'] = 'pokemon'; $params['type'] = 'pokemon_trainer'; $params['id'] = '1A-002'; $result = $client->index($params); 

Мы можем идти так глубоко, как хотим, но нам все еще нужно наблюдать за надлежащим хранением данных (не углубляясь, не структурируя и не логично и т.д.), когда мы индексируем их с помощью Elasticsearch, как мы это делаем в настройках СУБД.

Поиск документов

Мы можем искать существующие документы в определенном индексе, используя метод get или search . Основное различие между ними заключается в том, что метод get обычно используется, когда вы уже знаете идентификатор документа. Его также используют для получения только одного документа. С другой стороны, метод search() используется для поиска по нескольким документам, и вы можете использовать любое поле в документе для вашего запроса.

Получить

Сначала давайте начнем с метода get . Как и метод index , этот метод принимает массив в качестве аргумента. Массив должен содержать index , type и id документа, который вы хотите найти.

 $params = array(); $params['index'] = 'pokemon'; $params['type'] = 'pokemon_trainer'; $params['id'] = '1A-001'; $result = $client->get($params); 

Код выше вернет следующее:

 Array ( [_index] => pokemon [_type] => pokemon_trainer [_id] => 1A-001 [_version] => 1 [found] => 1 [_source] => Array ( [name] => Brock [age] => 15 [badges] => 0 ) ) 

Поиск с конкретными полями

Аргумент массива для метода search должен иметь index , type и ключи body . body — это то, где мы указываем запрос. Для начала, вот пример того, как мы используем его для возврата всех документов, имеющих возраст 15 лет.

 $params['index'] = 'pokemon'; $params['type'] = 'pokemon_trainer'; $params['body']['query']['match']['age'] = 15; $result = $client->search($params); 

Это возвращает следующее:

 Array ( [took] => 177 [timed_out] => [_shards] => Array ( [total] => 5 [successful] => 5 [failed] => 0 ) [hits] => Array ( [total] => 1 [max_score] => 1 [hits] => Array ( [0] => Array ( [_index] => pokemon [_type] => pokemon_trainer [_id] => 1A-001 [_score] => 1 [_source] => Array ( [name] => Brock [age] => 15 [badges] => 0 ) ) ) ) ) 

Давайте разберем результаты:

  • took — количество миллисекунд, которое потребовалось для завершения запроса.
  • timed_out — возвращает true если время ожидания истекло.
  • _shards — по умолчанию Elasticsearch распределяет данные на 5 сегментов. Если вы получаете 5 в качестве значения для total и successful то каждый шард в настоящее время здоров. Вы можете найти более подробное объяснение в этой теме Stackoverflow .
  • hits содержат результаты.

Однако метод, который мы использовали выше, позволяет искать только с глубиной первого уровня. Если мы хотим пойти дальше, мы должны использовать bool запросы. Для этого мы указываем bool как элемент для query . Затем мы можем перейти к нужному полю, используя . начиная с поля первого уровня до поля, которое мы хотим использовать в качестве запроса.

 $params['index'] = 'pokemon'; $params['type'] = 'pokemon_trainer'; $params['body']['query']['bool']['must'][]['match']['pokemon.psyduck.type'] = 'water'; $result = $client->search($params); 

Поиск по массивам

Мы можем выполнять поиск, используя массивы в качестве запроса (для соответствия нескольким значениям), указав элемент bool , затем must , terms а затем поле, которое мы хотим использовать для запроса. Мы указываем массив, содержащий значения, которые мы хотим сопоставить. В приведенном ниже примере мы выбираем документы, age которых равен 10 и 15 age .

 $params['index'] = 'pokemon'; $params['type'] = 'pokemon_trainer'; $params['body']['query']['bool']['must']['terms']['age'] = array(10, 15); 

Этот метод принимает только одномерные массивы.

Далее, давайте сделаем фильтрованный поиск. Чтобы использовать отфильтрованный поиск, мы должны указать filtered элемент и установить диапазон, который мы хотим вернуть для определенного поля. В приведенном ниже примере мы используем age в качестве поля. Мы выбираем документы, возраст которых больше или равен (gte) 11, но меньше или равен (lte) 20.

 $params['index'] = 'pokemon'; $params['type'] = 'pokemon_trainer'; $params['body']['query']['filtered']['filter']['range']['age']['gte'] = 11; $params['body']['query']['filtered']['filter']['range']['age']['lte'] = 20; $result = $client->search($params); 

ИЛИ И И

В земле СУБД мы привыкли использовать ключевые слова AND и OR для указания двух или более условий. Мы также можем сделать это с Elasticsearch, используя фильтрованный поиск. В приведенном ниже примере мы используем фильтр и, чтобы выбрать документы, которые имеют возраст 10 и количество значков 8. Возвращаются только те документы, которые соответствуют этим критериям.

 $params['index'] = 'pokemon'; $params['type'] = 'pokemon_trainer'; $params['body']['query']['filtered']['filter']['and'][]['term']['age'] = 10; $params['body']['query']['filtered']['filter']['and'][]['term']['badges'] = 8; $result = $client->search($params); 

Если вы хотите выбрать любой из них, вы можете использовать or вместо.

 $params['body']['query']['filtered']['filter']['or'][]['term']['age'] = 10; $params['body']['query']['filtered']['filter']['or'][]['term']['badges'] = 8; 

Ограничение результатов

Результаты могут быть ограничены определенным числом, указав поле size . Вот пример:

 $params['body']['query']['filtered']['filter']['and'][]['term']['age'] = 10; $params['body']['query']['filtered']['filter']['and'][]['term']['badges'] = 8; $params['size'] = 1; 

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

пагинация

В земле СУРБД у нас есть предел и смещение. В Elasticsearch у нас есть size и from . from позволяет нам указать индекс первого результата в наборе результатов. Документы индексируются нулем. Таким образом, для 10 результатов на странице, если у нас размер 10, мы добавляем 10 к значению from каждый раз, когда пользователь переходит на следующую страницу.

 $params['index'] = 'pokemon'; $params['type'] = 'pokemon_trainer'; $params['size'] = 10; $params['from'] = 10; // <-- will return second page 

Обновление документа

Чтобы обновить документ, нам сначала нужно получить старые данные документа. Для этого мы указываем index , type и id как мы делали ранее, а затем вызываем метод get . Текущие данные можно найти в _source . Все, что нам нужно сделать, это обновить текущие поля новыми значениями или добавить новые поля к этому элементу. Наконец, мы вызываем метод update с теми же параметрами, которые использовались для метода get.

 $params = array(); $params['index'] = 'pokemon'; $params['type'] = 'pokemon_trainer'; $params['id'] = '1A-001'; $result = $client->get($params); $result['_source']['age'] = 21; //update existing field with new value //add new field $result['_source']['pokemon'] = array( 'Onix' => array( 'type' => 'rock', 'moves' => array( 'Rock Slide' => array( 'power' => 100, 'pp' => 40 ), 'Earthquake' => array( 'power' => 200, 'pp' => 100 ) ) ) ); $params['body']['doc'] = $result['_source']; $result = $client->update($params); 

Это возвращает что-то похожее на следующее:

 Array ( [_index] => pokemon [_type] => pokemon_trainer [_id] => 1A-001 [_version] => 2 ) 

Обратите внимание, что _version увеличивается каждый раз, когда вы вызываете метод update , независимо от того, были ли обновления на самом деле.

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

Удаление документа

delete документ можно, вызвав метод delete . Этот метод принимает массив, содержащий index , type и id качестве аргумента.

 $params = array(); $params['index'] = 'pokemon'; $params['type'] = 'pokemon_trainer'; $params['id'] = '1A-001'; $result = $client->delete($params); 

Это возвращает следующее:

 Array ( [found] => 1 [_index] => pokemon [_type] => pokemon_trainer [_id] => 1A-001 [_version] => 7 ) 

Обратите внимание, что вы получите сообщение об ошибке, если попытаетесь извлечь удаленный документ, используя метод get .

Вывод

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

В целом, Elasticsearch — это хороший способ добавить функциональность поиска в ваши PHP-приложения. Если вы хотите узнать больше о том, как интегрировать Elasticsearch в ваши PHP-приложения, вы можете ознакомиться с серией статей Дэниела Сипоса о том, как интегрировать Elasticsearch с Drupal и Silex .

Однако, если вы предпочитаете более автоматические решения добавлению в свои приложения функции углубленного поиска, см. Эту серию .