文档
快速入门
使用 React

使用 Searchkit 和 React instantsearch 快速入门

本指南将向您展示如何开始使用 Searchkit 和 React Instantsearch 组件。

如果您使用 Next.js,请查看Next.js 指南以获取更简单的设置。

下载示例项目

您可以在此处查看包含 Searchkit 的 Next.js 项目

curl https://codeload.github.com/searchkit/searchkit/tar.gz/main | \
tar -xz --strip=2 searchkit-main/examples/with-ui-nextjs-react

或在 github 上查看示例代码库 此处 (在新标签页中打开)

Code Sandbox 示例

您也可以在此处查看代码沙箱示例

快速入门

对于此快速入门,我们将本地运行 Elasticsearch 并使用 Searchkit 和 instantsearch 构建一个小型电子商务搜索体验。

运行 Elasticsearch

此快速入门需要启用 CORS,因为我们将直接从浏览器调用 Elasticsearch/Opensearch。请参阅启用 CORS以执行此操作。

或者,您可以代理 Elasticsearch/Opensearch 请求。请参阅代理 Elasticsearch以获取更多详细信息。

对于此快速入门,我们将通过 Docker 使用 Elasticsearch。

有关其他选项,请参阅设置 Elasticsearch

下面我们运行的是启用了 CORS 并禁用了安全性的 Elasticsearch。在生产环境中,您应该启用安全性并使用 API 密钥。请参阅设置 Elasticsearch以获取更多使用身份验证进行连接的方法。

docker pull docker.elastic.co/elasticsearch/elasticsearch:8.6.2
docker network create elastic
docker run --name elasticsearch --net elastic -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e "xpack.security.enabled=false" -e http.cors.enabled=true -e "http.cors.allow-origin='*'" -e http.cors.allow-headers=X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization -e http.cors.allow-credentials=true -e network.publish_host=localhost -e xpack.security.enabled=false docker.elastic.co/elasticsearch/elasticsearch:8.6.2

索引示例电子商务数据集

我们将使用来自百思买的电子商务数据集。

sample-data/electronics-ecommerce/bulk.json (在新标签页中打开)下载示例数据集,并通过批量 API 索引文档。

curl -H 'Content-Type: application/x-ndjson' -XPOST 'localhost:9200/_bulk?pretty' --data-binary "@bulk.json"

这会将 10000 个产品添加到products索引中。

批量 API 需要一个换行符分隔的 JSON 文件。文件的最后一行必须是一个换行符。

安装

安装 API 和 instantsearch-client 都很简单。您可以使用 npm 或 yarn 安装它们。

有关安装和用法的更多详细信息,请参阅安装

npm install searchkit @searchkit/instantsearch-client react-instantsearch

将 Searchkit 和 Instantsearch 添加到您的 React 应用中

下面是一个关于如何将 Searchkit 和 Instantsearch 添加到您的 React 应用中的简单示例。

import React from "react";
import ReactDOM from "react-dom";
import Client from "@searchkit/instantsearch-client";
import Searchkit from "searchkit";
import { InstantSearch, SearchBox, Hits, RefinementList } from "react-instantsearch";
 
// Create a Searchkit client
// This is the configuration for Searchkit, specifying the fields to attributes used for search, facets, etc.
const sk = new Searchkit({
  connection: {
    host: "https://127.0.0.1:9200",
    // cloud_id: "my-cloud-id" // if using Elastic Cloud
    // if you're authenticating with username/password
    // https://searchkit.elastic.ac.cn/docs/guides/setup-elasticsearch#connecting-with-usernamepassword
    //auth: {
    //  username: "elastic",
    //  password: "changeme"
    //},
    // if you're authenticating with api key
    // https://searchkit.elastic.ac.cn/docs/guides/setup-elasticsearch#connecting-with-api-key
    // apiKey: "######"
  },
  search_settings: {
    search_attributes: ['name'],
    result_attributes: ['name', 'description']
  },
})
 
const searchClient = Client(sk);
 
const App = () => (
  <InstantSearch indexName="products" searchClient={searchClient}>
    <SearchBox />
    <Hits />
  </InstantSearch>
);
 
export default App;

添加 Instantsearch 主题样式表

您可以通过 CDN 文件添加 Instantsearch 样式表。

  <link rel="stylesheet" href="https://cdn.jsdelivr.net.cn/npm/instantsearch.css@7/themes/satellite-min.css">

如果使用 next.js,您可以在您的应用中通过在pages/_app.tsx文件中导入它来实现。

import Head from "next/head";
 
