hushidh
hushidh

Reputation: 11

How can I convert a BooleanQuery that uses a RangeQuery when migrating to the new version of Lucene?

I have an application that I inherited (using Sitecore CMS). We just recently upgraded Sitecore which required us to use a more recent version of Lucene.Net. Some of our old code broke. Problem is, I can't quite figure out what the code was trying to do. I'm not familiar with Lucene queries at all. In particular, I know that our RangeQueries have to now be TermRangeQueries, but I'm stuck when it comes to re-writing this code because I can't find an alternative for the BooleanQuery and it won't accept a TermRangeQuery as an input.

     BooleanQuery lQuery = new BooleanQuery();
     lQuery.Add(new TermQuery(new Term("_shorttemplateid", string.Format("{0}", ShortID.Encode(templateId).ToLowerInvariant()))),
                Lucene.Net.Search.Occur.MUST);
     if (string.IsNullOrEmpty(endDateItemFieldName))
     {
         lQuery.Add(
            new RangeQuery(
               new Term(startDateItemFieldName, startDateTime),
               new Term(startDateItemFieldName, endDateTime), true),
               Lucene.Net.Search.Occur.MUST);
     }
     else
     {
         lQuery.Add(
            new RangeQuery(
               new Term(startDateItemFieldName, startDate.ToString(DATE_TIME_FORMAT)),
               new Term(startDateItemFieldName, string.Format("{0}{1}", endDate.ToString("yyyyMMdd"), endTimeStamp)), true),
               Lucene.Net.Search.Occur.SHOULD);
         lQuery.Add(
            new RangeQuery(
               new Term(endDateItemFieldName, startDate.ToString(DATE_TIME_FORMAT)),
               new Term(endDateItemFieldName, string.Format("{0}{1}", endDate.ToString("yyyyMMdd"), endTimeStamp)), true),
               Lucene.Net.Search.Occur.MUST);
     }

Upvotes: 0

Views: 369

Answers (2)

Ahmed Okour
Ahmed Okour

Reputation: 2422

First, take some time to read how the new Search API with Sitecore 7+ works:

http://www.sitecore.net/Learn/Blogs/Technical-Blogs/Sitecore-7-Development-Team/Posts/2013/06/Sitecore-7-POCO-Explained.aspx

https://www.sitecore.net/Learn/Blogs/Technical-Blogs/Sitecore-7-Development-Team/Posts/2013/04/Sitecore-7-Patterns-for-Global-Search-Context-Reuse.aspx

Second, To rewrite your code in your example, create the following:

public class CustomSearchResult : Sitecore.ContentSearch.SearchTypes.SearchResultItem
{
    [IndexField("START DATE FIELD NAME")]
    public virtual DateTime EndDate {get; set;}

    [IndexField("END DATE FIELD NAME")]
    public virtual DateTime StartDate {get; set;}
} 

Now, You can perform your search as follow:

using Sitecore.ContentSearch;
using Sitecore.ContentSearch.Linq;
using Sitecore.ContentSearch.SearchTypes;
using Sitecore.ContentSearch.Linq.Utilities

using (var context = ContentSearchManager.GetIndex("sitecore_web_index").CreateSearchContext())
            {
                var results = context.GetQueryable<CustomSearchResult>().Where(i => i.TemplateId == Sitecore.Data.ID.Parse("TEMPLATE GUID") && i.StartDate > StartDateObject && i.EndDate < EndDateObject).GetResults().Hits.Select(i => i.Document.GetItem()).ToList();
                return results;
            }

Please note that StartDateObject and EndDateObject should be of type DateTime.

Hope this helps.

Upvotes: 0

Derek Hunziker
Derek Hunziker

Reputation: 13141

The code from your example is building a Lucene query using the following logic:

Pseudo-code:

Match all documents 

     That have a specific template ID

     AND

         IF an endDateItemFieldName is present

             The start date must be between date X and Y

         ELSE

             The start date can be between date X and Y
             But the end date must be between date X and Y

Behind the scenes, this results in a Lucene query that looks something similar to this:

+_shorttemplateid:3f2504e04f8941d39a0c0305e82c3301 start:[20020101 TO 20030101] +end:[20020101 TO 20030101]

In Sitecore 7+, much of the "Luceneness" has been abstracted away and is generated for you by a LINQ search provider. This allows you to switch between search implementations (for example, Solr) without any substantial refactoring of your code. Because LINQ is so widely known, working with the LINQ provider is often much easier for developers to grasp.

Here is an equivalent search query using the new LINQ provider.

using Sitecore.ContentSearch;
using Sitecore.ContentSearch.Converters;
using Sitecore.ContentSearch.SearchTypes;
using System;
using System.ComponentModel;

namespace YourNamespace
{
    public class DateSearchResultItem : SearchResultItem
    {
        [IndexField("startdate"), TypeConverter(typeof(IndexFieldDateTimeValueConverter))]
        public DateTime StartDate { get; set; }

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

And an example usage:

ISearchIndex index = ContentSearchManager.GetIndex("sitecore_web_index");
using (var context = index.CreateSearchContext())
{
    var results = context.GetQueryable<DateSearchResultItem>()
        .Where(item => item.TemplateId == new ID(templateId));

    if (String.IsNullOrEmpty(endDateItemFieldName))
    {
        results = results
            .Where(item => item.StartDate >= startDateTime)
            .Where(item => item.StartDate <= endDateTime);
    }
    else
    {
        results = results
            .Where(item => item.EndDate >= startDateTime)
            .Where(item => item.EndDate <= endDateTime);
    }

    var compiledQuery = results.GetResults();
    int totalMatches =  compiledQuery.TotalSearchResults;

    foreach (var hit in compiledQuery.Hits)
    {
        Item item = hit.Document.GetItem();
    }
}

Upvotes: 1

Related Questions