@@ -3,11 +3,16 @@ use std::collections::HashMap;
33use tantivy_fst:: raw:: CompiledAddr ;
44use tantivy_fst:: { Automaton , Map } ;
55
6+ use super :: term_set_query_fastfield:: FastFieldTermSetWeight ;
67use crate :: query:: score_combiner:: DoNothingCombiner ;
78use crate :: query:: { AutomatonWeight , BooleanWeight , EnableScoring , Occur , Query , Weight } ;
8- use crate :: schema:: { Field , Schema } ;
9+ use crate :: schema:: { Field , Schema , Type } ;
910use crate :: { SegmentReader , Term } ;
1011
12+ /// The term set query will use the fast field implementation if the number of terms is larger than
13+ /// this threshold.
14+ const TERM_SET_FAST_FIELD_CARDINALITY_THRESHOLD : usize = 1024 ;
15+
1116/// A Term Set Query matches all of the documents containing any of the Term provided
1217#[ derive( Debug , Clone ) ]
1318pub struct TermSetQuery {
@@ -22,11 +27,6 @@ impl TermSetQuery {
2227 terms_map. entry ( term. field ( ) ) . or_default ( ) . push ( term) ;
2328 }
2429
25- for terms in terms_map. values_mut ( ) {
26- terms. sort_unstable ( ) ;
27- terms. dedup ( ) ;
28- }
29-
3030 TermSetQuery { terms_map }
3131 }
3232
@@ -36,28 +36,60 @@ impl TermSetQuery {
3636 ) -> crate :: Result < BooleanWeight < DoNothingCombiner > > {
3737 let mut sub_queries: Vec < ( _ , Box < dyn Weight > ) > = Vec :: with_capacity ( self . terms_map . len ( ) ) ;
3838
39- for ( & field, sorted_terms ) in self . terms_map . iter ( ) {
39+ for ( & field, terms ) in self . terms_map . iter ( ) {
4040 let field_entry = schema. get_field_entry ( field) ;
4141 let field_type = field_entry. field_type ( ) ;
4242 if !field_type. is_indexed ( ) {
4343 let error_msg = format ! ( "Field {:?} is not indexed." , field_entry. name( ) ) ;
4444 return Err ( crate :: TantivyError :: SchemaError ( error_msg) ) ;
4545 }
4646
47- // In practice this won't fail because:
48- // - we are writing to memory, so no IoError
49- // - Terms are ordered
50- let map = Map :: from_iter (
51- sorted_terms
47+ let supported_for_ff = match field_type. value_type ( ) {
48+ Type :: U64 | Type :: I64 | Type :: F64 | Type :: Date | Type :: IpAddr => {
49+ // NOTE: Keep in sync with `FastFieldTermSetWeight::scorer`.
50+ true
51+ }
52+ Type :: Bool => {
53+ // Guaranteed to be low cardinality, so always more efficient to use posting
54+ // lists.
55+ false
56+ }
57+ Type :: Json | Type :: Str => {
58+ // Explicitly not supported yet: see `term_set_query_fastfield.rs`.
59+ false
60+ }
61+ _ => false ,
62+ } ;
63+
64+ // NOTE: At this point, the terms have not been deduped, and so this threshold may not
65+ // be perfectly accurate. But in the case of very large input sets, it's worth avoiding
66+ // sorting/deduping the terms until after we've determined their type.
67+ if field_type. is_fast ( )
68+ && supported_for_ff
69+ && terms. len ( ) > TERM_SET_FAST_FIELD_CARDINALITY_THRESHOLD
70+ {
71+ sub_queries. push ( (
72+ Occur :: Should ,
73+ Box :: new ( FastFieldTermSetWeight :: new ( field, terms. iter ( ) ) ?) ,
74+ ) ) ;
75+ } else {
76+ let mut sorted_terms: Vec < ( & [ u8 ] , u64 ) > = terms
5277 . iter ( )
53- . map ( |key| ( key. serialized_value_bytes ( ) , 0 ) ) ,
54- )
55- . map_err ( std:: io:: Error :: other) ?;
56-
57- sub_queries. push ( (
58- Occur :: Should ,
59- Box :: new ( AutomatonWeight :: new ( field, SetDfaWrapper ( map) ) ) ,
60- ) ) ;
78+ . map ( |key| ( key. serialized_value_bytes ( ) , 0 ) )
79+ . collect :: < Vec < _ > > ( ) ;
80+ sorted_terms. sort_unstable ( ) ;
81+ sorted_terms. dedup ( ) ;
82+ // In practice this won't fail because:
83+ // - we are writing to memory, so no IoError
84+ // - `sorted_terms` are ordered
85+ let map = Map :: from_iter ( sorted_terms. into_iter ( ) )
86+ . map_err ( |e| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e) ) ?;
87+
88+ sub_queries. push ( (
89+ Occur :: Should ,
90+ Box :: new ( AutomatonWeight :: new ( field, SetDfaWrapper ( map) ) ) ,
91+ ) ) ;
92+ }
6193 }
6294
6395 Ok ( BooleanWeight :: new (
@@ -87,6 +119,59 @@ impl Query for TermSetQuery {
87119 }
88120}
89121
122+ /// `InvertedIndexTermSetQuery` is the same as [TermSetQuery] but only uses the inverted index.
123+ #[ derive( Debug , Clone ) ]
124+ pub struct InvertedIndexTermSetQuery {
125+ terms_map : HashMap < Field , Vec < Term > > ,
126+ }
127+
128+ impl InvertedIndexTermSetQuery {
129+ /// Create a new `InvertedIndexTermSetQuery`.
130+ pub fn new < T : IntoIterator < Item = Term > > ( terms : T ) -> Self {
131+ let mut terms_map: HashMap < _ , Vec < _ > > = HashMap :: new ( ) ;
132+ for term in terms {
133+ terms_map. entry ( term. field ( ) ) . or_default ( ) . push ( term) ;
134+ }
135+
136+ for terms in terms_map. values_mut ( ) {
137+ terms. sort_unstable ( ) ;
138+ terms. dedup ( ) ;
139+ }
140+
141+ InvertedIndexTermSetQuery { terms_map }
142+ }
143+ }
144+
145+ impl Query for InvertedIndexTermSetQuery {
146+ fn weight ( & self , enable_scoring : EnableScoring < ' _ > ) -> crate :: Result < Box < dyn Weight > > {
147+ let mut sub_queries: Vec < ( _ , Box < dyn Weight > ) > = Vec :: with_capacity ( self . terms_map . len ( ) ) ;
148+ for ( & field, sorted_terms) in & self . terms_map {
149+ let schema = enable_scoring. schema ( ) ;
150+ let field_entry = schema. get_field_entry ( field) ;
151+ if !field_entry. field_type ( ) . is_indexed ( ) {
152+ let error_msg = format ! ( "Field {:?} is not indexed." , field_entry. name( ) ) ;
153+ return Err ( crate :: TantivyError :: SchemaError ( error_msg) ) ;
154+ }
155+ let map = Map :: from_iter (
156+ sorted_terms
157+ . iter ( )
158+ . map ( |key| ( key. serialized_value_bytes ( ) , 0 ) ) ,
159+ )
160+ . map_err ( |e| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e) ) ?;
161+
162+ sub_queries. push ( (
163+ Occur :: Should ,
164+ Box :: new ( AutomatonWeight :: new ( field, SetDfaWrapper ( map) ) ) ,
165+ ) ) ;
166+ }
167+ Ok ( Box :: new ( BooleanWeight :: new (
168+ sub_queries,
169+ false ,
170+ Box :: new ( DoNothingCombiner :: default) ,
171+ ) ) )
172+ }
173+ }
174+
90175struct SetDfaWrapper ( Map < Vec < u8 > > ) ;
91176
92177impl Automaton for SetDfaWrapper {
0 commit comments