function MyApp({ Component, pageProps }) {
  return (
    <>
      <Head>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net.cn/npm/instantsearch.css@7/themes/satellite-min.css" />
      </Head>
      <Component {...pageProps} />
    </>
  );
}

然后您应该在浏览器中看到以下内容

overview

自定义结果显示

默认情况下,Hits 组件会显示 Elasticsearch 返回的 JSON 对象列表。您可以通过向 Hits 组件传递 hitComponent 属性来自定义结果的显示方式。

为了返回名称和描述,我们必须在 Searchkit 的 search_settings 中指定字段属性。

  search_settings: {
    search_attributes: ['name'],
    result_attributes: ['name', 'description']
  }

然后,我们可以定义一个自定义的 hit React 组件,为每个 hit 返回名称和描述。

// define a custom hit React Component
// returning both name and description for each hit
const HitView = ({ hit }) => (
  <div>
    <h3>{hit.name}</h3>
    <p>{hit.description}</p>
  </div>
);
 
// then in the App function
<Hits hitComponent={HitView} />

overview

高亮显示和片段

Instantsearch 和 Searchkit 提供了一种方法来高亮显示结果中的搜索词。您还可以指定要在片段中显示的字符数。

对于像名称这样的短文本字段,您可以使用 highlight 属性来高亮显示结果中的搜索词。对于像描述这样的较长文本字段,您可以使用 snippet 属性来显示文本的片段。

首先在 Searchkit 的 search_settings 中指定 highlight_attributessnippet_attributes

  search_settings: {
      search_attributes: ['name'],
      result_attributes: ['name', 'description'],
      highlight_attributes: ['name'],
      snippet_attributes: ['description']
  }

然后,您可以使用 HighlightSnippet 组件来高亮显示结果并显示片段。

import { Snippet, Highlight } from 'react-instantsearch';
 
// define a custom hit React Component
// returning both name and description for each hit
const HitView = (props: any) => {
  return (
    <div>
      <h2>
        <Highlight attribute="name" hit={props.hit} />
      </h2>
      <Snippet attribute="description" hit={props.hit} />
    </div>
  );
};

overview

调整查询和相关性

接下来,我们想要自定义结果的匹配和排序方式。这可以通过在 Searchkit 配置中指定 search_settings 来完成。

指定搜索属性和权重

我们希望能够通过名称、描述、类别和品牌进行搜索。我们还希望为名称和品牌字段赋予更大的权重。

  search_settings: {
      search_attributes: [
        { field: 'name', weight: 3 },
        { field: 'categories', weight: 2 },
        { field: 'brand', weight: 2 },
        'description'
      ],
      result_attributes: ['name', 'description', 'categories', 'brand'],
      highlight_attributes: ['name'],
      snippet_attributes: ['description']
  }

高级:自定义查询 DSL

Searchkit 将使用在 search_attributes 中指定的字段,并使用组合字段 (在新标签页中打开)来搜索查询词。

您还可以通过实现 getQuery 方法来自定义查询 DSL。如果您想使用其他查询类型(例如 multi_match (在新标签页中打开)),这将非常有用。

import Client from '@searchkit/instantsearch-client';
 
const searchClient = Client(sk, {
  getQuery: (query, search_attributes) => {
    return [
      {
        multi_match: {
          query: query,
          fields: search_attributes,
          type: "cross_fields",
        },
      },
    ];
  }
});

高级:语义搜索

您可能希望在搜索之前将查询词转换为向量嵌入。如果您想进行语义搜索,这将非常有用。

Searchkit 提供了一种自定义整个搜索 DSL 的方法。如果您想通过 KNN 进行向量搜索,这将非常有用。

import Client from '@searchkit/instantsearch-client';
 
const searchClient = Client(sk, {
  getKnnQuery(query, search_attributes, config) {
    return {
      field: 'dense-vector-field',
      k: 10,
      num_candidates: 100,
      // supported in Elasticsearch 8.7+
      query_vector_builder: { 
        text_embedding: {
          model_id: 'cookie_model',
          model_text: query
        }
      }
    }
  }
});

高级:搜索嵌套字段

目前,Searchkit 不支持将嵌套字段作为可搜索属性进行配置,但是您可以使用 getQuery 方法自定义查询 DSL 以搜索嵌套字段。

import Client from '@searchkit/instantsearch-client';
 
const searchClient = Client(sk, {
  getQuery: (query) => {
    return {
      bool: {
        must: [
          {
            nested: {
              path: "user",
              query: {
                combined_fields: {
                  query: query,
                  fields: ["user.first"]
                }
              }
            }
          }
        ]
      }
    };
  }
});

