Haydn Chapman
Haydn Chapman

Reputation: 176

Query lucene index against IEnumerable field

I have a set of courses in a Lucene index, which contain zero or more available dates. I am trying to query the index so that any courses with an available date falling between a From/To date are returned.

The field is being brought back as a collection of DateTime objects using the property within my CourseSearchResultItem

 public class CourseSearchResultItem : SearchResultItem
 {
     [IndexField("availabledatesforcourse")]
     public IEnumerable<DateTime> AvailableDatesForCourse { get; set;}
 }

The query that is being constructed uses the following code:

 if (courseSearchCriteria.FromDate.HasValue)
     query = query.Where(c => c.AvailableDatesForCourse.Any(d => d >= courseSearchCriteria.FromDate.Value));

 if (courseSearchCriteria.ToDate.HasValue)
     query = query.Where(c => c.AvailableDatesForCourse.Any(d => d <= courseSearchCriteria.ToDate.Value));

Running this produces the following exception:

Unsupported lambda in Any call. Expression used 'd => (d >= Value (RCS.Web.Areas.RcsEng.Controllers.CoursesApiController +<>c__DisplayClass10_0).courseSearchCriteria.FromDate.Value)'

I know that this is a result of limitations of the Sitecore.Linq Any() method, but am unsure how to recreate the desired functionality. Has anybody had a similar issue that can shed some light on a solution?

Upvotes: 3

Views: 1149

Answers (2)

Aaron F.
Aaron F.

Reputation: 67

I ran into this same issue and followed Dan's steps which got me part of the way to a solution. The issue I was having was that SitecoreContentSearch was creating a query that Solr couldn't parse. I updated the linq query to this:

if (courseSearchCriteria.FromDate.HasValue && courseSearchCriteria.ToDate.HasValue)
query= query.Where(x => x.AvailableDatesForCourse.Between(courseSearchCriteria.FromDate.Value,courseSearchCriteria.ToDate.Value,Inclusion.Both));

This should create a query that looks like this :

availabledatesforcourse:[2018-07-13T15:48:42.975Z TO 2021-07-13T15:48:43.023Z]

This allows me to search a list of dates for any value that falls into this range.

Upvotes: 0

Dan Solovay
Dan Solovay

Reputation: 3154

Try the following.

Change your class to look like this:

 public class CourseSearchResultItem : SearchResultItem
 {
     [IgnoreIndexFieldAttribute]
     [IndexField("availabledatesforcourse")]
     public DateTime AvailableDatesForCourse { get; set;}
 }

Now change your query to look like this:

if (courseSearchCriteria.FromDate.HasValue)
     query = query.Where(c => c.AvailableDatesForCourse == courseSearchCriteria.FromDate.Value);

if (courseSearchCriteria.ToDate.HasValue)
 query = query.Where(c => c.AvailableDatesForCourse == courseSearchCriteria.ToDate.Value));

This sort of syntax works for values that are stored multiple times in a search document (for example, the _path field, which stores the GUIDs of all the parent items separately). It will be translated by the ContentSearch API into a query that Lucene can understand, something like this:

AvailableDatesForCourse:<your date>

Update The [IgnoreIndexFieldAttribute] will prevent a mapping error when ContentSearch hydrates your class, but will not prevent the field from being used in a query. I wrote a LinqScratchPad Gist to illustrate this, which you can load in the Sitecore LinqScratchPad at /sitecore/admin/LinqScratchPad.aspx, either by pasting it in or using the URL Fetch button. This finds all items that have "Media Item" in the name:

LinqScratchPad example

Upvotes: 5

Related Questions