SOLR子查询中实现无过滤权重提升的问题及解决方法
为什么第三个查询达不到预期效果?
问题出在你添加的额外条件上:-_template:(00000000000000000000000000000000)。这个条件只排除了模板ID为全零的文档,但你的原始58条结果里,有很多文档的模板既不属于那三个指定ID,也不是全零模板。这些文档无法满足你后面的OR条件(既不匹配三个模板,也不满足“不是全零模板”?不对,准确说,它们的模板ID不是全零,但也不在三个目标ID里,所以你的OR条件(匹配三个模板) OR (不是全零模板)对这些文档来说是成立的?哦不对,等一下,你的第三个查询里的OR条件是:
((_template:ID1)^35 OR _template:ID2^35 OR _template:ID3^35 OR (-_template:全零ID))
但原始查询已经排除了86b04a1523f542b98561e5780328a2ff这个模板,剩下的文档里,有一部分模板ID既不是三个目标ID,也不是全零ID,也不是被排除的那个ID——这些文档会满足-_template:全零ID吗?是的,但为什么结果还是36条?哦不对,不对,你是用AND把原始查询和这个OR条件连起来的,那如果OR条件里的-_template:全零ID是真的,那整个OR条件就是真的,那这些文档应该被保留啊?哦不,等一下,SOLR里的-_template:X是“文档的_template字段不等于X”,所以对于那些模板ID既不是三个目标,也不是全零,也不是被排除的那个的文档,-_template:全零ID是成立的,那OR条件就成立,那AND原始查询之后应该被保留啊?那为什么结果还是36条?
哦,不对,我搞错了——你的第三个查询里的OR条件是(匹配三个模板) OR (-_template:全零ID),但如果某个文档的模板ID就是全零,那它会被-_template:全零ID排除,而原始查询里没有排除全零模板的文档,所以这部分文档会被过滤掉。但你的原始结果里有22条是全零模板的?不对,原始是58,第三个是36,差22,那可能你的原始结果里有22条是全零模板的?但这不是你要的,你要保留所有原始结果,包括全零模板的。哦,原来如此!你的第三个查询里的-_template:全零ID把原始结果中模板为全零的文档过滤掉了,所以结果少了22条,变成36。
哦对,你要的是保留所有原始结果,所以不能添加任何排除性的条件,包括排除全零模板的。你的思路是错的,不应该排除任何东西,而是要让提升条件对所有原始结果都“通过”,只是对符合模板的加分。
正确的解决方案
核心思路是:把权重提升的条件变成可选匹配(SHOULD),但确保这个条件永远不会过滤掉任何原始结果。在SOLR语法里,就是给提升条件加上一个OR *:*(匹配所有文档),这样整个提升部分的条件永远为真,不会过滤任何文档,同时对匹配三个模板的文档应用权重提升。
正确的SOLR查询示例
?q=+((((-_template:(86b04a1523f542b98561e5780328a2ff) *:*) AND (_name:(*searchterm*) OR _name:(searchterm) OR _name:(searchterm*) OR (_name:(*searchterm*))^20 OR (_name:(searchterm~0.8))^20 OR _content:(searchterm~0.8) OR _description_t:(searchterm~0.8) OR type_t:(searchterm~0.8) OR variant_t:(searchterm~0.8))))) +(((_template:(3aa541dcf9d3471ba5d79d397b3e793c))^35 OR (_template:(a2f633e201ab473ea3a9962106921d27))^35 OR (_template:(060412222e0a475a9b2c3adc9cb7fb2e))^35) OR *:*))&start=0&rows=99&fl=_template,_title_t_nl,score&fq=_indexname:(customindex)&wt=xml
这里的关键改动:
- 用
+标记两个必须满足的条件块(对应布尔查询的MUST) - 第二个条件块里,在三个模板匹配的后面加上
OR *:*——这个*:*匹配所有文档,确保整个条件块永远为真,不会过滤任何原始结果 - 三个模板匹配的权重提升
^35依然生效,只有匹配的文档会获得额外加分
对应Sitecore PredicateBuilder的实现代码
using Sitecore.ContentSearch; using Sitecore.ContentSearch.Linq; using Sitecore.ContentSearch.Linq.PredicateBuilder; using System; // 假设你的搜索结果项类型是自定义的,这里用SearchResultItem示例 var context = ContentSearchManager.GetIndex("customindex").CreateSearchContext(); // 1. 构建原始查询Predicate var originalPredicate = PredicateBuilder.True<SearchResultItem>(); // 排除指定模板 originalPredicate = originalPredicate.And(p => !p.TemplateId.Equals(new Guid("86b04a1523f542b98561e5780328a2ff"))); // 名称、内容等匹配条件 originalPredicate = originalPredicate.And(p => p.Name.Contains("searchterm") || p.Name.Equals("searchterm") || p.Name.StartsWith("searchterm") || p.Name.Contains("searchterm").Boost(20) || p.Name.Like("searchterm", 0.8f).Boost(20) || p.Content.Like("searchterm", 0.8f) || p.Description.Like("searchterm", 0.8f) || p.Type.Like("searchterm", 0.8f) || p.Variant.Like("searchterm", 0.8f) ); // 2. 构建权重提升的Predicate var boostPredicate = PredicateBuilder.False<SearchResultItem>(); // 给三个模板添加权重提升 boostPredicate = boostPredicate.Or(p => p.TemplateId.Equals(new Guid("3aa541dcf9d3471ba5d79d397b3e793c"))).Boost(35); boostPredicate = boostPredicate.Or(p => p.TemplateId.Equals(new Guid("a2f633e201ab473ea3a9962106921d27"))).Boost(35); boostPredicate = boostPredicate.Or(p => p.TemplateId.Equals(new Guid("060412222e0a475a9b2c3adc9cb7fb2e"))).Boost(35); // 添加匹配所有文档的条件(对应SOLR的*:*),确保不会过滤任何结果 boostPredicate = boostPredicate.Or(PredicateBuilder.True<SearchResultItem>()); // 3. 组合两个Predicate:原始查询必须满足,提升条件也必须满足(但提升条件永远为真) var finalPredicate = originalPredicate.And(boostPredicate); // 执行查询并获取结果 var results = context.GetQueryable<SearchResultItem>() .Where(finalPredicate) .Select(p => new { p.TemplateId, p.Title, p.Score }) .GetResults();
总结
- 你之前的错误在于添加了排除全零模板的条件,过滤掉了原始结果中的部分文档
- 正确的做法是让提升条件对所有文档都“通过”,仅对符合模板条件的文档额外加分——通过
OR *:*(PredicateBuilder里的True())实现这一点 - 这样既保留了原始的58条结果,又能让指定模板的文档获得权重提升,排序时会更靠前
内容的提问来源于stack exchange,提问作者Peter M.

