Статьи

Elasticsearch для разработчиков Java: Elasticsearch из командной строки

Эта статья является частью нашего курса Академии под названием Elasticsearch Tutorial для разработчиков Java .

В этом курсе мы предлагаем серию руководств, чтобы вы могли разрабатывать свои собственные приложения на основе Elasticsearch. Мы охватываем широкий спектр тем, от установки и эксплуатации до интеграции Java API и создания отчетов. С нашими простыми учебными пособиями вы сможете запустить и запустить собственные проекты за минимальное время. Проверьте это здесь !

1. Введение

Из предыдущей части урока мы довольно хорошо поняли, что такое Elasticsearch , его основные концепции и возможности поиска, которые он может привнести в наши приложения. В этом разделе мы вступаем прямо в бой и собираемся применить наши знания на практике. В этом разделе curl и / или http будут единственными инструментами, которые мы собираемся использовать, чтобы подружиться с Elasticsearch .

Подводя итог, мы уже доработали наш индекс catalog книг и типы отображения, поэтому мы собираемся взять его оттуда. Чтобы максимально приблизить ситуацию к реальности, мы собираемся использовать кластер Elasticsearch с тремя узлами (все выполняются как контейнеры Docker ), а индекс catalog будет настроен с коэффициентом репликации два.

Как мы увидим, работа с кластером Elasticsearch имеет довольно много тонкостей по сравнению с автономным экземпляром, и лучше быть готовым к ним. Надеюсь, вы все еще помните из предыдущей части руководства, как запустить Elasticsearch, поскольку это будет единственной предпосылкой: наличие кластера и его запуск. С этим, давайте начнем!

2. Здоров ли мой кластер?

Первое, что вам нужно знать о вашем кластере Elasticsearch, прежде чем что-либо делать с ним, это его здоровье. Есть несколько способов сбора этой информации, но, возможно, самый простой и удобный из них — использование API-интерфейсов кластера , в частности конечной точки работоспособности кластера .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ http http://localhost:9200/_cluster/health
 
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "active_primary_shards": 0,
    "active_shards": 0,
    "active_shards_percent_as_number": 100.0,
    "cluster_name": "es-catalog",
    "delayed_unassigned_shards": 0,
    "initializing_shards": 0,
    "number_of_data_nodes": 3,
    "number_of_in_flight_fetch": 0,
    "number_of_nodes": 3,
    "number_of_pending_tasks": 0,
    "relocating_shards": 0,
    "status": "green",
    "task_max_waiting_in_queue_millis": 0,
    "timed_out": false,
    "unassigned_shards": 0
}

Среди этих деталей мы ищем индикатор status который должен быть установлен green , что означает, что все сегменты выделены, и кластер находится в хорошем рабочем состоянии.

3. Все о показателях

Наш кластер Elasticsearch полностью зеленый и готов к работе. Следующим логическим шагом будет создание индекса catalog с типами и настройками отображения, которые мы обрисовали ранее. Но прежде чем сделать это, давайте проверим, есть ли какие-либо индексы, уже созданные на этот раз с использованием API-интерфейсов индексов .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
$ http http://localhost:9200/_stats
 
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "_all": {
        "primaries": {},
        "total": {}
    },
    "_shards": {
        "failed": 0,
        "successful": 0,
        "total": 0
    },
    "indices": {}
}

Как и ожидалось, в нашем кластере еще ничего нет, и мы готовы приступить к созданию индекса для нашего каталога книг. Как мы знаем, Elasticsearch говорит на JSON, но манипулировать и говорить об использовании более или менее сложного JSON- документа из командной строки несколько обременительно. Давайте лучше сохраним настройки catalog и сопоставления в документе catalog-index.json .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
  "settings": {
    "index" : {
      "number_of_shards" : 5,
      "number_of_replicas" : 2
    }
  },
  "mappings": {
    "books": {
      "_source" : {
        "enabled": true
      },
      "properties": {
        "title": { "type": "text" },
        "categories" : {
          "type": "nested",
          "properties" : {
            "name": { "type": "text" }
          }
        },
        "publisher": { "type": "keyword" },
        "description": { "type": "text" },
        "published_date": { "type": "date" },
        "isbn": { "type": "keyword" },
        "rating": { "type": "byte" }
       }
   },
   "authors": {
     "properties": {
       "first_name": { "type": "keyword" },
       "last_name": { "type": "keyword" }
     },
     "_parent": {
        "type": "books"
      }
    }
  }
}

