Elasticsearch описание

Thank you for reading this post, don't forget to subscribe!

Инстру­мен­тов для рабо­ты с лога­ми нава­лом, но мы посмот­рим в сто­ро­ну ELK сте­ка от Elastic. А имен­но: Elasticsearch, Logstash и Kibana — хранилище/поисковик, обра­бот­чик дан­ных и тул­за для их визу­а­ли­за­ции. А нач­нём, есте­ствен­но, с пер­вой бук­вы это­го трёх-бук­вен­но­го алфа­ви­та — «E».

Что такое Elasticsearch

Elasticsearch — это быст­рый, гори­зон­таль­но мас­шта­би­ру­е­мый и очень бес­плат­ный гибрид NoSQL базы дан­ных и гуг­ла для неё. С миром он обща­ет­ся через HTTP API и через него уже полу­ча­ет на вход JSON доку­мен­ты для индек­са­ции и хра­не­ния. Хра­не­ние, прав­да, мож­но отклю­чить, и тогда оста­нет­ся толь­ко поис­ко­вик, воз­вра­ща­ю­щий айдиш­ки когда-то про­ин­дек­си­ро­ван­ных документов.

Установка

Так как Elasticsearch напи­сан на Java, уста­нав­ли­ва­ет­ся он очень про­сто:  кача­ем архив, рас­па­ко­вы­ва­ем и запус­ка­ем bin/elasticsearch. Прав­да, запу­стить его через Docker будет еще про­ще: docker run -d -p9200:9200 elasticsearch. Всё обще­ние будет про­хо­дить через порт 9200, так что самое вре­мя в него постучаться.

Осматриваемся

Вэби­на­ры и офи­ци­аль­ная доку­мен­та­ция обыч­но пока­зы­ва­ют дем­ки, исполь­зуя еще один про­дукт от Elastic — Kibana. Но так речь идёт об обыч­ном HTTP и JSON, отправ­лять и полу­чать запро­сы мож­но чем угод­но. Навер­ное, самый про­стой гото­вый инстру­мент — кон­соль и curl.

Итак, у нас есть порт 9200. Попро­бу­ем его пнуть про­сто так:

Непло­хо. Номер вер­сии в сере­дине отве­та и шут­ка юмо­ра вни­зу — запрос опре­де­лен­но того стоил.

Есть и дру­гие полез­ные запро­сы, для кото­рых в индек­се не обя­за­тель­но долж­ны быть дан­ные. Напри­мер, мож­но про­ве­рить ста­тус узла:

Или полу­чить спи­сок индексов:

Так как уста­нов­ка све­жая, то, разу­ме­ет­ся, индек­сов в ней ника­ких еще нет. Но мы как-то вне­зап­но нача­ли опе­ри­ро­вать спе­ци­фи­че­ски­ми тер­ми­на­ми, вро­де узла и индек­са, так что сто­ит сна­ча­ла разо­брать­ся с терминологией.

Терминология Elasticsearch

Итак, мы уже упо­мя­ну­ли узел (node), что на самом деле озна­ча­ет отдель­но взя­тый про­цесс elasticsearch с его дан­ны­ми и настрой­ка­ми. Даже один узел обра­зу­ет кла­стер. Но если узлов в кла­сте­ре несколь­ко, то вклю­чён­ный по умол­ча­нию шар­динг (дроб­ле­ние) индек­сов и репли­ка­ция шар­дов (shards) рез­ко повы­сит и выжи­ва­е­мость индек­сов за счёт избы­точ­но­сти, и ско­рость запро­сов к ним за счёт парал­ле­ли­за­ции. Похо­жий под­ход, кста­ти, рабо­та­ет и в Kafka, толь­ко там шар­ды назы­ва­ют­ся разделами.

