Skip to main content

shared/domain/ports/query/
convert.rs

1use mongodb::bson::{Document, doc};
2
3use super::{
4    builder::QueryBuilder,
5    value::{DbValue, Op},
6};
7
8pub trait IntoDbFilter {
9    fn into_mongo_filter(self) -> Document;
10    fn into_mongo_update(self) -> Document;
11    fn into_postgres_filter(self, offset: usize) -> (String, Vec<DbValue>);
12    fn into_postgres_update(self) -> (String, Vec<DbValue>);
13    fn into_sqlite_filter(self) -> (String, Vec<DbValue>);
14    fn into_sqlite_update(self) -> (String, Vec<DbValue>);
15}
16
17impl IntoDbFilter for QueryBuilder {
18    fn into_mongo_filter(self) -> Document {
19        let mut doc = Document::new();
20
21        // filters
22        for op in self.filters {
23            let value = op.value.into_boson(op.field);
24            let field = if op.field == "id" { "_id" } else { op.field };
25
26            match op.op {
27                Op::Eq => {
28                    doc.insert(field, value);
29                }
30                Op::Ne => {
31                    doc.insert(field, doc! { "$ne": value });
32                }
33                Op::Gt => {
34                    doc.insert(field, doc! { "$gt": value });
35                }
36                Op::Lt => {
37                    doc.insert(field, doc! { "$lt": value });
38                }
39                Op::In => {
40                    doc.insert(field, doc! { "$in": value });
41                }
42                Op::Null => {
43                    doc.insert(field, doc! { "$in": [mongodb::bson::Bson::Null] });
44                }
45                _ => {}
46            }
47        }
48
49        doc
50    }
51
52    fn into_mongo_update(self) -> Document {
53        let mut doc = Document::new();
54
55        // updates
56        if !self.updates.is_empty() {
57            let mut set_doc = Document::new();
58            let mut inc_doc = Document::new();
59            let mut unset_doc = Document::new();
60
61            for op in self.updates {
62                let value = op.value.into_boson(op.field);
63
64                match op.op {
65                    Op::Set => {
66                        set_doc.insert(op.field, value);
67                    }
68                    Op::Inc => {
69                        inc_doc.insert(op.field, value);
70                    }
71                    Op::Unset => {
72                        unset_doc.insert(op.field, "");
73                    }
74                    _ => {}
75                }
76            }
77
78            if !set_doc.is_empty() {
79                doc.insert("$set", set_doc);
80            }
81            if !inc_doc.is_empty() {
82                doc.insert("$inc", inc_doc);
83            }
84            if !unset_doc.is_empty() {
85                doc.insert("$unset", unset_doc);
86            }
87        }
88
89        doc
90    }
91    fn into_postgres_filter(self, offset: usize) -> (String, Vec<DbValue>) {
92        let clause = self
93            .filters
94            .iter()
95            .enumerate()
96            .map(|(i, f)| match f.op {
97                Op::Eq => format!("\"{}\" = ${}", f.field, i + 1 + offset),
98                Op::Null => format!("\"{}\" IS NULL", f.field), // no binding needed
99                Op::Ne => format!("\"{}\" != ${}", f.field, i + 1 + offset),
100                Op::Gt => format!("\"{}\" > ${}", f.field, i + 1 + offset),
101                Op::Lt => format!("\"{}\" < ${}", f.field, i + 1 + offset),
102                Op::In => format!("\"{}\" = ANY(${})", f.field, i + 1 + offset),
103                _ => unreachable!("update ops not valid in filter context"),
104            })
105            .collect::<Vec<_>>()
106            .join(" AND ");
107
108        let values = self.filters.iter().map(|f| f.value.clone()).collect();
109        (clause, values)
110    }
111    fn into_postgres_update(self) -> (String, Vec<DbValue>) {
112        let clause = self
113            .updates
114            .iter()
115            .enumerate()
116            .map(|(i, f)| match f.op {
117                Op::Set => format!("\"{}\" = ${}", f.field, i + 1),
118                Op::Inc => format!("\"{}\" = \"{}\" + ${}", f.field, f.field, i + 1),
119                Op::Unset => format!("\"{}\" = NULL", f.field),
120                _ => unreachable!("filter ops not valid in update context"),
121            })
122            .collect::<Vec<_>>()
123            .join(" , ");
124
125        let values = self.updates.iter().map(|f| f.value.clone()).collect();
126        (clause, values)
127    }
128
129    // -- Sqlite
130    fn into_sqlite_filter(self) -> (String, Vec<DbValue>) {
131        let clause = self
132            .filters
133            .iter()
134            .map(|f| match f.op {
135                Op::Eq => format!("\"{}\" = ?", f.field),
136                Op::Null => format!("\"{}\" IS NULL", f.field),
137                Op::Ne => format!("\"{}\" != ?", f.field),
138                Op::Gt => format!("\"{}\" > ?", f.field),
139                Op::Lt => format!("\"{}\" < ?", f.field),
140                // Op::In => format!("\"{}\" = ANY(?{})", f.field, i + 1),
141                Op::In => match &f.value {
142                    DbValue::List(list) => {
143                        let placeholders = list.iter().map(|_| "?").collect::<Vec<_>>().join(", ");
144                        format!("\"{}\" IN ({})", f.field, placeholders)
145                    }
146                    _ => unreachable!("In op must have List value"),
147                },
148                _ => unreachable!("update ops not valid in filter context"),
149            })
150            .collect::<Vec<_>>()
151            .join(" AND ");
152
153        // let values = self.filters.iter().map(|f| f.value.clone()).collect();
154        let values = self
155            .filters
156            .iter()
157            .flat_map(|f| match &f.value {
158                DbValue::List(list) => list.clone(),
159                _ => vec![f.value.clone()],
160            })
161            .collect();
162
163        (clause, values)
164    }
165    fn into_sqlite_update(self) -> (String, Vec<DbValue>) {
166        let clause = self
167            .updates
168            .iter()
169            .map(|f| match f.op {
170                Op::Set => format!("\"{}\" = ?", f.field),
171                Op::Inc => format!("\"{}\" = \"{}\" + ?", f.field, f.field),
172                Op::Unset => format!("\"{}\" = NULL", f.field),
173                _ => unreachable!("filter ops not valid in update context"),
174            })
175            .collect::<Vec<_>>()
176            .join(" , ");
177
178        let values = self.updates.iter().map(|f| f.value.clone()).collect();
179        (clause, values)
180    }
181}