use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use super::{Aggregation, FieldRef, Filter, MbqlQueryBuilder};
use crate::core::models::MetabaseId;
use crate::Result;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MbqlQuery {
#[serde(rename = "source-table", skip_serializing_if = "Option::is_none")]
pub(super) source_table: Option<MetabaseId>,
#[serde(rename = "aggregation", skip_serializing_if = "Vec::is_empty", default)]
pub(super) aggregations: Vec<Aggregation>,
#[serde(rename = "breakout", skip_serializing_if = "Vec::is_empty", default)]
pub(super) breakout: Vec<FieldRef>,
#[serde(rename = "filter", skip_serializing_if = "Option::is_none")]
pub(super) filter: Option<Filter>,
#[serde(rename = "order-by", skip_serializing_if = "Vec::is_empty", default)]
pub(super) order_by: Vec<OrderBy>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(super) limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(super) offset: Option<u32>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub(super) fields: Vec<FieldRef>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(super) expressions: Option<Value>,
}
impl MbqlQuery {
pub fn builder() -> MbqlQueryBuilder {
MbqlQueryBuilder::new()
}
pub fn source_table(&self) -> Option<MetabaseId> {
self.source_table
}
pub fn aggregations(&self) -> &[Aggregation] {
&self.aggregations
}
pub fn breakout(&self) -> &[FieldRef] {
&self.breakout
}
pub fn filter(&self) -> Option<&Filter> {
self.filter.as_ref()
}
pub fn order_by(&self) -> &[OrderBy] {
&self.order_by
}
pub fn limit(&self) -> Option<u32> {
self.limit
}
pub fn offset(&self) -> Option<u32> {
self.offset
}
pub fn fields(&self) -> &[FieldRef] {
&self.fields
}
pub fn to_json(&self) -> Result<Value> {
Ok(serde_json::to_value(self)?)
}
pub fn to_dataset_query(&self, database_id: MetabaseId) -> crate::core::models::DatasetQuery {
crate::core::models::DatasetQuery {
database: database_id,
query_type: "query".to_string(),
query: self.to_json().unwrap_or_default(),
parameters: None,
constraints: None,
}
}
}
impl Default for MbqlQuery {
fn default() -> Self {
Self {
source_table: None,
aggregations: Vec::new(),
breakout: Vec::new(),
filter: None,
order_by: Vec::new(),
limit: None,
offset: None,
fields: Vec::new(),
expressions: None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct OrderBy {
field: FieldRef,
direction: OrderDirection,
}
impl OrderBy {
pub fn new(field: FieldRef, direction: OrderDirection) -> Self {
Self { field, direction }
}
pub fn asc(field: FieldRef) -> Self {
Self::new(field, OrderDirection::Asc)
}
pub fn desc(field: FieldRef) -> Self {
Self::new(field, OrderDirection::Desc)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum OrderDirection {
Asc,
Desc,
}
impl Serialize for OrderBy {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeSeq;
let mut seq = serializer.serialize_seq(Some(2))?;
match self.direction {
OrderDirection::Asc => seq.serialize_element(&json!(["asc", self.field.to_json()]))?,
OrderDirection::Desc => {
seq.serialize_element(&json!(["desc", self.field.to_json()]))?
}
}
seq.end()
}
}
impl<'de> Deserialize<'de> for OrderBy {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let _value = Value::deserialize(deserializer)?;
Ok(OrderBy::asc(FieldRef::field_id(1)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mbql_query_default() {
let query = MbqlQuery::default();
assert!(query.source_table.is_none());
assert!(query.aggregations.is_empty());
assert!(query.breakout.is_empty());
assert!(query.filter.is_none());
assert!(query.order_by.is_empty());
assert!(query.limit.is_none());
assert!(query.offset.is_none());
}
#[test]
fn test_order_by_creation() {
let field = FieldRef::field_id(10);
let asc_order = OrderBy::asc(field.clone());
assert_eq!(asc_order.direction, OrderDirection::Asc);
let desc_order = OrderBy::desc(field);
assert_eq!(desc_order.direction, OrderDirection::Desc);
}
}