Сам по себе индекс — это про­сто кол­лек­ция доку­мен­тов. В пре­де­лах кла­сте­ра их может быть уйма. Внут­ри индек­са доку­мен­ты мож­но орга­ни­зо­вать по типам — про­из­воль­ным име­нам, опи­сы­ва­ю­щим похо­жие по струк­ту­ре доку­мен­ты. Нако­нец, сам доку­мент — это про­сто JSON. Обыч­ный скуч­ный JSON.

Создание, чтение, обновление и удаление

С дву­мя абза­ца­ми тео­рии разо­бра­лись, самое вре­мя что-нибудь сделать.

Создание

Доба­вить доку­мент в elasticsearch так же про­сто, как и сде­лать HTTP POST. Ну один в один:

Мы отпра­ви­ли доку­мент { "kind": "info", "message": "..."} в индекс под назва­ни­ем monitor, и тип с назва­ни­ем  logs. Ни индек­са, ни типа до это­го не суще­ство­ва­ло, и elasticsearch создал их, взяв назва­ния из URL. Вдо­ба­вок, он раз­ро­дил­ся ответ­ным JSON, в кото­рый поло­жил сге­не­ри­ро­ван­ный иден­ти­фи­ка­тор доку­мен­та (_id) и немно­го дру­гих дета­лей. Кста­ти, мож­но задать и свой _id, про­сто заме­нив POST на PUT и пере­дав иден­ти­фи­ка­тор в URL. Напри­мер, вот так -X PUT monitor/logs/42. Сло­во ?pretty в стро­ке запро­са про­сто вклю­ча­ет фор­ма­ти­ро­ва­ние ответов.

Так как добав­лять доку­мен­ты по одно­му то еще удо­воль­ствие, мож­но сра­зу доба­вить целую пач­ку через bulk запрос:

В bulk запро­се на каж­дый доку­мент идёт 2 JSON фраг­мен­та. Один пока­зы­ва­ет, что имен­но нуж­но делать с доку­мен­том (в нашем слу­чае «index»). Вто­рой фраг­мент — сам документ.

Чтение

Теперь, раз уж у нас уже есть что-то в индек­се, мож­но запро­сить что-нибудь назад. Если сде­лать пустой поиск, то мы полу­чим в ответ всё, что успе­ли добавить:

 

Но мож­но запро­сить кон­крет­ный доку­мент по его ID:

Обновление

По ана­ло­гии, зная иден­ти­фи­ка­тор доку­мен­та, мож­но его обно­вить. Так как моё началь­ство нерв­но отно­сит­ся к фра­зам в логах вро­де «Epic fail has just happened», его сто­ит заме­нить на что-нибудь другое:

Но на самом деле в этом слу­чае elasticsearch не обнов­ля­ет доку­мент, а про­сто заме­ня­ет его на дру­гой, сохра­няя преж­ний ID.

Удаление

Что­бы уда­лить что-нибудь есть HTTP DELETE. Например,

curl -X DELETE 127.0.0.1:9200/monitor/logs/AVoWe_7d6fU5oFCNC7jb

Поиск

Но добав­лять и полу­чать назад JSON уме­ют мно­гие NoSQL базы. Самая мощ­ная фиш­ка в elasticsearch — это, конеч­но, поиск. Есть два спо­со­ба, что­бы искать доку­мен­ты по индек­су: REST Request API для про­стых поис­ко­вых запро­сов, и более серьёз­ный Query DSL.

REST Request API про­сто добав­ля­ет допол­ни­тель­ный пара­метр в URL запро­са, вро­де такого:

Но через один ‘q='-пара­метр слож­ный запрос не собе­решь. В Query DSL, с дру­гой сто­ро­ны, мож­но и ком­би­ни­ро­вать клю­че­вые сло­ва, и соби­рать буле­вы выра­же­ния, добав­лять филь­тры — мно­го чего мож­но сде­лать, что­бы най­ти что-то конкретное.

Query DSL поиск это всё тот же HTTP GET запрос, но с немно­го более замо­ро­чен­ным син­так­си­сом. Напри­мер, если мы хотим най­ти логи, кото­рые так или ина­че каса­ют­ся памя­ти, но при этом не кри­тич­ны, мож­но собрать такой запрос:

