начиная
введение
Elasticsearch — полнотекстовая поисковая система с открытым исходным кодом и широкими возможностями расширения, которая выполняет поиск практически в режиме реального времени и использует ES в качестве поисковой системы для предоставления решений для нужд сложных поисковых функций.
Сценарии использования ЭП:
Интернет-магазин, поиск товаров.
ES взаимодействует с logstash, kibana и анализом логов.
Остальная часть этого руководства проведет вас через установку ES, запуск, просмотр и CRUD данных.Если вы завершили это руководство, у вас уже должно быть хорошее понимание ES, и я надеюсь, что оно вас вдохновит.
Базовые концепты
В ES есть несколько основных концепций, и понимание этих концепций с самого начала очень поможет вам в дальнейшем.
Почти в реальном времени (NRT)
ES — это поисковая система (платформа), работающая почти в реальном времени, что означает очень небольшую задержку между добавлением данных и возможностью поиска. (около 1 с)
Несколько серверов ES могут использоваться как кластер, и поиск может выполняться на любом узле. Кластер имеет имя по умолчанию (изменяемое) "elasticsearch", которое должно быть уникальным, поскольку узлы кластера присоединяются к кластеру по имени кластера.
Убедитесь, что у вас нет одного и того же имени кластера в той же среде, иначе узлы могут присоединиться к неожиданным кластерам.
узел
Узел — это отдельный сервер, который является частью кластера, хранит данные и участвует в функциях индексирования и поиска кластера. Как и кластеры, узлы идентифицируются по имени, которое по умолчанию является случайным универсальным уникальным идентификатором (UUID), назначаемым узлу при запуске. Если вы не хотите использовать значение по умолчанию, вы можете определить любое имя узла. Это имя важно для административных целей, поскольку вы хотите определить, какие серверы в сети соответствуют каким узлам в кластере ElasticSearch.
показатель
Индекс — это набор документов с некоторыми схожими свойствами. Например, у вас может быть индекс для данных о клиентах, еще один индекс для каталогов продуктов и еще один индекс для данных о заказах. Индекс идентифицируется по имени (которое должно быть написано строчными буквами), которое используется для ссылки на индекс при индексации, поиске, обновлении и удалении документов в нем. В пределах одного кластера вы можете определить столько индексов, сколько захотите.
Если вы изучили Mysql, вы можете временно понимать его как базу данных в MySql.
тип
Индекс может иметь несколько типов. Например, индекс может иметь типы статей, типы пользователей и типы комментариев. В индексе больше нельзя создавать несколько типов, и вся концепция типов будет удалена в будущем выпуске.
Индексы, созданные в Elasticsearch 7.0.0 или более поздней версии, больше не принимаются.
_default_
карта. Индексы, созданные в версии 6.x, будут продолжать работать в Elasticsearch 6.x, как и раньше. Типы устарели в API в версии 7.0, нарушая изменения в API создания индекса, размещения сопоставления, получения сопоставления, размещения шаблона, получения шаблона и получения сопоставления полей.
Документация
Документ — это основная единица информации, которая может быть проиндексирована. Например, у вас может быть документ для клиента, документ для продукта и, конечно же, документ для заказа. Документы представлены в формате JSON (Javascript Object Notation), а JSON — это повсеместно распространенный формат обмена данными в Интернете.
Внутри индекса/типа вы можете хранить столько документов, сколько хотите. Обратите внимание, что хотя документ физически существует в индексе, документ должен быть проиндексирован/назначен индексному типу.
Осколки и реплики
Индексы могут хранить большие объемы данных, которые могут превышать аппаратные ограничения одного узла. Например, один индекс из 1 миллиарда документов, занимающий 1 ТБ дискового пространства, может не поместиться на диске одного узла или быть слишком медленным для удовлетворения поисковых запросов одного узла.
Чтобы решить эту проблему, ElasticSearch предоставляет возможность разделить индекс на несколько частей (называемых осколками). При создании индекса просто укажите желаемое количество осколков. Каждый сегмент сам по себе является полнофункциональным и независимым «индексом», который может размещаться на любом узле кластера.
Почему осколок?
Это позволяет вам разделить/масштабировать количество контента по горизонтали
Он позволяет распределять и распараллеливать операции между осколками (возможно, на нескольких узлах), повышая производительность/пропускную способность.
Механизмы распределения осколков и того, как их документы объединяются обратно в поисковые запросы, полностью управляются ElasticSearch и прозрачны для вас как пользователя.
Очень полезно в сетевой/облачной среде, где сбои могут произойти в любое время, настоятельно рекомендуется использовать механизм аварийного переключения, когда сегмент/узел каким-либо образом отключается или исчезает по какой-либо причине. С этой целью ElasticSearch позволяет вам реплицировать одну или несколько реплик осколка индекса в так называемые осколки реплик или, для краткости, осколки реплик.
Зачем иметь копию?
Обеспечивает высокую доступность в случае сбоя сегмента/узла. Поэтому важно отметить, что сегмент реплики никогда не размещается на том же узле, что и исходный/основной сегмент, который его реплицировал.
Позволяет масштабировать объем/пропускную способность поиска, поскольку поиск может выполняться на всех репликах параллельно.
В общем, каждый индекс можно разделить на несколько осколков. Индекс также может быть реплицирован ноль раз (что означает отсутствие копий) или несколько раз. После репликации каждый индекс будет иметь первичные осколки (копии исходных осколков) и осколки-реплики (копии первичных осколков).
Количество сегментов и реплик можно определить для каждого индекса во время создания индекса. Вы также можете динамически изменять количество реплик в любое время после создания индекса. Вы можете использовать API сжатия и разделения, чтобы изменить количество сегментов для существующего индекса. При создании индекса рекомендуется учитывать количество сегментов и реплик.
По умолчанию каждому индексу в ElasticSearch назначается основной сегмент и реплика, а это означает, что если в кластере есть как минимум два узла, индекс будет иметь основной сегмент и еще один сегмент реплики (полная реплика), в сумме из двух осколков на индекс.
Каждый сегмент ElasticSearch представляет собой индекс Lucene. В индексе Lucene может быть максимальное количество документов. Начиная с Lucene-5843, ограничение составляет 2 147 483 519 (=integer.max_value-128) документов. Вы можете отслеживать размер шарда с помощью API (подробнее об этом позже).
Теперь приступим к самой веселой части...
Установить
Бинарные файлы доступны изwww.slastic.co/downloadsи все версии, выпущенные в прошлом. Для каждого выпуска доступна архивная версия, зависящая от платформы, для Windows, Linux и MacOS, а также пакеты DEB и RPM для Linux и установочные пакеты MSI для Windows.
Linux
Для простоты мы используем пакет tarb для установки
Загрузите ElasticSearch 7.1.1 Linux tar.
curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.1.1-linux-x86_64.tar.gz
распаковать
tar -xvf elasticsearch-7.1.1-linux-x86_64.tar.gz
Распаковка создаст набор файлов и папок в текущем каталоге, а затем мы входим в каталог bin.
cd elasticsearch-7.1.1/bin
Начальные узлы и отдельные кластеры
./elasticsearch
Windows
Пользователям Windows мы рекомендуем использовать установочный пакет msi. Пакет содержит графический интерфейс пользователя (GUI), который поможет вам в процессе установки.
Затем дважды щелкните загруженный файл, чтобы запустить графический интерфейс. На первом экране выберите каталог установки:
Затем выберите, установить ли его как службу или запустить ElasticSearch вручную по мере необходимости. Чтобы соответствовать примеру Linux, выберите не устанавливать как службу:
Для настройки просто оставьте значение по умолчанию:
Снимите все плагины, чтобы ничего не устанавливать:
После нажатия кнопки «Установить» ElasticSearch будет установлен:
По умолчанию ElasticSearch будет установлен в %ProgramFiles%\Elastic\ElasticSearch. Перейдите в каталог установки, откройте командную строку, введите
.\elasticsearch.exe
успешно запустить узел
Если с установкой все прошло успешно, вы увидите следующую кучу сообщений:
[2018-09-13T12:20:01,766][INFO ][o.e.e.NodeEnvironment ] [localhost.localdomain] using [1] data paths, mounts [[/home (/dev/mapper/fedora-home)]], net usable_space [335.3gb], net total_space [410.3gb], types [ext4][2018-09-13T12:20:01,772][INFO ][o.e.e.NodeEnvironment ] [localhost.localdomain] heap size [990.7mb], compressed ordinary object pointers [true][2018-09-13T12:20:01,774][INFO ][o.e.n.Node ] [localhost.localdomain] node name [localhost.localdomain], node ID [B0aEHNagTiWx7SYj-l4NTw][2018-09-13T12:20:01,775][INFO ][o.e.n.Node ] [localhost.localdomain] version[7.1.1], pid[13030], build[oss/zip/77fc20e/2018-09-13T15:37:57.478402Z], OS[Linux/4.16.11-100.fc26.x86_64/amd64], JVM["Oracle Corporation"/OpenJDK 64-Bit Server VM/10/10+46][2018-09-13T12:20:01,775][INFO ][o.e.n.Node ] [localhost.localdomain] JVM arguments [-Xms1g, -Xmx1g, -XX:+UseConcMarkSweepGC, -XX:CMSInitiatingOccupancyFraction=75, -XX:+UseCMSInitiatingOccupancyOnly, -XX:+AlwaysPreTouch, -Xss1m, -Djava.awt.headless=true, -Dfile.encoding=UTF-8, -Djna.nosys=true, -XX:-OmitStackTraceInFastThrow, -Dio.netty.noUnsafe=true, -Dio.netty.noKeySetOptimization=true, -Dio.netty.recycler.maxCapacityPerThread=0, -Dlog4j.shutdownHookEnabled=false, -Dlog4j2.disable.jmx=true, -Djava.io.tmpdir=/tmp/elasticsearch.LN1ctLCi, -XX:+HeapDumpOnOutOfMemoryError, -XX:HeapDumpPath=data, -XX:ErrorFile=logs/hs_err_pid%p.log, -Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m, -Djava.locale.providers=COMPAT, -XX:UseAVX=2, -Dio.netty.allocator.type=unpooled, -Des.path.home=/home/manybubbles/Workspaces/Elastic/master/elasticsearch/qa/unconfigured-node-name/build/cluster/integTestCluster node0/elasticsearch-7.0.0-alpha1-SNAPSHOT, -Des.path.conf=/home/manybubbles/Workspaces/Elastic/master/elasticsearch/qa/unconfigured-node-name/build/cluster/integTestCluster node0/elasticsearch-7.0.0-alpha1-SNAPSHOT/config, -Des.distribution.flavor=oss, -Des.distribution.type=zip][2018-09-13T12:20:02,543][INFO ][o.e.p.PluginsService ] [localhost.localdomain] loaded module [aggs-matrix-stats][2018-09-13T12:20:02,543][INFO ][o.e.p.PluginsService ] [localhost.localdomain] loaded module [analysis-common][2018-09-13T12:20:02,543][INFO ][o.e.p.PluginsService ] [localhost.localdomain] loaded module [ingest-common][2018-09-13T12:20:02,544][INFO ][o.e.p.PluginsService ] [localhost.localdomain] loaded module [lang-expression][2018-09-13T12:20:02,544][INFO ][o.e.p.PluginsService ] [localhost.localdomain] loaded module [lang-mustache][2018-09-13T12:20:02,544][INFO ][o.e.p.PluginsService ] [localhost.localdomain] loaded module [lang-painless][2018-09-13T12:20:02,544][INFO ][o.e.p.PluginsService ] [localhost.localdomain] loaded module [mapper-extras][2018-09-13T12:20:02,544][INFO ][o.e.p.PluginsService ] [localhost.localdomain] loaded module [parent-join][2018-09-13T12:20:02,544][INFO ][o.e.p.PluginsService ] [localhost.localdomain] loaded module [percolator][2018-09-13T12:20:02,544][INFO ][o.e.p.PluginsService ] [localhost.localdomain] loaded module [rank-eval][2018-09-13T12:20:02,544][INFO ][o.e.p.PluginsService ] [localhost.localdomain] loaded module [reindex][2018-09-13T12:20:02,545][INFO ][o.e.p.PluginsService ] [localhost.localdomain] loaded module [repository-url][2018-09-13T12:20:02,545][INFO ][o.e.p.PluginsService ] [localhost.localdomain] loaded module [transport-netty4][2018-09-13T12:20:02,545][INFO ][o.e.p.PluginsService ] [localhost.localdomain] no plugins loaded[2018-09-13T12:20:04,657][INFO ][o.e.d.DiscoveryModule ] [localhost.localdomain] using discovery type [zen][2018-09-13T12:20:05,006][INFO ][o.e.n.Node ] [localhost.localdomain] initialized[2018-09-13T12:20:05,007][INFO ][o.e.n.Node ] [localhost.localdomain] starting ...[2018-09-13T12:20:05,202][INFO ][o.e.t.TransportService ] [localhost.localdomain] publish_address {127.0.0.1:9300}, bound_addresses {[::1]:9300}, {127.0.0.1:9300}[2018-09-13T12:20:05,221][WARN ][o.e.b.BootstrapChecks ] [localhost.localdomain] max file descriptors [4096] for elasticsearch process is too low, increase to at least [65535][2018-09-13T12:20:05,221][WARN ][o.e.b.BootstrapChecks ] [localhost.localdomain] max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144][2018-09-13T12:20:08,355][INFO ][o.e.c.s.MasterService ] [localhost.localdomain] elected-as-master ([0] nodes joined)[, ], reason: master node changed {previous [], current [{localhost.localdomain}{B0aEHNagTiWx7SYj-l4NTw}{hzsQz6CVQMCTpMCVLM4IHg}{127.0.0.1}{127.0.0.1:9300}{testattr=test}]}[2018-09-13T12:20:08,360][INFO ][o.e.c.s.ClusterApplierService] [localhost.localdomain] master node changed {previous [], current [{localhost.localdomain}{B0aEHNagTiWx7SYj-l4NTw}{hzsQz6CVQMCTpMCVLM4IHg}{127.0.0.1}{127.0.0.1:9300}{testattr=test}]}, reason: apply cluster state (from master [master {localhost.localdomain}{B0aEHNagTiWx7SYj-l4NTw}{hzsQz6CVQMCTpMCVLM4IHg}{127.0.0.1}{127.0.0.1:9300}{testattr=test} committed version [1] source [elected-as-master ([0] nodes joined)[, ]]])[2018-09-13T12:20:08,384][INFO ][o.e.h.n.Netty4HttpServerTransport] [localhost.localdomain] publish_address {127.0.0.1:9200}, bound_addresses {[::1]:9200}, {127.0.0.1:9200}[2018-09-13T12:20:08,384][INFO ][o.e.n.Node ] [localhost.localdomain] started
Мы видим, что узел с именем «6-bjhwl» (другой набор символов в вашем примере) запустился и выбрал себя в качестве хоста в одном кластере. Пока не беспокойтесь о том, что означает мастер. Самое главное здесь то, что мы запустили узел в кластере.
Как упоминалось ранее, мы можем переопределить имя кластера или узла. При запуске ElasticSearch вы можете сделать это из командной строки следующим образом:
./elasticsearch -Ecluster.name=my_cluster_name -Enode.name=my_node_name
Также обратите внимание на строку с пометкой http, которая содержит информацию об http-адресе (192.168.8.112) и порте (9200), с которого можно получить доступ к узлу. По умолчанию ElasticSearch использует порт 9200 для предоставления доступа к своему REST API. Этот порт можно настроить при необходимости.
Обзор кластеров
Использование REST-API
Теперь, когда узел (и кластер) запущен и работает, следующим шагом будет понимание того, как с ним взаимодействовать. К счастью, ElasticSearch предоставляет очень полный и мощный REST API, который вы можете использовать для взаимодействия со своим кластером. Вот несколько операций, которые можно выполнить с помощью API:
Проверьте работоспособность, статус и статистику кластера, узла и индекса.
Управление данными и метаданными кластера, узла и индекса.
Выполнять CRUD (создавать, читать, обновлять, удалять) и выполнять операции поиска по индексам.
Выполняйте операции расширенного поиска, такие как разбивка на страницы, сортировка, фильтрация, создание сценариев, агрегирование и многие другие.
состояние кластера
Давайте начнем с базовой проверки работоспособности, которую мы можем использовать, чтобы увидеть, как работает кластер. Для этого мы будем использовать curl, но вы можете использовать любой инструмент, позволяющий выполнять вызовы HTTP/REST. Предположим, мы все еще находимся на том же узле, где мы запустили ElasticSearch и открыли другое окно командной оболочки.
Чтобы проверить работоспособность кластера, мы будем использовать_cat
API. Вы можете запустить приведенную ниже команду в консоли Kibana, нажав «просмотреть в консоли», или использовать curl, щелкнув ссылку «копировать как curl» ниже и вставив ее в терминал.
curl -X GET "localhost:9200/_cat/health?v"
Результат ответа:
epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent1475247709 17:01:49 elasticsearch green 1 1 0 0 0 0 0 0 - 100.0%
Мы видим, что кластер с именем «elasticsearch» находится в зеленом состоянии. Всякий раз, когда мы запрашиваем работоспособность кластера, мы получаем либо зеленый, либо желтый, либо красный цвет.
Зеленый - все работает (кластер полностью работоспособен)
Желтый — все данные доступны, но некоторые реплики не назначены (кластер полностью работоспособен)
красный - часть данных по какой-то причине недоступна (кластерная часть работает)
Примечание. Когда кластер окрашен в красный цвет, он будет продолжать обслуживать поисковые запросы из доступных сегментов, но вам может потребоваться исправить это, как только появятся неназначенные сегменты.
Из ответа выше мы видим, что всего имеется 1 узел, а осколков у нас 0, потому что в них еще нет данных. Обратите внимание, что поскольку мы используем имя кластера по умолчанию (ElasticSearch) и поскольку ElasticSearch по умолчанию обнаруживает другие узлы на одном компьютере, вы можете случайно запустить несколько узлов на одном компьютере, и все они присоединятся к одному кластеру. В этом случае вы можете увидеть несколько узлов в ответе выше.
Мы также можем получить список узлов в кластере:
curl -X GET "localhost:9200/_cat/nodes?v"
Результат ответа:
ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name127.0.0.1 10 5 5 4.46 mdi * PB2SGZY
Мы видим узел с именем «pb2sgzy», который является единственным узлом в текущем кластере.
Посмотреть все индексы
Теперь давайте посмотрим на наш индекс:
curl -X GET "localhost:9200/_cat/indices?v"
Результат ответа:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
Это означает, что у нас еще нет индекса в кластере.
создать индекс
Теперь давайте создадим индекс под названием «клиент» и снова перечислим все индексы:
curl -X PUT "localhost:9200/customer?pretty"curl -X GET "localhost:9200/_cat/indices?v"
Первая команда использует глагол put для создания индекса с именем «клиент». Мы просто добавляем в конце вызоваpretty
Прикажите ему красиво распечатать ответ JSON (если есть).
Результат ответа:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.sizeyellow open customer 95SQ4TSUT7mWBT7VNHH67A 1 1 0 0 260b 260b
Результат второй команды сообщает нам, что теперь у нас есть индекс с именем customer с одним основным сегментом и одной репликой (по умолчанию), содержащей ноль документов.
Вы также можете заметить желтую метку здоровья в списке клиентов. Напомним из нашего предыдущего обсуждения, что желтый цвет означает, что некоторые копии не были выделены. Причина, по которой это происходит с этим индексом, заключается в том, что по умолчанию ElasticSearch создает копию этого индекса. Поскольку в настоящее время работает только один узел, реплика не может быть выделена (для обеспечения высокой доступности), пока другой узел не присоединится к кластеру. Как только реплика будет размещена на втором узле, состояние индекса станет зеленым.
документ запроса
Теперь мы помещаем что-то в индекс клиента. Мы проиндексируем простой документ клиента с идентификатором 1 в индексе клиента следующим образом:
curl -X PUT "localhost:9200/customer/_doc/1?pretty" -H 'Content-Type: application/json' -d'{ "name": "John Doe"}'
Результат ответа:
{ "_index" : "customer", "_type" : "_doc", "_id" : "1", "_version" : 1, "result" : "created", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 0, "_primary_term" : 1}
Сверху видно, что новый документ клиента успешно создан в каталоге клиентов. Документ также имеет внутренний идентификатор 1, который мы указали при индексации.
Важно отметить, что ElasticSearch не требует явного создания индекса перед индексированием документов. В предыдущем примере, если индекса клиентов раньше не существовало, ElasticSearch автоматически создаст индекс.
Теперь давайте получим документ, который мы только что проиндексировали:
curl -X GET "localhost:9200/customer/_doc/1?pretty"
Результат ответа:
{ "_index" : "customer", "_type" : "_doc", "_id" : "1", "_version" : 1, "_seq_no" : 25, "_primary_term" : 1, "found" : true, "_source" : { "name": "John Doe" }}
кроме одного поляfound
, тут нет ничего необычного, что свидетельствует о том, что мы нашли документ с запрошенным ID 1 и другим полем_source
, который возвращает полный документ JSON, который мы проиндексировали на предыдущем шаге.
падение индекса
Теперь давайте отбросим только что созданный индекс и снова перечислим все индексы:
curl -X DELETE "localhost:9200/customer?pretty"curl -X GET "localhost:9200/_cat/indices?v"
Результат ответа:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
Это означает, что индекс был успешно удален, и теперь мы вернулись к тому, с чего начали, когда в кластере ничего не было.
Прежде чем мы пойдем дальше, давайте подробнее рассмотрим некоторые из изученных нами команд API:
#创建索引curl -X PUT "localhost:9200/customer"#创建文档(添加数据)curl -X PUT "localhost:9200/customer/_doc/1" -H 'Content-Type: application/json' -d'{ "name": "John Doe"}'#查询文档(查询数据)curl -X GET "localhost:9200/customer/_doc/1"#删除文档(删除数据)curl -X DELETE "localhost:9200/customer"
Если мы внимательнее посмотрим на приведенные выше команды, мы увидим шаблон доступа к данным в ElasticSearch. Эту закономерность можно обобщить следующим образом:
<HTTP Verb> /<Index>/<Endpoint>/<ID>
Этот шаблон доступа к REST очень распространен во всех командах API, и если вы его запомните, у вас будет отличный старт в освоении ElasticSearch.
изменить данные
ElasticSearch обеспечивает манипулирование данными и возможности поиска практически в реальном времени. По умолчанию ожидается задержка в одну секунду (интервал обновления) от индексации/обновления/удаления данных до появления данных в результатах поиска. Это важное отличие от других платформ, таких как SQL, где данные доступны сразу после завершения транзакции.
Создание/замена документов (изменение данных)
Мы уже видели, как индексировать один документ. Давайте еще раз вспомним эту команду:
curl -X PUT "localhost:9200/customer/_doc/1?pretty" -H 'Content-Type: application/json' -d'{ "name": "John Doe"}'
Опять же, приведенный выше код будет индексировать указанный документ в клиентском индексе с идентификатором 1. Если мы снова выполним приведенную выше команду с другим (или тем же) документом, ElasticSearch заменит (т. е. воссоздаст) новый документ с идентификатором 1 поверх существующего документа:
curl -X PUT "localhost:9200/customer/_doc/1?pretty" -H 'Content-Type: application/json' -d'{ "name": "Jane Doe"}'
Приведенное выше изменяет имя документа с идентификатором 1 с «Джон Доу» на «Джейн Доу». С другой стороны, если мы используем другой идентификатор, будет создан новый документ, а документ, уже находящийся в индексе, останется прежним.
Указанный выше индекс представляет собой новый документ с идентификатором 2.
При создании идентификатор необязателен. Если не указано, ElasticSearch сгенерирует случайный идентификатор. Фактический идентификатор, сгенерированный ElasticSearch (или любой другой идентификатор, который был явно указан в предыдущем примере), возвращается как часть вызова API индексации.
В этом примере показано, как индексировать документы без явных идентификаторов:
curl -X POST "localhost:9200/customer/_doc?pretty" -H 'Content-Type: application/json' -d'{ "name": "Jane Doe"}'
Обратите внимание, что в приведенном выше случае мы используемpost
глагол вместоput
, потому что мы не указали идентификатор.
обновить данные
Помимо возможности добавлять и заменять документы, мы также можем обновлять документы. Обратите внимание, что Elasticsearch на самом деле не выполняет обновление оверлеев скрыто. Вместо этого удалите старый документ и добавьте новый.
В этом примере исходное имя с идентификатором 1 изменяется на Джейн Доу. Дополнительные сведения см. в следующем примере:
curl -X POST "localhost:9200/customer/_update/1?pretty" -H 'Content-Type: application/json' -d'{ "doc": { "name": "Jane Doe" }}'
В этом примере показано, как обновить предыдущий документ (с идентификатором 1), изменив поле имени на «Джейн Доу» и добавив в него поле возраста:
curl -X POST "localhost:9200/customer/_update/1?pretty" -H 'Content-Type: application/json' -d'{ "doc": { "name": "Jane Doe", "age": 20 }}'
Обновления также можно выполнять с помощью простого скрипта, в этом примере используется скрипт для увеличения возраста на 5:
curl -X POST "localhost:9200/customer/_update/1?pretty" -H 'Content-Type: application/json' -d'{ "script" : "ctx._source.age += 5"}'
Elasticsearch предоставляет возможность обновлять несколько документов при заданных условиях, как и в Sql.updata ... where ...
Мы представим его подробно в следующих главах.
удалить данные
Удалить документы довольно просто.
В этом примере показано, как удалить предыдущего клиента с идентификатором 2:
curl -X DELETE "localhost:9200/customer/_doc/2?pretty"
См. API _delete_by_query, чтобы удалить все документы, соответствующие определенному запросу. Стоит отметить, что удаление всего индекса намного эффективнее, чем удаление всех документов с помощью API удаления по запросу._delete_by_query API
Подробно будет представлено позже.
пакетная обработка
В дополнение к возможности индексировать, обновлять и удалять отдельные документы, Elasticsearch также предоставляет возможность делать это массово с помощью _bulk API. Эта функция важна, потому что она обеспечивает очень эффективный механизм для максимально быстрого выполнения нескольких операций с минимальным числом сетевых циклов.
В качестве простого примера следующий вызов индексирует два документа (ID 1 — Джон Доу и ID 2 — Джейн Доу) в одной массовой операции:
curl -X POST "localhost:9200/customer/_bulk?pretty" -H 'Content-Type: application/json' -d'{"index":{"_id":"1"}}{"name": "John Doe" }{"index":{"_id":"2"}}{"name": "Jane Doe" }'
В этом примере обновляется первый документ (с идентификатором 1), а затем удаляется второй документ (с идентификатором 2) в одной массовой операции:
curl -X POST "localhost:9200/customer/_bulk?pretty" -H 'Content-Type: application/json' -d'{"update":{"_id":"1"}}{"doc": { "name": "John Doe becomes Jane Doe" } }{"delete":{"_id":"2"}}'
Обратите внимание, что для операции удаления после нее нет соответствующего исходного документа, поскольку операции удаления необходимо удалить только идентификатор документа.
Пакетный API не выйдет из строя в случае сбоя операции (он продолжит выполнение в случае ошибки и в конечном итоге вернет статус каждой операции). Если операция по какой-либо причине завершается с ошибкой, она продолжит обработку остальных операций после сбоя. Когда массовый API вернется, он присвоит каждой операции статус (в том же порядке, в котором операции были отправлены), чтобы проверить, не завершилась ли конкретная операция ошибкой.
Просмотр данных
образец
Теперь, когда мы знаем основы, давайте попробуем с более реалистичным набором данных. Я подготовил пример вымышленного документа с информацией о банковском счете клиента в формате JSON. Каждый документ имеет следующее:
{ "account_number": 0, "balance": 16623, "firstname": "Bradshaw", "lastname": "Mckenzie", "age": 29, "gender": "F", "address": "244 Columbus Place", "employer": "Euron", "email": "bradshawmckenzie@euron.com", "city": "Hobucken", "state": "CO"}
Данные генерируются с помощью www.json-generator.com/, поэтому не обращайте внимания на фактическое значение и семантику данных, поскольку все они генерируются случайным образом.
Загрузить образцы данных
Вы можете скачать образец набора данных (accounts.json) отсюда. Извлеките его в текущий каталог и загрузите в кластер следующим образом:
curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_bulk?pretty&refresh" --data-binary "@accounts.json"curl "localhost:9200/_cat/indices?v"
Результат ответа:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.sizeyellow open bank l7sSYV2cQXmu6_4rJWVIww 5 1 1000 0 128.6kb 128.6kb
Это означает, что мы только что успешно объединили 1000 проиндексированных документов в банковский индекс.
API поиска
Теперь давайте начнем с простых поисков. Существует два основных способа запуска поиска:
Один из них — отправить параметры поиска через URI запроса REST.
Другой — отправить параметры поиска через тело запроса REST.
Метод тела запроса позволяет вам быть более выразительным, а также определять поиск в более удобочитаемом формате JSON. Мы попробуем пример метода URI запроса, но в оставшейся части этого руководства мы будем использовать только метод тела запроса.
Доступ к REST API для поиска можно получить из_search
Доступ к конечной точке. В этом примере возвращаются все документы в банковском индексе:
curl -X GET "localhost:9200/bank/_search?q=*&sort=account_number:asc&pretty"
Давайте сначала проанализируем поисковый вызов. Ищем в индексе банка (_search
),q=*
Параметр указывает Elasticsearch на соответствие всем документам в индексе.sort=account_number:asc
Параметр предписывает отсортировать результаты по возрастанию, используя поле account_number каждого документа. такой же,pretty
Параметр просто указывает Elasticsearch возвращать хорошо напечатанные результаты JSON.
Соответствующие результаты (показаны частично):
{ "took" : 63, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value": 1000, "relation": "eq" }, "max_score" : null, "hits" : [ { "_index" : "bank", "_type" : "_doc", "_id" : "0", "sort": [0], "_score" : null, "_source" : {"account_number":0,"balance":16623,"firstname":"Bradshaw","lastname":"Mckenzie","age":29,"gender":"F","address":"244 Columbus Place","employer":"Euron","email":"bradshawmckenzie@euron.com","city":"Hobucken","state":"CO"} }, { "_index" : "bank", "_type" : "_doc", "_id" : "1", "sort": [1], "_score" : null, "_source" : {"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"} }, ... ] }}
Что касается ответа, мы можем видеть:
took
Время (в миллисекундах), которое потребовалось Elasticsearch для выполнения поиска.timed_out
Сообщите нам, если время поиска истекло_shards
Сообщает нам, сколько осколков было обыскано, и количество успешных/неудачных осколков поиска.hits
результаты поискаhits.total
Объект, содержащий информацию об общем количестве документов, соответствующих критериям поиска.hits.total.value
Общее количество попаданий.hits.total.relation
:hits.total.value
Значением является точное количество попаданий, в данном случае оно равноeq
или нижняя граница (больше или равна) общего количества попаданий, и в этом случае она равнаgte
.hits.hits
Фактический массив результатов поиска (по умолчанию первые 10 документов)hits.sort
ключ сортировки результатов (теряется при сортировке по количеству баллов)hits._score
иmax_score
- Игнорировать эти поля на данный момент
hits.total
Точность определяется параметром запросаtrack_total_hits
контролировать, когдаtrack_total_hits
Если установлено значение true, запросы будут точно отслеживать общее количество обращений.“relation”:“eq”
. Значение по умолчанию — 10 000, что означает, что общее количество обращений можно отследить ровно до 10 000 документов. явно помещаяtrack_total_hits
Установите значение true, чтобы обеспечить точный подсчет. Для получения дополнительной информации мы представим его в следующих главах.
Вот как искать с помощью тела запроса:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "match_all": {} }, "sort": [ { "account_number": "asc" } ]}'
Разница здесь в том, что вместо передачи q=* в URI мы предоставляем тело запроса в стиле json для _search API. Мы обсудим этот запрос JSON в следующем разделе.
Важно понимать, что когда у вас есть результаты поиска, Elasticsearch полностью обрабатывает запрос, не поддерживает какие-либо ресурсы на стороне сервера и не открывает курсор в результатах. Это резко контрастирует со многими другими платформами, такими как SQL, где вы можете сначала получить подмножество результатов запроса, а затем продолжать возвращать их на сервер, если вы хотите получить (или развернуть) остальные результаты, используя некоторые своего рода серверный курсор с сохранением состояния.
Ввести язык запросов
Elasticsearch предоставляет язык запросов в стиле JSON, который можно использовать для выполнения запросов. Это называется Query DSL. Язык запросов настолько всеобъемлющий, что поначалу может показаться пугающим, но лучший способ изучить его — начать с нескольких основных примеров.
Возвращаясь к предыдущему примеру, мы выполнили этот запрос:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "match_all": {} }}'
Тщательно проанализируйте вышеуказанное содержание,query
раздел говорит нам выполнить операцию запроса,match_all
Просто тип запроса, который мы хотим запустить, match_all просто ищет все документы в указанном индексе.
В дополнение к параметрам запроса мы можем передавать другие параметры, влияющие на результаты поиска. В конце предыдущего раздела мы прошли по сортировке, а здесь мы прошли по размеру:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "match_all": {} }, "size": 1}'
Обратите внимание, что если размер не указан, по умолчанию он равен 10.
Следующий пример выполняетmatch_all
И возвращаем документы с 10 по 19 (от и по размеру можно по аналогии с лимитом в mysql??):
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "match_all": {} }, "from": 10, "size": 10}'
Параметр from (отсчитывается от 0) указывает, с какого индекса документа начинать, а параметр size указывает, сколько документов нужно вернуть, начиная с параметра from. Эта функция полезна при реализации пагинации результатов поиска.
Обратите внимание, что если from не указано, значение по умолчанию равно 0.
Этот пример выполняетmatch_all
операцию и отсортировать результаты по балансу счета в порядке убывания и вернуть первые 10 документов (размер по умолчанию).
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "match_all": {} }, "sort": { "balance": { "order": "desc" } }}'
выполнить поиск
Теперь, когда мы рассмотрели некоторые основные параметры поиска, давайте углубимся в Query DSL. Давайте сначала посмотрим на возвращаемые поля документа. По умолчанию полные документы JSON возвращаются как часть всех поисков. Это называется "исходник" (тот что в поиск попал_source
поле). Если мы не хотим, чтобы возвращался весь исходный документ, мы можем просто запросить возврат нескольких полей из источника.
В этом примере показано, как вернуть два поля из поиска, номер счета и баланс (в!source
Внутри):
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "match_all": {} }, "_source": ["account_number", "balance"]}'
Обратите внимание, что приведенный выше пример только уменьшает_source
_ поле. он по-прежнему возвращает только файл с именем_source
поле, но оно содержит только поля account_number и balance.
Если вы уже знакомы с MySql, описанное выше несколько похоже на концепцию SQL SELECT из списка полей.
Теперь давайте перейдем к части запроса. Ранее мы видели, как использоватьmatch_all
запрос для сопоставления всех документов. Теперь давайте представимmatch
Новый запрос Query, который можно рассматривать как базовый поисковый запрос по полю (т. е. поиск по определенному полю или набору полей).
В этом примере возвращается номер счета 20
Аналогия mysql match похожа на условный запрос в mysql.
Например, чтобы вернуть номер счета 20:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "match": { "account_number": 20 } }}'
В этом примере возвращаются все учетные записи со словом «mill» в адресе:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "match": { "address": "mill" } }}'
В этом примере возвращаются все учетные записи со словом «мельница» или «переулок» в адресе:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "match": { "address": "mill lane" } }}'
Вариант соответствия (match_phrase), который возвращает все аккаунты с фразой «mill lane» в адресе:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "match_phrase": { "address": "mill lane" } }}'
Примечание. Если в совпадении добавлен пробел, оно будет рассматриваться как два слова, и будет запрошено любое слово.
match_parase будет игнорировать пробелы, рассматривать символ как единое целое и сопоставлять документы, которые содержат это целое в индексе.
Теперь давайте представим логические запросы. логические запросы позволяют нам объединять более мелкие запросы в более крупные, используя логическую логику.
Если вы знакомы с mysql, то обнаружите, что логический запрос на самом деле эквивалентен или нет...
Этот пример содержит два совпадающих запроса, которые возвращают «mill» в адресеиВсе аккаунты "Лейн":
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } }}'
В приведенном выше примере предложение bool must указывает все запросы, которые должны быть истинными, чтобы документ считался совпадающим.
Вместо этого этот пример содержит два совпадающих запроса и возвращает адреса, содержащие «mill».илиВсе аккаунты "Лейн":
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "bool": { "should": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } }}'
В приведенном выше примере предложение bool should указывает список запросов, любой из которых должен быть истинным, чтобы документ считался совпадающим.
Этот пример содержит два совпадающих запроса, которые не содержат слова «мельница» в адресе возврата.не содержитВсе аккаунты "Лейн":
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "bool": { "must_not": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } }}'
В приведенном выше примере предложение bool must_not указывает список запросов, ни один из которых не должен быть истинным, чтобы документ считался совпадающим.
Мы можем комбинировать предложения must, should и must_not вместе в логическом запросе. Кроме того, мы можем комбинировать логические запросы в этих логических предложениях, чтобы имитировать любую сложную многоуровневую логическую логику.
Этот пример возвращает учетные записи всех людей, которым исполнилось 40 лет, но которые не живут в ID(aho):
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "bool": { "must": [ { "match": { "age": "40" } } ], "must_not": [ { "match": { "state": "ID" } } ] } }}'
выполнить фильтр
В предыдущем разделе мы пропустили небольшую деталь, называемую оценкой документа (поле _score в результатах поиска). Оценка — это числовое значение, которое является относительной мерой того, насколько хорошо документ соответствует нашему указанному поисковому запросу. Чем выше балл, тем более актуален документ, чем ниже балл, тем менее актуален документ.
Но запросы не всегда должны генерировать оценки, особенно когда они используются только для «фильтрации» набора документов. Elasticsearch автоматически оптимизирует выполнение запросов, чтобы избежать бесполезных вычислений.
Запрос bool, который мы представили в предыдущем разделе, также поддерживает предложение filter, что позволяет нам использовать запрос для ограничения документов, которые будут сопоставляться с другими предложениями, без изменения способа расчета оценки. В качестве примера давайте представим запросы диапазона, которые позволяют нам фильтровать документы на основе диапазона значений. Обычно это используется для числовой фильтрации или фильтрации по дате.
В этом примере используется логический запрос для возврата всех учетных записей с балансом от 20 000 до 30 000, включая баланс. Другими словами, мы хотим найти счета с остатками больше или равными 20000 и меньше или равными 30000.
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "query": { "bool": { "must": { "match_all": {} }, "filter": { "range": { "balance": { "gte": 20000, "lte": 30000 } } } } }}'
Анализируя вышеизложенное, логический запрос содержитmatch_all
запрос (часть запроса) иrange
Запрос (раздел фильтра). Мы можем заменить любой другой запрос в разделе запросов и фильтров. В приведенном выше примере запрос диапазона имеет большой смысл, потому что все документы, попадающие в диапазон, совпадают «одинаково», и ни один документ не имеет большего смысла, чем имеющийся (поскольку он отфильтрован).
Кромеmatch_all、match、bool和range
В дополнение к запросам доступно множество других типов запросов, и мы не будем подробно обсуждать их здесь. Теперь, когда у нас есть базовое понимание того, как они работают, не должно быть слишком сложно применить эти знания при изучении и экспериментировании с другими типами запросов.
Выполнить агрегацию (аналог агрегатной функции mysql)
Агрегации предоставляют возможность группировать данные и извлекать статистику. Самый простой способ представить агрегаты — это грубо приравнять их к функциям SQL GROUP by и агрегатам SQL. В Elasticsearch вы можете выполнять поиск, который возвращает совпадения, возвращая агрегированные результаты отдельно от всех совпадений в одном ответе. Это очень мощно и эффективно, потому что вы можете выполнять запросы и множественные агрегации и получать результаты обеих (или любой) операций за один раз, избегая сетевых циклов с чистым и упрощенным API.
Сначала в этом примере все учетные записи группируются по состоянию, а затем возвращаются первые 10 состояний (по умолчанию) в порядке убывания количества (также по умолчанию):
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" } } }}'
В SQL приведенный выше агрегат концептуально такой же, как:
SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC LIMIT 10;
{ "took": 29, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped" : 0, "failed": 0 }, "hits" : { "total" : { "value": 1000, "relation": "eq" }, "max_score" : null, "hits" : [ ] }, "aggregations" : { "group_by_state" : { "doc_count_error_upper_bound": 20, "sum_other_doc_count": 770, "buckets" : [ { "key" : "ID", "doc_count" : 27 }, { "key" : "TX", "doc_count" : 27 }, { "key" : "AL", "doc_count" : 25 }, { "key" : "MD", "doc_count" : 25 }, { "key" : "TN", "doc_count" : 23 }, { "key" : "MA", "doc_count" : 21 }, { "key" : "NC", "doc_count" : 21 }, { "key" : "ND", "doc_count" : 21 }, { "key" : "ME", "doc_count" : 20 }, { "key" : "MO", "doc_count" : 20 } ] } }}
Мы видим, что ID (Айдахо) имеет 27 учетных записей, затем следует TX (Техас) с 27 учетными записями, затем AL (Алабама) с 25 учетными записями и так далее.
Обратите внимание, что мы устанавливаем size=0, чтобы не показывать результаты поиска, потому что мы хотим видеть в ответе только агрегированные результаты.
На основе предыдущей сводки в этом примере вычисляется средний баланс счета по штатам (опять же только для первых 10 штатов в порядке убывания количества):
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } }}'
Обратите внимание, как мы вложили агрегацию Average_balance в агрегацию group_by_state. Это общая схема для всех агрегатов. Вы можете произвольно вкладывать агрегаты в агрегаты, чтобы извлечь желаемые результаты из данных.
На основе предыдущей агрегации теперь сортируем средний баланс по убыванию:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword", "order": { "average_balance": "desc" } }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } }}'
Этот пример показывает, как мы можем сгруппировать по возрастной группе (20-29, 30-39, 40-49), затем по полу и, наконец, получить средний баланс счета для каждой возрастной группы, каждого пола:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'{ "size": 0, "aggs": { "group_by_age": { "range": { "field": "age", "ranges": [ { "from": 20, "to": 30 }, { "from": 30, "to": 40 }, { "from": 40, "to": 50 } ] }, "aggs": { "group_by_gender": { "terms": { "field": "gender.keyword" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } } }}'
Есть много других функций агрегации, которые мы не будем здесь подробно обсуждать. Если вы хотите поэкспериментировать дальше, справочное руководство Aggregate — хорошее место для начала.
в заключении
Elasticsearch — это одновременно и простой продукт, и сложный. До сих пор мы видели, что это такое, как увидеть его внутренности и как использовать его с некоторыми REST API. Надеемся, что это руководство помогло вам лучше понять, что такое Elasticsearch, и, что более важно, вдохновило вас на дальнейшие эксперименты с другими его замечательными функциями!