1
1
use std:: io;
2
2
use std:: ops:: { Bound , Range } ;
3
3
4
- use common:: BitSet ;
4
+ use common:: { BinarySerializable , BitSet } ;
5
5
6
+ use super :: range_query_u64_fastfield:: FastFieldRangeWeight ;
6
7
use crate :: core:: SegmentReader ;
7
8
use crate :: error:: TantivyError ;
8
9
use crate :: query:: explanation:: does_not_match;
9
10
use crate :: query:: range_query:: range_query_ip_fastfield:: IPFastFieldRangeWeight ;
10
11
use crate :: query:: { BitSetDocSet , ConstScorer , EnableScoring , Explanation , Query , Scorer , Weight } ;
11
12
use crate :: schema:: { Field , IndexRecordOption , Term , Type } ;
12
13
use crate :: termdict:: { TermDictionary , TermStreamer } ;
13
- use crate :: { DocId , Score } ;
14
+ use crate :: { DateTime , DocId , Score } ;
14
15
15
16
pub ( crate ) fn map_bound < TFrom , TTo , Transform : Fn ( & TFrom ) -> TTo > (
16
17
bound : & Bound < TFrom > ,
@@ -203,6 +204,40 @@ impl RangeQuery {
203
204
)
204
205
}
205
206
207
+ /// Create a new `RangeQuery` over a `date` field.
208
+ ///
209
+ /// The two `Bound` arguments make it possible to create more complex
210
+ /// ranges than semi-inclusive range.
211
+ ///
212
+ /// If the field is not of the type `date`, tantivy
213
+ /// will panic when the `Weight` object is created.
214
+ pub fn new_date_bounds (
215
+ field : Field ,
216
+ left_bound : Bound < DateTime > ,
217
+ right_bound : Bound < DateTime > ,
218
+ ) -> RangeQuery {
219
+ let make_term_val =
220
+ |val : & DateTime | Term :: from_field_date ( field, * val) . value_bytes ( ) . to_owned ( ) ;
221
+ RangeQuery {
222
+ field,
223
+ value_type : Type :: Date ,
224
+ left_bound : map_bound ( & left_bound, & make_term_val) ,
225
+ right_bound : map_bound ( & right_bound, & make_term_val) ,
226
+ }
227
+ }
228
+
229
+ /// Create a new `RangeQuery` over a `date` field.
230
+ ///
231
+ /// If the field is not of the type `date`, tantivy
232
+ /// will panic when the `Weight` object is created.
233
+ pub fn new_date ( field : Field , range : Range < DateTime > ) -> RangeQuery {
234
+ RangeQuery :: new_date_bounds (
235
+ field,
236
+ Bound :: Included ( range. start ) ,
237
+ Bound :: Excluded ( range. end ) ,
238
+ )
239
+ }
240
+
206
241
/// Create a new `RangeQuery` over a `Str` field.
207
242
///
208
243
/// The two `Bound` arguments make it possible to create more complex
@@ -252,6 +287,23 @@ impl RangeQuery {
252
287
}
253
288
}
254
289
290
+ fn is_type_valid_for_fastfield_range_query ( typ : Type ) -> bool {
291
+ match typ {
292
+ Type :: U64 | Type :: I64 | Type :: F64 | Type :: Bool | Type :: Date => true ,
293
+ Type :: IpAddr => true ,
294
+ Type :: Str | Type :: Facet | Type :: Bytes | Type :: Json => false ,
295
+ }
296
+ }
297
+
298
+ /// Returns true if the type maps to a u64 fast field
299
+ pub ( crate ) fn maps_to_u64_fastfield ( typ : Type ) -> bool {
300
+ match typ {
301
+ Type :: U64 | Type :: I64 | Type :: F64 | Type :: Bool | Type :: Date => true ,
302
+ Type :: IpAddr => false ,
303
+ Type :: Str | Type :: Facet | Type :: Bytes | Type :: Json => false ,
304
+ }
305
+ }
306
+
255
307
impl Query for RangeQuery {
256
308
fn weight ( & self , enable_scoring : EnableScoring < ' _ > ) -> crate :: Result < Box < dyn Weight > > {
257
309
let schema = enable_scoring. schema ( ) ;
@@ -265,12 +317,29 @@ impl Query for RangeQuery {
265
317
return Err ( TantivyError :: SchemaError ( err_msg) ) ;
266
318
}
267
319
268
- if field_type. is_ip_addr ( ) && field_type. is_fast ( ) {
269
- Ok ( Box :: new ( IPFastFieldRangeWeight :: new (
270
- self . field ,
271
- & self . left_bound ,
272
- & self . right_bound ,
273
- ) ) )
320
+ if field_type. is_fast ( ) && is_type_valid_for_fastfield_range_query ( self . value_type ) {
321
+ if field_type. is_ip_addr ( ) {
322
+ Ok ( Box :: new ( IPFastFieldRangeWeight :: new (
323
+ self . field ,
324
+ & self . left_bound ,
325
+ & self . right_bound ,
326
+ ) ) )
327
+ } else {
328
+ // We run the range query on u64 value space for performance reasons and simpicity
329
+ // assert the type maps to u64
330
+ assert ! ( maps_to_u64_fastfield( self . value_type) ) ;
331
+ let parse_from_bytes = |data : & Vec < u8 > | {
332
+ u64:: from_be ( BinarySerializable :: deserialize ( & mut & data[ ..] ) . unwrap ( ) )
333
+ } ;
334
+
335
+ let left_bound = map_bound ( & self . left_bound , & parse_from_bytes) ;
336
+ let right_bound = map_bound ( & self . right_bound , & parse_from_bytes) ;
337
+ Ok ( Box :: new ( FastFieldRangeWeight :: new (
338
+ self . field ,
339
+ left_bound,
340
+ right_bound,
341
+ ) ) )
342
+ }
274
343
} else {
275
344
Ok ( Box :: new ( RangeWeight {
276
345
field : self . field ,
0 commit comments