Агрегирование

В дове­сок к поис­ку дан­ные мож­но еще и агре­ги­ро­вать. Агре­ги­ро­ва­ние само по себе огром­ный топик, но в каче­стве при­ме­ра мож­но про­сто посчи­тать, сколь­ко логов у нас ско­пи­лось по категориям:

Так как _search URL дела­ет и поиск и агре­га­цию, а клю­че­вые сло­ва для поис­ка мы не предо­ста­ви­ли, в пара­мет­ры при­шлось доба­вить "size": 0, что­бы запрос не вер­нул всё кол­лек­цию доку­мен­тов в дове­сок к статистике.

Завершение

Обыч­но в этом месте я пишу «сего­дня мы едва про­шлись по X», но elasticsearch настоль­ко огром­ный, что даже сло­во «едва» оши­боч­но созда­ёт иллю­зию объ­ё­ма. Обнов­ле­ние доку­мен­тов через отправ­ку скрип­та, схе­мы полей, филь­тры, слож­ные запро­сы  и агре­ги­ро­ва­ние, рабо­та в кла­сте­рах, ана­ли­за­то­ры доку­мен­тов и куча дру­гих вещей — мы их вооб­ще не затронули.

Но и того, что мы посмот­ре­ли, наде­юсь, долж­но хва­тить, что­бы понять: elasticsearch — это вме­ня­е­мо про­стой поис­ко­вый дви­жок с понят­ным API и мил­ли­о­ном фич, кото­рые нуж­но гуг­лить и гуглить.

 

=================================================

ИСПОЛЬЗОВАНИЕ ELASTICSEARCH

1. ПРОСМОТР ВЕРСИИ ELASTICSEARCH

Для обще­ния с Elasticsearch исполь­зу­ет­ся RESTful API или, если гово­рить про­стым язы­ком, обык­но­вен­ные HTTP-запро­сы. И рабо­тать с ним мы можем пря­мо из бра­у­зе­ра. Но делать это­го не будем, а будем исполь­зо­вать Linux-ути­ли­ту curl. Для про­смот­ра инфор­ма­ции о сер­ви­се доста­точ­но обра­тить­ся по адре­су localhost:9200:

curl -XGET http://localhost:9200

Опция -X зада­ёт про­то­кол, кото­рый мы исполь­зу­ем. В этой ста­тье мы исполь­зу­ем не толь­ко GET, но и POST, PUT, DELETE и дру­гие. Если вы толь­ко что пере­за­пу­сти­ли сер­вис и полу­ча­е­те ошиб­ку "http://localhost:9200 - conection refused", то ниче­го страш­но­го - систе­ма загру­жа­ет­ся, надо подо­ждать несколь­ко минут.

Если буде­те пытать­ся ей поме­шать, може­те поте­рять уже про­ин­дек­си­ро­ван­ные дан­ные. Про­сто убе­ди­тесь, что сер­вис уже запу­щен (active) командой:

sudo systemctl status elasticsearch

2. СПИСОК ИНДЕКСОВ ELASTICSEARCH

Точ­но так же, как MySQL может иметь несколь­ко баз дан­ных, Elastic может иметь несколь­ко индек­сов. У каж­до­го из них может быть несколь­ко отдель­ных таб­лиц (type), каж­дая из кото­рых будет содер­жать доку­мен­ты (doc), кото­рые мож­но срав­нить с запи­ся­ми в таб­ли­це MySQL.

Что­бы посмот­реть теку­щий спи­сок индек­сов, исполь­зуй­те коман­ду _cat:

curl 'localhost:9200/_cat/indices?v&pretty'

Общий син­так­сис исполь­зо­ва­ния гло­баль­ных команд такой:

