Solutions:如何运用Elastic App Search快速建立出色的React搜索体验
建立搜索体验是一项艰苦的工作。 乍一看似乎很容易:建立一个搜索栏,将数据放入数据库,然后让用户输入对该数据库的查询。 但是,在数据建模,底层逻辑以及(当然)总体设计和用户体验方面,有很多事情要考虑。
我们将逐步介绍如何使用Elastic的开源Search UI库构建出色的基于React的搜索体验。 大约需要30分钟,然后您便可以将搜索带到需要它的任何应用程序中。
但是首先,是什么使创建搜索如此具有挑战性?
搜索是很难创建的
开发人员在搜索开发中采用许多错误的假设。比如许多相信的假设:
- “知道他们要寻找的客户将按照您期望的方式进行搜索。”
- “您可以编写一个查询解析器,该解析器将始终成功解析查询。”
- “一旦设置,下周搜索将以相同的方式进行。”
- “同义词很容易。”
- ...
得出的结论是,搜索面临许多挑战--而且这些挑战并不简单。 您需要考虑如何管理状态,构建用于过滤,构面,排序,分页,同义词,语言处理等等的组件,等等。 但是,总而言之:
建立出色的搜索需要两个复杂的部分:
(1)搜索引擎,它提供用于增强搜索功能的API
(2)搜索库,它描绘了搜索体验。
对于搜索引擎,我们将查看Elastic App Search。
为了获得搜索体验,我们将介绍一个操作系统搜索库:Search UI。
完成后,将如下所示。您也可以在地址(https://codesandbox.io/embed/happy-wilbur-hwzsh?view=preview&initialpath=%3Fq%3Dfinal fantasy)上进行在线体验。
搜索引擎: Elastic App Search
App Search可作为付费托管服务或免费的自助托管发行版(https://www.elastic.co/downloads/app-search?ultron=searchui-howto-react&blade=codeburst&hulk=content)提供。 我们将在本教程中使用托管服务,但是请记住,如果您自己托管,您的团队可以免费使用带有基本许可的Search UI和App Search。
计划:将代表有史以来最好的视频游戏的文档编入搜索引擎,然后设计和优化搜索体验以对其进行搜索。
首先,注册14天的试用期(https://www.elastic.co/products/app-search/service?ultron=searchui-howto-react&blade=codeburst&hulk=content)-无需信用卡。
创建一个引擎。 您可以选择13种不同的语言。
我们将其命名为video-games,并将语言设置为英语。
下载最佳视频游戏数据集(https://drive.google.com/file/d/14-3wzemyLzJh6XHVUotFsdl0tZ7K2v1E/view),然后使用导入程序将其上传到App Search。
接下来,单击进入引擎,然后选择“Credentials”选项卡。
使用仅对video-games引擎具有Limited Engine Access的方式创建新的Public Search Key。
我们可以记下我们刚创建的Public Search Key及Host Indentifier以便下面之用。
尽管看起来我们目前做的并不多,但我们现在拥有功能全面的搜索引擎,可以使用完善的搜索API来搜索我们的视频游戏数据。
到目前为止,这是我们所做的:
- 创建了一个搜索引擎
- 建立了索引文档
- 创建一个默认的索引schema
- 创建了一个有限的可以用于外界访问的凭证(credential)
让我们开始使用“Search UI”来建立我们的搜索体验。
搜索库:Search UI
我们将使用create-react-app(https://github.com/facebook/create-react-app)脚手架实用程序创建一个React应用:
npm install -g create-react-app
create-react-app video-game-search --use-npm
cd video-game-search
在此基础上,我们将安装Search UI和App Search连接器:
npm install --save @elastic/react-search-ui @elastic/search-ui-app-search-connector
并以开发模式启动该应用程序:
npm start
在您喜欢的文本编辑器中打开src/App.js
我们将从一些样板代码开始,注意评论部分!
// Step #1, import statements
import React from "react";
import AppSearchAPIConnector from "@elastic/search-ui-app-search-connector";
import { SearchProvider, Results, SearchBox } from "@elastic/react-search-ui";
import { Layout } from "@elastic/react-search-ui-views";
import "@elastic/react-search-ui-views/lib/styles/styles.css";
// Step #2, The connector
const connector = new AppSearchAPIConnector({
searchKey: "[YOUR_SEARCH_KEY]",
engineName: "video-games",
hostIdentifier: "[YOUR_HOST_IDENTIFIER]"
});
// Step #3: Configuration options
const configurationOptions = {
apiConnector: connector
// Let's fill this in together.
};
// Step #4, SearchProvider: The finishing touches
export default function App() {
return (
<SearchProvider config={configurationOptions}>
<div className="App">
<Layout
// Let's fill this in together.
/>
</div>
</SearchProvider>
);
}
Step 1: 导入声明
我们需要导入我们的Search UI依赖关系和React。
核心组件,连接器和视图组件包含在三个不同的程序包中:
@elastic/search-ui-app-search-connector
@elastic/react-search-ui
@elastic/react-search-ui-views
继续进行时,我们将详细了解它们
import React from "react";
import AppSearchAPIConnector from "@elastic/search-ui-app-search-connector";
import { SearchProvider, Results, SearchBox } from "@elastic/react-search-ui";
import { Layout } from "@elastic/react-search-ui-views";
我们还将为该项目导入默认样式表,这将使我们拥有良好的外观,而无需编写我们自己的CSS行:
import "@elastic/react-search-ui-views/lib/styles/styles.css";
Step 2: 连接器
我们有来自App Search的Public Search Key和Host Identifier。
是时候让他们工作了!
Search UI中的连接器对象使用credentials连接到App Search和超级搜索:
const connector = new AppSearchAPIConnector({
searchKey: "[YOUR_SEARCH_KEY]",
engineName: "video-games",
hostIdentifier: "[YOUR_HOST_IDENTIFIER]"
});
搜索用户界面可与任何搜索API配合使用。 但是通过连接器可以使搜索API正常工作,而无需进行任何更深入的配置。
Step 3: configurationOptions
在深入探讨configurationOptions之前,让我们花点时间进行反思。
我们将一组数据导入了搜索引擎。 但是,它是什么样的数据?
我们对数据了解的越多,我们就会越了解如何将数据呈现给搜索者。 这样一来,您便可以了解如何配置搜索体验。
我们来看一个对象,这是该数据集中所有对象中的一个:
{
"id":"final-fantasy-vii-ps-1997",
"name":"Final Fantasy VII",
"year":1997,
"platform":"PS",
"genre":"Role-Playing",
"publisher":"Sony Computer Entertainment",
"global_sales":9.72,
"critic_score":92,
"user_score":9,
"developer":"SquareSoft",
"image_url":"https://r.hswstatic.com/w_907/gif/finalfantasyvii-MAIN.jpg"
}
我们看到它有几个文本字段,例如name,year,platform等等,还有一些数字字段,例如critic_score,global_sales和user_score。
如果我们提出三个关键问题,我们将足够了解,以提供扎实的搜索体验:
- 大多数人将如何搜索? 以视频游戏的名称命名。
- 大多数人想要看到的结果是什么? 视频游戏的名称,类型,发行商,得分和平台。
- 大多数人将如何过滤,排序和构面? 按得分,体裁,发布者和平台分类。
然后,我们可以将这些答案转换为我们的configurationOptions:
const configurationOptions = {
apiConnector: connector,
searchQuery: {
search_fields: {
// 1. Search by name of video game.
name: {}
},
// 2. Results: name, genre, publisher, scores, and platform.
result_fields: {
name: {
// A snippet means that matching search terms will be wrapped in <em> tags.
snippet: {
size: 75, // Limit the snippet to 75 characters.
fallback: true // Fallback to a "raw" result.
}
},
genre: {
snippet: {
size: 50,
fallback: true
}
},
publisher: {
snippet: {
size: 50,
fallback: true
}
},
critic_score: {
// Scores are numeric, so we won't snippet.
raw: {}
},
user_score: {
raw: {}
},
platform: {
snippet: {
size: 50,
fallback: true
}
},
image_url: {
raw: {}
}
},
// 3. Facet by scores, genre, publisher, and platform, which we'll use to build filters later.
facets: {
user_score: {
type: "range",
ranges: [
{ from: 0, to: 5, name: "Not good" },
{ from: 5, to: 7, name: "Not bad" },
{ from: 7, to: 9, name: "Pretty good" },
{ from: 9, to: 10, name: "Must play!" }
]
},
critic_score: {
type: "range",
ranges: [
{ from: 0, to: 50, name: "Not good" },
{ from: 50, to: 70, name: "Not bad" },
{ from: 70, to: 90, name: "Pretty good" },
{ from: 90, to: 100, name: "Must play!" }
]
},
genre: { type: "value", size: 100 },
publisher: { type: "value", size: 100 },
platform: { type: "value", size: 100 }
}
}
};
我们已经将Search UI连接到我们的搜索引擎,现在我们有一些选项可以控制我们如何搜索数据,显示结果并探索这些结果。 但是我们需要一些东西来将所有内容绑定到Search UI的动态前端组件。
Step 4: SearchProvider
这是统治所有对象的对象。 SearchProvider是所有其他组件嵌套的地方。
Search UI提供了一个Layout组件,用于绘制典型的搜索布局。 有很深的自定义选项,但我们不会在本教程中介绍。
我们将做两件事:
- 将configurationOptions传递给SearchProvider。
- 将一些结构性构建基块放入Layout中,并添加两个基本组件:SearchBox和Results。
export default function App() {
return (
<SearchProvider config={configurationOptions}>
<div className="App">
<Layout
header={<SearchBox />}
// titleField is the most prominent field within a result: the result header.
bodyContent={<Results titleField="name" urlField="image_url" />}
/>
</div>
</SearchProvider>
);
}
至此,我们已经在前端建立了基础。 在运行此后端之前,还有一些其他细节需要在后端解决。 我们还应该研究相关性模型,以便针对该项目的独特需求微调搜索。
重新进入搜索平台
App Search具有强大且完善的搜索引擎功能。 它使曾经复杂的调优变得更加有趣。 只需单击几下,我们便可以进行细粒度的相关性调整和无缝的模式更改。
我们将首先调整schema以使其实际运行。
登录到App Search,输入video-games引擎,然后单击“Manage”部分下的“Schema”。
出现架构。 默认情况下,这11个字段中的每一个均被视为文本。
在configurationOptions对象中,我们定义了两个范围构面来帮助我们搜索数字:user_score和critic_score。 为了使range facet按预期工作,字段类型必须为数字(number)。
单击每个字段旁边的下拉菜单,将其更改为数字,然后单击“Update Types”。引擎会即时重新更新索引。 然后,当我们将构面(facet)组件添加到布局中时,范围过滤器将按预期运行。 现在,进入真正的漂亮东西。
下面的部分是高度相关的
具有三个关键的相关功能:Synonyms,Curations和Relevance Tuning。
在边栏中的“Search Settings”部分下选择每个功能:
Synonyms
世界各地的人们使用不同的词来形容事物。 同义词可帮助您创建被视为一个或一组相同的术语集。
就video game搜索引擎而言,我们知道人们会希望找到Final Fantasy。 但是也许他们会改用FF。
单击进入同义词,然后选择创建同义词集并输入术语:
单击Save。 您可以根据需要添加任意多个同义词集。
现在,搜索FF与搜索Final Fantasy的权重相同。
Curations
Curations是最最让人喜欢的。 如果有人搜索Final Fantasy或FF,该怎么办? 系列赛中有很多游戏-他们会得到哪些?
默认情况下,前五个结果如下所示:
1.最终幻想VIII
2.最终幻想X
3.最终幻想策略
4.最终幻想IX
5.最终幻想XIII
这似乎不正确……Final Fantasy VII是所有游戏中最好的Final Fantasy游戏。 而且Final Fantasy XIII不是很好! 😜
我们可以做到这一点,以便搜索Final Fantasy的人会收到Final Fantasy VII作为第一结果吗? 我们可以从搜索结果中删除Final Fnatasy XIII吗?
我们可以!
单击“Curations”,然后输入查询:“Final Fantasy”。
接下来,通过抓住表格最左侧的把手将“Final FantasyVII”文档拖到“Promoted Documents”部分。 然后单击“Final Fantasy XIII”文档上的“Hide Result”按钮(那个有一条线穿过眼睛的图标,下图列表中第三个图标):
现在,执行“Final Fantasy”或“FF”搜索的任何人都将首先看到“Final Fantasy VII”。
他们根本看不到Final Fantasy XIII。 哈!
我们可以升级和隐藏许多文档。 我们甚至可以对升级后的文档进行排序,因此我们可以完全控制每个查询顶部显示的内容。
Relevance tuning
单击边栏中的“Relevance Tuning”。
我们搜索一个文本字段:name字段。 但是,如果我们有多个文本字段可供人们搜索,例如name字段和description字段,该怎么办? 我们正在使用的video game数据集不包含description字段,因此我们假想一些文档以进行仔细考虑。
说我们的文档看起来像这样:
{
"name":"Magical Quest",
"description": "A dangerous journey through caves and such."
},
{
"name":"Dangerous Quest",
"description": "A magical journey filled with magical magic. Highly magic."
}
如果有人想找到游戏Magical Quest,他们会输入该内容作为查询。 但是第一个结果将是Dangerous Quest:
为什么? 因为在“Dangerous”的description中“Magical”一词出现了3次,所以搜索引擎不会知道一个字段比另一个字段更重要。 然后,它将使“Dangerous Quest”的排名更高。 这就是为什么存在相关性调整的难题。
我们可以选择一个字段,除其他外,还可以增加其相关性的权重:
我们看到,当我们增加权重时,正确的项目“ Magical Quest”上升到顶部,因为name字段变得更重要。 我们需要做的就是将滑块拖动到更高的值,然后单击“Save”。
现在,我们已经使用App Search实现了如下的任务:
- 调整schema,并将user_score和critic_score更改为数字字段。
- 微调关联(relevance)模型。
这样就总结出了精美的“仪表板”功能-每个功能都有一个匹配的API端点,如果您不是GUI的用户,则可以使用它们使程序以编程方式工作。
现在,让我们结束UI。
最后加工
此时,您的UI应该可以正常工作了。 尝试一些查询。 首先要说的是,我们缺少探索结果的工具,例如过滤,分面(facet),排序等,但是搜索有效。 我们需要完善用户界面。
在初始的src/App.js文件中,我们导入了三个基本组件:
import { SearchProvider, Results, SearchBox } from "@elastic/react-search-ui";
根据我们为配置选项定义的内容,让我们添加更多内容。
导入以下组件将启用UI中缺少的功能:
- PagingInfo:在当前页面上显示信息。
- ResultsPerPage:配置每页上显示多少个结果。
- Paging:浏览不同的页面。
- Facet:以数据类型独有的方式过滤和浏览数据。
- Sort:重新定向给定字段的结果。
import {
PagingInfo,
ResultsPerPage,
Paging,
Facet,
SearchProvider,
Results,
SearchBox,
Sorting
} from "@elastic/react-search-ui";
导入后,可以将组件放置到布局中。
布局组件将页面分为多个部分,可以通过prop将组件放置在这些部分中。
它包含以下部分:
- header:搜索框/栏
- bodyContent:结果容器
- sideContent:侧边栏,其中包含构面和排序选项
- bodyHeader:围绕结果的“包装器”,其中包含上下文丰富的信息,例如当前页面和每页结果数
- bodyFooter:用于在页面之间快速导航的分页选项
组件呈现数据。根据我们在configurationOptions中提供的搜索设置获取数据。现在,我们将每个组件放置在适当的布局部分中。
例如,我们在configurationOptions中描述了五个方面的维度,因此我们将创建五个方面的组件。每个Facet组件都将使用“字段”属性作为返回数据的键。
我们将它们与我们的Sorting组件一起放在sideContent部分中,然后将Paging,PagingInfo和ResultsPerPage组件放在最适合它们的部分中:
<Layout
header={<SearchBox />}
bodyContent={<Results titleField="name" urlField="image_url" />}
sideContent={
<div>
<Sorting
label={"Sort by"}
sortOptions={[
{
name: "Relevance",
value: "",
direction: ""
},
{
name: "Name",
value: "name",
direction: "asc"
}
]}
/>
<Facet field="user_score" label="User Score" />
<Facet field="critic_score" label="Critic Score" />
<Facet field="genre" label="Genre" />
<Facet field="publisher" label="Publisher" isFilterable={true} />
<Facet field="platform" label="Platform" />
</div>
}
bodyHeader={
<>
<PagingInfo />
<ResultsPerPage />
</>
}
bodyFooter={<Paging />}
/>
现在,让我们看一下本地开发环境中的搜索体验。
好多了! 我们提供了丰富的选项来探索搜索结果。
我们引入了一些额外的好处,例如多种排序选项,并且通过添加单个标志使发布者的面可过滤。 尝试使用空白查询进行搜索并浏览所有选项。
最后,让我们看一下搜索体验的最后一项功能。 这是一个受欢迎的...
自动完成 (Autocomplete)
搜索者喜欢自动完成功能,因为它可以提供即时反馈。 它的建议有两种形式:结果和查询。 取决于哪种口味,搜索者将收到相关结果或可能导致结果的潜在查询。
我们将重点关注自动填充作为一种查询建议形式。
这需要两个快速更改。
首先,我们需要将自动完成功能添加到configurationOptions对象中:
const configurationOptions = {
autocompleteQuery: {
suggestions: {
types: {
documents: {
// Which fields to search for suggestions
fields: ["name"]
}
},
// How many suggestions appear
size: 5
}
},
...
};
其次,我们需要根据SearchBox启用自动填充功能:
...
<Layout
...
header={<SearchBox autocompleteSuggestions={true} />}
/>
...
是的,就是这样。
尝试搜索-键入时,将显示自动完成查询建议。
总结
现在,我们拥有美观的功能性搜索体验。 而且,我们避免了人们在尝试实施搜索时经常会遇到的一堆陷阱。 30分钟还不错,你不是说吗?你可以在地址进行一个完美的体验。
如果你想进一步动态生成数据集,请参阅文章https://swiftype.com/documentation/app-search/api/documents#create
你可以在如下地址找到这个项目的源码:https://github.com/liu-xiao-guo/swiftype-video-game-search
参考:
【1】How to Build Great React Search Experiences Quickly
————————————————
版权声明:本文为CSDN博主「Elastic官方博客」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/UbuntuTouch/article/details/103101698