使用搜索应用程序客户端构建搜索体验

编辑

使用搜索应用程序客户端构建搜索体验

编辑

本文档是关于使用 搜索应用程序搜索应用程序客户端 构建搜索体验的操作指南。该客户端是一个旨在在浏览器中使用的 JavaScript 库。您将把此库集成到您的 Web 应用程序中,以简化对搜索应用程序的查询。

有一个 沙盒环境 可用于测试和实验 search-application-client 库。如果您想在不设置自己的 Web 应用程序的情况下尝试客户端,请跳转到那里。

克隆 存储库 并按照 README 中的说明开始操作。

目标

编辑

本指南假设您要构建具有以下搜索功能的 Web 应用程序

  • 具有自定义相关性的搜索栏和结果
  • 对结果的呈现方式进行控制,例如包含/排除字段以及突出显示匹配的术语
  • 诸如 facets、过滤器、排序、分页之类的 UI 控件

您可以将搜索应用程序视为持久化 Elasticsearch 更改的“服务器端”。您的 Web 应用程序充当查询搜索应用程序的“客户端”。您将对搜索应用程序和 Web 应用程序进行编辑以完成实现。

先决条件

编辑

要遵循本指南,您需要

  • 一个 Elastic 部署,它满足运行搜索应用程序的 先决条件

    • 如果您没有 Elastic 部署,请在 Elastic Cloud 上开始免费试用。
  • 一个 搜索应用程序

    • Kibana UI 中或使用 API 创建和管理搜索应用程序。
  • 一个 Web 应用程序,用于使用 搜索应用程序客户端 查询您的搜索应用程序。

安装和配置客户端

编辑

安装客户端

编辑

安装 使用 npm、yarn 或 CDN 的客户端。

选项 1:使用包管理器

要使用 npm 安装客户端,请运行以下命令

npm install @elastic/search-application-client

要使用 yarn 安装客户端,请运行以下命令

yarn add @elastic/search-application-client

选项 2:使用带有 HTML <script> 标签的 CDN

或者,您可以使用 CDN 安装客户端。将以下 <script> 标签添加到 Web 应用程序的 HTML 中

<script src="https://cdn.jsdelivr.net.cn/npm/@elastic/search-application-client@latest"></script>

导入和初始化客户端

编辑

安装完成后,您可以将客户端导入到您的 Web 应用程序中。您需要以下信息来初始化客户端

  • 您的搜索应用程序的 名称
  • 您的搜索应用程序的 URL 端点
  • 您的搜索应用程序的 API 密钥

在 Kibana UI 的 连接 页面上查找此信息。

选项 1:使用 JavaScript 模块
编辑

使用以下导入语句

import SearchApplicationClient from '@elastic/search-application-client';

使用您的部署详细信息配置客户端以开始发出搜索请求。您可以在 Kibana UI 的 连接 页面上生成 API 密钥。转到 搜索 > 搜索应用程序 > <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>

使用您的部署详细信息配置客户端以开始发出搜索请求。您可以在 Kibana UI 的 连接 页面上生成 API 密钥。转到 搜索 > 搜索应用程序 > <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 管理模板时,我们提供使用 Kibana 控制台 语法的 API 示例。

这是一个模板示例

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
  • 分页:fromsize

搜索功能

编辑

我们将探讨搜索体验所需的所有基本要素。您将学习如何使用搜索应用程序实现它们,并使用客户端查询它们。

有关可用方法及其参数的信息,请参阅 客户端存储库

自定义相关性

编辑

我们的简单模板使用跨所有字段的 query_string 搜索,但这可能不适合您的用例。您可以更新模板以提供更好的相关性召回。

在下面的示例中,我们正在对我们的模板使用 multi-match 查询,其中 best_fieldsphrase_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                \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                \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,您可以指定要突出显示哪些字段的匹配项。

在以下示例中,我们将 titleplot 指定为突出显示的字段。 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                \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                \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。sizefrom 参数允许您控制响应中返回的页面和命中次数。

我们可以使用客户端的 setSizesetFrom 方法来实现这一点。

// 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 方法将分面添加到您的查询中。

在以下示例中,我们只想返回电影

  • 由哈里森·福特担任演员
  • 由乔治·卢卡斯雷德利·斯科特执导
  • IMBD 评分大于 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
        }
      ]
    }
  ]
}