use mongodb::bson::{Document, doc};
use super::{
builder::QueryBuilder,
value::{DbValue, Op},
};
pub trait IntoDbFilter {
fn into_mongo_filter(self) -> Document;
fn into_mongo_update(self) -> Document;
fn into_postgres_filter(self, offset: usize) -> (String, Vec<DbValue>);
fn into_postgres_update(self) -> (String, Vec<DbValue>);
fn into_sqlite_filter(self) -> (String, Vec<DbValue>);
fn into_sqlite_update(self) -> (String, Vec<DbValue>);
}
impl IntoDbFilter for QueryBuilder {
fn into_mongo_filter(self) -> Document {
let mut doc = Document::new();
for op in self.filters {
let value = op.value.into_boson(op.field);
let field = if op.field == "id" { "_id" } else { op.field };
match op.op {
Op::Eq => {
doc.insert(field, value);
}
Op::Ne => {
doc.insert(field, doc! { "$ne": value });
}
Op::Gt => {
doc.insert(field, doc! { "$gt": value });
}
Op::Lt => {
doc.insert(field, doc! { "$lt": value });
}
Op::In => {
doc.insert(field, doc! { "$in": value });
}
Op::Null => {
doc.insert(field, doc! { "$in": [mongodb::bson::Bson::Null] });
}
_ => {}
}
}
doc
}
fn into_mongo_update(self) -> Document {
let mut doc = Document::new();
if !self.updates.is_empty() {
let mut set_doc = Document::new();
let mut inc_doc = Document::new();
let mut unset_doc = Document::new();
for op in self.updates {
let value = op.value.into_boson(op.field);
match op.op {
Op::Set => {
set_doc.insert(op.field, value);
}
Op::Inc => {
inc_doc.insert(op.field, value);
}
Op::Unset => {
unset_doc.insert(op.field, "");
}
_ => {}
}
}
if !set_doc.is_empty() {
doc.insert("$set", set_doc);
}
if !inc_doc.is_empty() {
doc.insert("$inc", inc_doc);
}
if !unset_doc.is_empty() {
doc.insert("$unset", unset_doc);
}
}
doc
}
fn into_postgres_filter(self, offset: usize) -> (String, Vec<DbValue>) {
let clause = self
.filters
.iter()
.enumerate()
.map(|(i, f)| match f.op {
Op::Eq => format!("\"{}\" = ${}", f.field, i + 1 + offset),
Op::Null => format!("\"{}\" IS NULL", f.field), Op::Ne => format!("\"{}\" != ${}", f.field, i + 1 + offset),
Op::Gt => format!("\"{}\" > ${}", f.field, i + 1 + offset),
Op::Lt => format!("\"{}\" < ${}", f.field, i + 1 + offset),
Op::In => format!("\"{}\" = ANY(${})", f.field, i + 1 + offset),
_ => unreachable!("update ops not valid in filter context"),
})
.collect::<Vec<_>>()
.join(" AND ");
let values = self.filters.iter().map(|f| f.value.clone()).collect();
(clause, values)
}
fn into_postgres_update(self) -> (String, Vec<DbValue>) {
let clause = self
.updates
.iter()
.enumerate()
.map(|(i, f)| match f.op {
Op::Set => format!("\"{}\" = ${}", f.field, i + 1),
Op::Inc => format!("\"{}\" = \"{}\" + ${}", f.field, f.field, i + 1),
Op::Unset => format!("\"{}\" = NULL", f.field),
_ => unreachable!("filter ops not valid in update context"),
})
.collect::<Vec<_>>()
.join(" , ");
let values = self.updates.iter().map(|f| f.value.clone()).collect();
(clause, values)
}
fn into_sqlite_filter(self) -> (String, Vec<DbValue>) {
let clause = self
.filters
.iter()
.map(|f| match f.op {
Op::Eq => format!("\"{}\" = ?", f.field),
Op::Null => format!("\"{}\" IS NULL", f.field),
Op::Ne => format!("\"{}\" != ?", f.field),
Op::Gt => format!("\"{}\" > ?", f.field),
Op::Lt => format!("\"{}\" < ?", f.field),
Op::In => match &f.value {
DbValue::List(list) => {
let placeholders = list.iter().map(|_| "?").collect::<Vec<_>>().join(", ");
format!("\"{}\" IN ({})", f.field, placeholders)
}
_ => unreachable!("In op must have List value"),
},
_ => unreachable!("update ops not valid in filter context"),
})
.collect::<Vec<_>>()
.join(" AND ");
let values = self
.filters
.iter()
.flat_map(|f| match &f.value {
DbValue::List(list) => list.clone(),
_ => vec![f.value.clone()],
})
.collect();
(clause, values)
}
fn into_sqlite_update(self) -> (String, Vec<DbValue>) {
let clause = self
.updates
.iter()
.map(|f| match f.op {
Op::Set => format!("\"{}\" = ?", f.field),
Op::Inc => format!("\"{}\" = \"{}\" + ?", f.field, f.field),
Op::Unset => format!("\"{}\" = NULL", f.field),
_ => unreachable!("filter ops not valid in update context"),
})
.collect::<Vec<_>>()
.join(" , ");
let values = self.updates.iter().map(|f| f.value.clone()).collect();
(clause, values)
}
}