Skip to main content

luci/query/
exists.rs

1//! ExistsQuery: match documents where a field has a value.
2//!
3//! Checks field norms — any doc with norm > 0 has the field indexed.
4//!
5//! See [[query-dsl#Term-Level Queries]] and [[architecture-query-execution#Step 6]].
6
7use crate::core::{DocId, FieldId, NO_MORE_DOCS, Result, ScoreMode, Scorer, TwoPhaseIterator};
8
9use crate::query::{BoundQuery, Query, ScorerSupplier};
10use crate::search::searcher::Searcher;
11use crate::segment::reader::SegmentReader;
12
13pub struct ExistsQuery {
14    pub field: String,
15}
16
17impl Query for ExistsQuery {
18    fn bind(&self, _searcher: &Searcher, _score_mode: ScoreMode) -> Result<Box<dyn BoundQuery>> {
19        Ok(Box::new(BoundExistsQuery {
20            field: self.field.clone(),
21        }))
22    }
23}
24
25struct BoundExistsQuery {
26    field: String,
27}
28
29impl BoundQuery for BoundExistsQuery {
30    fn scorer_supplier(&self, reader: &SegmentReader) -> Result<Option<Box<dyn ScorerSupplier>>> {
31        let field_id = match reader
32            .header()
33            .fields
34            .iter()
35            .find(|f| f.field_name == self.field)
36            .map(|f| f.field_id)
37        {
38            Some(id) => id,
39            None => return Ok(None),
40        };
41
42        // Check if norms exist for this field
43        if reader.norms(field_id).is_none() {
44            return Ok(None);
45        }
46
47        Ok(Some(Box::new(ExistsScorerSupplier {
48            field_id,
49            doc_count: reader.doc_count(),
50            segment_data: reader as *const SegmentReader,
51        })))
52    }
53}
54
55struct ExistsScorerSupplier {
56    field_id: FieldId,
57    doc_count: u32,
58    segment_data: *const SegmentReader,
59}
60
61unsafe impl Send for ExistsScorerSupplier {}
62
63impl ScorerSupplier for ExistsScorerSupplier {
64    fn cost(&self) -> u64 {
65        self.doc_count as u64
66    }
67
68    fn scorer(self: Box<Self>) -> Result<Box<dyn Scorer>> {
69        let reader = unsafe { &*self.segment_data };
70        let norms = reader.norms(self.field_id).unwrap();
71        Ok(Box::new(ExistsScorer {
72            doc_count: self.doc_count,
73            current: 0,
74            norms_fn: Box::new(move |doc_id: DocId| norms.norm(doc_id) > 0.0),
75        }))
76    }
77}
78
79struct ExistsScorer {
80    doc_count: u32,
81    current: u32,
82    norms_fn: Box<dyn Fn(DocId) -> bool + Send>,
83}
84
85impl ExistsScorer {
86    fn advance_to_existing(&mut self) {
87        while self.current < self.doc_count {
88            if (self.norms_fn)(DocId::new(self.current)) {
89                return;
90            }
91            self.current += 1;
92        }
93    }
94}
95
96impl Scorer for ExistsScorer {
97    fn doc_id(&self) -> DocId {
98        if self.current < self.doc_count {
99            DocId::new(self.current)
100        } else {
101            NO_MORE_DOCS
102        }
103    }
104
105    fn next(&mut self) -> DocId {
106        self.current += 1;
107        self.advance_to_existing();
108        self.doc_id()
109    }
110
111    fn advance(&mut self, target: DocId) -> DocId {
112        self.current = target.as_u32();
113        self.advance_to_existing();
114        self.doc_id()
115    }
116
117    fn score(&mut self) -> f32 {
118        1.0
119    }
120    fn two_phase(&mut self) -> Option<&mut dyn TwoPhaseIterator> {
121        None
122    }
123}