/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using Lucene.Net.Util;
namespace Lucene.Net.Search
{
/// Expert: A hit queue for sorting by hits by terms in more than one field.
/// Uses FieldCache.DEFAULT for maintaining
/// internal term lookup tables.
///
/// NOTE: This API is experimental and might change in
/// incompatible ways in the next release.
///
///
///
///
public abstract class FieldValueHitQueue : PriorityQueue
{
// had to change from internal to public, due to public accessability of FieldValueHitQueue
public /*internal*/ sealed class Entry : ScoreDoc
{
internal int slot;
internal Entry(int slot, int doc, float score)
: base(doc, score)
{
this.slot = slot;
}
public override System.String ToString()
{
return "slot:" + slot + " " + base.ToString();
}
}
/// An implementation of which is optimized in case
/// there is just one comparator.
///
private sealed class OneComparatorFieldValueHitQueue : FieldValueHitQueue
{
private FieldComparator comparator;
private int oneReverseMul;
public OneComparatorFieldValueHitQueue(SortField[] fields, int size):base(fields)
{
if (fields.Length == 0)
{
throw new System.ArgumentException("Sort must contain at least one field");
}
SortField field = fields[0];
comparator = field.GetComparator(size, 0);
oneReverseMul = field.reverse?- 1:1;
comparators[0] = comparator;
reverseMul[0] = oneReverseMul;
Initialize(size);
}
/// Returns whether a is less relevant than b.
/// ScoreDoc
/// ScoreDoc
/// true if document a should be sorted after document b.
public override bool LessThan(Entry hitA, Entry hitB)
{
System.Diagnostics.Debug.Assert(hitA != hitB);
System.Diagnostics.Debug.Assert(hitA.slot != hitB.slot);
int c = oneReverseMul * comparator.Compare(hitA.slot, hitB.slot);
if (c != 0)
{
return c > 0;
}
// avoid random sort order that could lead to duplicates (bug #31241):
return hitA.Doc > hitB.Doc;
}
}
/// An implementation of which is optimized in case
/// there is more than one comparator.
///
private sealed class MultiComparatorsFieldValueHitQueue : FieldValueHitQueue
{
public MultiComparatorsFieldValueHitQueue(SortField[] fields, int size):base(fields)
{
int numComparators = comparators.Length;
for (int i = 0; i < numComparators; ++i)
{
SortField field = fields[i];
reverseMul[i] = field.reverse?- 1:1;
comparators[i] = field.GetComparator(size, i);
}
Initialize(size);
}
public override bool LessThan(Entry hitA, Entry hitB)
{
System.Diagnostics.Debug.Assert(hitA != hitB);
System.Diagnostics.Debug.Assert(hitA.slot != hitB.slot);
int numComparators = comparators.Length;
for (int i = 0; i < numComparators; ++i)
{
int c = reverseMul[i] * comparators[i].Compare(hitA.slot, hitB.slot);
if (c != 0)
{
// Short circuit
return c > 0;
}
}
// avoid random sort order that could lead to duplicates (bug #31241):
return hitA.Doc > hitB.Doc;
}
}
// prevent instantiation and extension.
private FieldValueHitQueue(SortField[] fields)
{
// When we get here, fields.length is guaranteed to be > 0, therefore no
// need to check it again.
// All these are required by this class's API - need to return arrays.
// Therefore even in the case of a single comparator, create an array
// anyway.
this.fields = fields;
int numComparators = fields.Length;
comparators = new FieldComparator[numComparators];
reverseMul = new int[numComparators];
}
/// Creates a hit queue sorted by the given list of fields.
///
/// NOTE: The instances returned by this method
/// pre-allocate a full array of length numHits.
///
///
/// SortField array we are sorting by in priority order (highest
/// priority first); cannot be null or empty
///
/// The number of hits to retain. Must be greater than zero.
///
/// IOException
public static FieldValueHitQueue Create(SortField[] fields, int size)
{
if (fields.Length == 0)
{
throw new System.ArgumentException("Sort must contain at least one field");
}
if (fields.Length == 1)
{
return new OneComparatorFieldValueHitQueue(fields, size);
}
else
{
return new MultiComparatorsFieldValueHitQueue(fields, size);
}
}
internal virtual FieldComparator[] GetComparators()
{
return comparators;
}
internal virtual int[] GetReverseMul()
{
return reverseMul;
}
/// Stores the sort criteria being used.
protected internal SortField[] fields;
protected internal FieldComparator[] comparators;
protected internal int[] reverseMul;
public abstract override bool LessThan(Entry a, Entry b);
/// Given a queue Entry, creates a corresponding FieldDoc
/// that contains the values used to sort the given document.
/// These values are not the raw values out of the index, but the internal
/// representation of them. This is so the given search hit can be collated by
/// a MultiSearcher with other search hits.
///
///
/// The Entry used to create a FieldDoc
///
/// The newly created FieldDoc
///
///
///
internal virtual FieldDoc FillFields(Entry entry)
{
int n = comparators.Length;
System.IComparable[] fields = new System.IComparable[n];
for (int i = 0; i < n; ++i)
{
fields[i] = comparators[i][entry.slot];
}
//if (maxscore > 1.0f) doc.score /= maxscore; // normalize scores
return new FieldDoc(entry.Doc, entry.Score, fields);
}
/// Returns the SortFields being used by this hit queue.
internal virtual SortField[] GetFields()
{
return fields;
}
}
}