Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sitecore ContentSearch LINQ Unsupported expression node type: Parameter

I get the following exception message when trying to run the ToList method for the Sitecore ContentSearch LINQ query in the following code block:

Unsupported expression node type: Parameter. This could be due to ordering of   
expression statements. For example filtering after a select expression like this : 
queryable.Select(i => new { i.Id, i.Title }).Where(i => d.Title > "A"  

public virtual List<SearchResultItem> RunQuery(SearchParam param, bool showAllVersions, bool firstLoad)
{
    Assert.ArgumentNotNull(Index, "Sitecore.SharedSource.Search");
    var resultCollection = new List<SearchResultItem>();

    try
    {
        using (var context = this.Index.CreateSearchContext())
        {
            var result = context.GetQueryable<SearchResultItem>()
                                                .Where(x => HasFullText(x, param.FullTextQuery) &&
                        HasLanguage(x, param.Language) &&
                        HasRelation(x, param.RelatedIds) &&
                        HasTemplate(x, param.TemplateIds) &&
                        HasLocation(x, param.LocationIds)
                                                );
            resultCollection = result.ToList();
        }
    }
    catch (Exception exception)
    {
        Log.Error(exception.StackTrace, this);
        throw;
    }

    return resultCollection;
}

I can't figure out what causes this issue and I can't seem to reproduce the issue with standard .NET LINQ queries, in a standard Console Application (source code at the end).

Here is the source code for the HasLanguage, HasRelation, HasTemplate and HasLocation functions. I expect it has something to do with those because when I remove them and replace them with their implementation (where possible) I get no errors. However, when left inside the query they are not even accessed (tried debugging):

    protected bool HasRefinements(SearchResultItem pseudoResult, SafeDictionary<string> refinements)
    {
        if (refinements.Count <= 0) return false;
        foreach (var refinement in refinements)
        {
            var fieldName = refinement.Key.ToLowerInvariant();
            var fieldValue = refinement.Value;
            if (pseudoResult.GetField(fieldName).Value.Equals(IdHelper.ProcessGUIDs(fieldValue)))
            {
                return true;
            }
        }

        return false;
    }

    protected bool HasLanguage(SearchResultItem pseudoResult, string language)
    {
        if (String.IsNullOrEmpty(language)) return false;
        return pseudoResult.GetField(BuiltinFields.Language).Equals(language.ToLowerInvariant());
    }

    protected bool HasFullText(SearchResultItem pseudoResult, string searchText)
    {
        if (String.IsNullOrEmpty(searchText)) return false;
        return pseudoResult.Content.Contains(searchText);
    }

    protected bool HasId(SearchResultItem pseudoResult, string fieldName, string filter)
    {
        if (String.IsNullOrEmpty(fieldName) || String.IsNullOrEmpty(filter)) return false;
        var values = IdHelper.ParseId(filter);
        foreach (var value in values.Where(ID.IsID))
        {
            if (pseudoResult.GetField(fieldName).Value.Equals(IdHelper.ProcessGUIDs(value)))
            {
                return true;
            }
        }

        return false;
    }

    protected bool HasTemplate(SearchResultItem pseudoResult, string templateIds)
    {
        if (String.IsNullOrEmpty(templateIds)) return false;

        templateIds = IdHelper.NormalizeGuid(templateIds);
        return pseudoResult.TemplateId.ToString().Equals(templateIds);
    }

    protected bool HasLocation(SearchResultItem pseudoResult, string locationIds)
    {
        return HasId(pseudoResult, BuiltinFields.Path, locationIds);
    }

    protected bool HasRelation(SearchResultItem pseudoResult, string ids)
    {
        return HasId(pseudoResult, BuiltinFields.Links, ids);
    }  

And here is the source code for my test application using regular LINQ queries:

    static void Main(string[] args)
    {
        Program p = new Program();
        p.Process();
    }

    public void Process()
    {
        List<Boolean> flags = new List<Boolean>();
        flags.Add(true);
        flags.Add(false);
        flags.Add(false);
        flags.Add(true);
        flags.Add(false);
        bool b = true;
        try
        {
            List<Boolean> trueFlags = flags
                                        .Where<Boolean>(x => IsTrue(x, b))
                                        .ToList();
            Console.WriteLine(trueFlags.ToString());
            Console.ReadKey();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.StackTrace);
        }
    }

    public bool IsTrue(bool x, bool b)
    {
        return x ^ b;
    }  

I can't seem to find anything on this exception message on the internet.

like image 499
Bogdan Avatar asked Dec 25 '22 13:12

Bogdan


1 Answers

Sitecore LINQ isn't really like normal LINQ to objects in .NET. Like LINQ to SQL isnt like normal LINQ.

What actually happens is that Sitecore parses the expression tree and "converts/translates" your conditions to a Search Query. By default this is to a Lucene query expression - but using a SearchProvider it could also translate to a SOLR expression or Coveo expression. In the same way LINQ to SQL translates to a SQL expression. You can see that the LINQ is actually an IQueryable and not IEnumerable.

When working on the IQuerable Sitecore must know how to translate it to the search expression. Sitecore doesn't know how to translate your properties and methods in the LINQ expression and that is why you get the error.

You should change your expression to only hold something that can be translated or create a predicate. You should look into the PredicateBuilder

like image 51
Jens Mikkelsen Avatar answered Dec 28 '22 10:12

Jens Mikkelsen