Query Sitecore Lucene-index with ContentSearch-API

2019-05-10 09:50发布

问题:

At this moment i'm working on a project to implement Sitecore 7.0 Update 2

In my data template i have this field called Begin Date and another one End Date. These two fields are created with the type 'Date' (not Datetime). So it shows a datepicker when i edit and create items and i've filed some items with dummy-content and with Begin and End date from last month and the current month.

What i want to achieve is to get all the items within a selected month. My method contains a month and a year integer as parameter. This should control the items with the begin and end date it should get from the Lucene sitecore_master_index. The raw values of Sitecore for the Date-field are the ISO datetime-strings.

So this is the query where i try to get all items from that selected month.

private void GetItems(int month, int year)
{
    using (
        IProviderSearchContext context =
                ContentSearchManager.
GetIndex("sitecore_master_index").CreateSearchContext())
    {
         List<EventSearchResultItem> allEvents =     context.GetQueryable<EventSearchResultItem>(new     CultureExecutionContext(Sitecore.Context.Language.CultureInfo))
         .Where(s =>
                s.TemplateId == this.EventTemplateID &&
                ((s.BeginDate.Month == month && s.BeginDate.Year == year) || (s.EndDate.Month == month && s.EndDate.Year == year))
                    )
        .ToList();
    }
}

With this Where-statement i expect to return all items of the Events-template somewhere that should contain a date within that month. But it returns an empty resultset. Downside is that i can't debug the Lamba-expression, so unfortunately i don't know the value in that scope. But not something between year 1 and 9999 :) After the IQueryable has executed the query and returned something, the objects in the list contains the correct DateTime for the properties. So it maps the fields from the index correctly to the properties. Also if i remove the date-check so only the TemplateID check is the Where-clause, it returns results. But even comparing BeginDate to DateTime.MinValue and DateTime.MaxValue doens't return something.

It uses the POCO-class EvenSearchResultItem which i've created for this. In this class i've mapped the field to the property and added the TypeConverter to convert it to DateTime. At least, it should...

public class EventSearchResultItem : SearchResultItem
{
    [TypeConverter(typeof(IndexFieldDateTimeValueConverter))]
    [IndexField("__begin_date")]
    public DateTime BeginDate { get; set; }

    [TypeConverter(typeof(IndexFieldDateTimeValueConverter))]
    [IndexField("__end_date")]
    public DateTime EndDate { get; set; }
}

And in the Sitecore.ContentSearch.Lucene.DefaultIndexConfiguration.config i've added the field within the -tag (also tried -tag, but not that different in result). See:

<field luceneName="__begin_date" storageType="yes" indexType="tokenized" format="yyyyMMdd">Begin Date</field>
<field luceneName="__end_date" storageType="yes" indexType="tokenized" format="yyyyMMdd">End Date</field>

So in Luke (the Java-application to view the contents of a Lucene-index) after an re-index the field appears and contains the given date for that item (in yyyyMMdd > 20140214). Just like other date-fields such as __smallCreatedDate.

I can solve this by modifying my query to:

List<EventSearchResultItem> allEvents = context.GetQueryable<EventSearchResultItem>()
    .Where(s =>
        (s["Begin Date"].StartsWith(string.Concat(year, month.ToString("00"))) || s["End Date"].StartsWith(string.Concat(year, month.ToString("00"))))
    )
    .ToList();

This was a solution in another question here on Stack Overflow. But i don't consider that as best practice and reliable. Google didn't had any other alternatives unfortunately. I guess something as i imagine it would be possible right?

Does anyone has experience filtering Lucene-results on DateTime from IQueryable? And can point me in the right direction?

回答1:

Try the following :

private void GetItems(int month, int year)
        {
            DateTime startDate = new DateTime(year,month,1);
            DateTime endDate = new DateTime(year,month, DateTime.DaysInMonth(year, month));
            using ( IProviderSearchContext context = ContentSearchManager.GetIndex("sitecore_master_index").CreateSearchContext())
            {
                List<EventSearchResultItem> allEvents = context.GetQueryable<EventSearchResultItem>(new CultureExecutionContext(Sitecore.Context.Language.CultureInfo))
                .Where(s =>
                       s.TemplateId == this.EventTemplateID &&
                       ((s.BeginDate >= startDate) || (s.EndDate <= endDate))
                           )
               .ToList();
            }
        }

Edit: Just to explain why you approach didn't work, when lucene index any date field, it gets indexed as number, the format will be 'yyyyMMdd', for example 18 Feb 2014 is indexed as 20140218, so as you can see it get stored as a whole number, and the year,month and day are in the same field, so you can't compare with year only or month only etc.

Now in Sitecore linq, if you want to query against a date field, you MUST compare with a 'DateTime' type, Sitecore knows that DateTime object must be converted to 'yyyyMMdd' format before passing this to Lucene.



回答2:

You could try to remove the underscores from the field definition in the index configuration and in the EventSearchResult item. I've seen issues with this before.

<field luceneName="begindate" storageType="yes" indexType="tokenized" format="yyyyMMdd">Begin Date</field>
<field luceneName="enddate" storageType="yes" indexType="tokenized" format="yyyyMMdd">End Date</field>

[TypeConverter(typeof(IndexFieldDateTimeValueConverter))]
    [IndexField("begindate")]
    public DateTime BeginDate { get; set; }

    [TypeConverter(typeof(IndexFieldDateTimeValueConverter))]
    [IndexField("enddate")]
    public DateTime EndDate { get; set; }