/*
* 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 PriorityQueue = Lucene.Net.Util.PriorityQueue;
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.
///
/// This class will not resolve SortField.AUTO types, and expects the type
/// of all SortFields used for construction to already have been resolved.
/// {@link SortField#DetectFieldType(IndexReader, String)} is a utility method which
/// may be used for field type detection.
///
/// NOTE: This API is experimental and might change in
/// incompatible ways in the next release.
///
///
/// 2.9
///
/// $Id:
///
///
///
///
///
public abstract class FieldValueHitQueue:PriorityQueue
{
internal sealed class Entry
{
internal int slot;
internal int docID;
internal float score;
internal Entry(int slot, int docID, float score)
{
this.slot = slot;
this.docID = docID;
this.score = score;
}
public override System.String ToString()
{
return "slot:" + slot + " docID:" + docID + " score=" + score;
}
}
/// An implementation of {@link FieldValueHitQueue} 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];
// AUTO is resolved before we are called
System.Diagnostics.Debug.Assert(field.GetType() != SortField.AUTO);
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(System.Object a, System.Object b)
{
Entry hitA = (Entry) a;
Entry hitB = (Entry) b;
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.docID > hitB.docID;
}
}
/// An implementation of {@link FieldValueHitQueue} 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];
// AUTO is resolved before we are called
System.Diagnostics.Debug.Assert(field.GetType() != SortField.AUTO);
reverseMul[i] = field.reverse?- 1:1;
comparators[i] = field.GetComparator(size, i);
}
Initialize(size);
}
public override bool LessThan(System.Object a, System.Object b)
{
Entry hitA = (Entry) a;
Entry hitB = (Entry) b;
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.docID > hitB.docID;
}
}
// 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(System.Object a, System.Object 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].Value(entry.slot);
}
//if (maxscore > 1.0f) doc.score /= maxscore; // normalize scores
return new FieldDoc(entry.docID, entry.score, fields);
}
/// Returns the SortFields being used by this hit queue.
internal virtual SortField[] GetFields()
{
return fields;
}
}
}