curl 'localhost:9200/_команда/имя?параметр1&параметр2'

  • _команда - обыч­но начи­на­ет­ся с под­чер­ки­ва­ния и ука­зы­ва­ет основ­ное дей­ствие, кото­рое надо сделать;
  • имя - пара­метр коман­ды, ука­зы­ва­ет, над чем нуж­но выпол­нить дей­ствие, или уточ­ня­ет, что надо делать;
  • параметр1 - допол­ни­тель­ные пара­мет­ры, кото­рые вли­я­ют на отоб­ра­же­ние, фор­ма­ти­ро­ва­ние, точ­ность выво­да и так далее;

Напри­мер, коман­да _cat так­же может отоб­ра­зить общее "здо­ро­вье" индек­сов (health) или спи­сок актив­ных узлов (nodes). Пара­метр v вклю­ча­ет более подроб­ный вывод, а pretty сооб­ща­ет, что надо фор­ма­ти­ро­вать вывод в фор­ма­те json (что­бы было красиво).

3. ИНДЕКСАЦИЯ ДАННЫХ

Вооб­ще, вам не обя­за­тель­но созда­вать индекс. Вы може­те про­сто начать запи­сы­вать в него дан­ные, как буд­то бы он и таб­ли­ца уже суще­ству­ет. Про­грам­ма создаст всё авто­ма­ти­че­ски. Для запи­си дан­ных исполь­зу­ет­ся коман­да _index. Вот толь­ко одно­строч­ной коман­ды curl нам будет уже недо­ста­точ­но, надо доба­вить ещ сами дан­ные в фор­ма­те json, поэто­му созда­дим неболь­шой скрипт:

vi elastic_index.sh

#!/bin/bash
curl -XPUT 'http://localhost:9200/app/data?pretty ' -H 'Content-Type: application/json'  -d  '
{
"name":"Ivan",
"age" :"18",
"degree" : "90",
}'

Здесь я запи­сы­ваю дан­ные: индекс app, таб­ли­цу data. Так как они не суще­ству­ют, систе­ма их создаст. Как види­те, сами дан­ные нам нуж­но пере­дать в фор­ма­те json. Пере­да­ём заго­ло­вок с помо­щью опции -H, будем отсы­лать json, а потом с помо­щью -d пере­да­ём сами дан­ные. Дан­ные пред­став­ля­ют из себя четыре(точно?, а то я про­сто вижу 3) поля:

  • name - имя;
  • age - возраст;
  • degree - оценка.

Нам сле­ду­ет ещё оста­но­вить­ся на син­так­си­се фор­ма­та json:

{
  "имя_поля" : "значение_поля",
  "имя_поля1": "значение_поля",
  "имя_поля2": {
    "имя_поля3": "зна­че­ние поля",
     "имя_поля4": ["значение1", "значение2"]
  }
}

Если вы зна­ко­мы с JavaScript, то уже зна­е­те этот фор­мат. Если нет, то ниче­го страш­но­го, сей­час раз­бе­рём­ся. Дан­ные пред­став­ля­ют­ся в виде пар имя: зна­че­ние. И имя поля, и его зна­че­ние нуж­но брать в кавыч­ки и раз­де­лять их двое­то­чи­ем. Каж­дая пара отде­ля­ет­ся от сле­ду­ю­щей с помо­щью ?ЭТО ЧТО ЗА ЗВЕРЬ ТАКОЙ?комы&. Но если пар боль­ше нет, то кома не ста­вит­ся. При­чём каж­дое поле может иметь в каче­стве зна­че­ния либо текст, либо ещё один набор полей, заклю­чён­ный в фигур­ные скоб­ки {}. Если нуж­но пере­чис­лить два эле­мен­та без назва­ния поля, надо исполь­зо­вать не фигур­ные скоб­ки, а квад­рат­ные [] (мас­сив), как в поле4. Для удоб­ства фор­ма­ти­ро­ва­ния исполь­зуй­те про­бе­лы, табу­ля­ции Elasticsearch не понимает.

Теперь сохра­ня­ем скрипт и запускаем:

sh elastic_index.sh

Если всё про­шло успеш­но, то вы уви­ди­те такое сообщение:

