forked from apache/lucenenet
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSerializedDVStrategy.cs
More file actions
366 lines (312 loc) · 15 KB
/
Copy pathSerializedDVStrategy.cs
File metadata and controls
366 lines (312 loc) · 15 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
359
360
361
362
363
364
365
366
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Queries.Function;
using Lucene.Net.Search;
using Lucene.Net.Spatial.Queries;
using Lucene.Net.Spatial.Util;
using Lucene.Net.Util;
using Spatial4n.Context;
using Spatial4n.IO;
using Spatial4n.Shapes;
using System;
using System.Collections;
using System.IO;
namespace Lucene.Net.Spatial.Serialized
{
/*
* 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>
/// A <see cref="SpatialStrategy"/> based on serializing a Shape stored into BinaryDocValues.
/// This is not at all fast; it's designed to be used in conjunction with another index based
/// SpatialStrategy that is approximated(like <see cref="Prefix.RecursivePrefixTreeStrategy"/>)
/// to add precision or eventually make more specific / advanced calculations on the per-document
/// geometry.
/// The serialization uses Spatial4j's <see cref="BinaryCodec"/>.
///
/// @lucene.experimental
/// </summary>
public class SerializedDVStrategy : SpatialStrategy
{
/// <summary>
/// A cache heuristic for the buf size based on the last shape size.
/// </summary>
//TODO do we make this non-volatile since it's merely a heuristic?
private volatile int indexLastBufSize = 8 * 1024;//8KB default on first run
/// <summary>
/// Constructs the spatial strategy with its mandatory arguments.
/// </summary>
/// <exception cref="ArgumentNullException"><paramref name="ctx"/> or <paramref name="fieldName"/> is <c>null</c> or <paramref name="fieldName"/> is empty.</exception>
public SerializedDVStrategy(SpatialContext ctx, string fieldName)
: base(ctx, fieldName)
{
}
public override Field[] CreateIndexableFields(IShape shape)
{
// LUCENENET specific - added guard clause
if (shape is null)
throw new ArgumentNullException(nameof(shape));
int bufSize = Math.Max(128, (int)(this.indexLastBufSize * 1.5));//50% headroom over last
MemoryStream byteStream = new MemoryStream(bufSize);
BytesRef bytesRef = new BytesRef();//receiver of byteStream's bytes
try
{
m_ctx.BinaryCodec.WriteShape(new BinaryWriter(byteStream), shape);
//this is a hack to avoid redundant byte array copying by byteStream.toByteArray()
byteStream.WriteTo(new OutputStreamAnonymousClass(bytesRef));
}
catch (Exception e) when (e.IsIOException())
{
throw RuntimeException.Create(e);
}
this.indexLastBufSize = bytesRef.Length;//cache heuristic
return new Field[] { new BinaryDocValuesField(FieldName, bytesRef) };
}
private sealed class OutputStreamAnonymousClass : MemoryStream
{
private readonly BytesRef bytesRef;
public OutputStreamAnonymousClass(BytesRef bytesRef)
{
// LUCENENET specific - added guard clause
this.bytesRef = bytesRef ?? throw new ArgumentNullException(nameof(bytesRef));
}
public override void Write(byte[] buffer, int index, int count)
{
bytesRef.Bytes = buffer;
bytesRef.Offset = index;
bytesRef.Length = count;
}
}
public override ValueSource MakeDistanceValueSource(IPoint queryPoint, double multiplier)
{
//TODO if makeShapeValueSource gets lifted to the top; this could become a generic impl.
return new DistanceToShapeValueSource(MakeShapeValueSource(), queryPoint, multiplier, m_ctx);
}
public override ConstantScoreQuery MakeQuery(SpatialArgs args)
{
throw UnsupportedOperationException.Create("This strategy can't return a query that operates" +
" efficiently. Instead try a Filter or ValueSource.");
}
/// <summary>
/// Returns a <see cref="Filter"/> that should be used with <see cref="FilteredQuery.QUERY_FIRST_FILTER_STRATEGY"/>.
/// Use in another manner is likely to result in an <see cref="NotSupportedException"/>
/// to prevent misuse because the filter can't efficiently work via iteration.
/// </summary>
public override Filter MakeFilter(SpatialArgs args)
{
// LUCENENET specific - added guard clause
if (args is null)
throw new ArgumentNullException(nameof(args));
ValueSource shapeValueSource = MakeShapeValueSource();
ShapePredicateValueSource predicateValueSource = new ShapePredicateValueSource(
shapeValueSource, args.Operation, args.Shape);
return new PredicateValueSourceFilter(predicateValueSource);
}
/// <summary>
/// Provides access to each shape per document as a ValueSource in which
/// <see cref="FunctionValues.ObjectVal(int)"/> returns a <see cref="IShape"/>.
/// </summary>
//TODO raise to SpatialStrategy
public virtual ValueSource MakeShapeValueSource()
{
return new ShapeDocValueSource(FieldName, m_ctx.BinaryCodec);
}
/// <summary>
/// This filter only supports returning a DocSet with a GetBits(). If you try to grab the
/// iterator then you'll get a <see cref="NotSupportedException"/>.
/// </summary>
internal class PredicateValueSourceFilter : Filter
{
private readonly ValueSource predicateValueSource;//we call boolVal(doc)
public PredicateValueSourceFilter(ValueSource predicateValueSource)
{
// LUCENENET specific - added guard clause
this.predicateValueSource = predicateValueSource ?? throw new ArgumentNullException(nameof(predicateValueSource));
}
public override DocIdSet GetDocIdSet(AtomicReaderContext context, IBits? acceptDocs)
{
return new DocIdSetAnonymousClass(this, context, acceptDocs);
}
private sealed class DocIdSetAnonymousClass : DocIdSet
{
private readonly PredicateValueSourceFilter outerInstance;
private readonly AtomicReaderContext context;
private readonly IBits? acceptDocs;
public DocIdSetAnonymousClass(PredicateValueSourceFilter outerInstance, AtomicReaderContext context, IBits? acceptDocs)
{
// LUCENENET specific - added guard clauses
this.outerInstance = outerInstance ?? throw new ArgumentNullException(nameof(outerInstance));
this.context = context ?? throw new ArgumentNullException(nameof(context));
this.acceptDocs = acceptDocs;
}
public override DocIdSetIterator GetIterator()
{
throw UnsupportedOperationException.Create(
"Iteration is too slow; instead try FilteredQuery.QUERY_FIRST_FILTER_STRATEGY");
//Note that if you're truly bent on doing this, then see FunctionValues.getRangeScorer
}
public override IBits Bits
{
get
{
//null Map context -- we simply don't have one. That's ok.
FunctionValues predFuncValues = outerInstance.predicateValueSource.GetValues(null, context);
return new BitsAnonymousClass(predFuncValues, context, acceptDocs);
}
}
private sealed class BitsAnonymousClass : IBits
{
private readonly FunctionValues predFuncValues;
private readonly AtomicReaderContext context;
private readonly IBits? acceptDocs;
public BitsAnonymousClass(FunctionValues predFuncValues, AtomicReaderContext context, IBits? acceptDocs)
{
// LUCENENET specific - added guard clauses
this.predFuncValues = predFuncValues ?? throw new ArgumentNullException(nameof(predFuncValues));
this.context = context ?? throw new ArgumentNullException(nameof(context));
this.acceptDocs = acceptDocs;
}
public bool Get(int index)
{
if (acceptDocs != null && !acceptDocs.Get(index))
return false;
return predFuncValues.BoolVal(index);
}
public int Length => context.Reader.MaxDoc;
}
}
public override bool Equals(object? o)
{
if (this == o) return true;
if (o is null || GetType() != o.GetType()) return false;
PredicateValueSourceFilter that = (PredicateValueSourceFilter)o;
if (!predicateValueSource.Equals(that.predicateValueSource)) return false;
return true;
}
public override int GetHashCode()
{
return predicateValueSource.GetHashCode();
}
}//PredicateValueSourceFilter
/// <summary>
/// Implements a <see cref="ValueSource"/> by deserializing a <see cref="IShape"/> in from <see cref="BinaryDocValues"/> using <see cref="BinaryCodec"/>.
/// </summary>
/// <seealso cref="MakeShapeValueSource()"/>
internal class ShapeDocValueSource : ValueSource
{
private readonly string fieldName;
private readonly BinaryCodec binaryCodec;//spatial4n
internal ShapeDocValueSource(string fieldName, BinaryCodec binaryCodec)
{
// LUCENENET specific - added guard clauses
this.fieldName = fieldName ?? throw new ArgumentNullException(nameof(fieldName));
this.binaryCodec = binaryCodec ?? throw new ArgumentNullException(nameof(binaryCodec));
}
public override FunctionValues GetValues(IDictionary context, AtomicReaderContext readerContext)
{
// LUCENENET specific - added guard clause
if (readerContext is null)
throw new ArgumentNullException(nameof(readerContext));
BinaryDocValues docValues = readerContext.AtomicReader.GetBinaryDocValues(fieldName);
return new FunctionValuesAnonymousClass(this, docValues);
}
private sealed class FunctionValuesAnonymousClass : FunctionValues
{
private readonly ShapeDocValueSource outerInstance;
private readonly BinaryDocValues docValues;
public FunctionValuesAnonymousClass(ShapeDocValueSource outerInstance, BinaryDocValues docValues)
{
// LUCENENET specific - added guard clauses
this.outerInstance = outerInstance ?? throw new ArgumentNullException(nameof(outerInstance));
this.docValues = docValues ?? throw new ArgumentNullException(nameof(docValues));
}
private int bytesRefDoc = -1;
private readonly BytesRef bytesRef = new BytesRef();//scratch
private bool FillBytes(int doc)
{
if (bytesRefDoc != doc)
{
docValues.Get(doc, bytesRef);
bytesRefDoc = doc;
}
return bytesRef.Length != 0;
}
public override bool Exists(int doc)
{
return FillBytes(doc);
}
public override bool BytesVal(int doc, BytesRef target)
{
// LUCENENET specific - added guard clause
if (target is null)
throw new ArgumentNullException(nameof(target));
if (FillBytes(doc))
{
target.Bytes = bytesRef.Bytes;
target.Offset = bytesRef.Offset;
target.Length = bytesRef.Length;
return true;
}
else
{
target.Length = 0;
return false;
}
}
public override object? ObjectVal(int docId)
{
if (!FillBytes(docId))
return null;
BinaryReader dataInput = new BinaryReader(
new MemoryStream(bytesRef.Bytes, bytesRef.Offset, bytesRef.Length));
try
{
return outerInstance.binaryCodec.ReadShape(dataInput);
}
catch (Exception e) when (e.IsIOException())
{
throw RuntimeException.Create(e);
}
}
public override Explanation Explain(int doc)
{
return new Explanation(float.NaN, ToString(doc));
}
public override string ToString(int doc)
{
return outerInstance.GetDescription() + "=" + ObjectVal(doc);//TODO truncate?
}
}
public override bool Equals(object? o)
{
if (this == o) return true;
if (o is null || GetType() != o.GetType()) return false;
ShapeDocValueSource that = (ShapeDocValueSource)o;
if (!fieldName.Equals(that.fieldName, StringComparison.Ordinal)) return false;
return true;
}
public override int GetHashCode()
{
int result = fieldName.GetHashCode();
return result;
}
public override string GetDescription()
{
return "shapeDocVal(" + fieldName + ")";
}
}//ShapeDocValueSource
}
}