views:

20

answers:

2

Hi folks. I want to sort my lucene(.net) search results by a date field (date1), but if date1 is not set, I'd like to use date2.

The traditional sort method is to sort by date1, and then sort the values that are the same by date 2. This would mean that whenever I did fall back to date2, these values would be at the top (or bottom) of the result set. I'd like to interleave the date2 values with the date1 values.

In other words, I want to sort on (date1 != null ? date1 : date2).

Is this possible in lucene?

I reckon I could do this in the index creation phase (just put the relevant date value in a new field) but I don't have enough control of the indexing process to be able to do this, so would like a sorting solution.

Any ideas?

Thanks Matt

A: 

I have an idea that may work:

  • use Search(Query, Filter, Sort)
  • In order to create the Sort object, use the Sort(SortField[]) constructor
  • Create a SortField for both date fields. for each of them use a ScoreDocComparator to handle the case of null values. In this case the Compare() function will return zero. Please see this blog post about using custom sort in Java Lucene. I believe it is not hard to translate this into Lucene.net.
Yuval F
A: 

Turns out it's very easy. You do have to implement ScoreDocComparator, as Yuval suggested. However, you only need to implement it once (I have a document with two dates, I don't want to sort by date1 then date2, but rather by date1 if it's specified, or date2 if not. Think of an actual date and a provisional date. I want to use the actual date if it's available, but if not, then the provisional date is good enough).

Here's my code:

public class ActualOrProvisionalDateSortComparator : ScoreDocComparator
{
    private readonly StringIndex actualDates;
    private readonly StringIndex provisionalDates;

    public TxOrCreatedDateSortComparator(IndexReader reader, FieldCache fieldCache)
    {
        actualDates = fieldCache.GetStringIndex(reader, "actualDate");
        provisionalDates = fieldCache.GetStringIndex(reader, "provisionalDate");
    }

    public int Compare(ScoreDoc i, ScoreDoc j)
    {
        var date1 = GetValue(i.doc);
        var date2 = GetValue(j.doc);

        return date1.CompareTo(date2);
    }

    public IComparable SortValue(ScoreDoc i)
    {
        return GetValue(i.doc);
    }

    public int SortType()
    {
        return SortField.CUSTOM;
    }

    private string GetValue(int doc)
    {
        return actualDates.Lookup[actualDates.Order[doc]] ?? provisionalDates.Lookup[provisionalDates.Order[doc]];
    }
}

My ActualOrProvisionalDateSortComparatorSource passes in FieldCache_Fields.DEFAULT and we're away!

citizenmatt