如何使用Solr Provider控制Sitecore ContentSearch中嵌套查询的优先级?

版本详细信息:我正在使用Siter 7.5 build 141003,使用Solr v4.7作为搜索引擎/索引服务器。 我也使用标准的Sitecore Solr提供程序,没有自定义索引器。

目标目标:我使用Sitecore ContentSearch LINQ和PredicateBuilder来编译一些灵活的嵌套查询。 目前,我需要在特定的“根项目”中进行搜索,同时排除名称中带有“文件夹”的模板,也不包括路径中带有“/ testing”的项目。 在某些时候,“根项目”可能不止一个项目,路径也可以包含(目前只是“/ testing”。在这些情况下,想法是使用PredicateBuilder构建一个外部“AND”谓词与内部“或“s”表示多个“根项目”和路径排除项。

问题:目前,我正在处理有关嵌套顺序和这些谓词/条件优先级的问题。 我一直在测试几种方法和组合,但我一直遇到的问题是!TemplateName.Contains和Item [“_ fullpath”]。包含优先于Paths.Contains,最终每次产生0结果。

我正在使用Search.log检查查询输出,我一直在手动测试Solr管理员,对它运行查询以比较结果。 下面,您将找到我尝试使用Sitecore Linq的组合示例,以及它们为Solr生成的查询。

原始代码示例:

使用列表进行根项目的原始测试

// sometimes will be 1, sometimes will be multiple var rootItems = new List { pathID }; // simplified to 1 item for now var query = context.GetQueryable(); var folderFilter = PredicateBuilder.True().And(i => !i.TemplateName.Contains("folder") && !i["_fullpath"].Contains("/testing")); var pathFilter = PredicateBuilder.False(); pathFilter = rootItems.Aggregate(pathFilter, (current, id) => current.Or(i => i.Paths.Contains(id))); folderFilter = folderFilter.And(pathFilter); query.Filter(folderFilter).GetResults(); 

查询输出:( – _ plalatename:(* folder *)AND -_fullpath:(* / testing *))AND _path:(730c169987a44ca7a9ce294ad7151f13)

正如您在上面的输出中所看到的,在两个“不包含”filter周围有一组内部括号,它们优先于Path 1。 当我在Solr管理员中运行此确切查询时,它返回0结果。 但是,如果我删除了内括号,因此它只是一个“AND”集,它会返回预期的结果。

我使用PredicateBuilder的不同组合和方法进一步测试了这一点,并且每个组合产生相同的查询。 我甚至尝试将两个单独的filter(“query.Filter(pred1).Filter(pred2)”)添加到我的主查询对象,它会产生相同的输出。

其他代码示例:

Alt键。 1 – 直接将“Paths.Contains”添加到文件夹filter

 var query = context.GetQueryable(); var folderFilter = PredicateBuilder.True().And(i => !i.TemplateName.Contains("folder") && !i["_fullpath"].Contains("/testing")); folderFilter = folderFilter.And(i => i.Paths.Contains(pathID)); query.Filter(folderFilter).GetResults(); 

查询输出:( – _ plalatename:(* folder *)AND -_fullpath:(* / testing *))AND _path:(730c169987a44ca7a9ce294ad7151f13)

替代2 – 两个谓词加入第一个

 var query = context.GetQueryable(); var folderFilter = PredicateBuilder.True().And(i => !i.TemplateName.Contains("folder") && !i["_fullpath"].Contains("/testing")); var pathFilter = PredicateBuilder.False().Or(i => i.Paths.Contains(pathID)); folderFilter = folderFilter.And(pathFilter); query.Filter(folderFilter).GetResults(); 

查询输出:( – _ plalatename:(* folder *)AND -_fullpath:(* / testing *))AND _path:(730c169987a44ca7a9ce294ad7151f13)

Alt 3 – 两个“内部”谓词,一个用于“Not”,一个用于“Paths”连接到外部谓词

 var query = context.GetQueryable(); var folderFilter = PredicateBuilder.True().And(i => !i.TemplateName.Contains("folder") && !i["_fullpath"].Contains("/testing")); var pathFilter = PredicateBuilder.False().Or(i => i.Paths.Contains(pathID)); var finalPredicate = PredicateBuilder.True().And(folderFilter).And(pathFilter); query.Filter(finalPredicate).GetResults(); 

查询输出:( – _ plalatename:(* folder *)AND -_fullpath:(* / testing *))AND _path:(730c169987a44ca7a9ce294ad7151f13)

结论:最终,我正在寻找的是控制这些嵌套查询/条件的优先级的方法,或者我如何构建它们以将路径放在第一位,以及之后的“Not”filter。 如上所述,在某些条件下,我们将有多个“根项”和多个路径排除项,我需要查询更多类似的内容:

(-_templatename:(* folder *)AND -_fullpath:(* / testing *)AND(_path:(730c169987a44ca7a9ce294ad7151f13)OR _path:(12c1aa7f60fa4e8d9f0a983bbbb40d8b)))

要么

(-_templatename:(* folder *)AND -_fullpath:(* / testing *)AND(_path:(730c169987a44ca7a9ce294ad7151f13)))

当我直接在Solr管理员中运行它们时,这两个查询都会返回我期望/需要的结果。 但是,我似乎无法想出使用Sitecore ContentSearch Linq以这种方式输出查询的方法或操作顺序。

有没有其他人有我如何实现这一目标的经验? 根据建议,我也愿意在没有Sitecore Linq的情况下收集这条查询,如果我可以将它结合回IQueryable来调用“GetFacets”和“GetResults”。

更新:我没有包括我所做的所有修改,因为这可能会让我失去多长时间。 也就是说,我在原始示例(顶部)上尝试了另一个轻微变化,其结果与其他示例类似:

 var folderFilter = PredicateBuilder.True().And(i => !i.TemplateName.Contains("folder")).And(i => !i["_fullpath"].Contains("/testing")); var rootItems = new List { pathID, path2 }; // or paths separately var pathFilter = PredicateBuilder.False(); pathFilter = rootItems.Aggregate(pathFilter, (current, id) => current.Or(i => i.Paths.Contains(id))); var finalPredicate = folderFilter.And(pathFilter); var query = context.GetQueryable(); query.Filter(finalPredicate).GetResults(); 

查询输出: (( – _ plalatename:(* folder *)AND -_fullpath:(* / testing *))AND(_path:(730c169987a44ca7a9ce294ad7151f13)OR _path:(12c1aa7f60fa4e8d9f0a983bbbb40d8b)))

它仍然是围绕“_templatename”和“_fullpath”条件引起问题的内部括号。

谢谢。

好吧,我在这里提出了这个问题,并将情况发布到Sitecore支持,我刚收到回复和一些其他信息。

根据Solr wiki( http://wiki.apache.org/solr/FAQ ),在“搜索”部分,问题为什么’foo AND -baz’匹配文档,但’foo AND(-bar)’不是吗? 回答为什么结果会回来0。

布尔查询必须至少有一个“正”表达式(即; MUST或SHOULD)才能匹配。 Solr试图帮助解决这个问题,如果要求执行一个在最顶层只包含否定子句的BooleanQuery,它会添加一个匹配所有文档查询(即::)

如果顶级BoolenQuery包含一个嵌套的BooleanQuery,其中只包含negated子句,那嵌套查询将不会被修改,并且(根据定义)它不匹配任何文档 – 如果需要,则表示外部查询将不匹配。

我不确定在Sitecore Solr提供程序中构造查询的完全是什么,或者他们为什么在嵌套查询中将负数分组在一起,但是只有负数的嵌套查询会按预期返回0结果,根据Solr文档。 然后,诀窍是在子查询中添加“全部匹配”查询(*:*)。

对于我认为可能遇到这种情况的任何查询,不必手动执行此操作,支持代表提供了补丁DLL来替换提供程序,它将自动修改嵌套查询以解决此问题。

他们还将此记录为错误,并提供了该问题的参考号398622

现在,生成的查询如下所示:

 ((-_templatename:(*folder*) AND -_fullpath:(*/testing*) AND *:*) AND _path:(730c169987a44ca7a9ce294ad7151f13)) 

或者,对于多个查询:

 ((-_templatename:(*folder*) AND -_fullpath:(*/testing*) AND *:*) AND (_path:(730c169987a44ca7a9ce294ad7151f13) OR _path:(12c1aa7f60fa4e8d9f0a983bbbb40d8b))) 

结果按预期返回。 如果有其他人遇到这个,我会使用Sitecore支持的参考号,看看他们是否可以提供补丁。 您还必须更新Solr.Index和Solr.Indexes.Analytics配置文件中使用的提供程序。

如果最后的两个工作样本是正确的,那么你需要将查询的各个部分分开,而不是在一次调用中包含2个语句,这就是导致语句的初始部分嵌套的原因:

 // the path part of the query. OR together all the locations var pathFilter = PredicateBuilder.False(); pathFilter = pathFilter.Or(i => i.Paths.Contains(pathID)); pathFilter = pathFilter.Or(i => i.Paths.Contains(pathID2)); ... // the exclusions, build them up seprately var query = PredicateBuilder.True(); query = query.And(i => !i.TemplateName.Contains("folder")); query = query.And(i => !i["_fullpath"].Contains("/testing")); // join both parts together query = query.And(pathFilter); 

这应该给你(伪):

 !templateName.Contains("folder") AND !_fullpath.Contains("/testing") AND (path.Contains(pathID1) || path.Contains(pathID2)) 

如果您尝试排除某些模板,则可以通过更新Sitecore.ContentSearch.Solr.DefaultIndexConfiguration.config中ExcludeTemplate设置将它们排除在ExcludeTemplate位置的索引中。 您无需担心在查询中明确排除它:

  {11111111-1111-1111-1111-111111111111} {22222222-2222-2222-2222-222222222222}  

我尝试了下面的代码,它确实产生了你需要的输出查询,诀窍是在创建Pathfilter查询时使用PredicateBuilder.True() ,不确定这是来自Content Search API的正常行为,还是它的bug

 var query = context.GetQueryable(); var folderFilter = PredicateBuilder.True().And(i => !i.TemplateName.Contains("folder") && !i["_fullpath"].Contains("/testing")); var pathFilter = PredicateBuilder.True(); pathFilter = pathFilter.Or(i => i.Paths.Contains(Path1) || i.Paths.Contains(Path2)); folderFilter = folderFilter.And(pathFilter);