Rick Amsler
Rick Amsler

Reputation: 1

Dynamic lists in Linq Query - solr - Sitecore

So here is the summary:

Sitecore - SOLR index query

I have items that I am trying to retrieve using a set of sites that can vary.

I have a query:

query = query.Where(x => x.Language == this.ItemLanguage)
             .Where(x => x.Templates.Contains(new Guid("94c1f3e5ac174a319cc5bbb942fe80c6")));

this will return all of the items correctly.

What I need is to add a dynamic list call for this query.

Something like:

.Where(x => x.Site.Any(y => siteNames.Contains(y)));

I have tried adding this line of code and I get an error:

System.ArgumentException: 'Argument must be array'

(some details)

Items returned (x) have a field "Site" of List<string>

siteNames is a List<String> of variable site names

The following code works in other places:

.Where(x => x.Site.Contains("somesite"));

Is there a way to manage a dynamic list or will I need to manually generate this expression based on the number of items in the siteNames list?

.Where(x => x.Site.Contains("dynamic") || x.Site.Contains("dynamic")
         || x.Site.Contains("dynamic") || x.Site.Contains("dynamic") 
         || and so on);

Here is the full code example:

using (var context = Sitecore.ContentSearch.ContentSearchManager.GetIndex(AccelConstants.SEARCH_INDEX).CreateSearchContext())
{
            IQueryable<SearchResultModel> query = context.GetQueryable<LMSSearchResultModel>();
            SearchResults<SearchResultModel> results = null;
            List<string> siteNames = new List<string>();
            siteNames = this.SiteNames;
            // Define the base search
            query = query.Where(x => x.Language == this.ItemLanguage)
                         .Where(x => x.Templates.Contains(new Guid("94c1f3e5ac174a319cc5bbb942fe80c6")))
                         .Where(x => x.Site.Any(p => siteNames.Contains(p)));

            // Execute the query
            results = query.GetResults();
}

the site field is a solr field of Site_SM which outputs like this: The reason the name "Site" is used is that Sites is also a field. This is not code that I have control over so I am working with what I have.

"site_sm":["login", "admin", "service", "covid19", "scheduler", "system", "publisher"],

The search results model simply converts computed solr fields to c#

public class SearchResultModel : SearchResultItem
{
    [IndexField("_templates")]
    public List<Guid> Templates { get; set; }

    [IndexField("site_sm")]
    public List<string> Site { get; set; }
}

Upvotes: 0

Views: 1294

Answers (3)

Luisfer.R
Luisfer.R

Reputation: 11

Use Predicate Builder to create a dinamic OR (I just use it to fix this issue). Sitecore have a implementation for this is: //using Sitecore.ContentSearch.Linq.Utilities

private IQueryable<LMSSearchResultModel> LimitSearchBySite(IQueryable<LMSSearchResultModel> query, IEnumerable<string> sites)
{
            if (sites != null && sites.Any())
            {
                // https://www.albahari.com/nutshell/predicatebuilder.aspx
                var predicate = PredicateBuilder.False<StoreLocatorResult>();

                foreach (string s in site)
                    predicate = predicate.Or(p => p.Site.Contains(s));

                return query.Where(predicate);
            }
            return query;
}

if you are using Solr and C# directly the best way is to use this site as reference: https://www.albahari.com/nutshell/predicatebuilder.aspx

At the end is the same development I did but you need to add the PredicateBuilder class to your project

So this will be what you need to replace:

using (var context = Sitecore.ContentSearch.ContentSearchManager.GetIndex(AccelConstants.SEARCH_INDEX).CreateSearchContext())
{
            IQueryable<SearchResultModel> query = context.GetQueryable<LMSSearchResultModel>();
            SearchResults<SearchResultModel> results = null;
            List<string> siteNames = new List<string>();
            siteNames = this.SiteNames;
            // Define the base search
            query = query.Where(x => x.Language == this.ItemLanguage)
                         .Where(x => x.Templates.Contains(new Guid("94c1f3e5ac174a319cc5bbb942fe80c6")));
            query = LimitSearchBySite(query, siteNames);

            // Execute the query
            results = query.GetResults();
}

Upvotes: 1

asontu
asontu

Reputation: 4659

I ran across this exact problem and found this StackOverflow answer searching for the error. It turns out you have to take the error literally and change the List<string> Site to a string[] Site in order to use .Any() in a predicate.

public class SearchResultModel : SearchResultItem
{
    [IndexField("_templates")]
    public List<Guid> Templates { get; set; }

    [IndexField("site_sm")]
    public string[] Site { get; set; }
}

Upvotes: 0

Rick Amsler
Rick Amsler

Reputation: 1

OK, so here is a rather hacked way of making my search work.

using (var context = Sitecore.ContentSearch.ContentSearchManager.GetIndex(AccelConstants.SEARCH_INDEX).CreateSearchContext())
{
     IQueryable<SearchResultModel> query = context.GetQueryable<LMSSearchResultModel>();
     SearchResults<SearchResultModel> results = null;
     List<string> siteNames = new List<string>();
     siteNames = this.SiteNames;

     // Define the base search
     // remove site filtering from query
     query = query.Where(x => x.Language == this.ItemLanguage)
                  .Where(x => x.Templates.Contains(new Guid("94c1f3e5ac174a319cc5bbb942fe80c6")));

     // Execute the query
     results = query.GetResults();

     // Get the results
     foreach (var hit in results.Hits)
     {
           //move site filter code to here
           if (hit.Document != null && hit.Document.Site.Any(p => siteNames.Contains(p)))
           {
                // Add the model to the results.                     
           }
           else
           {

           }
     }
}

By moving the site filter code to after the query, the Site List<string> is instantiated and has value at the time of the call. Which seems to have been my issue.

This then allows me to filter by the sites in the siteNames List<string> and get my final result set.

I don't know if this is the best way of making this section of code work but it does work.

Upvotes: 0

Related Questions