Знакомство с Elasticsearch

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

Установка Elasticsearch

Для установки 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 является самой стабильной, поэтому мы будем использовать именно её. Если вам нужна какая-то другая версия, то архив релизов можно найти тут.

Затем распаковываем и запускаем установку.

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. Для начала давайте установим PHP клиент для работы с Elasticsearch.

composer require elasticsearch/elasticsearch

Затем создаём новый php файл, который будем использовать для примеров.

<?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
)

В приведённом примере мы не указали ID документа. В этом случае Elasticsearch автоматически присвоит уникальный ID. Это можно сделать и самим:

$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);

Вложенность может быть какой угодно глубины (однако увлекаться этим не стоит).

Поиск по документам

Для поиска следует использовать метод get или search: get в случа, если вы знаете ID документа. Так же следует отметить, что данный метод возвращает только один документ. Для поиска набора результатов по различным критериям (по полям) следует использовать метод search().

Get

Давайте начнём с метода 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 то каждый шард работает корректно.
  • 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.

$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);

OR и AND

При работе с запросами в реляционных СУБД мы часто используем ключевые слова AND или OR. В Elasticsearch у нас тоже есть такая возможность: для этого просто указываем ключ and. Выберем все документы, где возраст age равен 10 и badge равен 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;

Возвращает только один документ.

Постраничное разделение

В реляционных СУБД есть возможность выбирать данные по различным сегментам, используя ключевые слова LIMIT и OFFSET. В Elasticsearch тоже есть аналоги: ключи size и from. from позволяет нам осуществить выборку с нужного нам “отступа”. Индексация документов начинается с нуля. Вот как мы можем получить вторую страницу результатов:

$params['index'] = 'pokemon';
$params['type']  = 'pokemon_trainer';

$params['size'] = 10;
$params['from'] = 10; // <-- возврат второй страницы

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

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

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


$result['_source']['age'] = 21; //задаём полю новое значение

//добавляем новое поле
$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. В массиве аргументов следует указать поля 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 приложение гибкий и быстрый поиск.

Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: http://www.sitepoint.com/introduction-to-elasticsearch-in-php/
Перевел: Станислав Протасевич
Урок создан: 21 Августа 2015
Просмотров: 28447
Правила перепечатки


5 последних уроков рубрики "PHP"

  • Фильтрация данных с помощью zend-filter

    Когда речь идёт о безопасности веб-сайта, то фраза "фильтруйте всё, экранируйте всё" всегда будет актуальна. Сегодня поговорим о фильтрации данных.

  • Контекстное экранирование с помощью zend-escaper

    Обеспечение безопасности веб-сайта — это не только защита от SQL инъекций, но и протекция от межсайтового скриптинга (XSS), межсайтовой подделки запросов (CSRF) и от других видов атак. В частности, вам нужно очень осторожно подходить к формированию HTML, CSS и JavaScript кода.

  • Подключение Zend модулей к Expressive

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

  • Совет: отправка информации в Google Analytics через API

    Предположим, что вам необходимо отправить какую-то информацию в Google Analytics из серверного скрипта. Как это сделать. Ответ в этой заметке.

  • Подборка PHP песочниц

    Подборка из нескольких видов PHP песочниц. На некоторых вы в режиме online сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.

^ Наверх ^