И используйте этот документ в качестве входных данных для создания индексного API .

01
02
03
04
05
06
07
08
09
10
11
$ http PUT http://localhost:9200/catalog < catalog-index.json
 
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "acknowledged": true,
    "shards_acknowledged": true
}

Несколько слов следует сказать об использовании acknowledged свойства ответа в большинстве API-интерфейсов Elasticsearch , особенно тех, которые применяют мутации. Как правило, это значение просто указывает, завершена ли операция до истечения времени ожидания ( “true” ) или может скоро вступить в силу ( “false” ). В дальнейшем мы увидим больше примеров его использования в другом контексте.

Вот и все, и мы выпустили наш catalog . Чтобы убедиться в достоверности этого факта, мы могли бы попросить Elasticsearch вернуть настройки индекса catalog .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ http http://localhost:9200/catalog/_settings
 
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "catalog": {
        "settings": {
            "index": {
                "creation_date": "1487428863824",
                "number_of_replicas": "2",
                "number_of_shards": "5",
                "provided_name": "catalog",
                "uuid": "-b63dCesROC5UawbHz8IYw",
                "version": {
                    "created": "5020099"
                }
            }
        }
    }
}

Круто, именно то, что мы заказали. Вы можете задаться вопросом, как отреагирует Elasticsearch , если бы мы попытались обновить настройки индекса , увеличив число сегментов (как мы знаем, не все настройки индекса могут быть обновлены после создания индекса).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
$ echo '{"index":{"number_of_shards":6}}' | http PUT http://localhost:9200/catalog/_settings
 
HTTP/1.1 400 Bad Request
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "error": {
        "reason": "can't change the number of shards for an index",
        "root_cause": [
            ...
        ],
        "type": "illegal_argument_exception"
    },
    "status": 400
}

Ответ об ошибке не вызывает удивления (обратите внимание, что детали ответа были уменьшены только в целях иллюстрации). Наряду с настройками очень легко получить типы сопоставления для определенного индекса, например:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
$ http http://192.168.99.100:9200/catalog/_mapping
 
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "catalog": {
        "mappings": {
            "authors": {
                ...
            },
            "books": {
                ...
            }
        }
    }
}

В целом отображения индекса для существующих полей не могут быть обновлены; Однако есть некоторые исключения из правила . Одной из важнейших функций API индексов является возможность выполнения процесса анализа для определенного типа и поля отображения индекса без фактической отправки каких-либо документов.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ http http://localhost:9200/catalog/_analyze field=books.title text="Elasticsearch: The Definitive Guide. A Distributed Real-Time Search and Analytics Engine"
 
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "tokens": [
        {
            "end_offset": 13,
            "position": 0,
            "start_offset": 0,
            "token": "elasticsearch",
            "type": ""
        },
        {
            "end_offset": 18,
            "position": 1,
            "start_offset": 15,
            "token": "the",
            "type": ""
        },
         
        ...
 
        {
            "end_offset": 88,
            "position": 11,
            "start_offset": 82,
            "token": "engine",
            "type": ""
        }
    ]
}

Это исключительно полезно в том случае, если вы хотите проверить параметры типов отображения перед тем, как выбросить кучу данных в Elasticsearch для индексации.

И последнее, но не менее важное, есть одна важная деталь о состояниях индекса. Любой конкретный индекс может находиться в opened (полностью работоспособном) или closed (заблокированном для операций чтения / записи, заархивированном было бы хорошей аналогией) состоянии. Что касается всего остального, Elasticsearch предоставил API для этого .

01
02
03
04
05
06
07
08
09
10
$ http POST http://localhost:9200/catalog/_open
 
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "acknowledged": true
}

4. Документы, дополнительные документы,…

Пустой индекс без документов не очень полезен, поэтому давайте переключимся с API индексов на другой замечательный API документов . Мы начнем исследовать его, используя самые простые операции с одним документом , опираясь на следующий документ book.json :

