工程

如何使用 React 和 Elastic App Search 打造应用程序搜索体验

当用户搜索某个内容时,他们希望获得相关的结果。但相关的结果只是赢得访客好感的一部分因素;为用户提供良好的搜索体验也很重要。搜索不仅要快速,积极响应用户输入,还要让用户感觉到智能和高效。

本教程将通过演示如何使用 React 和 Elastic App Search JavaScript 客户端打造流畅、强大的搜索体验,向您讲授应用程序搜索方面的知识。最后,您会得到一个外观好看、相关度高、基于 React 构建的应用程序;使用该应用程序,您可以按分面和作为 URI 一部分维护的状态进行排序,实时搜索各种 npm 包。

完整的代码可以在 GitHub 上找到。


刚好!

我们新发布了一篇文章,其中介绍了如何使用 Search UI(Elastic 的一个开源库)更快地打造出色的搜索体验。

读一读吧


要求

要开始操作,您需要具备以下内容:

  1. 最新版本的 Node.js
  2. 最新版本的 npm
  3. Elastic App Search Service 账号或有效的 14 天免费试用版
  4. 大约 30 分钟的时间。

App Search 简介

应用程序是围绕数据构建的。Facebook 以一种有趣的方式呈现“朋友”数据,迅速占据了我们的社交圈。eBay 最初是寻找和购买二手商品的最便捷方式。维基百科让读者可以更轻松地了解万事万物

应用程序的存在是为了解决数据问题。在这方面,搜索是必不可少的伙伴。如果是主流的应用程序,搜索将是它所提供服务中的关键部分:查找朋友、产品、对话或文章。数据集越大、越有趣,应用程序就会越受欢迎,尤其是在搜索相关性高且可获得回报的情况下。

Elastic App Search 是在 Elasticsearch 的基础上构建的,Elasticsearch 是一个开源的分布式 RESTful 风格搜索引擎。使用 Elastic App Search,开发人员可以访问一组性能稳健的 API 终端,这些终端经过优化,可以处理高级应用程序搜索用例。

开始构建引擎

首先,在 App Search 中创建一个引擎

引擎会采集用于编制索引的对象。您的数据就是对象;它可以是朋友的资料、产品或维基页面。将数据采集到 App Search 中之后,就会根据灵活的模式为数据建立索引,并针对搜索进行优化。这样,我们可以利用不同的客户端库来打造愉快的搜索体验。

在本示例中,我们将调用 node-modules 引擎。

引擎创建完成后,我们需要凭据页面中的三个信息:

  1. 主机标识符,前缀为 host-
  2. 专用 API 密钥,前缀为 private-
  3. 公共搜索密钥,前缀为 search-

有了这些信息,我们就可以克隆项目,进入目录,签出 starter 分支,然后运行 npm install:

$ git clone https://github.com/swiftype/app-search-demo-react.git
$ cd react-tutorial && git checkout starter && npm install

太好了,我们已经准备好应用程序。但搜索需要数据...

采集 ~

在大多数情况下,您的对象将存放在数据库或后端 API 中。在本示例中,我们将使用静态 .json 文件。存储库包含两个脚本:init-data.js 和 index-data.js。init-data.js 是一个抓取器,用于从 npm 获取格式良好的 node-module 数据。数据存在于 node-modules.json 文件中。index-data.js 是一个索引器,它会将数据采集到 App Search 引擎中,为其建立索引。

为了运行索引器脚本,我们需要同时传递主机标识符专用 API 密钥

$ REACT_APP_HOST_IDENTIFIER={Your Host Identifier} \
REACT_APP_API_KEY={Your Private API Key} \
npm run index-data

对象以 100 个为一批快速发送到我们的 App Search 引擎,然后构建索引。

现在,我们应该为新创建的引擎创建一个仪表板,其中有大约 9,500 个 npm 包被索引为文档。浏览一下这些数据,熟悉其中的内容,可能会有所帮助。

app_search_engine_overview.png