Это зна­чит, что доку­мент добав­лен в индекс. Теперь вы може­те сно­ва посмот­реть спи­сок индексов.

4. ИНФОРМАЦИЯ ОБ ИНДЕКСЕ

Мы зна­ем спи­сок создан­ных индек­сов, но как посмот­реть спи­сок типов (таб­лиц) в индек­се и как узнать, какие и каких типов поля созда­ны в индек­се? Для это­го мож­но исполь­зо­вать коман­ду _mapping. Но перед тем, как мы перей­дём к ней, надо вер­нуть­ся к син­так­си­су. Коман­ды могут при­ме­нять­ся как к гло­баль­но, так и для отдель­но­го индек­са или для отдель­но­го типа. Син­так­сис будет выгля­деть так:

curl 'localhost:9200/индекс/тип/_команда/имя?параметр1&параметр2'

Что­бы посмот­реть все индек­сы и их поля, мож­но при­ме­нить коман­ду глобально:

curl 'localhost:9200/_mapping?pretty'

 

Или толь­ко для индек­са app:

curl 'localhost:9200/app/_mapping?pretty'

Толь­ко для типа data индек­са app:

curl 'localhost:9200/app/data/_mapping?pretty'

В резуль­та­те про­грам­ма вер­ну­ла нам ответ, в кото­ром пока­зан индекс app, в нём есть тип data, а даль­ше в поле properties пере­чис­ле­ны все поля, кото­рые есть в этом типе: age, degree и name. Каж­дое поле име­ет свои параметры.

5. ИНФОРМАЦИЯ О ПОЛЕ И МУЛЬТИПОЛЯ

Каж­дое поле опи­сы­ва­ет­ся таким спис­ком параметров:

"age" : {
   "type" : "text",
       "fields" : {
          "keyword" : {
             "type" : "keyword",
            "ignore_above" : 256
         }
    }
}