01
02
03
04
05
06
07
08
09
10
11
12
13
{
  "title": "Elasticsearch: The Definitive Guide. A Distributed Real-Time Search and Analytics Engine",
  "categories": [
      { "name": "analytics" },
      { "name": "search" },
      { "name": "database store" }
  ],
  "publisher": "O'Reilly",
  "description": "Whether you need full-text search or real-time analytics of structured data—or both—the Elasticsearch distributed search engine is an ideal way to put your data to work. This practical guide not only shows you how to search, analyze, and explore data with Elasticsearch, but also helps you deal with the complexities of human language, geolocation, and relationships.",
  "published_date": "2015-02-07",
  "isbn": "978-1449358549",
  "rating": 4
}

Перед отправкой этого JSON в Elasticsearch было бы здорово немного поговорить об идентификации документов. Каждый документ в Elasticsearch имеет уникальный идентификатор, который хранится в специальном поле _id . Вы можете предоставить его при загрузке документа в Elasticsearch (как мы делаем в приведенном ниже примере, используя isbn поскольку это отличный пример естественного идентификатора), или он будет сгенерирован и назначен Elasticsearch .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
$ http PUT http://localhost:9200/catalog/books/978-1449358549 < book.json
 
HTTP/1.1 201 Created
Location: /catalog/books/978-1449358549
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "_id": "978-1449358549",
    "_index": "catalog",
    "_shards": {
        "failed": 0,
        "successful": 3,
        "total": 3
    },
    "_type": "books",
    "_version": 1,
    "created": true,
    "result": "created"
}

Наш первый документ только что попал в catalog по catalog books . Но у нас также есть тип authors , который находится в отношениях родитель / ребенок с books . Давайте дополним книгу ее авторами из документа author.json .

01
02
03
04
05
06
07
08
09
10
11
12
[
  {
    "first_name": "Clinton",
    "last_name": "Gormley",
    "_parent": "978-1449358549"
  },
  {
    "first_name": "Zachary",
    "last_name": "Tong",
    "_parent": "978-1449358549"
  }
]

В книге более одного автора, поэтому мы все еще можем использовать API единого документа , индексируя каждый авторский документ по одному. Тем не менее, давайте не будем этого делать, а переключимся на API для массовых документов и немного изменим наш документ author.json для совместимости с форматом API для массовых документов .

1
2
3
4
{ "index" : { "_index" : "catalog", "_type" : "authors", "_id": "1", "_parent": "978-1449358549" } }
{ "first_name": "Clinton", "last_name": "Gormley" }
{ "index" : { "_index" : "catalog", "_type" : "authors", "_id": "2", "_parent": "978-1449358549" } }
{ "first_name": "Zachary", "last_name": "Tong" }

Готово, давайте сохраним этот документ как авторы-bulk.json и направим его непосредственно в конечную точку API для массовых документов .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
$ http POST http://localhost:9200/_bulk < authors-bulk.json
 
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "errors": false,
    "items": [
        {
            "index": {
                "_id": "1",
                "_index": "catalog",
                "_shards": {
                    "failed": 0,
                    "successful": 3,
                    "total": 3
                },
                "_type": "authors",
                "_version": 5,
                "created": false,
                "result": "updated",
                "status": 200
            }
        },
        {
            "index": {
                "_id": "2",
                "_index": "catalog",
                "_shards": {
                    "failed": 0,
                    "successful": 3,
                    "total": 3
                },
                "_type": "authors",
                "_version": 2,
                "created": true,
                "result": "created",
                "status": 201
            }
        }
    ],
    "took": 105
}

А у нас есть книги и авторские документы в качестве первых граждан catalog ! Настало время вернуть эти документы обратно.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ http http://localhost:9200/catalog/books/978-1449358549
 
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "_id": "978-1449358549",
    "_index": "catalog",
    "_source": {
        "categories": [
            { "name": "analytics" },
            { "name": "search"},
            { "name": "database store" }
        ],
        "description": "...",
        "isbn": "978-1449358549",
        "published_date": "2015-02-07",
        "publisher": "O'Reilly",
        "rating": 4,
        "title": "Elasticsearch: The Definitive Guide. A Distributed Real-Time Search and Analytics Engine"
    },
    "_type": "books",
    "_version": 1,
    "found": true
}

