forked from apache/lucenenet
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSorter.cs
More file actions
358 lines (315 loc) · 12.5 KB
/
Copy pathSorter.cs
File metadata and controls
358 lines (315 loc) · 12.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
using Lucene.Net.Diagnostics;
using Lucene.Net.Search;
using Lucene.Net.Support;
using Lucene.Net.Util;
using Lucene.Net.Util.Packed;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace Lucene.Net.Index.Sorter
{
/*
* 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.
*/
/// <summary>
/// Sorts documents of a given index by returning a permutation on the document
/// IDs.
/// @lucene.experimental
/// </summary>
internal sealed class Sorter
{
internal readonly Sort sort;
/// <summary>
/// Creates a new Sorter to sort the index with <paramref name="sort"/>.
/// </summary>
internal Sorter(Sort sort)
{
if (sort.NeedsScores)
{
throw new ArgumentException("Cannot sort an index with a Sort that refers to the relevance score");
}
this.sort = sort;
}
/// <summary>
/// A permutation of doc IDs. For every document ID between <c>0</c> and
/// <see cref="IndexReader.MaxDoc"/>, <c>OldToNew(NewToOld(docID))</c> must
/// return <c>docID</c>.
/// </summary>
internal abstract class DocMap
{
/// <summary>
/// Given a doc ID from the original index, return its ordinal in the
/// sorted index.
/// </summary>
public abstract int OldToNew(int docID);
/// <summary>
/// Given the ordinal of a doc ID, return its doc ID in the original index.
/// </summary>
public abstract int NewToOld(int docID);
/// <summary>
/// Return the number of documents in this map. This must be equal to the
/// <see cref="IndexReader.MaxDoc"/> number of documents of the
/// <see cref="AtomicReader"/> which is sorted.
/// </summary>
public abstract int Count { get; }
}
/// <summary>
/// Check consistency of a <see cref="DocMap"/>, useful for assertions.
/// </summary>
internal static bool IsConsistent(DocMap docMap)
{
int maxDoc = docMap.Count;
for (int i = 0; i < maxDoc; ++i)
{
int newID = docMap.OldToNew(i);
int oldID = docMap.NewToOld(newID);
if (Debugging.AssertsEnabled)
{
Debugging.Assert(newID >= 0 && newID < maxDoc, "doc IDs must be in [0-{0}[, got {1}", maxDoc, newID);
Debugging.Assert(i == oldID, "mapping is inconsistent: {0} --oldToNew--> {1} --newToOld--> {2}", i, newID, oldID);
}
if (i != oldID || newID < 0 || newID >= maxDoc)
{
return false;
}
}
return true;
}
/// <summary>
/// A comparer of doc IDs.
/// </summary>
internal abstract class DocComparer : IComparer<int>
{
/// <summary>
/// Compare <paramref name="docID1"/> against <paramref name="docID2"/>. The contract for the return value is the
/// same as <see cref="IComparer{T}.Compare(T, T)"/>.
/// </summary>
public abstract int Compare(int docID1, int docID2);
}
private sealed class DocValueSorter : TimSorter
{
private readonly int[] docs;
private readonly Sorter.DocComparer comparer;
private readonly int[] tmp;
internal DocValueSorter(int[] docs, Sorter.DocComparer comparer)
: base(docs.Length / 64)
{
this.docs = docs;
this.comparer = comparer;
tmp = new int[docs.Length / 64];
}
protected override int Compare(int i, int j)
{
return comparer.Compare(docs[i], docs[j]);
}
protected override void Swap(int i, int j)
{
int tmpDoc = docs[i];
docs[i] = docs[j];
docs[j] = tmpDoc;
}
protected override void Copy(int src, int dest)
{
docs[dest] = docs[src];
}
protected override void Save(int i, int len)
{
Arrays.Copy(docs, i, tmp, 0, len);
}
protected override void Restore(int i, int j)
{
docs[j] = tmp[i];
}
protected override int CompareSaved(int i, int j)
{
return comparer.Compare(tmp[i], docs[j]);
}
}
/// <summary>
/// Computes the old-to-new permutation over the given comparer.
/// </summary>
private static Sorter.DocMap Sort(int maxDoc, DocComparer comparer)
{
// check if the index is sorted
bool sorted = true;
for (int i = 1; i < maxDoc; ++i)
{
if (comparer.Compare(i - 1, i) > 0)
{
sorted = false;
break;
}
}
if (sorted)
{
return null;
}
// sort doc IDs
int[] docs = new int[maxDoc];
for (int i = 0; i < maxDoc; i++)
{
docs[i] = i;
}
DocValueSorter sorter = new DocValueSorter(docs, comparer);
// It can be common to sort a reader, add docs, sort it again, ... and in
// that case timSort can save a lot of time
sorter.Sort(0, docs.Length); // docs is now the newToOld mapping
// The reason why we use MonotonicAppendingLongBuffer here is that it
// wastes very little memory if the index is in random order but can save
// a lot of memory if the index is already "almost" sorted
MonotonicAppendingInt64Buffer newToOld = new MonotonicAppendingInt64Buffer();
for (int i = 0; i < maxDoc; ++i)
{
newToOld.Add(docs[i]);
}
newToOld.Freeze();
for (int i = 0; i < maxDoc; ++i)
{
docs[(int)newToOld.Get(i)] = i;
} // docs is now the oldToNew mapping
MonotonicAppendingInt64Buffer oldToNew = new MonotonicAppendingInt64Buffer();
for (int i = 0; i < maxDoc; ++i)
{
oldToNew.Add(docs[i]);
}
oldToNew.Freeze();
return new DocMapAnonymousClass(maxDoc, newToOld, oldToNew);
}
private sealed class DocMapAnonymousClass : Sorter.DocMap
{
private readonly int maxDoc;
private readonly MonotonicAppendingInt64Buffer newToOld;
private readonly MonotonicAppendingInt64Buffer oldToNew;
public DocMapAnonymousClass(int maxDoc, MonotonicAppendingInt64Buffer newToOld, MonotonicAppendingInt64Buffer oldToNew)
{
this.maxDoc = maxDoc;
this.newToOld = newToOld;
this.oldToNew = oldToNew;
}
public override int OldToNew(int docID)
{
return (int)oldToNew.Get(docID);
}
public override int NewToOld(int docID)
{
return (int)newToOld.Get(docID);
}
public override int Count => maxDoc;
}
/// <summary>
/// Returns a mapping from the old document ID to its new location in the
/// sorted index. Implementations can use the auxiliary
/// <see cref="Sort(int, DocComparer)"/> to compute the old-to-new permutation
/// given a list of documents and their corresponding values.
/// <para>
/// A return value of <c>null</c> is allowed and means that
/// <c>reader</c> is already sorted.
/// </para>
/// <para>
/// <b>NOTE:</b> deleted documents are expected to appear in the mapping as
/// well, they will however be marked as deleted in the sorted view.
/// </para>
/// </summary>
internal DocMap Sort(AtomicReader reader)
{
SortField[] fields = sort.GetSort();
int[] reverseMul = new int[fields.Length];
FieldComparer[] comparers = new FieldComparer[fields.Length];
for (int i = 0; i < fields.Length; i++)
{
reverseMul[i] = fields[i].IsReverse ? -1 : 1;
comparers[i] = fields[i].GetComparer(1, i);
comparers[i].SetNextReader(reader.AtomicContext);
comparers[i].SetScorer(FAKESCORER);
}
DocComparer comparer = new DocComparerAnonymousClass(reverseMul, comparers);
return Sort(reader.MaxDoc, comparer);
}
private sealed class DocComparerAnonymousClass : DocComparer
{
private readonly int[] reverseMul;
private readonly FieldComparer[] comparers;
public DocComparerAnonymousClass(int[] reverseMul, FieldComparer[] comparers)
{
this.reverseMul = reverseMul;
this.comparers = comparers;
}
public override int Compare(int docID1, int docID2)
{
try
{
for (int i = 0; i < comparers.Length; i++)
{
// TODO: would be better if copy() didn't cause a term lookup in TermOrdVal & co,
// the segments are always the same here...
comparers[i].Copy(0, docID1);
comparers[i].SetBottom(0);
int comp = reverseMul[i] * comparers[i].CompareBottom(docID2);
if (comp != 0)
{
return comp;
}
}
return docID1.CompareTo(docID2); // docid order tiebreak
}
catch (Exception e) when (e.IsIOException())
{
throw RuntimeException.Create(e);
}
}
}
/// <summary>
/// Returns the identifier of this <see cref="Sorter"/>.
/// <para>This identifier is similar to <see cref="object.GetHashCode()"/> and should be
/// chosen so that two instances of this class that sort documents likewise
/// will have the same identifier. On the contrary, this identifier should be
/// different on different <see cref="Search.Sort">sorts</see>.
/// </para>
/// </summary>
public string ID => sort.ToString();
public override string ToString()
{
return ID;
}
internal static readonly Scorer FAKESCORER = new ScorerAnonymousClass();
private sealed class ScorerAnonymousClass : Scorer
{
public ScorerAnonymousClass()
: base(null)
{
}
public override float GetScore()
{
throw UnsupportedOperationException.Create();
}
public override int Freq => throw UnsupportedOperationException.Create();
public override int DocID => throw UnsupportedOperationException.Create();
public override int NextDoc()
{
throw UnsupportedOperationException.Create();
}
public override int Advance(int target)
{
throw UnsupportedOperationException.Create();
}
public override long GetCost()
{
throw UnsupportedOperationException.Create();
}
}
}
}