Пара­метр type - ука­зы­ва­ет тип поля. В дан­ном слу­чае про­грам­ма реши­ла, что это текст, хотя это чис­ло. Даль­ше инте­рес­нее, у нас есть ещё пара­метр fields. Он зада­ёт так назы­ва­е­мые под­по­ля или муль­ти­по­ля. Посколь­ку Elasticsearch - это инстру­мент поис­ка тек­ста, то для обра­бот­ки тек­ста исполь­зу­ют­ся ана­ли­за­то­ры, нор­ма­ли­за­то­ры и токе­ни­за­то­ры, кото­рые могут при­во­дить сло­ва к кор­не­вой фор­ме, пере­во­дить текст в ниж­ний регистр, раз­би­вать текст на отдель­ные сло­ва или фра­зы. Вс` это нуж­но для поис­ка и по умол­ча­нию при­ме­ня­ет­ся к каж­до­му тек­сто­во­му полю.

Но если вам нуж­но про­сто про­ве­рить точ­ное вхож­де­ние фра­зы без её изме­не­ний, то с про­ана­ли­зи­ро­ван­ным таким обра­зом полем у вас ниче­го не полу­чить­ся. Поэто­му раз­ра­бот­чи­ки при­ду­ма­ли муль­ти­по­ля. Они содер­жат те же дан­ные, что и основ­ное поле, но к ним мож­но при­ме­нять дру­гие ана­ли­за­то­ры, или вооб­ще их не при­ме­нять. В нашем при­ме­ре вид­но, что систе­ма авто­ма­ти­че­ски созда­ла под­по­ле типа keyword, кото­рое содер­жит неиз­ме­нен­ный вари­ант текста.

Таким обра­зом, если вы захо­ти­те про­ве­рить точ­ное вхож­де­ние, то нуж­но обра­щать­ся имен­но к полю age.keyword а не к age. Или же отклю­чить ана­ли­за­тор для age. Ана­ли­за­то­ры выхо­дят за рам­ки этой ста­тьи, но имен­но эта инфор­ма­ция нам ещё пона­до­бит­ся дальше.

6. УДАЛЕНИЕ ИНДЕКСА

Что­бы уда­лить индекс, доста­точ­но исполь­зо­вать вме­сто GET про­то­кол DELTE и пере­дать имя индекса:

curl -XDELETE 'http://localhost:9200/app?pretty'

Теперь индекс уда­лён. Созда­дим ещё один такой же вручную.

7. РУЧНОЕ СОЗДАНИЕ ИНДЕКСА

Про­грам­ма созда­ла для цифр тек­сто­вые поля, к ним при­ме­ня­ет­ся ана­лиз тек­ста и индек­са­ция, а это потреб­ля­ет допол­ни­тель­ные ресур­сы и память. Поэто­му луч­ше созда­вать индекс вруч­ную и настра­и­вать такие поля, какие нам надо. Пер­вых два поля сде­ла­ем int без под­по­лей, а тре­тье оста­вим как есть. Запрос будет выгля­деть так:

vi elastic_createindex.sh

curl -XPUT 'http://localhost:9200/app?pretty ' -H 'Content-Type: application/json' -d '
{
"mappings" : {
"data" : {
"properties" : {
"age" : {
"type" : "integer"
},
"degree" : {
"type" : "integer"
},
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}'

Как види­те, здесь исполь­зу­ет­ся тот же син­так­сис, кото­рый про­грам­ма воз­вра­ща­ет при вызо­ве коман­ды _mapping. Оста­лось запу­стить скрипт, что­бы создать индекс:

sh elastic_createindex.sh

Что­бы всё про­шло нор­маль­но, необ­хо­ди­мо уда­лить индекс, кото­рый был создан авто­ма­ти­че­ски, поэто­му преды­ду­щий пункт рас­по­ло­жен там не зря. Вы може­те менять mappings для суще­ству­ю­ще­го индек­са, но в боль­шин­стве слу­ча­ев для при­ме­не­ния изме­не­ний к суще­ству­ю­щим дан­ным индекс надо пере­ин­дек­си­ро­вать. Напри­мер, если вы добав­ля­е­те новое муль­ти­по­ле, то в нём мож­но будет рабо­тать толь­ко с новы­ми дан­ны­ми. Ста­рые дан­ные, кото­рые были там до добав­ле­ния, при поис­ке будут недоступны.

5. МАССОВАЯ ИНДЕКСАЦИЯ ДАННЫХ

Даль­ше я хотел бы пого­во­рить о поис­ке, усло­ви­ях и филь­тра­ции, но что­бы почув­ство­вать пол­ную мощь­ность всех этих инстру­мен­тов нам, нужен пол­но­цен­ный индекс. В каче­стве индек­са мы будем исполь­зо­вать один из демон­стра­ци­он­ных индек­сов, в кото­рый раз­ло­же­ны пье­сы Шекс­пи­ра. Для загруз­ки индек­са наберите:

wget https://download.elastic.co/demos/kibana/gettingstarted/shakespeare_6.0.json

В фай­ле нахо­дят­ся дан­ные в фор­ма­те json, кото­рые надо про­ин­дек­си­ро­вать. Это мож­но сде­лать с помо­щью коман­ды _bluck:

curl -H 'Content-Type: application/x-ndjson' -XPOST 'localhost:9200/shakespeare/doc/_bulk?pretty' --data-binary @shakespeare_6.0.json

Индек­са­ция рабо­та­ет так же, как и при руч­ном добав­ле­нии дан­ных, но бла­го­да­ря опти­ми­за­ции коман­ды _bluck, выда­ёт резуль­тат намно­го быстрее.

6. ПОИСК ПО ИНДЕКСУ

Для поис­ка или, дру­ги­ми сло­ва­ми, выбор­ки дан­ных в Elasticsearch исполь­зу­ет­ся коман­да _search. Если вызвать коман­ду без пара­мет­ров, то будут обра­ба­ты­вать­ся все доку­мен­ты. Но выве­де­ны будут толь­ко пер­вые 10, пото­му что это огра­ни­че­ние по умолчанию:

curl -XGET 'http://localhost:9200/shakespeare/doc/_search?pretty'

Здесь мы выбра­ли пер­вые десять доку­мен­тов из индек­са shakespeare и таб­ли­цы doc. Что­бы выбрать боль­ше, пере­дай­те пара­метр size со зна­че­ни­ем, напри­мер 10000:

curl -XGET 'http://localhost:9200/shakespeare/doc/_search?size=10000&pretty'

Самый про­стой при­мер поис­ка - пере­дать поис­ко­вый запрос в пара­мет­ре q. При этом поиск Elasticsearch будет выпол­нять­ся во всех полях индек­са. Напри­мер, най­дём все, что каса­ет­ся Эдга­ра (EDGAR):

curl -XGET 'http://localhost:9200/shakespeare/doc/_search?q=EDGAR&pretty '

Но как вы пони­ма­е­те, всё это очень не точ­но и чаще все­го надо искать по опре­де­лён­ным полям. В Elasticsearch суще­ству­ет несколь­ко типов поис­ка. Основ­ные из них:

  • term - точ­ное сов­па­де­ние иско­мой стро­ки со стро­кой в индек­се или термом;
  • match - все сло­ва долж­ны вхо­дить в стро­ку, в любом порядке;
  • match_phrase - вся фра­за долж­на вхо­дить в строку;
  • query_string - все сло­ва вхо­дят в стро­ку в любом поряд­ке, мож­но искать по несколь­ким полям, исполь­зуя регу­ляр­ные выражения;

Син­так­сис term такой:

"query" {
  "term" {
    "имя_поля": "что искать"
  }
}

Напри­мер, най­дем запи­си, где гово­рит Эдгар с помо­щью term:

vi elastic_searchterm.sh

#!/bin/bash
curl -XGET 'http://localhost:9200/shakespeare/doc/_search?pretty ' -H 'Content-Type: application/json' -d '
{
"query" : {
"term" : {
"speaker.keyword" : "EDGAR"
}
}
}'

sh elastic_searchterm.sh

Мы нашли десять реплик, кото­рые дол­жен ска­зать Эдгар. Даль­ше испы­та­ем неточ­ный поиск с помо­щью match. Син­так­сис такой же, поэто­му я его при­во­дить не буду. Най­дём пред­ло­же­ния, кото­рые содер­жат сло­ва of love:

#!/bin/bash
curl -XGET 'http://localhost:9200/shakespeare/doc/_search?pretty ' -H 'Content-Type: application/json' -d '
{
"query" : {
"match" : {
"text_entry" : "love of"
}
}
}'

С query_string и match_phrase раз­бе­рё­тесь сами, если будет нужно.

8. ОПЕРАТОРЫ AND И OR ДЛЯ ПОИСКА

Если вы хоти­те сде­лать выбор­ку по несколь­ким полям и исполь­зо­вать для это­го опе­ра­то­ры AND и OR, то вам пона­до­бит­ся кон­струк­ция bool. Син­так­сис её такой:

"query": {
  "bool" : {
    "must" : [
         {"поле1" : "усло­вие"},
         {"поле2" : "усло­вие"},
    ],
    "filter": {},
    "must_not" : {}
    "should" : {}
  }
}

Обра­ти­те вни­ма­ние на син­так­сис. Посколь­ку у нас два эле­мен­та под­ряд, мы исполь­зу­ем мас­сив []. Но так как даль­ше нам сно­ва нуж­но созда­вать пары ключ:значение, то в мас­си­ве откры­ва­ют­ся фигур­ные скоб­ки. Кон­струк­ция bool объ­еди­ня­ет в себе несколь­ко параметров:

  • must - все усло­вия долж­ны вер­нуть true;
  • must_not - все усло­вия долж­ны вер­нуть false;
  • should - одно из усло­вий долж­но вер­нуть true;
  • filter - то же самое что и match, но не вли­я­ет на оцен­ку релевантности.

Напри­мер, отбе­рём все запи­си, где Helen гово­рит про любовь:

vi elastic_searchbool.sh

#!/bin/bash
curl -XGET 'http://localhost:9200/shakespeare/doc/_search?pretty ' -H 'Content-Type: application/json' -d '
{
"query" : {
"bool" : {
"must": [
{"match" : {
"text_entry" : "love"
}},
{"match": {
"speaker": "helen"
}}
]
}
}
}'

Как види­те, най­де­но толь­ко два результата.

9. ГРУППИРОВКА

И послед­нее, о чём мы сего­дня пого­во­рим, - груп­пи­ров­ка запи­сей в Еlasticsearch и сум­ми­ро­ва­ние зна­че­ний по ним. Это ана­лог запро­са GOUP BY в MySQL. Груп­пи­ров­ка выпол­ня­ет­ся с помо­щью кон­струк­ции aggregations или aggs. Син­так­сис её такой:

"aggregations" : {
  "назва­ние" : {
     "тип_группировки" : {
        параметры
     },
     дочерние_группировки
  }
}

Раз­бе­рём по порядку:

  • назва­ние - ука­зы­ва­ем про­из­воль­ное назва­ние для дан­ных, исполь­зу­ет­ся при выводе;
  • тип_группировки - функ­ция груп­пи­ров­ки, кото­рая будет исполь­зо­вать­ся, напри­мер terms, sum, avg, count и так далее;
  • пара­мет­ры - поля, кото­рые будем груп­пи­ро­вать и дру­гие допол­ни­тель­ные параметры;
  • дочер­ние груп­пи­ров­ки - в каж­дую груп­пи­ров­ку мож­но вло­жить ещё одну или несколь­ко дру­гих таких же груп­пи­ро­вок, что дела­ет этот инстру­мент очень мощным.

Давай­те под­счи­та­ем, сколь­ко отдель­ных реплик для каж­до­го чело­ве­ка. Для это­го будем исполь­зо­вать груп­пи­ров­ку terms:

vi elastic-group.sh

#!/bin/bash
curl -XGET 'http://localhost:9200/shakespeare/doc/_search?pretty&size=10000 ' -H 'Content-Type: application/json' -d '
{
"aggs" : {
"replics" : {
"terms": {
"field": "speaker.keyword"
}
}
}
}'

В резуль­та­те запро­са Еlasticsearch получим:

Сна­ча­ла пой­дут все най­ден­ные доку­мен­ты, а затем в раз­де­ле aggregations мы уви­дим наши зна­че­ния. Для каж­до­го име­ни есть doc_count, в кото­ром содер­жит­ся коли­че­ство вхож­де­ний это­го сло­ва. Что­бы про­де­мон­стри­ро­вать рабо­ту вло­жен­ных груп­пи­ро­вок, давай­те най­дём сум­му и сред­нее зна­че­ние поля line_number для каж­до­го участника:

#!/bin/bash
curl -XGET 'http://localhost:9200/shakespeare/doc/_search?pretty&size=10000 ' -H 'Content-Type: application/json' -d '
{
"aggs" : {
"replics" : {
"terms": {
"field": "speaker.keyword"
},
"aggs" : {
"total_number": {
"sum": {"field": "line_id"}
},
"avg_number": {
"avg": {"field": "line_id"}
}
}
}
}
}'

В сум­ме у нас очень боль­шие чис­ла, поэто­му они отоб­ра­жа­ют­ся в экс­по­нен­ци­аль­ном фор­ма­те. А вот в сред­нем зна­че­нии всё вполне понятно.

{
"key" : "DUKE VINCENTIO",
"doc_count" : 909,
"total_number" : {
"value" : 5.4732683E7
},
"avg_number" : {
"value" : 60211.97249724973
}
}

Тут key - имя пер­со­на­жа, total_number.value - сум­ма поля, avg_number.value - сред­нее зна­че­ние поля.