Skip to main content

vantage_mongodb/select/
render.rs

1//! Human-readable preview rendering for MongoSelect.
2
3use super::MongoSelect;
4use super::join::filter_uses_joins;
5use crate::condition::MongoCondition;
6
7impl MongoSelect {
8    /// Render a human-readable preview string (for debug/logging).
9    pub fn preview(&self) -> String {
10        let coll = self.collection.as_deref().unwrap_or("?");
11
12        let filter_str = if self.conditions.is_empty() {
13            "{}".to_string()
14        } else {
15            format!("<{} conditions>", self.conditions.len())
16        };
17
18        if !self.pre_stages.is_empty() || !self.joins.is_empty() {
19            return self.preview_aggregate(coll, &filter_str);
20        }
21
22        let mut parts = vec![format!("db.{}.find({})", coll, filter_str)];
23
24        if let Some(proj) = self.build_projection() {
25            parts.push(format!(".projection({})", proj));
26        }
27        if let Some(sort) = self.build_sort() {
28            parts.push(format!(".sort({})", sort));
29        }
30        if let Some(skip) = self.skip {
31            parts.push(format!(".skip({})", skip));
32        }
33        if let Some(limit) = self.limit {
34            parts.push(format!(".limit({})", limit));
35        }
36
37        parts.join("")
38    }
39
40    /// The read pipeline (`as_read_pipeline`) spelled as
41    /// `db.<coll>.aggregate([...])`, in the same stage order, with each
42    /// filter summarised the same way as the `find` form. A deferred
43    /// condition is not resolved here, so it counts as one on stored fields.
44    fn preview_aggregate(&self, coll: &str, filter_str: &str) -> String {
45        let joined = self
46            .conditions
47            .iter()
48            .filter(
49                |c| matches!(c, MongoCondition::Doc(d) if filter_uses_joins(d, self.joins.len())),
50            )
51            .count();
52        let stored = self.conditions.len() - joined;
53        let summary = |n: usize| {
54            let filter = match n {
55                n if n == self.conditions.len() => filter_str.to_string(),
56                n => format!("<{n} conditions>"),
57            };
58            (n > 0).then(|| format!("{{ \"$match\": {filter} }}"))
59        };
60        let stages = self.read_layout(summary(stored), summary(joined), |d| d.to_string());
61        format!("db.{}.aggregate([{}])", coll, stages.join(", "))
62    }
63}