响应能力

引擎填充了数据并准备好之后,我们就可以开始构建核心应用程序了。

$ npm start

从项目目录中启动 npm 将打开 React 样板文件。它从 App.cs 中提取样式,让我们可以更好地根据自身需求进行定制。

之后,我们将需要一个搜索框,在其中输入搜索查询。用户会寻找这些有用的矩形,因为搜索引擎和浏览器已经让用户形成习惯:在这里输入,找到想要的内容!

//App.css
...
.App-search-box {
height:40px;
width:500px;
font-size:1em;
margin-top:10px;
}

我们还需要将 App Search 的访问凭据放在一个安全的地方,比如 .env 文件中。

在项目根目录下创建一个 .env 文件并进行填充,如下所示:

//.env
REACT_APP_HOST_IDENTIFIER={your host identifier, prefixed with host-}
REACT_APP_SEARCH_KEY={your public search key, prefixed with search-}

将变量安全地隐藏起来后,我们就可以开始编写搜索逻辑了。

开始搜索

App.js 文件中存放了核心逻辑。这个文件和其他大多数启动文件都是由 create-react-app 创建的;create-react-app 是一个无需任何配置即可引导 React 应用程序启动的工具。在编写一些逻辑来测试搜索之前,我们需要安装 Swiftype App Search JavaScript 客户端库:

$ npm install --save swiftype-app-search-javascript

将以下代码放入 App.js 中。它将执行基本搜索。

我们将把 foo 硬编码为我们的示例搜索词:

import * as SwiftypeAppSearch from "swiftype-app-search-javascript";
const client = SwiftypeAppSearch.createClient({
hostIdentifier: process.env.REACT_APP_HOST_IDENTIFIER,
apiKey: process.env.REACT_APP_SEARCH_KEY,
engineName: "node-modules"
});
//我们可以查询任何内容,以 foo 为例。
const query = "foo";
const options = {};
client.search(query, options)
.then(resultList => console.log(resultList))
.catch(error => console.log(error))

浏览器将刷新,并通过 console.log 创建一个 resultList 数组。要查看这个数组,我们可以打开浏览器的开发人员控制台。我们可以用另一个字符串替换 foo 查询,这样可以尝试更多的查询。更改了查询并刷新页面后,可以看到调整后的结果集。

太棒了!这样我们就已经在 node-module 中进行搜索了。

结果良好

我们用一个简单的模式进行搜索,但是隐藏在 console.log 中的结果没有什么用处。我们将删除基本的 React 样式和之前的代码,然后进行扩展。

我们将创建以下内容:

  1. 一个状态变量,它将包含一个响应属性。
  2. 一个 performQuery 方法,它将使用 client.search 来查询 App Search。它将查询结果存储在响应属性中。
  3. 一个 componentDidMount 生命周期挂钩,它将在应用程序加载时运行一次。我们将再次查询 foo,但可以使用任何想要的查询。
  4. 结构化 HTML,用于保存结果数据输出和结果总数。
//App.js
// ...已截断!
class App extends Component {
state = {
// 一个新的状态属性,用于保存最近的查询响应
response: null
};
componentDidMount() {
/*在 componentDidMount 中调用此方法可以确保结果
在应用首次加载时显示在屏幕上*/
this.performQuery("foo");
}
// 用于执行查询并存储响应的方法
performQuery = queryString => {
client.search(queryString, {}).then(
response => {
// 现在添加此行可以检查完整的响应
console.log(response);
this.setState({ response });
},
error => {
console.log(`error: ${error}`);
}
);
};
render() {
const {response} = this.state;
if (!response) return null;
return (


Node Module Search



{/*显示此查询的结果总数*/}

{response.info.meta.page.total_results} Results


{/*遍历结果,并显示它们的 Name 和 Description*/}
{response.results.map(result => (

Name: {result.getRaw("name")}


Description: {result.getRaw("description")}



))}

);
}
}
// ...已截断!

