1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 using System; 19 using Lucene.Net.Util; 20 21 namespace Lucene.Net.Search 22 { 23 24 /// <summary> Expert: A hit queue for sorting by hits by terms in more than one field. 25 /// Uses <c>FieldCache.DEFAULT</c> for maintaining 26 /// internal term lookup tables. 27 /// 28 /// <b>NOTE:</b> This API is experimental and might change in 29 /// incompatible ways in the next release. 30 /// 31 /// </summary> 32 /// <seealso cref="Searcher.Search(Query,Filter,int,Sort)"></seealso> 33 /// <seealso cref="FieldCache"></seealso> 34 public abstract class FieldValueHitQueue : PriorityQueue<FieldValueHitQueue.Entry> 35 { 36 // had to change from internal to public, due to public accessability of FieldValueHitQueue 37 public /*internal*/ sealed class Entry : ScoreDoc 38 { 39 internal int slot; 40 Entry(int slot, int doc, float score)41 internal Entry(int slot, int doc, float score) 42 : base(doc, score) 43 { 44 45 this.slot = slot; 46 } 47 ToString()48 public override System.String ToString() 49 { 50 return "slot:" + slot + " " + base.ToString(); 51 } 52 } 53 54 /// <summary> An implementation of <see cref="FieldValueHitQueue" /> which is optimized in case 55 /// there is just one comparator. 56 /// </summary> 57 private sealed class OneComparatorFieldValueHitQueue : FieldValueHitQueue 58 { 59 60 private FieldComparator comparator; 61 private int oneReverseMul; 62 OneComparatorFieldValueHitQueue(SortField[] fields, int size)63 public OneComparatorFieldValueHitQueue(SortField[] fields, int size):base(fields) 64 { 65 if (fields.Length == 0) 66 { 67 throw new System.ArgumentException("Sort must contain at least one field"); 68 } 69 70 SortField field = fields[0]; 71 comparator = field.GetComparator(size, 0); 72 oneReverseMul = field.reverse?- 1:1; 73 74 comparators[0] = comparator; 75 reverseMul[0] = oneReverseMul; 76 77 Initialize(size); 78 } 79 80 /// <summary> Returns whether <c>a</c> is less relevant than <c>b</c>.</summary> 81 /// <param name="hitA">ScoreDoc</param> 82 /// <param name="hitB">ScoreDoc</param> 83 /// <returns><c>true</c> if document <c>a</c> should be sorted after document <c>b</c>.</returns> LessThan(Entry hitA, Entry hitB)84 public override bool LessThan(Entry hitA, Entry hitB) 85 { 86 System.Diagnostics.Debug.Assert(hitA != hitB); 87 System.Diagnostics.Debug.Assert(hitA.slot != hitB.slot); 88 89 int c = oneReverseMul * comparator.Compare(hitA.slot, hitB.slot); 90 if (c != 0) 91 { 92 return c > 0; 93 } 94 95 // avoid random sort order that could lead to duplicates (bug #31241): 96 return hitA.Doc > hitB.Doc; 97 } 98 } 99 100 /// <summary> An implementation of <see cref="FieldValueHitQueue" /> which is optimized in case 101 /// there is more than one comparator. 102 /// </summary> 103 private sealed class MultiComparatorsFieldValueHitQueue : FieldValueHitQueue 104 { 105 MultiComparatorsFieldValueHitQueue(SortField[] fields, int size)106 public MultiComparatorsFieldValueHitQueue(SortField[] fields, int size):base(fields) 107 { 108 109 int numComparators = comparators.Length; 110 for (int i = 0; i < numComparators; ++i) 111 { 112 SortField field = fields[i]; 113 114 reverseMul[i] = field.reverse?- 1:1; 115 comparators[i] = field.GetComparator(size, i); 116 } 117 118 Initialize(size); 119 } 120 LessThan(Entry hitA, Entry hitB)121 public override bool LessThan(Entry hitA, Entry hitB) 122 { 123 System.Diagnostics.Debug.Assert(hitA != hitB); 124 System.Diagnostics.Debug.Assert(hitA.slot != hitB.slot); 125 126 int numComparators = comparators.Length; 127 for (int i = 0; i < numComparators; ++i) 128 { 129 int c = reverseMul[i] * comparators[i].Compare(hitA.slot, hitB.slot); 130 if (c != 0) 131 { 132 // Short circuit 133 return c > 0; 134 } 135 } 136 137 // avoid random sort order that could lead to duplicates (bug #31241): 138 return hitA.Doc > hitB.Doc; 139 } 140 } 141 142 // prevent instantiation and extension. FieldValueHitQueue(SortField[] fields)143 private FieldValueHitQueue(SortField[] fields) 144 { 145 // When we get here, fields.length is guaranteed to be > 0, therefore no 146 // need to check it again. 147 148 // All these are required by this class's API - need to return arrays. 149 // Therefore even in the case of a single comparator, create an array 150 // anyway. 151 this.fields = fields; 152 int numComparators = fields.Length; 153 comparators = new FieldComparator[numComparators]; 154 reverseMul = new int[numComparators]; 155 } 156 157 /// <summary> Creates a hit queue sorted by the given list of fields. 158 /// 159 /// <p/><b>NOTE</b>: The instances returned by this method 160 /// pre-allocate a full array of length <c>numHits</c>. 161 /// 162 /// </summary> 163 /// <param name="fields">SortField array we are sorting by in priority order (highest 164 /// priority first); cannot be <c>null</c> or empty 165 /// </param> 166 /// <param name="size">The number of hits to retain. Must be greater than zero. 167 /// </param> 168 /// <throws> IOException </throws> Create(SortField[] fields, int size)169 public static FieldValueHitQueue Create(SortField[] fields, int size) 170 { 171 172 if (fields.Length == 0) 173 { 174 throw new System.ArgumentException("Sort must contain at least one field"); 175 } 176 177 if (fields.Length == 1) 178 { 179 return new OneComparatorFieldValueHitQueue(fields, size); 180 } 181 else 182 { 183 return new MultiComparatorsFieldValueHitQueue(fields, size); 184 } 185 } 186 GetComparators()187 internal virtual FieldComparator[] GetComparators() 188 { 189 return comparators; 190 } 191 GetReverseMul()192 internal virtual int[] GetReverseMul() 193 { 194 return reverseMul; 195 } 196 197 /// <summary>Stores the sort criteria being used. </summary> 198 protected internal SortField[] fields; 199 protected internal FieldComparator[] comparators; 200 protected internal int[] reverseMul; 201 LessThan(Entry a, Entry b)202 public abstract override bool LessThan(Entry a, Entry b); 203 204 /// <summary> Given a queue Entry, creates a corresponding FieldDoc 205 /// that contains the values used to sort the given document. 206 /// These values are not the raw values out of the index, but the internal 207 /// representation of them. This is so the given search hit can be collated by 208 /// a MultiSearcher with other search hits. 209 /// 210 /// </summary> 211 /// <param name="entry">The Entry used to create a FieldDoc 212 /// </param> 213 /// <returns> The newly created FieldDoc 214 /// </returns> 215 /// <seealso cref="Searchable.Search(Weight,Filter,int,Sort)"> 216 /// </seealso> FillFields(Entry entry)217 internal virtual FieldDoc FillFields(Entry entry) 218 { 219 int n = comparators.Length; 220 System.IComparable[] fields = new System.IComparable[n]; 221 for (int i = 0; i < n; ++i) 222 { 223 fields[i] = comparators[i][entry.slot]; 224 } 225 //if (maxscore > 1.0f) doc.score /= maxscore; // normalize scores 226 return new FieldDoc(entry.Doc, entry.Score, fields); 227 } 228 229 /// <summary>Returns the SortFields being used by this hit queue. </summary> GetFields()230 internal virtual SortField[] GetFields() 231 { 232 return fields; 233 } 234 } 235 }