Легко! Однако, чтобы получить документы из коллекции authors , которые являются потомками их соответствующих документов из коллекции books , мы должны предоставить родительский идентификатор вместе с собственным идентификатором документа, например:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
$ http http://localhost:9200/catalog/authors/1?parent=978-1449358549
 
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "_id": "1",
    "_index": "catalog",
    "_parent": "978-1449358549",
    "_routing": "978-1449358549",
    "_source": {
        "first_name": "Clinton",
        "last_name": "Gormley"
    },
    "_type": "authors",
    "_version": 1,
    "found": true
}

Это одна из особенностей работы с отношениями родитель / ребенок в Elasticsearch . Как уже упоминалось, вы можете моделировать такие отношения более простым способом, но наша цель — научиться справляться с этим, если вы решите пойти по этому пути в своих приложениях.

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

Чтобы закончить, давайте взглянем на API векторов терминов, который возвращает все детали и статистику о терминах, например, в полях документа (вставлена ​​только небольшая часть ответа):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
$ http http://localhost:9200/catalog/books/978-1449358549/_termvectors?fields=description
 
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "_id": "978-1449358549",
    "_index": "catalog",
    "_type": "books",
    "_version": 1,
    "found": true,
    "term_vectors": {
        "description": {
            "field_statistics": {
                "doc_count": 1,
                "sum_doc_freq": 46,
                "sum_ttf": 60
            },
            "terms": {
                "analyze": {
                    "term_freq": 1,
                    "tokens": [ ... ]
                },
                "and": {
                    "term_freq": 2,
                    "tokens": [ ... ]
 
                },
                "complexities": {
                    "term_freq": 1,
                    "tokens": [ ... ]
 
                },
                "data": {
                    "term_freq": 3,
                    "tokens": [ ... ]
 
                },
                ...
            }
        }
    },
    "took": 5
}

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

5. Что делать, если типы моих карт неоптимальны

Очень часто со временем вы можете обнаружить, что ваши типы картирования могут быть неоптимальными и могут быть улучшены. Однако Elasticsearch поддерживает только ограниченные модификации по сравнению с существующими типами отображения. К счастью, Elasticsearch предоставляет специальный API переиндексации , например:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ echo '{"source": {"index": "catalog"}, "dest": {"index": "catalog-v2"}}' | http POST http://localhost:9200/_reindex
 
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "batches": 0,
    "created": 200,
    "deleted": 0,
    "failures": [],
    "noops": 0,
    "requests_per_second": -1.0,
    "retries": {
        "bulk": 0,
        "search": 0
    },
    "throttled_millis": 0,
    "throttled_until_millis": 0,
    "timed_out": false,
    "took": 265,
    "total": 200,
    "updated": 0,
    "version_conflicts": 0
}

Хитрость заключается в том, чтобы создать новый индекс с обновленными типами сопоставления catalog-v2 , а затем просто попросить Elasticsearch извлечь все документы из старого индекса ( catalog ) и поместить их в новый ( catalog-v2 ), и, наконец, поменять местами индексы. Обратите внимание, это также работает не только для локальных, но и для удаленных индексов .

Несмотря на простоту, этот API по-прежнему считается экспериментальным и может не подходить во всех случаях, например, если ваш индекс действительно массивный или ваш Elasticsearch испытывает высокую нагрузку и должен устанавливать приоритеты запросов приложений.

6. Время поиска

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

Чтобы продемонстрировать различные функции поиска, нам понадобится еще пара документов, загрузите их в свой кластер Elasticsearch из books-and -hors-bulk.json, используя наш дружественный API для массовых документов .

1
$ http POST http://localhost:9200/_bulk < books-and-authors-bulk.json

