Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions dozer-sql/expression/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ pub enum Error {
FailedToCalculateVincentyDistance(
#[from] dozer_types::geo::vincenty_distance::FailedToConvergeError,
),
#[error("Fail to deserialize: {0}")]
FailedToDeserialize(
#[from] dozer_types::errors::types::DeserializationError,
),

#[error("Invalid like escape: {0}")]
InvalidLikeEscape(#[from] like::InvalidEscapeError),
Expand Down
43 changes: 41 additions & 2 deletions dozer-sql/expression/src/json_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,28 @@ use crate::arg_utils::validate_num_arguments;
use crate::error::Error;
use crate::execution::Expression;

use dozer_types::json_types::JsonValue;
use dozer_types::types::Record;
use dozer_types::json_types::{field_to_json_value, json_value_to_serde_json, JsonValue, serde_json_to_json_value};
use dozer_types::types::{FieldType, Record};
use dozer_types::types::{Field, Schema};
use jsonpath::{JsonPathFinder, JsonPathInst};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use dozer_types::serde_json;
use dozer_types::serde_json::Value;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
pub enum JsonFunctionType {
JsonValue,
JsonQuery,
JsonObject,
}

impl Display for JsonFunctionType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
JsonFunctionType::JsonValue => f.write_str("JSON_VALUE".to_string().as_str()),
JsonFunctionType::JsonQuery => f.write_str("JSON_QUERY".to_string().as_str()),
JsonFunctionType::JsonObject => f.write_str("JSON_OBJECT".to_string().as_str()),
}
}
}
Expand All @@ -29,6 +33,7 @@ impl JsonFunctionType {
match name {
"json_value" => Some(JsonFunctionType::JsonValue),
"json_query" => Some(JsonFunctionType::JsonQuery),
"json_object" => Some(JsonFunctionType::JsonObject),
_ => None,
}
}
Expand All @@ -42,9 +47,43 @@ impl JsonFunctionType {
match self {
JsonFunctionType::JsonValue => self.evaluate_json_value(schema, args, record),
JsonFunctionType::JsonQuery => self.evaluate_json_query(schema, args, record),
JsonFunctionType::JsonObject => self.evaluate_json_object(schema, args, record),
}
}

pub(crate) fn evaluate_json_object(
&self,
schema: &Schema,
args: &mut [Expression],
record: &Record,
) -> Result<Field, Error> {
let mut json_output = serde_json::Map::new();
for arg in args {
if arg.get_type(schema)?.return_type == FieldType::String {
let arg_string = arg.to_string(schema);
let object_name: &str = arg_string.split(":").collect::<Vec<&str>>()[0].trim();
let mut object_value: &str = arg_string.split(":").collect::<Vec<&str>>()[1].trim();
if object_value.contains(".") {
object_value = object_value.split(".").collect::<Vec<&str>>()[1];
}
let column_num = schema.fields.iter().position(|x| x.name == object_value);
if let Some(idx) = column_num {
let val = Expression::Column {index: idx}.evaluate(record, schema)?;
json_output.insert(
object_name.to_string(),
json_value_to_serde_json(&field_to_json_value(val))
);
} else {
json_output.insert(
object_name.to_string(),
json_value_to_serde_json(&field_to_json_value(Field::String(object_value.to_string())))
);
}
}
}
Ok(Field::Json(serde_json_to_json_value(Value::Object(json_output))?))
}

pub(crate) fn evaluate_json_value(
&self,
schema: &Schema,
Expand Down
28 changes: 28 additions & 0 deletions dozer-sql/src/expression/tests/json_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,34 @@ fn test_json_value() {
);

assert_eq!(f, Field::Json(String::from("Bristol").into()));

let town = "Bristol";
let f = run_fct(
"SELECT JSON_OBJECT('id: user.id', 'town: user.town', 'town_2: Bristol') as info FROM users",
Schema::default()
.field(
FieldDefinition::new(
String::from("id"),
FieldType::Int,
false,
SourceDefinition::Dynamic,
),
false,
)
.field(
FieldDefinition::new(
String::from("town"),
FieldType::String,
false,
SourceDefinition::Dynamic,
),
false,
)
.clone(),
vec![Field::Int(1), Field::String(town.to_string())],
);

assert_eq!(f, Field::Json(json!({"id": 1, "town": "Bristol", "town_2": "Bristol"})));
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion dozer-types/src/json_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ pub fn field_to_json_value(field: Field) -> JsonValue {
}
}

fn json_value_to_serde_json(value: &JsonValue) -> Value {
pub fn json_value_to_serde_json(value: &JsonValue) -> Value {
// Note that while this cannot fail, the other way might, as our internal JSON
// representation does not support `inf`, `-inf` and NaN
ijson::from_value(value).expect("Json to Json conversion should never fail")
Expand Down
2 changes: 1 addition & 1 deletion dozer-types/src/types/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,7 @@ impl Display for Field {
Field::Boolean(true) => {
write!(f, "TRUE")
}
Field::String(s) => f.write_str(s),
Field::String(s) => write!(f, "{s}"),
Field::Text(t) => write!(f, "{t}"),
Field::Date(d) => write!(f, "{}", d.format(DATE_FORMAT)),
Field::Timestamp(t) => write!(f, "{}", t.to_rfc3339()),
Expand Down