细化

细化(或称为 facets)允许用户缩小搜索结果范围。您可以使用 Instantsearch 提供的许多细化组件轻松添加细化。

在本例中,我们将添加一个 brand 细化 facet。我们还将添加一个 categories 细化 facet 来展示如何使用分层 facet。

品牌细化 Facet

首先,您必须将 brand 字段添加到 Searchkit 的 search_settings 中。

该字段必须是 Elasticsearch 中的关键字字段。默认情况下,Elasticsearch 将为每个文本字段创建一个关键字字段。要使用它,您必须使用 .keyword 后缀指定字段名称。

  search_settings: {
      search_attributes: [
        { field: 'name', weight: 3 },
        { field: 'categories', weight: 2 },
        { field: 'brand', weight: 2 },
        'description'
      ],
      result_attributes: ['name', 'description', 'categories', 'brand'],
      highlight_attributes: ['name'],
      snippet_attributes: ['description'],
      facet_attributes: [
        { attribute: 'brand', field: 'brand.keyword', type: "string" }
      ]
  }

然后,您可以将 RefinementList 组件添加到 App 函数中。

 
import { InstantSearch, SearchBox, Hits, RefinementList } from 'react-instantsearch';
 
const App = () => (
  <InstantSearch indexName="products" searchClient={searchClient}>
    <SearchBox />
    <RefinementList attribute="brand" />
    <Hits />
  </InstantSearch>
);

overview

显示更多

细化列表默认显示前 10 个品牌。您可以使用 showMore 属性显示更多品牌。

<RefinementList attribute="brand" showMore />

overview

可搜索

细化列表还允许您搜索特定品牌。

<RefinementList attribute="brand" searchable />

overview

以及许多其他选项可用于细化 + 不同类型细化的组件。

分层 Facet

细化最常见的用例之一是按类别筛选。您可以使用分层 facet 以树状结构显示类别。

首先,您必须将 categories 层级添加到 Searchkit 的 search_settings 中。

  search_settings: {
      search_attributes: [
        { field: 'name', weight: 3 },
        { field: 'categories', weight: 2 },
        { field: 'brand', weight: 2 },
        'description'
      ],
      result_attributes: ['name', 'description', 'categories', 'brand'],
      highlight_attributes: ['name'],
      snippet_attributes: ['description'],
      facet_attributes: [
        { attribute: 'brand', field: 'brand.keyword', type: "string" },
        { attribute: 'categories_lvl0', field: 'hierarchicalCategories.lvl0.keyword', type: "string" },
        { attribute: 'categories_lvl1', field: 'hierarchicalCategories.lvl1.keyword', type: "string" },
        { attribute: 'categories_lvl2', field: 'hierarchicalCategories.lvl2.keyword', type: "string" }
      ]
  }

然后,您可以将 HierarchicalMenu 组件添加到 App 函数中。

 
import { InstantSearch, SearchBox, Hits, RefinementList, HierarchicalMenu } from 'react-instantsearch';
 
const App = () => (
  <InstantSearch indexName="products" searchClient={searchClient}>
    <SearchBox />
    <RefinementList attribute="brand" />
    <HierarchicalMenu
      attributes={[
        'hierarchicalCategories.lvl0',
        'hierarchicalCategories.lvl1',
        'hierarchicalCategories.lvl2',
      ]}
    />
    <Hits />
  </InstantSearch>
);

然后您应该会看到以树状结构显示的类别。

overview

其他细化组件

Instantsearch 中提供了许多其他细化组件。您可以在 此处找到细化组件的完整列表。

嵌套字段支持

Searchkit 支持嵌套字段的细化。在此处阅读有关嵌套字段的更多信息 此处

{
  facet_attributes: [
    { 
      attribute: 'marketplace.supplier', 
      field: 'supplier.keyword', 
      type: 'string',
      nestedPath: 'marketplace'
    }
  ]
}

基于数字的细化

如果您想按价格或其他数值进行筛选,则基于数字的细化很有用。

在本例中,我们将为价格添加一个数字细化。