Имея несколько документов в наших коллекциях, мы могли бы начать выполнять поисковые запросы к ним, используя наиболее доступную форму API поиска, которая принимает критерии поиска в URI посредством строки запроса. Например, давайте поищем термин « engine (имея в виду фразу search engine ).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
$ http POST http://localhost:9200/catalog/books/_search?q=engine
 
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "_shards": {
        "failed": 0,
        "successful": 5,
        "total": 5
    },
    "hits": {
        "hits": [
            {
                "_id": "978-1449358549",
                "_index": "catalog",
                "_score": 0.7503276,
                "_source": {
                    "categories": [
                        { "name": "analytics },
                        { "name": "search" },
                        { "name": "database store" }
                    ],
                    "description": " Whether you need full-text search or real-time ...",
                    "isbn": "978-1449358549",
                    "published_date": "2015-02-07",
                    "publisher": "O'Reilly",
                    "rating": 4,
                    "title": " Elasticsearch: The Definitive Guide. ..."
                },
                "_type": "books"
            }
        ],
        "max_score": 0.7503276,
        "total": 1
    },
    "timed_out": false,
    "took": 22
}

Хорошая отправная точка, этот API довольно полезен для быстрого и поверхностного поиска, но его возможности очень ограничены. Поиск с использованием API тела запроса — это совершенно другой зверь, который раскрывает всю мощь Elasticsearch . Он построен на основе JSON Query DSL , лаконичного и интуитивно понятного языка для построения произвольно сложных поисковых запросов.

Query DSL позволяет описать несколько типов запросов , каждый из которых имеет собственный синтаксис и параметры. Однако существует набор общих параметров, таких как sort , from , size , сохраненные_файлы (на самом деле список действительно длинный ), которые не зависят от типа запроса и могут применяться для любого из них.

В следующих двух разделах мы собираемся переключиться с http на curl, так как последний немного удобнее при работе с полезными нагрузками JSON .

Первый тип запроса, который мы собираемся опробовать с помощью Query DSL, — это сопоставление всех запросов . В некоторой степени это не совсем запрос, потому что он просто соответствует всем документам. Таким образом, он может дать много результатов и, как правило, всегда комментируйте ваши запросы с разумным ограничением размера , вот пример:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
$ curl –i http://localhost:9200/catalog/books/_search?pretty -d '                                                                                                                                 
{
    "size": 10,
    "query": {
        "match_all" : {
        }
    }
}'
 
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 3112
{
  "took" : 13,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "catalog",
        "_type" : "books",
        "_id" : "978-1449358549",
        "_score" : 1.0,
        "_source" : {
          "title" : "Elasticsearch: The Definitive Guide ...",
          "categories" : [
            { "name" : "analytics" },
            { "name" : "search" },
            { "name" : "database store" }
          ],
          "publisher" : "O'Reilly",
          "description" : "Whether you need full-text ...",
          "published_date" : "2015-02-07",
          "isbn" : "978-1449358549",
          "rating" : 4
        }
      },
      ...
    ]
  }
}

Следующий тип является реальным типом запроса и называется классом полнотекстовых запросов, которые выполняют поиск по полям полнотекстового документа (вероятно, наиболее широко используемых). В базовой форме он сопоставляется с одним полем документа, как, например, description книги.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
$ curl -i http://localhost:9200/catalog/books/_search?pretty -d '
{
    "query": {
        "match" : {
            "description" : "engine"
        }
    }
}'
 
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 1271
{
  "took" : 17,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.28004453,
    "hits" : [
      {
        "_index" : "catalog",
        "_type" : "books",
        "_id" : "978-1449358549",
        "_score" : 0.28004453,
        "_source" : {
          "title" : "Elasticsearch: The Definitive Guide. ...",
          "categories" : [
            { "name" : "analytics" },
            { "name" : "search" },
            { "name" : "database store" }
          ],
          "publisher" : "O'Reilly",
          "description" : "Whether you need full-text ...",
          "published_date" : "2015-02-07",
          "isbn" : "978-1449358549",
          "rating" : 4
        }
      }
    ]
  }
}

Но полнотекстовые запросы очень мощные и имеют довольно много других вариантов, включая match_phrase , match_phrase_prefix , multi_match , common_terms , query_string и simple_query_string .

