我正在尝试弄清楚如何根据关键字匹配过滤大量文档。
我的 SQL 数据库中有 20+00 万个带有 ID 和(几个)文本字段的条目,我想获取文本与一组关键字匹配的所有 ID。这包括更复杂的表达式,例如:
(term1 NEAR term2 NEAR term3) AND NOT "A phrase" AND @fieldXYZ "wildcards%aswell*"
结果不需要以任何方式评分、排序或排名。
据我所知,Lucene/Solr、Sphinx 和 ElasticSearch 的强大功能是超快地回馈 TOP 文档,但它们并不是真的打算回馈所有文档。
我知道可以使用Lucene中的自定义收集器来执行此操作(请参阅从Lucene中的查询中检索所有匹配文档的最有效方法是什么,未排序?)并且可能使用Solr/Elasticsearch中的光标/滚动,但我想知道是否有任何其他技术专门针对此问题进行了优化?
据我所知,Lucene/Solr、Sphinx 和 ElasticSearch 的强大功能是超快地回馈 TOP 文档,但它们并不是真的打算回馈所有文档。
实际上,这曾经是真的,但近年来已经好多了。当涉及到其他软件选项时,我会尊重其他人,但Lucene确实在4.x系列的早期得到了一些改进,可以用光标进行高效的深度分页。
Elasticsearch有一个特别好的API:滚动搜索。若要使用它,请提供带有 scroll
参数的搜索查询。然后,它返回一个scroll_id
游标,您可以使用该游标对每个页面发出后续请求。
如果您不关心排序,只想返回所有文档,那么您还可以指定搜索类型scan
。这将以最有效的顺序返回所有文档,不应用特定的排序。
我已经在这里掩盖了一些细节,您需要查看滚动搜索文档以获取更全面的描述。
从SOLR-5463中的Solr 4.7开始,Solr还支持深度分页。它增加了对与搜索请求一起使用的cursorMark
参数的支持。然后,Solr 返回指向每个后续页面的nextCursorMark
。
请参阅 Solr 的结果分页文档中的"使用游标"部分。
听起来 OP 已经熟悉这些选项,但我认为为了其他有类似问题的人而充实一下是值得的。
同样有趣的是:我对 Elasticsearch 滚动搜索的机制和效率的看法。
如果它对处理相同问题的任何人有帮助,这是我要采用的解决方案。
我正在使用带有自定义收集器的Lucene,该收集器存储所有匹配的ID,而无需任何处理:
class IDCollector : Collector
{
// Offset for multiple reader
private int docBase;
// Stores IDs for all hits
public List<int> HitList { get; set; }
public IDCollector()
{
this.HitList = new List<int>(INITIAL_CAPACITY);
}
public override void Collect(int doc)
{
HitList.Add(doc + docBase);
}
// Order of docs does not matter
public override bool AcceptsDocsOutOfOrder { get { return true; } }
// Default implementation, docBase is the offset from reader
public override void SetNextReader(IndexReader reader, int docBase)
{
this.docBase = docBase;
}
// Scoring is not necessary
public override void SetScorer(Scorer scorer) { }
}
这样,对于像term1* OR term2* OR term3* OR term4*
这样的查询,可以在大约 5.5 秒内为每个匹配的文档收集所有 ~30mio ID。
不幸的是,但可能是不可避免的,即使没有任何评分、排序或类似的点击处理,搜索速度也非常依赖于点击次数。