作者:来自 Elastic Amit Khandelwal
探索处理自动完成的不同方法,从基础到高级,包括输入时搜索、查询时间、完成建议器和索引时间。
在本文中,我们将介绍如何避免严重的性能错误、Elasticsearch 默认解决方案为何不适用以及重要的实施注意事项。
所有现代网站都在其搜索栏上提供自动补全(autocomplete)功能(输入时搜索),以改善用户体验(没有人愿意输入整个搜索词……)。自动补全必须比标准搜索更快,因为自动完成的全部目的是在用户输入时开始显示结果。如果延迟较高,则会导致用户体验不佳。
以下是著名问答网站 Quora 上的自动完成搜索示例。这是一个很好的自动完成示例:搜索 “elasticsearch auto” 时,以下帖子开始显示在搜索栏中:
注意搜索结果中,有与 Elasticsearch 的自动扩展、自动标记和自动完成功能相关的问题。用户可以进一步输入一些字符来细化搜索结果。
Elasticsearch 中自动完成的各种方法/输入时搜索
有多种方法可以实现自动完成功能,大致分为四大类:
- Search as you type
这是一种数据类型,旨在方便自动完成查询,而无需事先了解自定义分析器设置。Elasticsearch 内部存储同一文本的各种标记(edge n-gram, shingles),因此可用于前缀和中缀补全。
如果你不熟悉 Elasticsearch 的高级功能(其他三种方法都需要这些功能),那么这种方法会很方便。在 “Search as you type - 边输入边搜索” 中,不需要进行太多配置,即可使其适用于简单的用例和代码示例。更多详细信息请参阅我们的文档。
- 查询时 - query time
可以通过将匹配查询(match queries)更改为前缀查询(prefix queries)来实现自动完成。在对 token(索引)到 token(搜索查询 token)匹配的匹配查询时,前缀查询(如其名称所建议)从搜索 token 开始的所有 token 匹配,因此匹配的文档数量(结果)很高。
正如所解释的那样,前缀查询不是一个准确的 token 匹配,而是基于字符串中的字符匹配,这是非常昂贵的,并获取了很多文档。 Elasticsearch 内部使用 B+树(B+ tree)类型的数据结构来存储其 token。了解倒排索引(indices)使用的数据结构的内部以及不同类型的查询如何影响性能和结果是有用的。
Elasticsearch 在 7.2 版本中引入了 Match Boolean Prefix 查询(Match boolean prefix query)。这是一种结合了 Match 查询和 Prefix 查询的方式,兼具两者的优势。它在处理多个搜索词时特别有用。例如,如果搜索词为 "foo bar baz",那么与其对所有搜索词执行前缀搜索(这种方式开销大且结果较少),该查询仅对最后一个词执行前缀搜索,而对前面的词进行匹配,且顺序不限。这样可以提高搜索结果的速度和相关性。
- 完成建议 - Completion suggester
如果你为诸如电子商务和酒店搜索网站等搜索词提供建议,这将很有用。搜索栏提供了查询建议,而不是在实际搜索结果中出现的建议,并且选择了完善建议者提供的建议之一后,它提供了搜索结果。
为了使 completion suggester 工作,必须将 suggestions 索引为任何其他字段。你还可以选择添加权重字段来对建议进行排名。
如果你具有 autocomplete suggestions 的外部来源,例如搜索分析,则这种方法是理想的选择。
代码样本
索引定义
1. PUT movies
2. {
3. "mappings": {
4. "properties": {
5. "movie_name": {
6. "type": "completion"
7. }
8. }
9. }
10. }
响应:
1. {
2. "acknowledged": true,
3. "shards_acknowledged": true,
4. "index": "movies"
5. }
索引 suggstions
1. PUT movies/_doc/1?refresh
2. {
3. "movie_name" : {
4. "input": [ "Avengers", "Infinity War" ],
5. "weight" : 34
6. }
7. }
响应:
1. {
2. "_index": "movies",
3. "_id": "1",
4. "_version": 1,
5. "result": "created",
6. "forced_refresh": true,
7. "_shards": {
8. "total": 2,
9. "successful": 1,
10. "failed": 0
11. },
12. "_seq_no": 0,
13. "_primary_term": 1
14. }
搜素
1. POST movies/_search?pretty
2. {
3. "suggest": {
4. "song-suggest": {
5. "prefix": "inf",
6. "completion": {
7. "field": "movie_name"
8. }
9. }
10. }
11. }
响应:
1. {
2. "took": 2,
3. "timed_out": false,
4. "_shards": {
5. "total": 1,
6. "successful": 1,
7. "skipped": 0,
8. "failed": 0
9. },
10. "hits": {
11. "total": {
12. "value": 0,
13. "relation": "eq"
14. },
15. "max_score": null,
16. "hits": []
17. },
18. "suggest": {
19. "song-suggest": [
20. {
21. "text": "inf",
22. "offset": 0,
23. "length": 3,
24. "options": [
25. {
26. "text": "Infinity War",
27. "_index": "movies",
28. "_id": "1",
29. "_score": 1,
30. "_source": {
31. "movie_name": {
32. "input": [
33. "Avengers",
34. "Infinity War"
35. ]
36. }
37. }
38. }
39. ]
40. }
41. ]
42. }
43. }
- 索引时 - index time
有时,要求只是 prefix completion 或 autocomplete 中的 infix 完成。使用 Custom-Analyzers 看到自动完成的实现并不少见,这涉及以与用户的搜索词相匹配的方式索引令牌。
如果我们继续进行示例,我们将查看由 “elasticsearch Autocopterete”,“ elasticsearch Auto-Tag”,“ elasticsearch auto Scaling” 和 “Elasticsearch automatically” 组成的文档。默认的 analyzer 不会为 “autocomplete”,“autoscaling” 和 “automatically” 产生任何部分 token,并且搜索 “auto” 不会产生任何结果。
为了克服上述问题, edge ngram 或 n-gram tokenizer 用于在 Elasticsearch 中索引 token,如文档中所述,并搜索时分析器(search time analyzer )以获得自动完成结果。
上述方法使用了 Match 查询,这种查询速度较快,因为它基于字符串比较(使用哈希码)。此外,索引中精确匹配的 token 相对较少,从而进一步提高了查询效率。
性能考虑
上述几乎所有方法在较小的搜索负载的较小数据集上都可以正常工作,但是当你有大量索引获得大量自动索引建议查询时,上述查询的 SLA 和性能是必不可少的。以下要点应帮助你选择最适合你需求的方法:
- Ngram 或 Edge Ngram token 会显著增加索引大小,因此需要根据应用需求和系统容量合理设定
min_gram和max_gram的限制。合理规划可以在生产环境中避免许多问题。 - 允许空前缀或仅包含少量字符的前缀查询可能会匹配索引中的所有文档,从而导致整个集群崩溃。因此,最佳实践是仅在少数字段上执行前缀查询,并限制前缀查询的最小字符数。上述的 Match Boolean Prefix 查询可以很好地解决这一问题。
- Elasticsearch 提供的 "search_as_you_type" 数据类型会以多种格式对输入文本进行分词。由于它是 Elasticsearch 提供的标准解决方案,无法满足所有业务需求,因此在使用时应仔细检查业务场景下的所有边界情况。此外,由于该方法会以多种格式对字段进行分词,可能会增加 Elasticsearch 索引存储的大小。
- Completion Suggest 采用独立索引的方式存储建议词,但无法用于获取搜索结果,因此适用于自动补全场景,而不适用于一般的搜索需求。
- 索引时优化(Index-time approach)速度快,因为查询时的计算开销较小,但它需要更多的前期工作,例如 重新索引(reindexing)、容量规划(capacity planning)和额外的磁盘存储成本。相比之下,查询时优化(Query-time approach)更容易实现,但搜索查询成本更高。理解这一权衡(trade-off)非常重要,因为在大多数情况下,用户需要在两者之间做出选择,并清楚其影响,以便解决各种性能问题。
想获得 Elastic 认证吗?找出何时进行下一个 Elasticsearch工程师培训!
Elasticsearch 包含新功能,可帮助你为用例构建最佳的搜索解决方案。潜入我们的示例笔记本上,以了解更多信息,开始免费的云试验,或者现在在本地机器上尝试 Elastic。