Двигаясь дальше, мы входим в мир запросов на уровне терминов, которые работают с точными терминами и обычно используются для типов полей, таких как числа, даты и ключевые слова. Книжное поле publisher — хороший кандидат, чтобы испытать это.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
$ curl -i http://localhost:9200/catalog/books/_search?pretty -d '
{
   "size": 10,
   "_source": [ "title" ],
   "query": {
        "term" : {
            "publisher" : "Manning"
        }
    }
}' 
 
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 675
 
{
  "took" : 21,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 0.18232156,
    "hits" : [
      {
        "_index" : "catalog",
        "_type" : "books",
        "_id" : "978-1617291623",
        "_score" : 0.18232156,
        "_source" : {
          "title" : "Elasticsearch in Action"
        }
      },
      {
        "_index" : "catalog",
        "_type" : "books",
        "_id" : "978-1617292774",
        "_score" : 0.18232156,
        "_source" : {
          "title" : "Relevant Search: With applications ..."
        }
      }
    ]
  }
}

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

Запросы на присоединение являются исключительно интересными в контексте нашего индекса catalog книг. Эти запросы позволяют выполнять поиск по вложенным объектам или документам с родительскими / дочерними отношениями. Например, давайте выясним все книги в категории analytics .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
$ curl -i http://localhost:9200/catalog/books/_search?pretty -d '
{
   "size": 10,
   "_source": [ "title", "categories" ],
   "query": {
        "nested": {
            "path": "categories",
            "query" : {
                "match": {
                    "categories.name" : "analytics"
                }
            }
       }
    }
}'
 
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 1177
 
{
  "took" : 45,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 1.3112576,
    "hits" : [
      {
        "_index" : "catalog",
        "_type" : "books",
        "_id" : "978-1617291623",
        "_score" : 1.3112576,
        "_source" : {
          "categories" : [
            { "name" : "analytics" },
            { "name" : "search" },
            { "name" : "database store" }
          ],
          "title" : "Elasticsearch in Action"
        }
      },
      {
        "_index" : "catalog",
        "_type" : "books",
        "_id" : "978-1449358549",
        "_score" : 1.0925692,
        "_source" : {
          "categories" : [
            { "name" : "analytics" },
            { "name" : "search" },
            { "name" : "database store" }
          ],
          "title" : "Elasticsearch: The Definitive Guide ..."
        }
      }
    ]
  }
}

Точно так же мы могли бы искать все книги, написанные Клинтоном Гормли , используя отношения родителей и детей между books и коллекциями authors .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
$ curl -i http://localhost:9200/catalog/books/_search?pretty -d '
{
   "size": 10,
   "_source": [ "title" ],
   "query": {
       "has_child" : {
            "type" : "authors",
            "inner_hits" : {
                "size": 5
            },
            "query" : {
                "term" : {
                    "last_name" : "Gormley"
                }
            }
        }
    }
}'
 
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 1084
 