我们点击保存的那一刻,结果将出现在 http://localhost:3000 中,共有 27 条结果,还有一些看起来很美观的模块。如果出现问题,我们可以检查控制台,因为我们在代码中嵌套了两个 console.logs。

出色的搜索框填充

我们一直将 foo 硬编码到查询中。搜索的最大价值在于它始于自由的表达。当您开发出了出色的搜索体验后,就能针对更常用的表达进行优化,从而创建出相关度最高的结果集。这一切都始于一个空白的画板:搜索框。

为了打造一个强大的搜索框,我们将向 state 添加一个名为 queryString 的属性。为了用新字符串更新 queryString,我们将创建一个 updateQuery 方法;利用 onChange 句柄来更新 queryString,并在每次用户更改搜索框中的文本时触发新的搜索。

我们完整的 App 类现在如下所示:

//src/App.js
// ...已截断!
class App extends Component {
state = {
// 一个新的状态属性,用于跟踪搜索框中的值
queryString: "",
response: null
};
componentDidMount() {
// 删除“node”的硬编码搜索
this.performQuery(this.state.queryString);
}
// 每次用户在搜索框中输入时,处理 onChange 事件。
updateQuery = e => {
const queryString = e.target.value;
this.setState(
{
queryString // 保存用户输入的查询字符串
},
() => {
this.performQuery(queryString); // 触发新的搜索
}
);
};
performQuery = queryString => {
client.search(queryString, {}).then(
response => {
this.setState({
response
});
},
error => {
console.log(`error: ${error}`);
}
);
};
render() {
const {response, queryString} = this.state;
if (!response) return null;
return (


Node Module Search



{/*一个搜索框,连接到我们的查询字符串 value 和 onChange
句柄*/}

);
}
}
// ...已截断!

Debounce 方法!

在这个迭代中,每次在搜索框中检测到更改时都会进行搜索,这种操作对我们的系统来说可能过于密集。为了解决这个问题,我们将应用一个由 Lodash 提供的 _debounce_ 函数。

$ npm install --save lodash

Debounce 是一种基于定义的毫秒数对入站请求数量进行速率限制的方法。可能遇到这样的情况:用户正在考虑如何表达他们的查询,输入了错别字,或者打字非常快,等等,因此,我们不需要对每个检测到的更改都进行查询。

通过将 performQuery 方法封装在 Lodash 的 debounce 函数中,我们可以指定 200 毫秒的速率限制,即在下一次搜索查询开始之前,必须有 200 毫秒没有任何输入:

//App.js
// ...已截断!
import { debounce } from "lodash"; // Import debounce
// ...已截断!
performQuery = debounce(queryString => {
client.search(queryString, {}).then(
response => {
this.setState({
response
});
},
error => {
console.log(`error: ${error}`);
}
);
}, 200); // 200 毫秒。
// ...已截断!

限制速率除了让我们的服务器可以休息一下,还能帮助用户顺利地进行查询。这将大有帮助!毕竟用户感受十分重要。

后续操作

这是基于 React 构建高质量搜索体验的开始。往后,我们还可以添加很多出色的功能。我们可以添加样式或实现动态 App Search 功能,比如分面搜索结果管理相关度调整。或者,我们还可以探索分析 API 套件,发掘有关用户搜索活动的有价值的见解。

如果我们想真正地深入了解,可以参阅主分支中的 README,它对教程内容进行了扩展,创建了基于 URI 的状态管理,添加了巧妙的样式、分页、筛选和分面搜索。通过一些样式方面的定制,可以将它作为高质量搜索体验的基础。

总结

过去,构建相关度高且有价值的应用程序搜索是非常复杂的。通过 Elastic App Search 这种方便又可控的方法,您可以在 Web 应用程序中编写有价值、可微调的搜索引擎。最值得一提的是,不论是工程师还是不太懂技术的利益相关者,都可以在一个精巧而直观的仪表板中管理关键功能。您可以直接进入 App Search,免费试用 14 天,无需提供信用卡信息。