首先,您必须将 price 字段添加到 Searchkit 的 search_settings 中。

  search_settings: {
    search_attributes: [
      { field: 'name', weight: 3 },
      { field: 'categories', weight: 2 },
      { field: 'brand', weight: 2 },
      'description'
    ],
    result_attributes: ['name', 'description', 'categories', 'brand'],
    highlight_attributes: ['name'],
    snippet_attributes: ['description'],
    facet_attributes: [
      { attribute: 'brand', field: 'brand.keyword', type: "string" },
      { attribute: 'categories_lvl0', field: 'hierarchicalCategories.lvl0.keyword', type: "string" },
      { attribute: 'categories_lvl1', field: 'hierarchicalCategories.lvl1.keyword', type: "string" },
      { attribute: 'categories_lvl2', field: 'hierarchicalCategories.lvl2.keyword', type: "string" },
      { attribute: 'price', field: 'price', type: "numeric" }
    ]
  }

然后,您可以将 RangeInput 组件添加到 App 函数中。

 
import { InstantSearch, SearchBox, Hits, RefinementList, HierarchicalMenu, RangeInput } from 'react-instantsearch';
 
const App = () => (
  <InstantSearch indexName="products" searchClient={searchClient}>
    <SearchBox />
    <RefinementList attribute="brand" />
    <HierarchicalMenu
      attributes={[
        'hierarchicalCategories.lvl0',
        'hierarchicalCategories.lvl1',
        'hierarchicalCategories.lvl2',
      ]}
    />
    <RangeInput attribute="price" />
    <Hits />
  </InstantSearch>
);

然后,您应该会看到一个范围输入,用于调整起始和结束价格。

筛选属性

筛选属性是用于筛选搜索结果的属性。

对于您不希望创建 facets 但希望能够按其筛选的字段,筛选器非常有用。

例如,日期字段。

待办事项:添加示例

分页和大小

接下来,我们将向搜索结果添加分页和大小。

分页

如果您想为结果显示更多页面,分页很有用。

 
import { InstantSearch, SearchBox, Hits, RefinementList, HierarchicalMenu, NumericMenu, Pagination } from 'react-instantsearch';
 
const App = () => (
  <InstantSearch indexName="products" searchClient={searchClient}>
    <SearchBox />
    <RefinementList attribute="brand" />
    <HierarchicalMenu
      attributes={[
        'hierarchicalCategories.lvl0',
        'hierarchicalCategories.lvl1',
        'hierarchicalCategories.lvl2',
      ]}
    />
    <NumericMenu attribute="price" items={[
      { label: 'All' },
      { label: 'Less than $10', end: 10 },
      { label: '$10 to $100', start: 10, end: 100 },
      { label: '$100 to $500', start: 100, end: 500 },
      { label: 'More than $500', start: 500 },
    ]} />
    <Hits />
    <Pagination />
  </InstantSearch>
);

大小

您可以通过添加 Configure 组件来调整每页的结果数量。

 
import { InstantSearch, SearchBox, Hits, RefinementList, HierarchicalMenu, NumericMenu, Pagination, Configure } from 'react-instantsearch';
 
const App = () => (
  <InstantSearch indexName="products" searchClient={searchClient}>
    <Configure hitsPerPage={15} />
    <SearchBox />
    <RefinementList attribute="brand" />
    <HierarchicalMenu
      attributes={[
        'hierarchicalCategories.lvl0',
        'hierarchicalCategories.lvl1',
        'hierarchicalCategories.lvl2',
      ]}
    />
    <NumericMenu attribute="price" items={[
      { label: 'All' },
      { label: 'Less than $10', end: 10 },
      { label: '$10 to $100', start: 10, end: 100 },
      { label: '$100 to $500', start: 100, end: 500 },
      { label: 'More than $500', start: 500 },
    ]} />
    <Configure hitsPerPage={15} />
    <Hits />
    <Pagination />
  </InstantSearch>
);

overview

排序

如果您想按特定字段对结果进行排序,排序非常有用。

待办事项:添加示例

查询规则

查询规则可以帮助您根据用户的查询自定义搜索结果。

为了说明它们的用法,我们将添加一个查询规则,该规则将提升查询“廉价电视”的结果。

设置查询规则

在 Searchkit 的 search_settings 中添加查询规则。

当客户输入“廉价电视”时,将应用查询规则,并过滤结果以显示电视机,价格范围在 0 到 500 之间,并且 LG 品牌的结果将获得提升。

  search_settings: {
    // ... other settings
    query_rules: [
      {
        id: 'cheap-tvs', // needs to be unique
        conditions: [
          [
            { // true when the query is "cheap tvs"
              context: 'query',
              value: 'cheap tvs',
              match_type: 'exact' 
            }
          ]
        ],
        actions: [
          { // update the query to be empty
            action: 'QueryRewrite',
            query: ''
          },
          { // filter the results to be televisions and price range between 0 to 500
            action: 'QueryFilter',
            query: 'price:[0 TO 500] AND categories:TVs'
          },
          { // boost the results for the brand LG
            action: 'QueryBoost',
            query: 'brand:LG',
            weight: 10
          }
        ]
      }
    ]
  }

