入门
在本演练中,我们将开始使用 Next.js 构建搜索体验。
您不需要使用 Next.js 才能使用 Searchkit,但这是最简单的入门方法。
在本演练中,我们将
- 设置一个 api 路由以从 Elasticsearch 获取结果
- 使用 React InstantSearch 显示结果 🎉
下载示例项目
您可以在此处查看带有 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 示例
您也可以在此处查看代码沙箱示例
创建 Next.js 应用
本教程将使用新的 Next.js App Router。如果您使用的是 pages,请在遵循本教程时牢记这一点。
首先,我们需要创建一个 Next.js 应用。我们可以通过运行以下命令来实现
npx create-next-app@latest
并按照说明操作。
导航到新创建的目录。
安装依赖项
接下来,我们需要为该项目安装依赖项
npm install @searchkit/instantsearch-client @searchkit/api react-instantsearch
设置 Node API
在 app/api/search
目录中创建一个名为 route.ts
的新文件,并添加以下代码
import Client from "@searchkit/api";
import { NextRequest, NextResponse } from 'next/server'
const apiConfig = {
connection: {
host: "<replace-with-your-elasticsearch-host>",
// if you are authenticating with an api key
// https://searchkit.elastic.ac.cn/docs/guides/setup-elasticsearch#connecting-with-api-key
// apiKey: '###'
// if you are authenticating with a username/password combo
// https://searchkit.elastic.ac.cn/docs/guides/setup-elasticsearch#connecting-with-usernamepassword
// auth: {
// username: "elastic",
// password: "changeme"
// },
},
search_settings: {
highlight_attributes: ["title", "actors"],
search_attributes: ["title", "actors"],
result_attributes: ["title", "actors"],
facet_attributes: ["type", "rated"],
},
};
const apiClient = Client(apiConfig);
export async function POST(req: NextRequest, res: NextResponse) {
const data = await req.json()
const results = await apiClient.handleRequest(data)
return NextResponse.json(results)
}
将 host 和 apiKey 替换为您自己的 Elasticsearch 主机和 API 密钥。apiKey 是可选的,但在生产环境中建议使用。您可以在 此处 (在新标签页中打开) 找到有关 API 密钥的更多信息。
这将在 /api/search
路径下设置一个新的 Next.js 路由处理程序 (在新标签页中打开)。此路由将处理搜索请求并使用 InstantSearch Elasticsearch 适配器来处理请求。然后将响应返回给客户端。
有关 API 配置的更多信息,请参阅 API 配置 文档。
设置前端
现在我们已经设置了 API,我们可以开始构建前端了。我们将使用 react-instantsearch (在新标签页中打开) 来构建搜索体验。
首先,我们需要在 app
目录中创建一个新文件(如果不存在),名为 page.tsx
,并添加以下代码
import { InstantSearch, SearchBox, Hits } from "react-instantsearch";
import createClient from "@searchkit/instantsearch-client";
const searchClient = createClient({
url: "/api/search",
});
export default function Search() {
return (
<InstantSearch
searchClient={searchClient}
indexName="<elasticsearch index or alias name>"
>
<SearchBox />
<Hits />
</InstantSearch>
);
}
Instantsearch 将使用 searchClient
向我们之前创建的 API 发出请求。 indexName
是我们要搜索的索引的名称。
运行应用
现在我们已经设置好了一切,我们可以运行该应用并查看搜索体验。
npm run dev
IMAGE1
可搜索属性
现在我们已经设置了搜索体验,我们可以添加其他搜索功能了。
调整搜索字段
我们可以通过更新search_attributes
在 apiConfig
对象中的 app/api/search/route.ts
文件来调整搜索字段。
search_attributes: ["title^3", "actors", "plot"],
上面我们将标题的权重提升了 3 倍。这意味着标题将比其他字段具有更高的权重。这将确保标题在搜索结果中具有更高的重要性。
覆盖默认查询
我们可以选择通过在 handleRequest
方法(在 app/api/search/route.ts
文件中调用)中实现 getQuery
函数来覆盖默认搜索查询。
此函数将接收查询,并返回将用于搜索索引的 Elasticsearch 查询。
const results = await apiClient.handleRequest(body, {
getQuery: (query, search_attributes) => {
return [
{
combined_fields: {
query,
fields: search_attributes,
},
},
];
},
});
自定义结果命中
我们可以添加一个自定义命中组件来显示结果。我们可以在 components
目录中创建一个名为 Hit.ts
的新文件,并添加以下代码
下面我们使用来自 react-instantsearch
的 Highlight
组件来突出显示标题和演员字段中的搜索词。
import { Highlight } from "react-instantsearch";
const hitView = (props) => {
return (
<div>
<h2>
<Highlight hit={props.hit} attribute="title" />
</h2>
<br />
<Highlight hit={props.hit} attribute="actors" />
</div>
);
};
我们需要将 attribute
属性传递给 highlight_attributes
配置,以告知要为哪些字段提供突出显示选项。
highlight_attributes: ["title", "actors"],
然后,我们可以在 app/page.tsx
文件中导入 Hit
组件,并将其传递给父 Hits
组件。
import Hit from "../components/Hit";
export default function Search() {
return (
<InstantSearch searchClient={searchClient} indexName="movies">
<SearchBox />
<Hits hitComponent={Hit} />
</InstantSearch>
);
}
分面
添加细化列表分面
首先更新 apiConfig
对象(在 app/api/search/route.ts
文件中),以添加 type
分面。
facet_attributes: [{ attribute: "type", "type": "string" }],
这假设索引中存在一个 type
字段,该字段是 keyword
类型字段。
如果该字段是 text
类型字段,则可以定义并使用 type.keyword
子字段。
facet_attributes: [{ attribute: "type", field: "type.keyword", type: "string" }],
然后,我们可以将 RefinementList
组件添加到 pages/search.js
文件中。
import {
InstantSearch,
SearchBox,
Hits,
RefinementList,
} from "react-instantsearch";
export default function Search() {
return (
<InstantSearch searchClient={searchClient} indexName="movies">
<SearchBox />
<RefinementList attribute="type" />
<Hits hitComponent={Hit} />
</InstantSearch>
);
}
使其可搜索
默认情况下,RefinementList
组件将显示分面中的所有值。我们可以通过添加 searchable
属性使其可搜索。
<RefinementList attribute="type" searchable />
添加基于数字范围的分面
首先更新 apiConfig
对象(在 app/page.tsx
文件中),以添加 imdbrating
分面。这需要 imdbrating
字段在 Elasticsearch 索引中为数字类型字段,例如 float
。
facet_attributes: [
{ attribute: "imdbrating", type: "numeric" },
{ attribute: "type", field: "type.keyword", type: "string" }
],
然后,我们可以将 RangeInput
组件添加到 app/page.tsx
文件中。
import {
InstantSearch,
SearchBox,
Hits,
RangeInput,
} from "react-instantsearch";
<RangeInput
attribute="imdbrating"
/>;
服务器端渲染
下面我们添加以下额外的导入
- 来自
react-instantsearch
的getServerState
函数 - 来自
react-dom/server
的renderToString
函数 - 来自
react-instantsearch
的InstantSearchServerState
和InstantSearchSSRProvider
组件 - 来自
react-instantsearch-router-nextjs
的createInstantSearchRouterNext
函数 - 来自
next/router
的singletonRouter
然后,我们将 InstantSearch
组件包装在 InstantSearchSSRProvider
组件中,并将 serverState
属性传递给它。
这使我们能够在服务器上渲染搜索体验并将初始状态发送到客户端。这将使搜索体验加载速度更快,并提高 SEO。
import {
InstantSearch, SearchBox, Hits, RefinementList, RangeInput,
InstantSearchServerState, InstantSearchSSRProvider, getServerState
} from 'react-instantsearch';
import { renderToString } from 'react-dom/server';
import Client from '@searchkit/instantsearch-client'
import { GetServerSideProps } from 'next';
import { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs';
import singletonRouter from 'next/router';
type WebProps = {
serverState?: InstantSearchServerState;
url?: string;
serverUrl?: string;
};
export default function Web({ serverState, url, serverUrl }: WebProps) {
const searchClient = Client({
url: serverUrl + '/api/product-search',
});
return (
<InstantSearchSSRProvider {...serverState}>
<div className="ais-InstantSearch">
<InstantSearch searchClient={searchClient} indexName="movies">
<SearchBox />
<RefinementList attribute="type" searchable />
<RangeInput attribute="imdbrating" />
<Hits hitComponent={Hit} />
</InstantSearch>
</div>
</InstantSearchSSRProvider>
);
}
export const getServerSideProps: GetServerSideProps<WebProps> =
async function getServerSideProps({ req }) {
const protocol = req.headers.referer?.split('://')[0] || 'http';
const serverUrl = `${protocol}://${req.headers.host}`;
const url = `${protocol}://${req.headers.host}${req.url}`;
const serverState = await getServerState(<Web url={url} serverUrl={serverUrl} />, {
renderToString,
});
return {
props: {
serverState,
url,
serverUrl
},
};
};
总结
我们使用 Elasticsearch 和 Algolia InstantSearch 从头开始快速构建了一个非常不错的搜索体验。我们还学习了如何通过调整搜索字段和覆盖默认 Elasticsearch 查询来自定义搜索体验。