{
  "took" : 38,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "catalog",
        "_type" : "books",
        "_id" : "978-1449358549",
        "_score" : 1.0,
        "_source" : {
          "title" : "Elasticsearch: The Definitive Guide ..."
        },
        "inner_hits" : {
          "authors" : {
            "hits" : {
              "total" : 1,
              "max_score" : 0.6931472,
              "hits" : [
                {
                  "_type" : "authors",
                  "_id" : "1",
                  "_score" : 0.6931472,
                  "_routing" : "978-1449358549",
                  "_parent" : "978-1449358549",
                  "_source" : {
                    "first_name" : "Clinton",
                    "last_name" : "Gormley"
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

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

Другие типы запросов , такие как гео-запросы , специализированные запросы и запросы по интервалам, работают очень схожим образом, поэтому мы просто пропустим их и закончим, изучив составные запросы . Примеры, которые мы видели до сих пор, включали запросы только с одним критерием поиска, но в Query DSL также есть способ составления сложных запросов . Давайте рассмотрим пример использования запроса bool, который представляет собой композицию некоторых типов запросов, которые мы уже видели.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
$ curl -i http://localhost:9200/catalog/books/_search?pretty -d '
{
   "size": 10,
   "_source": [ "title", "publisher" ],
   "query": {
       "bool" : {
          "must" : [
              {
                  "range" : {
                      "rating" : { "gte" : 4 }
                  }
              },
              {
                  "has_child" : {
                      "type" : "authors",
                      "query" : {
                          "term" : {
                              "last_name" : "Gormley"
                          }
                      }
                  }
              },
              {
                  "nested": {
                      "path": "categories",
                      "query" : {
                          "match": {
                              "categories.name" : "search"
                          }
                      }
                  }
              }
          ]
       }
    }
}'
 
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 531
 
{
  "took" : 79,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 3.0925694,
    "hits" : [
      {
        "_index" : "catalog",
        "_type" : "books",
        "_id" : "978-1449358549",
        "_score" : 3.0925694,
        "_source" : {
          "publisher" : "O'Reilly",
          "title" : "Elasticsearch: The Definitive Guide.  ..."
        }
      }
    ]
  }
}

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

7. Мутации по запросу

Удивительно (или нет), но Elasticsearch может использовать запросы для выполнения мутаций, таких как обновление или удаление документов в индексе. Например, следующий фрагмент удалит все книги, которые имеют низкий рейтинг в нашем каталоге, опубликованном Manning .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ curl -i http://localhost:9200/catalog/books/_delete_by_query?pretty -d '
{
   "query": {
      "bool": {
          "must": [
              { "range" : { "rating" : { "lt" : 3 } } }
          ],
          "filter": [
             { "term" :  { "publisher" : "Manning" } }
          ]
      }
   }
}'
 
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 296
 
{
  "took" : 12,
  "timed_out" : false,
  "total" : 0,
  "deleted" : 0,
  "batches" : 0,
  "version_conflicts" : 0,
  "noops" : 0,
  "retries" : {
    "bulk" : 0,
    "search" : 0
  },
  "throttled_millis" : 0,
  "requests_per_second" : -1.0,
  "throttled_until_millis" : 0,
  "failures" : [ ]
}

Он использует тот же Query DSL и, для иллюстрации того, как можно использовать фильтрацию, включает filter как часть запроса. Но вместо возврата соответствующих документов будут применены обновления или удаления.

API удаления по запросу можно использовать для преодоления ограничений обычного API удаления и удаления дочерних документов в случае удаления их родительского документа.

8. Знай свои запросы лучше

Иногда вы можете обнаружить, что ваши поисковые запросы возвращают документы в том порядке, в котором вы не ожидаете, ранжируя одни документы выше, чем другие. Чтобы помочь вам, Elasticsearch предоставляет два очень полезных API. Одним из них является API объяснения , который вычисляет объяснение оценки для запроса (и конкретного документа, если необходимо).

Объяснение можно получить, указав параметр explain как часть запроса:

01
02
03
04
05
06
07
08
09
10
$ curl -i http://localhost:9200/catalog/books/_search?pretty -d '
{
   "size": 10,
   "explain": true,
   "query": {
        "term" : {
            "publisher" : "Manning"
        }
    }
}

Или используя выделенную конечную точку API объяснения и конкретный документ, например:

1
2
3
4
5
6
7
8
$ curl -i http://localhost:9200/catalog/books/978-1617292774/_explain?pretty -d '
{
   "query": {
        "term" : {
            "publisher" : "Manning"
        }
    }
}'

Ответы не были включены намеренно, поскольку тонны полезной информации возвращаются. Еще одна очень полезная функция Elasticsearch — это API-интерфейс валидации, который позволяет выполнять валидацию запроса без его фактического выполнения, например:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
$ curl -i http://localhost:9200/catalog/books/_validate/query?pretty -d ' {
   "query": {
        "term" : {
            "publisher" : "Manning"
        }
    }                           
}'
 
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 98
 
{
  "valid" : true,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  }
}

Оба API-интерфейса очень полезны для устранения проблем с релевантностью или анализа потенциально важных поисковых запросов без их выполнения в работающем кластере Elasticsearch .

9. От поиска к пониманию

Часто вы можете оказаться в ситуации, когда поиска просто недостаточно, вам нужны какие-то агрегации поверх совпадений. Отличным примером будет гранение (или, как Elasticsearch называет это, термины агрегации ), где результаты поиска сгруппированы в сегменты.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
$ curl -i http://localhost:9200/catalog/books/_search?pretty -d '
{
   "query": {
        "match" : {
            "description" : "elasticsearch"
        }
    },
    "aggs" : {
        "publisher" : {
            "terms" : { "field" : "publisher" }
        }
    }
}'
 
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 3447
 
{
  "took" : 176,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 0.38828257,
    "hits" : [
      {
          ...
      }
    ]
  },
  "aggregations" : {
    "publisher" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "Manning",
          "doc_count" : 2
        },
        {
          "key" : "O'Reilly",
          "doc_count" : 1
        }
      ]
    }
  }
}

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
$ curl -i http://localhost:9200/catalog/books/_search?pretty -d '
{
  "aggs" : {
      "authors": {
        "children": {
          "type" : "authors"
        },
        "aggs": {
          "top-authors": {
            "terms": {
            "script" : {
              "inline": "doc['first_name'].value + ' ' + doc['last_name'].value",
              "lang": "painless"
            },
            "size": 10
          }
        }
      }
    }
  }
}'
 
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 1031
{
  "took": 381,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 5,
    "max_score": 1,
    "hits": [
      ...
    ]
  },
  "aggregations": {
    "authors": {
      "doc_count": 6,
      "top-authors": {
        "doc_count_error_upper_bound": 0,
        "sum_other_doc_count": 0,
        "buckets": [
          {
            "key": "Clinton Gormley",
            "doc_count": 1
          },
          {
            "key": "Doug Turnbull",
            "doc_count": 1
          },
          {
            "key": "Matthew Lee Hinman",
            "doc_count": 1
          },
          {
            "key": "Radu Gheorghe",
            "doc_count": 1
          },
          {
            "key": "Roy Russo",
            "doc_count": 1
          },
          {
            "key": "Zachary Tong",
            "doc_count": 1
          }
        ]
      }
    }
  }
}

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

