Sitecore Search Predicate Builder multiple keyword search with boosting not working as desired

荒凉一梦 提交于 2019-12-05 05:39:18
David Masters

I managed to get this to work with the following:

    public static Expression<Func<T, bool>> GetSearchTermPredicate<T>(string searchTerm) 
        where T : ISearchableItem
    {
        var actualPhraseInTitlePredicate = PredicateBuilder.True<T>()
            .And(r => r.Title.Contains(searchTerm));

        var actualPhraseInFileNamePredicate = PredicateBuilder.True<T>()
            .And(r => r.FileName.Contains(searchTerm));

        var actualPhraseInContentPredicate = PredicateBuilder.True<T>()
            .And(r => r.Content.Contains(searchTerm));

        var actualPhraseInDocumentPredicate = PredicateBuilder.True<T>()
            .And(r => r.DocumentContents.Contains(searchTerm));

        var terms = searchTerm.Split(' ');

        var titleContainsAllTermsPredicate = PredicateBuilder.True<T>();

        foreach (var term in terms)
            titleContainsAllTermsPredicate 
                = titleContainsAllTermsPredicate.And(r => r.Title.Contains(term).Boost(2f));

        var fileNameAllTermsContains = PredicateBuilder.True<T>();

        foreach (var term in terms)
            fileNameAllTermsContains 
                = fileNameAllTermsContains.And(r => r.FileName.Contains(term));

        var contentContainsAllTermsPredicate = PredicateBuilder.True<T>();

        foreach (var term in terms)
            contentContainsAllTermsPredicate 
                = contentContainsAllTermsPredicate.And(r => r.Content.Contains(term));

        var documentContainsAllTermsPredicate = PredicateBuilder.True<T>();

        foreach (var term in terms)
            documentContainsAllTermsPredicate 
                = documentContainsAllTermsPredicate.And(r => r.DocumentContents.Contains(term));


        var predicate = actualPhraseInTitlePredicate.Boost(3f)
            .Or(actualPhraseInFileNamePredicate.Boost(2.5f))
            .Or(actualPhraseInContentPredicate.Boost(2f))
            .Or(actualPhraseInDocumentPredicate.Boost(1.5f))
            .Or(titleContainsAllTermsPredicate.Boost(1.2f))
            .Or(fileNameAllTermsContains.Boost(1.2f))
            .Or(contentContainsAllTermsPredicate)
            .Or(documentContainsAllTermsPredicate);

        return predicate;
    }

It's obviously quite a bit more code, but I think separating the predicates makes more sense for boosting to work effectively.

The main issue with the previous code was two fold:

  1. PredicateBuilder.Or(actualPhrasePredicate.Boost(2f), individualWordsPredicate) doesn't seem to include the predicate being Or'd. When doing a .ToString() on the resulting joined predicate, the expression didn't contain anything for the individualWordsPredicate
  2. After fixing that it still didn't work, and this was because I was using PredicateBuilder.False<T>() for the individualWordsPredicate. When looking at the expression it was basically producing (False AND Field.Contains(keyword)) which of course will never evaluate to true. Using .True<T>() fixed this.
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!