2016年12月,我们开始研究Ambar——一个文档搜索系统。Ambar使用ElasticSearch作为核心搜索引擎。
在Ambar开发的过程中,我们处理了很多与ES相关的问题,我们想分享我们得到的宝贵经验。让我们从每个搜索系统的一个重要功能开始——高亮显示搜索结果。
在任何搜索系统的可用性中,适当的结果高亮显示是最有价值的部分,首先,它为用户提供了关于内部搜索逻辑的必要信息,以及为什么显示该结果。此外,它也使我们能够仅仅通过快速浏览重点而不是下载和浏览整个文档来估计结果。
因为Ambar是一个文档搜索系统,我说的文档也是指文件,所以它必须处理非常大的文件(就全文搜索而言),大小大于100Mb。本文介绍了在利用ElasticSearch高亮显示大型文档时如何达到高性能。
定义问题
Ambar使用ES作为搜索引擎,搜索经过解析的文件/文档内容及其元数据。下面是Ambar在ES中存储一个文档的例子:
{
sha256: "1a4ad2c5469090928a318a4d9e4f3b21cf1451c7fdc602480e48678282ced02c",
meta: [
{
id: "21264f64460498d2d3a7ab4e1d8550e4b58c0469744005cd226d431d7a5828d0",
short_name: "quarter.pdf",
full_name: "//winserver/store/reports/quarter.pdf",
source_id: "crReports",
extension: ".pdf",
created_datetime: "2017-01-14 14:49:36.788",
updated_datetime: "2017-01-14 14:49:37.140",
extra: [],
indexed_datetime: "2017-01-16 18:32:03.712"
}
],
content: {
size: 112387192,
indexed_datetime: "2017-01-16 18:32:33.321",
author: "John Smith",
processed_datetime: "2017-01-16 18:32:33.321",
length: "",
language: "",
state: "processed",
title: "Quarter Report (Q4Y2016)",
type: "application/pdf",
text: ".... laaaaaarge text here ...."
}
}
上面的JSON文档是一个解析后的.pdf文件,里面有财务报告,文件大小约为100Mb。content.text字段包含报告的解析文本,其大小也约为100Mb。
让我们做一个简单的实验。索引1000个文档,如我以前指定的文档,而不定义任何索引调优或自定义映射。然后看看ES会多快地搜索它们,并高亮显示content.text字段中的检索关键字。
结果如下:
- 在content.text字段中进行match_phrase搜索会耗费5-30秒
- 突出显示content.text字段中的文本内容,每次命中平均需要10秒
这种结果是不能接受的。任何使用搜索系统的用户都希望在点击“搜索”按钮后立即得到搜索结果,而不需要等待半分钟就会出现第一个结果。让我们来看看高亮显示这个缓慢突出的问题并解决它。
选择高亮策略
ES 和 Lucene底层有三种高亮策略可供选择,这是官方文档链接,三种策略如下:
- Plain - ES中默认的高亮显示,它是最慢的,但它做了最精确的高亮显示,几乎完全匹配Lucene的搜索逻辑。要高亮显示检索关键字,它必须将整个文档加载到内存中并重新分析它。
- Postings - 更快的一个。它将文档的字段分割成句子,并使用BM25算法对匹配的结果进行标记,从而对结果进行排序,但它需要在索引中额外存储句子的位置。
- Fast Vector Highlighting (FVH) - 似乎是最快的,特别是对于大型文档。需要为索引中的每个令牌存储位置偏移量。在本例中,要对检索词进行高亮显示,它不需要检索整个文档,只需检索接近命中的令牌,由于每个令牌的位置是已知的,因此这个速度非常快。
因此,现在你可以猜到为什么ES可以开箱即用地对大文档中的检索关键字高亮显示。对于每次命中检索整个文档并重新分析它的性能非常昂贵,尤其是对于大于1Mb的文档。
由于我们绝对不能使用普通的高亮显示方式,我们测试了Postings和FVH。最后的选择是FVH,原因如下:
- 如果使用FVH,一个100Mb的文档高亮显示大约需要10-20毫秒,Postings大约需要一秒钟
- Postings并不总是正确地将文档的字段划分为句子,这就是为什么高亮显示的大小会有很大的差异(在某些情况下,从50个单词到数千个单词)。FVH没有这种问题,因为它检索固定数量的令牌,而不是句子。
- Postings以任何顺序突出显示令牌,在复杂查询中不能正常工作。对于引用,它不会正确地突出显示具有指定slop值的match_phrase查询的结果。它将把它解释为bool查询,高亮显示整个文档字段中的每个匹配令牌。
在FVH测试中,我们发现了一个非常棘手的问题。它确实解释了match_phrase查询,而不是Lucene的搜索。它只按查询中指定的顺序突出显示令牌,但Lucene的搜索将令牌按任意顺序解释为命中。如果您正在搜索“John Smith”短语,但是文档在其字段中有“Smith John”值,ES将检索该文档作为命中结果,但FVH不会高亮显示它。解决这个问题的办法是短语置换。我们提交不同的查询以搜索和高亮显示,Search获取默认查询,高亮显示通过修改源短语中所有单词位置的变化而构建查询。
总结
ES实际上可以处理大型文档,并且仍然能够提供相当好的性能,重要的是正确地设置索引并记住所有与ES相关的问题。
编译自:Highlighting Large Documents in ElasticSearch