@@ -10,24 +10,22 @@ use columnar::{
10
10
StrColumn ,
11
11
} ;
12
12
use common:: bounds:: { BoundsRange , TransformBound } ;
13
- use common:: BinarySerializable ;
14
13
15
14
use super :: fast_field_range_doc_set:: RangeDocSet ;
16
15
use crate :: query:: { AllScorer , ConstScorer , EmptyScorer , Explanation , Query , Scorer , Weight } ;
17
- use crate :: schema:: { Field , Type , ValueBytes } ;
16
+ use crate :: schema:: { Type , ValueBytes } ;
18
17
use crate :: { DocId , DocSet , Score , SegmentReader , TantivyError , Term } ;
19
18
20
19
/// `FastFieldRangeWeight` uses the fast field to execute range queries.
21
20
#[ derive( Clone , Debug ) ]
22
21
pub struct FastFieldRangeWeight {
23
22
bounds : BoundsRange < Term > ,
24
- field : Field ,
25
23
}
26
24
27
25
impl FastFieldRangeWeight {
28
26
/// Create a new FastFieldRangeWeight
29
- pub ( crate ) fn new ( field : Field , bounds : BoundsRange < Term > ) -> Self {
30
- Self { bounds, field }
27
+ pub ( crate ) fn new ( bounds : BoundsRange < Term > ) -> Self {
28
+ Self { bounds }
31
29
}
32
30
}
33
31
@@ -46,12 +44,12 @@ impl Weight for FastFieldRangeWeight {
46
44
if self . bounds . is_unbounded ( ) {
47
45
return Ok ( Box :: new ( AllScorer :: new ( reader. max_doc ( ) ) ) ) ;
48
46
}
49
- let field_type = reader. schema ( ) . get_field_entry ( self . field ) . field_type ( ) ;
50
47
51
48
let term = self
52
49
. bounds
53
50
. get_inner ( )
54
51
. expect ( "At least one bound must be set" ) ;
52
+ let field_type = reader. schema ( ) . get_field_entry ( term. field ( ) ) . field_type ( ) ;
55
53
assert_eq ! (
56
54
term. typ( ) ,
57
55
field_type. value_type( ) ,
@@ -62,10 +60,6 @@ impl Weight for FastFieldRangeWeight {
62
60
let field_name = term. get_full_path ( reader. schema ( ) ) ;
63
61
64
62
let get_value_bytes = |term : & Term | term. value ( ) . value_bytes_payload ( ) ;
65
- let get_term_u64_internal_representation = |term : & Term | {
66
- let bytes = term. value ( ) . value_bytes_payload ( ) ;
67
- u64:: from_be ( BinarySerializable :: deserialize ( & mut & bytes[ ..] ) . unwrap ( ) )
68
- } ;
69
63
70
64
let term_value = term. value ( ) ;
71
65
if field_type. is_json ( ) {
@@ -175,11 +169,35 @@ impl Weight for FastFieldRangeWeight {
175
169
field_type
176
170
) ;
177
171
178
- let bounds = self . bounds . map_bound ( get_term_u64_internal_representation) ;
172
+ let bounds = self . bounds . map_bound_res ( |term| {
173
+ let value = term. value ( ) ;
174
+ let val = if let Some ( val) = value. as_u64 ( ) {
175
+ val
176
+ } else if let Some ( val) = value. as_i64 ( ) {
177
+ val. to_u64 ( )
178
+ } else if let Some ( val) = value. as_f64 ( ) {
179
+ val. to_u64 ( )
180
+ } else if let Some ( val) = value. as_date ( ) {
181
+ val. to_u64 ( )
182
+ } else {
183
+ return Err ( TantivyError :: InvalidArgument ( format ! (
184
+ "Expected term with u64, i64, f64 or date, but got {:?}" ,
185
+ term
186
+ ) ) ) ;
187
+ } ;
188
+ Ok ( val)
189
+ } ) ?;
179
190
180
191
let fast_field_reader = reader. fast_fields ( ) ;
181
- let Some ( ( column, _col_type) ) =
182
- fast_field_reader. u64_lenient_for_type ( None , & field_name) ?
192
+ let Some ( ( column, _col_type) ) = fast_field_reader. u64_lenient_for_type (
193
+ Some ( & [
194
+ ColumnType :: U64 ,
195
+ ColumnType :: I64 ,
196
+ ColumnType :: F64 ,
197
+ ColumnType :: DateTime ,
198
+ ] ) ,
199
+ & field_name,
200
+ ) ?
183
201
else {
184
202
return Ok ( Box :: new ( EmptyScorer ) ) ;
185
203
} ;
@@ -212,7 +230,7 @@ fn search_on_json_numerical_field(
212
230
boost : Score ,
213
231
) -> crate :: Result < Box < dyn Scorer > > {
214
232
// Since we don't know which type was interpolated for the internal column whe
215
- // have to check for all types (only one exists)
233
+ // have to check for all numeric types (only one exists)
216
234
let allowed_column_types: Option < & [ ColumnType ] > =
217
235
Some ( & [ ColumnType :: F64 , ColumnType :: I64 , ColumnType :: U64 ] ) ;
218
236
let fast_field_reader = reader. fast_fields ( ) ;
@@ -455,7 +473,8 @@ pub mod tests {
455
473
use crate :: query:: range_query:: range_query_u64_fastfield:: FastFieldRangeWeight ;
456
474
use crate :: query:: { QueryParser , RangeQuery , Weight } ;
457
475
use crate :: schema:: {
458
- Field , NumericOptions , Schema , SchemaBuilder , FAST , INDEXED , STORED , STRING , TEXT ,
476
+ DateOptions , Field , NumericOptions , Schema , SchemaBuilder , FAST , INDEXED , STORED , STRING ,
477
+ TEXT ,
459
478
} ;
460
479
use crate :: { Index , IndexWriter , Term , TERMINATED } ;
461
480
@@ -518,6 +537,89 @@ pub mod tests {
518
537
Ok ( ( ) )
519
538
}
520
539
540
+ #[ test]
541
+ fn test_date_range_query ( ) {
542
+ let mut schema_builder = Schema :: builder ( ) ;
543
+ let options = DateOptions :: default ( )
544
+ . set_precision ( common:: DateTimePrecision :: Microseconds )
545
+ . set_fast ( ) ;
546
+ let date_field = schema_builder. add_date_field ( "date" , options) ;
547
+ let schema = schema_builder. build ( ) ;
548
+
549
+ let index = Index :: create_in_ram ( schema. clone ( ) ) ;
550
+ {
551
+ let mut index_writer = index. writer_with_num_threads ( 1 , 50_000_000 ) . unwrap ( ) ;
552
+ // This is added a string and creates a string column!
553
+ index_writer
554
+ . add_document ( doc ! ( date_field => DateTime :: from_utc(
555
+ OffsetDateTime :: parse( "2022-12-01T00:00:01Z" , & Rfc3339 ) . unwrap( ) ,
556
+ ) ) )
557
+ . unwrap ( ) ;
558
+ index_writer
559
+ . add_document ( doc ! ( date_field => DateTime :: from_utc(
560
+ OffsetDateTime :: parse( "2023-12-01T00:00:01Z" , & Rfc3339 ) . unwrap( ) ,
561
+ ) ) )
562
+ . unwrap ( ) ;
563
+ index_writer
564
+ . add_document ( doc ! ( date_field => DateTime :: from_utc(
565
+ OffsetDateTime :: parse( "2015-02-01T00:00:00.001Z" , & Rfc3339 ) . unwrap( ) ,
566
+ ) ) )
567
+ . unwrap ( ) ;
568
+ index_writer. commit ( ) . unwrap ( ) ;
569
+ }
570
+
571
+ // Date field
572
+ let dt1 =
573
+ DateTime :: from_utc ( OffsetDateTime :: parse ( "2022-12-01T00:00:01Z" , & Rfc3339 ) . unwrap ( ) ) ;
574
+ let dt2 =
575
+ DateTime :: from_utc ( OffsetDateTime :: parse ( "2023-12-01T00:00:01Z" , & Rfc3339 ) . unwrap ( ) ) ;
576
+ let dt3 = DateTime :: from_utc (
577
+ OffsetDateTime :: parse ( "2015-02-01T00:00:00.001Z" , & Rfc3339 ) . unwrap ( ) ,
578
+ ) ;
579
+ let dt4 = DateTime :: from_utc (
580
+ OffsetDateTime :: parse ( "2015-02-01T00:00:00.002Z" , & Rfc3339 ) . unwrap ( ) ,
581
+ ) ;
582
+
583
+ let reader = index. reader ( ) . unwrap ( ) ;
584
+ let searcher = reader. searcher ( ) ;
585
+ let count = |range_query : RangeQuery | searcher. search ( & range_query, & Count ) . unwrap ( ) ;
586
+ assert_eq ! (
587
+ count( RangeQuery :: new(
588
+ Bound :: Included ( Term :: from_field_date( date_field, dt3) ) ,
589
+ Bound :: Excluded ( Term :: from_field_date( date_field, dt4) ) ,
590
+ ) ) ,
591
+ 1
592
+ ) ;
593
+ assert_eq ! (
594
+ count( RangeQuery :: new(
595
+ Bound :: Included ( Term :: from_field_date( date_field, dt3) ) ,
596
+ Bound :: Included ( Term :: from_field_date( date_field, dt4) ) ,
597
+ ) ) ,
598
+ 1
599
+ ) ;
600
+ assert_eq ! (
601
+ count( RangeQuery :: new(
602
+ Bound :: Included ( Term :: from_field_date( date_field, dt1) ) ,
603
+ Bound :: Included ( Term :: from_field_date( date_field, dt2) ) ,
604
+ ) ) ,
605
+ 2
606
+ ) ;
607
+ assert_eq ! (
608
+ count( RangeQuery :: new(
609
+ Bound :: Included ( Term :: from_field_date( date_field, dt1) ) ,
610
+ Bound :: Excluded ( Term :: from_field_date( date_field, dt2) ) ,
611
+ ) ) ,
612
+ 1
613
+ ) ;
614
+ assert_eq ! (
615
+ count( RangeQuery :: new(
616
+ Bound :: Excluded ( Term :: from_field_date( date_field, dt1) ) ,
617
+ Bound :: Excluded ( Term :: from_field_date( date_field, dt2) ) ,
618
+ ) ) ,
619
+ 0
620
+ ) ;
621
+ }
622
+
521
623
fn get_json_term < T : FastValue > ( field : Field , path : & str , value : T ) -> Term {
522
624
let mut term = Term :: from_field_json_path ( field, path, true ) ;
523
625
term. append_type_and_fast_value ( value) ;
@@ -548,6 +650,10 @@ pub mod tests {
548
650
"date" : "2023-12-01T00:00:01Z"
549
651
} ) ;
550
652
index_writer. add_document ( doc ! ( json_field => doc) ) . unwrap ( ) ;
653
+ let doc = json ! ( {
654
+ "date" : "2015-02-01T00:00:00.001Z"
655
+ } ) ;
656
+ index_writer. add_document ( doc ! ( json_field => doc) ) . unwrap ( ) ;
551
657
552
658
index_writer. commit ( ) . unwrap ( ) ;
553
659
}
@@ -631,6 +737,13 @@ pub mod tests {
631
737
) ) ,
632
738
2
633
739
) ;
740
+ assert_eq ! (
741
+ count( RangeQuery :: new(
742
+ Bound :: Included ( get_json_term( json_field, "id_i64" , 1000i64 ) ) ,
743
+ Bound :: Excluded ( get_json_term( json_field, "id_i64" , 1001i64 ) ) ,
744
+ ) ) ,
745
+ 1
746
+ ) ;
634
747
635
748
// u64 on i64
636
749
assert_eq ! (
@@ -718,6 +831,18 @@ pub mod tests {
718
831
) ) ,
719
832
0
720
833
) ;
834
+ // Date precision test. We don't want to truncate the precision
835
+ let dt3 = DateTime :: from_utc (
836
+ OffsetDateTime :: parse ( "2015-02-01T00:00:00.001Z" , & Rfc3339 ) . unwrap ( ) ,
837
+ ) ;
838
+ let dt4 = DateTime :: from_utc (
839
+ OffsetDateTime :: parse ( "2015-02-01T00:00:00.002Z" , & Rfc3339 ) . unwrap ( ) ,
840
+ ) ;
841
+ let query = RangeQuery :: new (
842
+ Bound :: Included ( get_json_term ( json_field, "date" , dt3) ) ,
843
+ Bound :: Excluded ( get_json_term ( json_field, "date" , dt4) ) ,
844
+ ) ;
845
+ assert_eq ! ( count( query) , 1 ) ;
721
846
}
722
847
723
848
#[ derive( Clone , Debug ) ]
@@ -796,13 +921,10 @@ pub mod tests {
796
921
writer. add_document ( doc ! ( field=>52_000u64 ) ) . unwrap ( ) ;
797
922
writer. commit ( ) . unwrap ( ) ;
798
923
let searcher = index. reader ( ) . unwrap ( ) . searcher ( ) ;
799
- let range_query = FastFieldRangeWeight :: new (
800
- field,
801
- BoundsRange :: new (
802
- Bound :: Included ( Term :: from_field_u64 ( field, 50_000 ) ) ,
803
- Bound :: Included ( Term :: from_field_u64 ( field, 50_002 ) ) ,
804
- ) ,
805
- ) ;
924
+ let range_query = FastFieldRangeWeight :: new ( BoundsRange :: new (
925
+ Bound :: Included ( Term :: from_field_u64 ( field, 50_000 ) ) ,
926
+ Bound :: Included ( Term :: from_field_u64 ( field, 50_002 ) ) ,
927
+ ) ) ;
806
928
let scorer = range_query
807
929
. scorer ( searcher. segment_reader ( 0 ) , 1.0f32 )
808
930
. unwrap ( ) ;
@@ -1158,13 +1280,10 @@ pub mod ip_range_tests {
1158
1280
}
1159
1281
writer. commit ( ) . unwrap ( ) ;
1160
1282
let searcher = index. reader ( ) . unwrap ( ) . searcher ( ) ;
1161
- let range_weight = FastFieldRangeWeight :: new (
1162
- ips_field,
1163
- BoundsRange :: new (
1164
- Bound :: Included ( Term :: from_field_ip_addr ( ips_field, ip_addrs[ 1 ] ) ) ,
1165
- Bound :: Included ( Term :: from_field_ip_addr ( ips_field, ip_addrs[ 2 ] ) ) ,
1166
- ) ,
1167
- ) ;
1283
+ let range_weight = FastFieldRangeWeight :: new ( BoundsRange :: new (
1284
+ Bound :: Included ( Term :: from_field_ip_addr ( ips_field, ip_addrs[ 1 ] ) ) ,
1285
+ Bound :: Included ( Term :: from_field_ip_addr ( ips_field, ip_addrs[ 2 ] ) ) ,
1286
+ ) ) ;
1168
1287
1169
1288
let count =
1170
1289
crate :: query:: weight:: Weight :: count ( & range_weight, searcher. segment_reader ( 0 ) ) . unwrap ( ) ;
0 commit comments