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%20fantasy)上进行在线体验。

搜索引擎: 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应用:

1
2
3
npm install -g create-react-app
create-react-app video-game-search --use-npm
cd video-game-search

在此基础上,我们将安装Search UI和App Search连接器:

1
npm install --save @elastic/react-search-ui @elastic/search-ui-app-search-connector

并以开发模式启动该应用程序:

1
npm start

在您喜欢的文本编辑器中打开src/App.js

我们将从一些样板代码开始,注意评论部分!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 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。

核心组件,连接器和视图组件包含在三个不同的程序包中:

1
2
3
@elastic/search-ui-app-search-connector
@elastic/react-search-ui
@elastic/react-search-ui-views

继续进行时,我们将详细了解它们

1
2
3
4
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行:

1
import "@elastic/react-search-ui-views/lib/styles/styles.css";

Step 2: 连接器

我们有来自App Search的Public Search Key和Host Identifier。

是时候让他们工作了!

Search UI中的连接器对象使用credentials连接到App Search和超级搜索:

1
2
3
4
5
const connector = new AppSearchAPIConnector({
searchKey: "[YOUR_SEARCH_KEY]",
engineName: "video-games",
hostIdentifier: "[YOUR_HOST_IDENTIFIER]"
});

搜索用户界面可与任何搜索API配合使用。 但是通过连接器可以使搜索API正常工作,而无需进行任何更深入的配置。

Step 3: configurationOptions

在深入探讨configurationOptions之前,让我们花点时间进行反思。

我们将一组数据导入了搜索引擎。 但是,它是什么样的数据?

我们对数据了解的越多,我们就会越了解如何将数据呈现给搜索者。 这样一来,您便可以了解如何配置搜索体验。

我们来看一个对象,这是该数据集中所有对象中的一个:

1
2
3
4
5
6
7
8
9
10
11
12
13
{ 
"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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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组件,用于绘制典型的搜索布局。 有很深的自定义选项,但我们不会在本教程中介绍。

我们将做两件事:

  1. 将configurationOptions传递给SearchProvider。
  2. 将一些结构性构建基块放入Layout中,并添加两个基本组件:SearchBox和Results。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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字段,因此我们假想一些文档以进行仔细考虑。

说我们的文档看起来像这样:

1
2
3
4
5
6
7
8
{ 
"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文件中,我们导入了三个基本组件:

1
import { SearchProvider, Results, SearchBox } from "@elastic/react-search-ui";

根据我们为配置选项定义的内容,让我们添加更多内容。

导入以下组件将启用UI中缺少的功能:

  • PagingInfo:在当前页面上显示信息。
  • ResultsPerPage:配置每页上显示多少个结果。
  • Paging:浏览不同的页面。
  • Facet:以数据类型独有的方式过滤和浏览数据。
  • Sort:重新定向给定字段的结果。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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组件放在最适合它们的部分中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<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对象中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const configurationOptions = {
autocompleteQuery: {
suggestions: {
types: {
documents: {
// Which fields to search for suggestions
fields: ["name"]
}
},
// How many suggestions appear
size: 5
}
},
...
};

其次,我们需要根据SearchBox启用自动填充功能:

1
2
3
4
5
6
...
<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

-------------本文结束 感谢您的阅读-------------
如果觉得我的文章对您有用,请随意打赏.您的支持将鼓励我继续创作!

  • 本文标题: Solutions:如何运用Elastic App Search快速建立出色的React搜索体验
  • 文章作者: 永夜初晗凝碧天
  • 发布时间: 2020年01月02日 - 10:01:43
  • 更新时间: 2020年01月02日 - 10:01:94
  • 本文链接: https://yongnights.github.io/2020/01/02/Solutions:如何运用Elastic App Search快速建立出色的React搜索体验/
  • 版权声明: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。