如何构建带范围的搜索建议和搜索查询更正
您只有一次机会通过提供相关的搜索结果来让购物者留在您的电商网站上。根据 Harris Poll 的调查,76% 的在线购物者在零售网站搜索失败后都会弃用这个网站。
因此,优化您的搜索体验至关重要,只有这样买家才能快速找到他们需要的信息。这就是现代搜索体验背后的原理:现在,仅仅提供一个能够返回匹配产品的搜索框是不够的。您的电商网站必须包括一整套搜索工具,从而通过个性化方式将用户引导至他们想要的产品。
在本篇博文中,您将学习如何通过添加两个常见功能来开始优化电商网站的搜索体验,这两个功能是:带范围的建议和查询更正(又称为“您是不是要找?”)。
请注意下面的示例是使用 Elastic 8.5 进行测试的。
带范围的建议
电子商务零售商面临的一个常见挑战是包含很多不同品类的庞大产品目录。在这种情况下,很难通过解析一个简单的查询就了解用户感兴趣的可能是什么领域或品类。举个例子,如果用户在电子产品零售网站上搜索“纯平”,他们可能会在电视、电视柜、电脑显示器等品类下都看到结果。如果用户要找的是电视,则向他们展示纯平电视柜对于他们在购买旅程中的这一阶段可能就会毫无意义。
带范围的建议可以通过在特定品类或品牌内建议查询,帮助用户将搜索限定在他们最感兴趣的主题上。
这是一个很强大的功能,能够帮助用户快速看到更精准的结果集。而且,使用 Search UI 和 Elastic 实施带范围的搜索建议也很简单。下一部分将会为您一步步演示如何构建。
收集建议相关数据
如在上面的示例中所述,在用户输入时,系统将会向他们展示建议的查询和相关的范围。为了构建这些建议,很常见的一种做法是使用分析数据来收集最常见的查询以及用户通常会将此查询关联至什么范围。
举个例子,假设在查看了我们电商网站的分析数据后,我们发现热门查询是“lcd tv”(LCD 电视),而且用户最经常点击的产品属于“TV & Home Theater”(电视和家庭影院)品类。
使用这一数据,我们可以构建一个表示相应的带范围建议的文档,就像下面:
{
"name": "lcd tv",
"weight": 112,
"category": {
"name": "TV & Home Theater",
"value": 111
}
}
此文档包含一个查询和一个相关品类。如有需要,还可以更复杂一些;例如,我们可以拥有一个品类列表,或者添加其他范围,好比“brands”(品牌)。
然后我们可以在 Elasticsearch 中创建专属索引,这里我们将它命名为“suggest”,用此索引来存储我们使用分析数据构建的带范围建议的列表。既可以使用数据转换实现这一点,也可以使用自定义代码(例如 Python 脚本)实现这一点。对于我们的索引“suggest”,其索引映射示例 位于此处。
自动完成 (autocomplete) 建议
现在已经拥有了准备就绪可供使用的建议列表,接下来我们需要将它添加到我们的搜索体验中。
使用 Search UI 的话,只需几分钟就能在 Elastic 的基础上构建搜索体验。然后我们可以使用新构建的界面作为我们带范围建议的基础。
Search UI 会使用配置对象来量身定制您的搜索以满足您的需求。下面是建议配置所对应的配置对象的一个片段。在这种情况下,我们指定要查询的索引和字段,以及作为结果的一部分要返回的字段:
suggestions: {
types: {
popularQueries: {
search_fields: {
"name.suggest": {} // fields used to query
},
result_fields: {
name: {
raw: {}
},
"category.name": {
raw: {}
}
},
index: "suggest",
queryType: "results"
}
},
size: 5
}
然后我们配置 SearchBox 组件,以便在导航至搜索结果页面时将品类作为查询的一部分进行传递:
// Search bar component
<SearchBox
onSelectAutocomplete={(suggestion, config, defaultHandler) => {
// User selects a scoped suggestion - Category
if (suggestion.name && suggestion.category) {
const params = { q: suggestion.name.raw, category: suggestion.category.raw.name };
// Navigate to search result page with category passed as parameters
navigate({
pathname: '/search',
search: `?${createSearchParams(params)}`,
});
// User selects normal suggestion
} else if (suggestion) {
// Navigate to search result page
window.location.href = "/search?q=" + suggestion.suggestion;
}
defaultHandler(suggestion);
}}
autocompleteSuggestions={{
popularQueries: {
sectionTitle: "Popular queries",
queryType: "results",
displayField: "name",
categoryField: "category"
}
}}
autocompleteView={AutocompleteView}
/>
请注意,我们传递了一个 AutocompleteView 函数来对自动完成 (autocomplete) 视图进行定制,这样我们就能同时展示类别和所建议的查询了。
下面是 AutocompleteView 函数的一个代码片段,显示了如何展示带关联范围的查询:
{suggestions.slice(0,1).map((suggestion) => {
index++;
const suggestionValue = getDisplayField(suggestion)
const suggestionScope = getCategoryField(suggestion)
return (
<li
{...getItemProps({
key: suggestionValue,
index: index - 1,
item: {
suggestion: suggestionValue,
...suggestion.result
}
})}
>
<span>{suggestionValue}</span>
<ul><span style={{marginLeft: "20px"}}>in {suggestionScope}</span></ul>
</li>
);
})}
然后在搜索结果页面上,我们只需处理查询参数并过滤结果集合:
const [searchParams] = useSearchParams();
useEffect(() => {
if (searchParams.get('category')) addFilter("department", [searchParams.get('category')], "all")
}, [searchParams]);
将一切都搞定后,结果就像下面这样:
查询更正,亦称为“您是不是要找?”
当购物者由于查询词选择不当或者拼写错误而不能看到相关的搜索结果时,会变得十分沮丧。为了避免不能返回任何结果或者仅返回极少结果,最佳实践之一是建议更好的查询,通常称为“您是不是要找?”
有很多种不同方法可以实施此功能,但使用 Search UI 和 Elastic 是最为直接的构建方法。
分析数据
我们构建“您是不是要找?”功能所用的数据集与我们在实施带范围的建议时所用的数据集相似,所以您可以在用户的查询未返回结果时向用户建议热门查询。
对于“您是不是要找?”使用相似的文档结构
{
"name": "lcd tv",
"weight": 112,
"category": {
"name": "TV & Home Theater",
"value": 111
}
}
这一步骤的关键是数据在 Elasticsearch 中的索引方式。为了提供相关的建议,您需要使用由 Elasticsearch 提供的具体功能,例如定制分析器。
下面是在我们的示例中所用的索引设置和映射:
{
"settings":
{
"index":
{
"number_of_shards": 1,
"analysis":
{
"analyzer":
{
"trigram":
{
"type": "custom",
"tokenizer": "standard",
"filter":
[
"lowercase",
"shingle"
]
},
"reverse":
{
"type": "custom",
"tokenizer": "standard",
"filter":
[
"lowercase",
"reverse"
]
}
},
"filter":
{
"shingle":
{
"type": "shingle",
"min_shingle_size": 2,
"max_shingle_size": 3
}
}
}
}
},
"mappings":
{
"properties":
{
"category":
{
"properties":
{
"name":
{
"type": "text",
"fields":
{
"keyword":
{
"type": "keyword",
"ignore_above": 256
}
}
},
"value":
{
"type": "long"
}
}
},
"name":
{
"type": "search_as_you_type",
"doc_values": "false",
"max_shingle_size": 3,
"fields":
{
"reverse":
{
"type": "text",
"analyzer": "reverse"
},
"suggest":
{
"type": "text",
"analyzer": "trigram"
}
}
},
"weight":
{
"type": "rank_feature",
"fields":
{
"numeric":
{
"type": "integer"
}
}
}
}
}
}
请参考提示器文档深入了解为什么使用特定设置和映射来检索相关的建议。
为搜索做准备
现在您的索引已准备就绪,您可以开始搜索体验部分了。为了基于查询检索建议,需要利用 Elasticsearch API。如果不想在客户端添加太多复杂性,您可以在 Elasticsearch 中创建一个包含查询的搜索模板。然后从客户端,您只需要执行搜索模板:
PUT _scripts/did-you-mean-template
{
"script": {
"lang": "mustache",
"source": {
"suggest": {
"text": "{{query_string}}",
"simple_phrase": {
"phrase": {
"field": "name.suggest",
"size": 1,
"direct_generator": [
{
"field": "name.suggest",
"suggest_mode": "always"
},
{
"field": "name.reverse",
"suggest_mode": "always",
"pre_filter": "reverse",
"post_filter": "reverse"
}
]
}
}
}
}
}
}
从应用程序获取建议
为了从 Elasticsearch 获得建议,我们添加了一个后端 API,客户端可在 /api/suggest 上使用此 API。这一新 API 会调用 Elasticsearch,执行搜索模板,并传递您想要为其提供建议的查询,然后返回查询建议。后端 API 允许您降低前端部分的复杂性。
client.searchTemplate({
index: "suggest",
id: 'did-you-mean-template',
params: {
query_string: req.body.query
}
})
例如,如果查询是“spakers”,API 将会返回建议“speakers”。 这是基于之前采集的包含最热门查询的数据而得出的。此 suggest API 将会返回一个在语义上与查询最接近的词汇。
向结果页面添加“您是不是要找?”
现在我们可以向前端应用程序添加“您是不是要找”功能。对于这一部分,我们需要继续使用我们在带范围建议部分所用的同一 React 应用程序。
我们添加的新 API 可以从 React 应用程序进行使用,并且如果当前查询没有结果,会显示一条建议。
这里的想法是针对用户输入的每条查询都获得一条建议。如果没有结果,则用户将会看到一条建议。其他实施方法也是可以的:如果结果数量太少,您可以显示建议,或者您可以直接替代用户的查询,自动执行建议。
在我们的应用程序中,有一个名为 SearchResults 的 React 组件,该组件可显示搜索结果。在那里,我们可以添加一个函数,从我们的后端 API /cn/api/suggest 获取建议。
const fetchDidYouMeanSuggestion = async (query) => {
const response = await fetch('/api/did_you_mean', {
method: 'POST',
headers: {
Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/json',
},
body: JSON.stringify(query)
})
const body = await response.json();
if (response.status !== 200) throw Error(body.message);
return body;
}
然后随着查询的变化,您可以根据用户的微调,通过调用此 API 来刷新建议:
// Get search params from dom router
const [searchParams] = useSearchParams();
useEffect(() => {
// When the searchParams contains a query
if (searchParams.get('q')) {
// Set query for Search UI - Run the search
setSearchTerm(searchParams.get('q'))
// Fetch suggestion from backend API
fetchDidYouMeanSuggestion({ query: searchParams.get('q') }).then(res => {
setSuggestion(res.body?.suggest?.simple_phrase[0]?.options[0]?.text)
})
.catch(err => console.log(err));
}
}, [searchParams]);
最后,如果用户的查询没有结果,则显示建议:
{wasSearched && totalResults == 0 && <span>No results to show{suggestion ? <>, did you mean <span style={{ cursor: "pointer", color: "blue" }} onClick={() => navigateSuggest(suggestion)}>{suggestion}</span>?</> : "."}</span>}
最终结果看起来就像下面这样:
结论
在本篇博文中,您学了如何使用 Elastic 企业搜索轻松构建“您是不是要找?”和带范围建议这两项功能。这些功能可以轻松集成到您现有的搜索体验中,这样您的用户就可以更快地看到要找的内容了。如想查看构建这些示例所用的代码,您可以在这个 GitHub 存储库中找到。
打造现代搜索体验对电商网站和很多其他用例(例如客户支持、网站搜索、内部工作场所搜索以及自定义应用程序)而言都至为关键。您可以遵循本博文中的步骤来为您的所有最终用户加速打造更棒的搜索体验。