1
2
3
4
"script" : {                          
  "inline": "doc['first_name'].value + ' ' + doc['last_name'].value"
  "lang": "painless"                
}

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

10. Следите за своим кластерным дыханием

Кластеры Elasticsearch — это живые «существа», и за ними следует внимательно следить и контролировать, чтобы заранее выявлять любые проблемы и быстро реагировать на них. Конечная точка работоспособности кластера, которую мы видели ранее, — это самый простой способ получить общее состояние кластера высокого уровня.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ http http://localhost:9200/_cluster/health
 
HTTP/1.1 200 OK
content-encoding: gzip
content-type: application/json; charset=UTF-8
transfer-encoding: chunked
 
{
    "active_primary_shards": 5,
    "active_shards": 5,
    "active_shards_percent_as_number": 20.0,
    "cluster_name": "es-catalog",
    "delayed_unassigned_shards": 0,
    "initializing_shards": 0,
    "number_of_data_nodes": 1,
    "number_of_in_flight_fetch": 0,
    "number_of_nodes": 1,
    "number_of_pending_tasks": 0,
    "relocating_shards": 0,
    "status": "red",
    "task_max_waiting_in_queue_millis": 0,
    "timed_out": false,
    "unassigned_shards": 20
}

Если ваш кластер становится red (как в примере выше), проблема, безусловно, решается. Чтобы помочь вам, Elasticsearch имеет API статистики кластера, API состояния кластера, API статистики уровня узла кластера и API статистики индексов узла кластера .

Чуть в стороне остается еще одна чрезвычайно важная группа API, API-интерфейсы Cat . Они отличаются в том смысле, что представление не в JSON, а на основе текста, с компактным и выровненным выводом, подходящим для терминалов.

11. Выводы

В этом разделе руководства мы рассмотрели многие функции Elasticsearch, изучив их через RESTful API , используя только инструменты командной строки. По большому счету, это лишь малая часть того, что Elasticsearch предлагает через API, и официальная документация — отличное место, чтобы изучить их все. Надеемся, что на данный момент нам достаточно комфортно с Elasticsearch и знаем, как с ним работать.

12. Что дальше

В следующей части руководства мы собираемся изучить несколько разновидностей нативных API, которые Elasticsearch может предложить разработчикам Java / JVM. Эти API являются основными строительными блоками любого Java / JVM-приложения, которое использует возможности Elasticsearch .