Skip to main content

summa_core/query/
all.rs

1//! The complete document universe, including documents with missing fields.
2use super::{AllDocSet, CountFuture, DocBitset, DocPredicate, DocSet, Query, Scorer, ScorerFuture};
3use crate::segment::SegmentReader;
4use crate::{DocId, Score};
5
6/// Matches every document, including documents with missing fields.
7#[derive(Debug, Clone, Copy)]
8pub struct AllQuery;
9
10impl std::fmt::Display for AllQuery {
11    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12        f.write_str("All")
13    }
14}
15
16impl Query for AllQuery {
17    fn scorer<'a>(&self, reader: &'a SegmentReader, _limit: usize) -> ScorerFuture<'a> {
18        let scorer = AllScorer::new(reader.num_docs());
19        Box::pin(async move { Ok(Box::new(scorer) as Box<dyn Scorer>) })
20    }
21
22    #[cfg(feature = "sync")]
23    fn scorer_sync<'a>(
24        &self,
25        reader: &'a SegmentReader,
26        _limit: usize,
27    ) -> crate::Result<Box<dyn Scorer + 'a>> {
28        Ok(Box::new(AllScorer::new(reader.num_docs())))
29    }
30
31    fn count_estimate<'a>(&self, reader: &'a SegmentReader) -> CountFuture<'a> {
32        let count = reader.num_docs();
33        Box::pin(async move { Ok(count) })
34    }
35
36    fn is_filter(&self) -> bool {
37        true
38    }
39
40    fn as_doc_predicate<'a>(&self, reader: &'a SegmentReader) -> Option<DocPredicate<'a>> {
41        let count = reader.num_docs();
42        Some(Box::new(move |doc| doc < count))
43    }
44
45    fn as_doc_bitset(&self, reader: &SegmentReader) -> Option<DocBitset> {
46        Some(DocBitset::all(reader.num_docs()))
47    }
48}
49
50struct AllScorer(AllDocSet);
51
52impl AllScorer {
53    fn new(count: u32) -> Self {
54        Self(AllDocSet::new(count))
55    }
56}
57
58impl DocSet for AllScorer {
59    fn doc(&self) -> DocId {
60        self.0.doc()
61    }
62
63    fn advance(&mut self) -> DocId {
64        self.0.advance()
65    }
66
67    fn seek(&mut self, target: DocId) -> DocId {
68        self.0.seek(target)
69    }
70
71    fn size_hint(&self) -> u32 {
72        self.0.size_hint()
73    }
74}
75
76impl Scorer for AllScorer {
77    fn score(&self) -> Score {
78        1.0
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use crate::TERMINATED;
86    #[tokio::test]
87    async fn exclusion_filter_includes_documents_without_metadata_in_every_execution_path() {
88        use crate::query::{BooleanQuery, FilteredQuery, RangeQuery};
89        use crate::{Document, Index, IndexConfig, IndexWriter, RamDirectory, Schema};
90        use std::sync::Arc;
91        let mut schema = Schema::builder();
92        let date = schema.add_i64_field("date", true, false);
93        schema.set_fast(date, true);
94        let dir = RamDirectory::new();
95        let config = IndexConfig::default();
96        let mut writer = IndexWriter::create(dir.clone(), schema.build(), config.clone())
97            .await
98            .unwrap();
99        for date_value in [Some(-1), Some(0), None] {
100            let mut doc = Document::new();
101            if let Some(value) = date_value {
102                doc.add_i64(date, value);
103            }
104            writer.add_document(doc).unwrap();
105        }
106        writer.commit().await.unwrap();
107        let index = Index::open(dir, config).await.unwrap();
108        let searcher = index.reader().await.unwrap().searcher().await.unwrap();
109        let reader = &searcher.segment_readers()[0];
110        let filter =
111            BooleanQuery::new()
112                .must(AllQuery)
113                .must_not(RangeQuery::i64(date, Some(0), Some(0)));
114        let predicate = filter.as_doc_predicate(reader).unwrap();
115        let bits = filter.as_doc_bitset(reader).unwrap();
116        for doc in 0..3 {
117            assert_eq!(predicate(doc), doc != 1);
118            assert_eq!(bits.contains(doc), doc != 1);
119        }
120        let query = FilteredQuery::new(Arc::new(AllQuery), vec![Arc::new(filter)]);
121        let check = |mut scorer: Box<dyn Scorer + '_>| {
122            let mut docs = Vec::new();
123            while scorer.doc() != TERMINATED {
124                docs.push(scorer.doc());
125                scorer.advance();
126            }
127            assert_eq!(docs, [0, 2]);
128        };
129        check(query.scorer(reader, 10).await.unwrap());
130        #[cfg(feature = "sync")]
131        check(
132            query
133                .scorer_sync_with_options(reader, 10, super::super::ScorerOptions::default())
134                .unwrap(),
135        );
136    }
137
138    #[test]
139    fn complete_universe_has_no_tail_documents_and_seek_never_rewinds() {
140        for count in [0, 1, 63, 64, 65, 129] {
141            let bits = DocBitset::all(count);
142            let mut scorer = AllScorer::new(count);
143            for doc in 0..count {
144                assert!(bits.contains(doc));
145                assert_eq!(scorer.doc(), doc);
146                assert_eq!(scorer.score(), 1.0);
147                scorer.advance();
148            }
149            assert_eq!(bits.next_set_bit(count), None);
150            assert_eq!(scorer.doc(), TERMINATED);
151            assert_eq!(scorer.advance(), TERMINATED);
152            assert_eq!(scorer.seek(0), TERMINATED);
153        }
154        let mut scorer = AllScorer::new(10);
155        assert_eq!(scorer.seek(5), 5);
156        assert_eq!(scorer.seek(2), 5);
157        assert_eq!(scorer.advance(), 6);
158    }
159}