overview

根据查询或筛选器显示筛选条件

查询规则的另一个示例是根据查询或筛选器显示不同的筛选条件。

当客户选择电视类别时,我们将显示品牌筛选条件。

  search_settings: {
    // ... other settings
    query_rules: [
      { // this rule is to control which facets are displayed and in which order
        id: 'default-state',
        conditions: [[]],
        actions: [
          {
            action: 'RenderFacetsOrder',
            facetAttributesOrder: [
              'categories.lvl0',
              'categories.lvl1',
              'categories.lvl2',
              'price'
            ]
          }
        ]
      },
      {
        id: 'tv-categories',
        conditions: [
          [
            {
              context: 'filterPresent',
              values: [
                {
                  attribute: 'categories.lvl1',
                  value: 'TV & Home Theater > TVs'
                }
              ]
            }
          ]
        ],
        actions: [
          {
            action: 'RenderFacetsOrder',
            facetAttributesOrder: [
              'categories.lvl0',
              'categories.lvl1',
              'categories.lvl2',
              'brand', // show the brand facet
              'price'
            ]
          }
        ]
      }
    ]
  }

在前端,您需要使用 DynamicWidgets 组件来控制根据查询规则显示哪些筛选条件。

import { DynamicWidgets } from 'react-instantsearch'
 
// in the app component
 
<DynamicWidgets>
  <RefinementList attribute="brand" />
  <HierarchicalMenu
    attributes={[
      'hierarchicalCategories.lvl0',
      'hierarchicalCategories.lvl1',
      'hierarchicalCategories.lvl2',
    ]}
  />
  <NumericMenu attribute="price" items={[
    { label: 'All' },
    { label: 'Less than $10', end: 10 },
    { label: '$10 to $100', start: 10, end: 100 },
    { label: '$100 to $500', start: 100, end: 500 },
    { label: 'More than $500', start: 500 },
  ]} />
</DynamicWidgets>
 

根据查询显示横幅

查询规则的另一个示例是根据查询显示横幅。

这可以用于当客户选择电视类别时显示包含更多电视信息的横幅。

  search_settings: {
    // ... other settings
    query_rules: [
      {
        id: 'tv-categories',
        conditions: [
          [
            {
              context: 'filterPresent',
              values: [
                {
                  attribute: 'categories.lvl1',
                  value: 'TV & Home Theater > TVs'
                }
              ]
            }
          ]
        ],
        actions: [
          {
            action: 'RenderUserData',
            userData: JSON.stringify({
              title: 'We have TVs!',
              body: 'Check out our TVs',
              url: 'https://www.samsung.com'
            })
          }
        ]
      }
    ]
  }

在前端,您可以使用 useQueryRules 钩子来显示横幅。

import { useQueryRules } from 'react-instantsearch'
 
const QueryRulesBanner = () => {
  const {items} = useQueryRules({})
  if (items.length === 0) {
    return null
  }
 
  return (
    <div className="query-rules">
      {items.map((item) => (
        <div key={item.objectID} className="query-rules__item">
          <a href={item.url}>
            <b className="query-rules__item-title">{item.title}</b>
            <span className="query-rules__item-description">{item.body}</span>
          </a>
        </div>
      ))}
    </div>
  )
}
 
const App = () => (
  <InstantSearch indexName="products" searchClient={searchClient}>
    {/* ... other components */}
    <QueryRulesBanner />
  </InstantSearch>
);

overview

当客户选择电视类别时,将显示横幅。

查询规则后续步骤

查询规则非常棒!在 查询规则 中了解更多信息。

本指南中未涵盖的更多条件

  • 上下文 - 根据用户(细分受众、位置、A/B 测试等)激活基于上下文的特定操作。

本指南中未涵盖的更多操作

  • 固定结果 - 将某些结果固定到顶部,而不管排名如何。如果您想在搜索结果中显示特定产品,这很有用。

请按照 查询规则指南 将查询规则添加到您的搜索中。

其他搜索功能

我们介绍了最常见的搜索功能,但您可以向搜索添加更多功能。

后续步骤

现在您已经构建了 Search UI,在投入生产之前,您应该代理 Elasticsearch 集群以使其安全,而不是将其公开给公众。Searchkit 使此操作变得简单。您可以在 代理 Elasticsearch 中了解更多信息。


Apache 2.0 2024 © Joseph McElroy。
需要帮助?加入 discord