Построение поискового опыта с клиентом приложения Search
В данном документе приведено пошаговое руководство по созданию поискового опыта с помощью приложения поиска, используя клиентскую библиотеку для приложений поиска. Библиотека — это JavaScript-библиотека, предназначенная для использования в браузере. Вы интегрируете эту библиотеку в ваше веб-приложение, чтобы упростить запросы к вашему поисковому приложению.
Доступна песочница для тестирования и экспериментов с search-application-client библиотекой. Перейдите туда, если хотите попробовать клиент, не настраивая собственное веб-приложение.
Скопируйте репозиторий и следуйте инструкциям в файле README, чтобы начать работу.
Цель
Это руководство предполагает, что вы хотите создать веб-приложение с следующими поисковыми функциями:
- Поисковая строка и результаты с пользовательской релевантностью
- Управление представлением результатов, например, включение/исключение полей и выделение совпадающих терминов
- Элементы управления интерфейсом пользователя, такие как фасеты, фильтры, сортировка, пагинация
Вы можете рассматривать приложение поиска как «серверную сторону», которая сохраняет изменения в Elasticsearch. Ваше веб-приложение выступает в роли «клиентской стороны», которая отправляет запросы к приложению поиска. Вы будете вносить изменения как в приложение поиска, так и в веб-приложение для завершения реализации.
Предварительные условия
Для выполнения этого руководства вам потребуется:
-
Развертывание Elastic, которое соответствует предварительным условиям для запуска приложения поиска.
- Если у вас нет развертывания Elastic, начните бесплатную пробную версию на Elastic Cloud.
-
Приложение поиска.
- Создавайте и управляйте приложениями поиска в интерфейсе Kibana или с помощью API.
-
Веб-приложение для запроса вашего приложения поиска с использованием клиента приложения поиска.
Установить и настроить клиента
Установить клиента
Установите клиента с помощью npm, yarn или CDN.
Вариант 1: Использование менеджера пакетов
Для установки клиента с помощью npm выполните следующую команду:
npm install @elastic/search-application-client
Для установки клиента с помощью yarn выполните следующую команду:
yarn add @elastic/search-application-client
Вариант 2: Использование CDN с тегом HTML <script>
В качестве альтернативы вы можете установить клиента с помощью CDN. Добавьте следующий <script> тег в HTML вашего веб-приложения:
<script src="https://cdn.jsdelivr.net/npm/@elastic/search-application-client@latest"></script>
Импорт и инициализация клиента
После установки вы можете импортировать клиента в свое веб-приложение. Вам потребуется следующая информация для инициализации клиента:
- Имя вашего приложения поиска
- URL конечной точки вашего приложения поиска
- Ключ API вашего приложения поиска
Найдите эту информацию на странице Подключение в интерфейсе Kibana.
Вариант 1: Использование JavaScript-модулей
Используйте следующее утверждение импорта:
import SearchApplicationClient from '@elastic/search-application-client';
Настройте клиента с помощью данных вашего развертывания, чтобы начать отправку поисковых запросов. Вы можете сгенерировать ключ API на странице Подключение в интерфейсе Kibana. Перейдите на Поиск > Приложения поиска > <MY_SEARCH_APPLICATION> > Подключение. Следующая информация будет предварительно заполнена для инициализации клиента:
import Client from '@elastic/search-application-client'
const request = Client(
'my-search-application', // search application name
'url-from-connect-page', // url-host
'api-key-from-connect-page', // api-key
{
// optional configuration
}
) После настройки вы сможете отправлять поисковые запросы в ваше приложение поиска, используя API клиента, например так:
const results = await request()
.query('star wars')
.search() Вариант 2: Использование CDN
В качестве альтернативы, если вы используете CDN, вы можете импортировать клиента с помощью следующего утверждения:
<script> const Client = window['SearchApplicationClient']; </script>
Настройте клиента с помощью данных вашего развертывания, чтобы начать отправку поисковых запросов. Вы можете сгенерировать ключ API на странице Подключение в интерфейсе Kibana. Перейдите на Поиск > Приложения поиска > <MY_SEARCH_APPLICATION> > Подключение. Следующая информация будет предварительно заполнена для инициализации клиента:
<script>
const request = Client(
'my-example-app', // search application name
'url-from-connect-page', // url-host
'api-key-from-connect-page', // api-key
{
// optional configuration
}
)
</script> После настройки вы сможете отправлять поисковые запросы в ваше приложение поиска, используя API клиента, например так:
<script>
const results = await request()
.query('star wars')
.search()
</script> Работа со вашей поисковой шаблоном
Клиент приложения поиска предназначен для работы с любым шаблоном поиска, который вы создадите. Вы будете использовать API приложения поиска для создания и управления своими поисковыми шаблонами.
При работе с API приложений поиска для управления шаблонами мы предоставляем примеры API с использованием синтаксиса консоли Kibana.
Вот пример шаблона:
resp = client.search_application.put(
name="my-example-app",
search_application={
"indices": [
"my-example-app"
],
"template": {
"script": {
"lang": "mustache",
"source": "\n {\n \"query\": {\n \"bool\": {\n \"must\": [\n {{#query}}\n {\n \"query_string\": {\n \"query\": \"{{query}}\",\n \"search_fields\": {{#toJson}}search_fields{{/toJson}}\n }\n }\n {{/query}}\n ]\n }\n }\n }\n ",
"params": {
"query": "",
"search_fields": ""
}
}
}
},
)
print(resp) const response = await client.searchApplication.put({
name: "my-example-app",
search_application: {
indices: ["my-example-app"],
template: {
script: {
lang: "mustache",
source:
'\n {\n "query": {\n "bool": {\n "must": [\n {{#query}}\n {\n "query_string": {\n "query": "{{query}}",\n "search_fields": {{#toJson}}search_fields{{/toJson}}\n }\n }\n {{/query}}\n ]\n }\n }\n }\n ',
params: {
query: "",
search_fields: "",
},
},
},
},
});
console.log(response); PUT _application/search_application/my-example-app
{
"indices": ["my-example-app"],
"template": {
"script": {
"lang": "mustache",
"source": """
{
"query": {
"bool": {
"must": [
{{#query}}
{
"query_string": {
"query": "{{query}}",
"search_fields": {{#toJson}}search_fields{{/toJson}}
}
}
{{/query}}
]
}
}
}
""",
"params": {
"query": "",
"search_fields": ""
}
}
}
} Это позволит вам добавить любые параметры шаблона, которые вам нужны, в шаблон, а затем предоставить значения в запросе клиента. Используйте addParameter для вставки фактических значений в параметры вашего шаблона.
Например, передайте значения для search_fields следующим образом:
const results = await request()
.query('star wars') // requires the template to use query parameter
.addParameter('search_fields', ['title', 'description'])
.search() Пример шаблона
Мы рекомендуем начать работу с шаблоном-заготовкой, предоставленным в репозитории клиента. Просмотреть этот скрипт, чтобы увидеть, как это используется. Параметр dictionary используется для передачи определения JSON-схемы, которое описывает структуру и правила валидации для объекта запроса. Эта схема важна, потому что она ограничивает использование определенных функций в запросе Elasticsearch. Просмотреть схему.
Каждая поисковая функция в этом руководстве требует функции, включенной в этот шаблон. Эти функции требуют наличия определенных параметров в шаблоне:
- Запрос:
query - Фильтры:
_es_filters - Фасетирование:
_es_filtersи_es_aggs - Сортировка:
_es_sort_fields - Пагинация:
fromиsize
Функции поиска
Мы рассмотрим все необходимые основы для поискового опыта. Вы узнаете, как реализовать их, используя ваше приложение поиска и запросить их с помощью клиента.
Обратитесь к репозиторию клиента за информацией о доступных методах и их параметрах.
Настройка релевантности
Наш простой шаблон использует query_string поиск по всем полям, но это может не подойти для вашего случая. Вы можете обновить шаблон, чтобы обеспечить лучшую релевантность.
В приведенном ниже примере мы используем запрос multi-match для нашего шаблона, с запросами best_fields и phrase_prefix, нацеленными на разные поля поиска.
resp = client.search_application.put(
name="my-example-app",
search_application={
"indices": [
"example-index"
],
"template": {
"script": {
"lang": "mustache",
"source": "\n {\n \"query\": {\n \"bool\": {\n \"must\": [\n {{#query}}\n {\n \"multi_match\" : {\n \"query\": \"{{query}}\",\n \"fields\": [ \"title^4\", \"plot\", \"actors\", \"directors\" ]\n }\n },\n {\n \"multi_match\" : {\n \"query\": \"{{query}}\",\n \"type\": \"phrase_prefix\",\n \"fields\": [ \"title^4\", \"plot\"]\n }\n },\n {{/query}}\n ],\n \"filter\": {{#toJson}}_es_filters{{/toJson}}\n }\n },\n \"aggs\": {{#toJson}}_es_aggs{{/toJson}},\n \"from\": {{from}},\n \"size\": {{size}},\n \"sort\": {{#toJson}}_es_sort_fields{{/toJson}}\n }\n ",
"params": {
"query": "",
"_es_filters": {},
"_es_aggs": {},
"_es_sort_fields": {},
"size": 10,
"from": 0
},
"dictionary": {}
}
}
},
)
print(resp) const response = await client.searchApplication.put({
name: "my-example-app",
search_application: {
indices: ["example-index"],
template: {
script: {
lang: "mustache",
source:
'\n {\n "query": {\n "bool": {\n "must": [\n {{#query}}\n {\n "multi_match" : {\n "query": "{{query}}",\n "fields": [ "title^4", "plot", "actors", "directors" ]\n }\n },\n {\n "multi_match" : {\n "query": "{{query}}",\n "type": "phrase_prefix",\n "fields": [ "title^4", "plot"]\n }\n },\n {{/query}}\n ],\n "filter": {{#toJson}}_es_filters{{/toJson}}\n }\n },\n "aggs": {{#toJson}}_es_aggs{{/toJson}},\n "from": {{from}},\n "size": {{size}},\n "sort": {{#toJson}}_es_sort_fields{{/toJson}}\n }\n ',
params: {
query: "",
_es_filters: {},
_es_aggs: {},
_es_sort_fields: {},
size: 10,
from: 0,
},
dictionary: {},
},
},
},
});
console.log(response); PUT _application/search_application/my-example-app
{
"indices": ["example-index"],
"template": {
"script": {
"lang": "mustache",
"source": """
{
"query": {
"bool": {
"must": [
{{#query}}
{
"multi_match" : {
"query": "{{query}}",
"fields": [ "title^4", "plot", "actors", "directors" ]
}
},
{
"multi_match" : {
"query": "{{query}}",
"type": "phrase_prefix",
"fields": [ "title^4", "plot"]
}
},
{{/query}}
],
"filter": {{#toJson}}_es_filters{{/toJson}}
}
},
"aggs": {{#toJson}}_es_aggs{{/toJson}},
"from": {{from}},
"size": {{size}},
"sort": {{#toJson}}_es_sort_fields{{/toJson}}
}
""",
"params": {
"query": "",
"_es_filters": {},
"_es_aggs": {},
"_es_sort_fields": {},
"size": 10,
"from": 0
},
"dictionary": {
// add dictionary restricting
// _es_filters, _es_sort_fields & _es_aggs params
// Use example provided in repo: https://github.com/elastic/search-application-client/blob/main/bin/request_schema.json
}
}
}
} Обратитесь к примерам различных типов запросов, включая комбинации текстового поиска, поиска kNN, поиска ELSER, гибридного поиска с RRF и других.
Случай использования: Я хочу динамически изменять поля поиска
Если вам нужно настроить search_fields во время запроса, вы можете добавить новый параметр в шаблон (например: search_fields) и использовать метод addParameter для передачи полей в шаблон.
Случай использования: я хочу повысить релевантность результатов, учитывая близость к пользователю
Вы можете добавить дополнительные параметры шаблона для отправки географических координат пользователя. Затем используйте function_score для повышения релевантности документов, которые соответствуют определённому geo_distance от пользователя.
Поля результатов
По умолчанию все поля возвращаются в поле _source. Чтобы ограничить возвращаемые поля, укажите поля в шаблоне.
resp = client.search_application.put(
name="my-example-app",
search_application={
"indices": [
"example-index"
],
"template": {
"script": {
"lang": "mustache",
"source": "\n {\n \"query\": {\n \"bool\": {\n \"must\": [\n {{#query}}\n {{/query}}\n ],\n \"filter\": {{#toJson}}_es_filters{{/toJson}}\n }\n },\n \"_source\": {\n \"includes\": [\"title\", \"plot\"]\n },\n \"aggs\": {{#toJson}}_es_aggs{{/toJson}},\n \"from\": {{from}},\n \"size\": {{size}},\n \"sort\": {{#toJson}}_es_sort_fields{{/toJson}}\n }\n ",
"params": {
"query": "",
"_es_filters": {},
"_es_aggs": {},
"_es_sort_fields": {},
"size": 10,
"from": 0
},
"dictionary": {}
}
}
},
)
print(resp) const response = await client.searchApplication.put({
name: "my-example-app",
search_application: {
indices: ["example-index"],
template: {
script: {
lang: "mustache",
source:
'\n {\n "query": {\n "bool": {\n "must": [\n {{#query}}\n {{/query}}\n ],\n "filter": {{#toJson}}_es_filters{{/toJson}}\n }\n },\n "_source": {\n "includes": ["title", "plot"]\n },\n "aggs": {{#toJson}}_es_aggs{{/toJson}},\n "from": {{from}},\n "size": {{size}},\n "sort": {{#toJson}}_es_sort_fields{{/toJson}}\n }\n ',
params: {
query: "",
_es_filters: {},
_es_aggs: {},
_es_sort_fields: {},
size: 10,
from: 0,
},
dictionary: {},
},
},
},
});
console.log(response); PUT _application/search_application/my-example-app
{
"indices": ["example-index"],
"template": {
"script": {
"lang": "mustache",
"source": """
{
"query": {
"bool": {
"must": [
{{#query}}
// ...
{{/query}}
],
"filter": {{#toJson}}_es_filters{{/toJson}}
}
},
"_source": {
"includes": ["title", "plot"]
},
"aggs": {{#toJson}}_es_aggs{{/toJson}},
"from": {{from}},
"size": {{size}},
"sort": {{#toJson}}_es_sort_fields{{/toJson}}
}
""",
"params": {
"query": "",
"_es_filters": {},
"_es_aggs": {},
"_es_sort_fields": {},
"size": 10,
"from": 0
},
"dictionary": {
// add dictionary restricting _es_filters and _es_aggs params
// Use the dictionary example provided in repo: https://github.com/elastic/search-application-client/blob/main/bin/request_schema.json
}
}
}
} Случай использования: я хочу динамически настраивать поля результатов
Если вам нужно настроить возвращаемые поля во время запроса, вы можете добавить новый параметр в шаблон (например: result_fields) и использовать метод addParameter для передачи полей в шаблон.
Выделение и фрагменты
Поддержка выделения легко добавить в шаблон. С помощью API выделения вы можете указать, какие поля вы хотите выделить для совпадений.
В следующем примере мы указываем title и plot в качестве полей для выделения. title обычно имеет короткий размер значения, по сравнению с plot, который является переменным и, как правило, длиннее.
Мы указываем, что заголовок должен быть fragment_size поля 0, чтобы вернуть весь текст при наличии выделения. Мы указываем, что фрагмент должен быть fragment_size поля 200, где каждый выделенный фрагмент будет длиной до 200 символов.
resp = client.search_application.put(
name="my-example-app",
search_application={
"indices": [
"example-index"
],
"template": {
"script": {
"lang": "mustache",
"source": "\n {\n \"query\": {\n \"bool\": {\n \"must\": [\n {{#query}}\n {{/query}}\n ],\n \"filter\": {{#toJson}}_es_filters{{/toJson}}\n }\n },\n \"_source\": {\n \"includes\": [\"title\", \"plot\"]\n },\n \"highlight\": {\n \"fields\": {\n \"title\": { \"fragment_size\": 0 },\n \"plot\": { \"fragment_size\": 200 }\n }\n },\n \"aggs\": {{#toJson}}_es_aggs{{/toJson}},\n \"from\": {{from}},\n \"size\": {{size}},\n \"sort\": {{#toJson}}_es_sort_fields{{/toJson}}\n }\n ",
"params": {
"query": "",
"_es_filters": {},
"_es_aggs": {},
"_es_sort_fields": {},
"size": 10,
"from": 0
},
"dictionary": {}
}
}
},
)
print(resp) const response = await client.searchApplication.put({
name: "my-example-app",
search_application: {
indices: ["example-index"],
template: {
script: {
lang: "mustache",
source:
'\n {\n "query": {\n "bool": {\n "must": [\n {{#query}}\n {{/query}}\n ],\n "filter": {{#toJson}}_es_filters{{/toJson}}\n }\n },\n "_source": {\n "includes": ["title", "plot"]\n },\n "highlight": {\n "fields": {\n "title": { "fragment_size": 0 },\n "plot": { "fragment_size": 200 }\n }\n },\n "aggs": {{#toJson}}_es_aggs{{/toJson}},\n "from": {{from}},\n "size": {{size}},\n "sort": {{#toJson}}_es_sort_fields{{/toJson}}\n }\n ',
params: {
query: "",
_es_filters: {},
_es_aggs: {},
_es_sort_fields: {},
size: 10,
from: 0,
},
dictionary: {},
},
},
},
});
console.log(response); PUT _application/search_application/my-example-app
{
"indices": ["example-index"],
"template": {
"script": {
"lang": "mustache",
"source": """
{
"query": {
"bool": {
"must": [
{{#query}}
// ...
{{/query}}
],
"filter": {{#toJson}}_es_filters{{/toJson}}
}
},
"_source": {
"includes": ["title", "plot"]
},
"highlight": {
"fields": {
"title": { "fragment_size": 0 },
"plot": { "fragment_size": 200 }
}
},
"aggs": {{#toJson}}_es_aggs{{/toJson}},
"from": {{from}},
"size": {{size}},
"sort": {{#toJson}}_es_sort_fields{{/toJson}}
}
""",
"params": {
"query": "",
"_es_filters": {},
"_es_aggs": {},
"_es_sort_fields": {},
"size": 10,
"from": 0
},
"dictionary": {
// add dictionary restricting _es_filters and _es_aggs params
// Use the dictionary example provided in repo: https://github.com/elastic/search-application-client/blob/main/bin/request_schema.json
}
}
}
} Если совпадение найдено, это вернёт результаты с полем выделения. Например:
{
"hits": [
{
"_index": "movies",
"_type": "_doc",
"_id": "1",
"_score": 0.2876821,
"_source": {
"title": "The Great Gatsby",
"plot": "The Great Gatsby is a novel by F. Scott Fitzgerald that follows the story of Jay Gatsby, a wealthy and mysterious man, as he tries to win back the love of his life, Daisy Buchanan."
},
"highlight": {
"title": ["The Great <em>Gatsby</em>"],
"plot": [
"The Great <em>Gatsby</em> is a novel by F. Scott Fitzgerald that follows the story of <em>Jay</em> <em>Gatsby</em>, a wealthy and mysterious man, as he tries to win back the love of his life, Daisy Buchanan."
]
}
}
]
} Помощник выделения
При отображении полей на фронтенде вам необходимо сначала определить, есть ли у поля выделение. Чтобы упростить это, мы предоставляем помощник.
import Client, { Highlight } from '@elastic/search-application-client'
// example React component
const ResultsList = ({ hits } ) => {
return hits.map((hit) => (
<div className="result">
<div className="title">{Highlight(hit, "title")}</div>
<div className="description">{Highlight(hit, "plot")}</div>
</div>
))
} Пейджинг
Для использования пейджирования установите номер страницы и размер страницы. По умолчанию размер страницы составляет 10. Параметры size и from позволяют вам управлять страницей и количеством совпадений, возвращаемых в ответе.
Это можно сделать, используя клиент с методами setSize и setFrom.
// page 1 const results = await request() .setSize(20) .setFrom(0) .search() // page 2 const results = await request() .setSize(20) .setFrom(20) .search()
Сортировка
Для сортировки укажите имя поля и порядок сортировки или pass _score для сортировки по релевантности. Требуется параметр _es_sort_fields_fields в шаблоне поиска. Обратитесь к нашему примеру шаблона, чтобы увидеть, где это используется.
По умолчанию результаты будут отсортированы по убыванию релевантности. Если вам нужно отсортировать по полю, отличному от релевантности, используйте метод setSort с массивом объектов.
const results = await request()
.setSort([{ year: 'asc' }, '_score'])
.search() Фильтрование
Клиент приложения поиска также поддерживает фильтры и фасеты. Чтобы использовать их, вам нужно добавить два параметра:
-
_es_filters -
_es_aggs
Обратитесь к нашему примеру шаблона, чтобы увидеть, где они используются.
Базовое фильтрование
С шаблоном, настроенным для использования фильтров, используйте метод setFilter для добавления фильтров в ваш запрос.
Шаблонная схема по умолчанию поддерживает фильтры типа term, range, match, nested, geo_bounding_box и geo_distance. Если вам нужно использовать определённый тип условия, вы можете обновить схему шаблона.
Ниже приведён пример использования setFilter.
// return only "star wars" movies that are rated PG
const results = await request()
.query('star wars')
.setFilter({
term: {
'rated.enum': 'PG',
},
})
.search() Фасеты
Клиент поддерживает настройку фасетов для результатов. Укажите фасеты при инициализации клиента. Например, давайте добавим фасеты для актёров, режиссёров и рейтинга IMDB.
const request = Client(
'my-example-app', // search application name
'https://d1bd36862ce54c7b903e2aacd4cd7f0a.us-east4.gcp.elastic-cloud.com:443', // api-host
'api-key-from-connect-page', // api-key
{
facets: {
actors: {
type: 'terms',
field: 'actors.keyword',
disjunctive: true,
},
directors: {
type: 'terms',
field: 'director.keyword',
size: 20,
disjunctive: true,
},
imdbrating: {
type: 'stats',
field: 'imdbrating',
},
},
}
) В Elasticsearch тип keyword используется для полей, которые должны быть искомыми в их точном, неизменённом виде. Это означает, что такие запросы чувствительны к регистру. Мы используем этот тип для фасетов, потому что фасеты требуют агрегирования и фильтрации данных на основе точных значений или терминов.
Используйте метод addFacetFilter для добавления фасетов в запрос.
В следующем примере мы хотим вернуть только фильмы:
- С участием Харрисона Форда в качестве актёра
- Режиссёрские работы Джорджа Лукаса или Ридли Скотта
- С рейтингом IMDB выше 7.5
const results = await request()
.addFacetFilter('actors', 'Harrison Ford')
.addFacetFilter('directors', 'George Lucas')
.addFacetFilter('directors', 'Ridley Scott')
.addFacetFilter('imdbrating', {
gte: 7.5,
})
.search() Вы можете получить доступ к фасетам в результатах:
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 0,
"hits": [
{
"_index": "imdb_movies",
"_id": "tt0076759",
"_score": 0,
"_source": {
"title": "Star Wars: Episode IV - A New Hope",
"actors": [
"Mark Hamill",
"Harrison Ford",
"Carrie Fisher",
"Peter Cushing"
],
"plot": "Luke Skywalker joins forces with a Jedi Knight, a cocky pilot, a wookiee and two droids to save the universe from the Empire's world-destroying battle-station, while also attempting to rescue Princess Leia from the evil Darth Vader.",
"poster": "https://s3-eu-west-1.amazonaws.com/imdbimages/images/MV5BMTU4NTczODkwM15BMl5BanBnXkFtZTcwMzEyMTIyMw@@._V1_SX300.jpg"
}
},
{
"_index": "imdb_movies",
"_id": "tt0083658",
"_score": 0,
"_source": {
"title": "Blade Runner",
"actors": [
"Harrison Ford",
"Rutger Hauer",
"Sean Young",
"Edward James Olmos"
],
"plot": "Deckard, a blade runner, has to track down and terminate 4 replicants who hijacked a ship in space and have returned to Earth seeking their maker.",
"poster": "https://s3-eu-west-1.amazonaws.com/imdbimages/images/MV5BMTA4MDQxNTk2NDheQTJeQWpwZ15BbWU3MDE2NjIyODk@._V1_SX300.jpg"
}
}
]
},
"aggregations": {},
"facets": [
{
"name": "imdbrating_facet",
"stats": {
"min": 8.300000190734863,
"max": 8.800000190734863,
"avg": 8.550000190734863,
"sum": 17.100000381469727,
"count": 2
}
},
{
"name": "actors_facet",
"entries": [
{
"value": "Harrison Ford",
"count": 2
},
{
"value": "Carrie Fisher",
"count": 1
},
{
"value": "Edward James Olmos",
"count": 1
},
{
"value": "Mark Hamill",
"count": 1
},
{
"value": "Peter Cushing",
"count": 1
},
{
"value": "Rutger Hauer",
"count": 1
},
{
"value": "Sean Young",
"count": 1
}
]
},
{
"name": "directors_facet",
"entries": [
{
"value": "Steven Spielberg",
"count": 3
},
{
"value": "Andrew Davis",
"count": 1
},
{
"value": "George Lucas",
"count": 1
},
{
"value": "Irvin Kershner",
"count": 1
},
{
"value": "Richard Marquand",
"count": 1
},
{
"value": "Ridley Scott",
"count": 1
}
]
}
]
}
© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/search-application-client.html