@@ -81,7 +81,8 @@ impl Weight for FastFieldRangeWeight {
81
81
& self . upper_bound ,
82
82
column. min_value ( ) ,
83
83
column. max_value ( ) ,
84
- ) ;
84
+ )
85
+ . unwrap_or ( 1 ..=0 ) ; // empty range
85
86
if value_range. is_empty ( ) {
86
87
return Ok ( Box :: new ( EmptyScorer ) ) ;
87
88
}
@@ -102,26 +103,28 @@ impl Weight for FastFieldRangeWeight {
102
103
}
103
104
}
104
105
106
+ // Returns None, if the range cannot be converted to a inclusive range (which equals to a empty
107
+ // range).
105
108
fn bound_to_value_range < T : MonotonicallyMappableToU64 > (
106
109
lower_bound : & Bound < T > ,
107
110
upper_bound : & Bound < T > ,
108
111
min_value : T ,
109
112
max_value : T ,
110
- ) -> RangeInclusive < T > {
113
+ ) -> Option < RangeInclusive < T > > {
111
114
let mut start_value = match lower_bound {
112
115
Bound :: Included ( val) => * val,
113
- Bound :: Excluded ( val) => T :: from_u64 ( val. to_u64 ( ) + 1 ) ,
116
+ Bound :: Excluded ( val) => T :: from_u64 ( val. to_u64 ( ) . checked_add ( 1 ) ? ) ,
114
117
Bound :: Unbounded => min_value,
115
118
} ;
116
119
if start_value. partial_cmp ( & min_value) == Some ( std:: cmp:: Ordering :: Less ) {
117
120
start_value = min_value;
118
121
}
119
122
let end_value = match upper_bound {
120
123
Bound :: Included ( val) => * val,
121
- Bound :: Excluded ( val) => T :: from_u64 ( val. to_u64 ( ) - 1 ) ,
124
+ Bound :: Excluded ( val) => T :: from_u64 ( val. to_u64 ( ) . checked_sub ( 1 ) ? ) ,
122
125
Bound :: Unbounded => max_value,
123
126
} ;
124
- start_value..=end_value
127
+ Some ( start_value..=end_value)
125
128
}
126
129
127
130
#[ cfg( test) ]
@@ -295,6 +298,9 @@ pub mod tests {
295
298
let gen_query_inclusive = |field : & str , range : RangeInclusive < u64 > | {
296
299
format ! ( "{}:[{} TO {}]" , field, range. start( ) , range. end( ) )
297
300
} ;
301
+ let gen_query_exclusive = |field : & str , range : RangeInclusive < u64 > | {
302
+ format ! ( "{}:{{{} TO {}}}" , field, range. start( ) , range. end( ) )
303
+ } ;
298
304
299
305
let test_sample = |sample_docs : Vec < Doc > | {
300
306
let mut ids: Vec < u64 > = sample_docs. iter ( ) . map ( |doc| doc. id ) . collect ( ) ;
@@ -310,6 +316,20 @@ pub mod tests {
310
316
let query = gen_query_inclusive ( "ids" , ids[ 0 ] ..=ids[ 1 ] ) ;
311
317
assert_eq ! ( get_num_hits( query_from_text( & query) ) , expected_num_hits) ;
312
318
319
+ // Exclusive range
320
+ let expected_num_hits = docs
321
+ . iter ( )
322
+ . filter ( |doc| {
323
+ ( ids[ 0 ] . saturating_add ( 1 ) ..=ids[ 1 ] . saturating_sub ( 1 ) ) . contains ( & doc. id )
324
+ } )
325
+ . count ( ) ;
326
+
327
+ let query = gen_query_exclusive ( "id" , ids[ 0 ] ..=ids[ 1 ] ) ;
328
+ assert_eq ! ( get_num_hits( query_from_text( & query) ) , expected_num_hits) ;
329
+
330
+ let query = gen_query_exclusive ( "ids" , ids[ 0 ] ..=ids[ 1 ] ) ;
331
+ assert_eq ! ( get_num_hits( query_from_text( & query) ) , expected_num_hits) ;
332
+
313
333
// Intersection search
314
334
let id_filter = sample_docs[ 0 ] . id_name . to_string ( ) ;
315
335
let expected_num_hits = docs
0 commit comments