我们的数据库包含包含大量元数据的文档,包括这些文档之间的关系。虚构的例子:
<document>
<metadata>
<document-number>ID 12345 : 2012</document-number>
<publication-year>2012</publication-year>
<cross-reference>ID 67890 : 1995</cross-reference>
<cross-reference>ID 67890 : 1998</cross-reference>
<cross-reference>ID 67891 : 2000</cross-reference>
<cross-reference>ID 12345 : 2004</cross-reference>
<supersedes>ID 12345 : 2004</supersedes>
...
</metadata>
</document>
<document>
<metadata>
<document-number>ID 12345 : 2004</document-number>
<publication-year>2004</publication-year>
<cross-reference>ID 67890 : 1995</cross-reference>
<cross-reference>ID 67890 : 1998</cross-reference>
<cross-reference>ID 67891 : 2000</cross-reference>
<cross-reference>ID 12345 : 2012</cross-reference>
<cross-reference>ID 12345 : 2001</cross-reference>
<superseded-by>ID 12345 : 2012</superseded-by>
<supersedes>ID 12345 : 2001</supersedes>
...
</metadata>
</document>
我们正在使用基于Marklogic搜索API的1-box搜索,以允许用户搜索这些文档。搜索语法描述了各种约束和搜索选项,但大多数(并且默认情况下)它们通过定义为包含大多数元数据元素的字段进行搜索,并且(有些)精心选择权重(这里真正重要的是{{1权重最高。)
问题是业务需要非常具体的结果排序,我想不出使用搜索API实现它的方法。
导致问题的要求是,如果用户搜索与文档编号匹配(例如他们搜索“12345”),则具有该文档编号的所有文档应位于结果集的顶部,按降序日期排序。将它们放在结果集的顶部是很容易的; document-number
的权重最高,因此按分数排序可以正常工作。问题是按日期排序的辅助排序不起作用,因为即使所有document-number
匹配的得分高于其他文档,它们也没有相同的得分,因此它们结束按照搜索词在其余元数据中出现的频率排序;这完全没有意义。
我认为我们真正需要的是一种方法,只需通过与搜索项匹配的最高加权元素来获得搜索API分数结果,而无需参考文档中的任何其他匹配项。我已经看过评分算法了,看不到那样做了;我错过了什么或这是不可能的?显然,它不一定是我们订购的document-number
;如果有其他方法可以获得文档中单个最佳匹配的分数并将其用于排序,那就没问题了。
还有其他一些我甚至没想过的解决方案吗?
我想过做两次搜索(一次在score
上,一次在整个元数据树上),然后将结果组合起来,但这似乎会给分页和性能造成很大的痛苦。哪种方式首先打败了使用搜索API的目的。
我应该补充一点,在结果集中包含其他匹配是正确的,因此我们不能只搜索document-number
。
答案 0 :(得分:3)
我认为您已经达到了高级搜索API可以为您做的限制。不过,我有一些技巧可供建议。这些不是100%强大,但它们可能对业务来说足够好。然后你可以继续申请。对不起,如果我听起来愤世嫉俗或不屑一顾,但我不相信微观管理搜索结果。
最简单:重新排序内存中的第一页。第一页可能比您向用户显示的页面大一点。因为它的大小仍然有限,你可以制定相当复杂的规则而不会受到太大影响。这将解决你的“降序日期”问题。第1页的结果与第2页不完全匹配,但这可能已经足够了。
在复杂性的下一步,考虑使用文档质量来处理降序日期问题。 http://markmail.org等使用此方法。在插入或更新每个文档时,使用从日期派生的数字设置文档质量。这可能是自1970年以来的数天或数周或数月,或使用其他固定日期。较新的结果将倾向于浮动到顶部。如果任何其他提升倾向于淹没基于日期的提升,你可能会接近你想要的。
在分析查询以提取潜在的提升术语时可能还有一些用处。如有必要,您可以在每个提升术语上开始递归运行xdmp:exists(cts:search(doc(), $query))
,就好像它是一个独立的查询一样。一旦找到true()
结果就会纾困:这意味着您将以极高的权重提升该查询字词,使其浮动到顶部。
一旦你知道提升术语是什么,重写整个查询以将所有其他术语权重设置为更低的值,甚至可能为0.权重越低,那些非提升术语将干扰基于日期的术语越少质量和增加重量。如果没有提升期限,您可能需要进行其他调整。顺便说一句,这一切都比听起来便宜。除了xdmp:exists
调用之外,它只是内存表达式评估。
但是,这些只是推动分数的伎俩。他们不会给你绝对控制你正在寻找的排名。根据我的经验,微观管理分数的尝试注定要失败。我敢打赌,无论您的业务经理说什么,您的用户都会对原始TF / IDF感到满意。
答案 1 :(得分:2)
另一种方法是按照你的建议使用两次搜索。在文档编号(最好是文档日期)上放置范围索引,从查询中提取任何潜在的文档编号值(search:parse
,提取,然后search:resolve
是一个好策略),然后执行cts:元素范围查询,用于匹配具有日期降序的文档编号值的文档。如果没有足够的结果来填充您的N结果页面,那么从搜索API获取下一个N-x结果。您可以跟踪第一个结果集中返回的文档,并从第二个结果集中排除这些URI。跟踪分页并不会太糟糕。
这可能不如第一个解决方案那么好,但是对于大多数解决方案而言,附加范围索引查询与较短搜索API查询的时间差异应该可以忽略不计。