I'm trying to update my document, if I don't pass a field it is being deleted.
my document is
{
"_id":"27fc47a4-0730-446c-8acd-41aa6e227406",
"user_id":"a07c8c2f-e83a-47f7-80dc-a18407f997e1",
"pet":{
"name":"Hello",
"bio":"Hello",
"gender":"Male",
"can_live_with_other_cats":true,
"can_live_with_other_dogs":true
},
"status":"Pending",
"created_at":{
"$date":"2021-11-06T22:30:41.977Z"
}
}
I tried to update with
"pet":{
"name":"Hello",
"bio":"Hello",
"gender":"Male",
},
"status":"Pending",
"created_at":{
"$date":"2021-11-06T22:30:41.977Z"
}
he is deleting the "can_live_with_other_cats":true and "can_live_with_other_dogs":true
how can i update without deleting my field?
async fn update(&self, adoption: &dto::adoption::update::Adoption) -> Result<(), Error> {
let mongo_collection = MongoClient::get_collection("adoptions").await;
let mongo_model = datamodel::insert_adoption::PetDataModel::from(&adoption.pet);
let query = doc! {"_id": adoption.id.to_string()};
let doc = mongodb::bson::to_document(&mongo_model).unwrap();
println!("{}", doc);
let update = doc!{"$set": {"pet": doc} };
mongo_collection
.update_one(query, update, None)
.await
.map(|_| ())
.map_err(|e| domain::errors::Error::internal_server_error(e.to_string()))
}
My struct to update
pub struct PetDataModel {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bio: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub gender: Option<GenderDataModel>,
#[serde(skip_serializing_if = "Option::is_none")]
pub can_live_with_other_cats: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub can_live_with_other_dogs: Option<bool>,
}
Thanks a lot
CodePudding user response:
$set
with override the key with the value provide as per the example passing object will override the pet
key with the supplied object which does not contain the keys.
"pet":{
"name":"Hello",
"bio":"Hello",
"gender":"Male"
},
"status":"Pending",
"created_at":{
"$date":"2021-11-06T22:30:41.977Z"
}
To update without overriding existing keys you can use the following format as show
For eg:
"pet.name":"Hello",
"pet.bio":"Hello",
"pet.gender":"Male"
"pet.status":"Pending",
"pet.created_at.$date": "2021-11-06T22:30:41.977Z"
You can use this code from rust-flatten-json
to flatten the json
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate error_chain;
use serde_json::value::Value;
use serde_json::map::Map;
error_chain! {
foreign_links {
Json(::serde_json::Error);
}
}
pub fn flatten(nested_value: &Value, flat_value: &mut Value, parent_key: Option<String>, infer_type: bool, separator: Option<&str>) -> Result<()> {
// if object
if let Some(nested_dict) = nested_value.as_object() {
flatten_object(flat_value, &parent_key, nested_dict, infer_type, separator)?;
} else if let Some(v_array) = nested_value.as_array() {
let new_k = parent_key.unwrap_or_else(||String::from(""));
flatten_array(flat_value, &new_k, v_array, infer_type, separator)?;
} else {
error!("Expected object, found something else: {:?}", nested_value)
}
Ok(())
}
fn flatten_object(flat_value: &mut Value, parent_key: &Option<String>, nested_dict: &Map<String, Value>, infer_type: bool, separator: Option<&str>) -> Result<()> {
let sep = if let Some(sep) = separator {
sep
} else {
"."
};
for (k, v) in nested_dict.iter() {
let new_k = match parent_key {
Some(ref key) => format!("{}{}{}", key, sep, k),
None => k.clone()
};
// if nested value is object recurse with parent_key
if let Some(obj) = v.as_object() {
flatten_object(flat_value, &Some(new_k), obj, infer_type, separator)?;
// if array
} else if let Some(v_array) = v.as_array() {
// if array is not empty
if !v_array.is_empty() {
// traverse array
flatten_array(flat_value, &new_k, v_array, infer_type, separator)?;
// if array is empty insert empty array into flat_value
} else if let Some(value) = flat_value.as_object_mut() {
let empty: Vec<Value> = vec!();
value.insert(k.to_string(), json!(empty));
}
// if no object or array insert value into the flat_value we're building
} else if let Some(value) = flat_value.as_object_mut() {
infer_type_and_insert(v, new_k, value, infer_type)?;
}
}
Ok(())
}
fn infer_type_and_insert(v: &Value, new_k: String, value: &mut Map<String, Value>, infer_type: bool) -> Result<()> {
let new_val;
if infer_type {
if let Some(string) = v.as_str() {
new_val = match string.parse::<i64>() {
Ok(i) => serde_json::to_value(i)?,
Err(_) => match string.parse::<f64>() {
Ok(f) => serde_json::to_value(f)?,
Err(_) => match string.parse::<bool>() {
Ok(b) => serde_json::to_value(b)?,
Err(_) => serde_json::to_value(string)?
}
}
};
} else {
new_val = v.clone();
}
} else {
new_val = v.clone();
};
value.insert(new_k, new_val);
Ok(())
}
fn flatten_array(flat_value: &mut Value, new_k: &str, v_array: &[Value], infer_type: bool, separator: Option<&str>) -> Result<()> {
for (i, obj) in v_array.iter().enumerate() {
let array_key = format!("{}.{}", new_k, i);
// if element is object or array recurse
if obj.is_object() | obj.is_array() {
flatten(obj, flat_value, Some(array_key), infer_type, separator)?;
// else insert value in the flat_value we're building
} else if let Some(value) = flat_value.as_object_mut() {
infer_type_and_insert(obj, array_key, value, infer_type)?;
}
}
Ok(())
}
let value: Value = match serde_json::from_str(&input) {
Ok(value) => value,
Err(e) => {
error!("{}", &input);
panic!("{}", e);
}
};
let mut flat_value: Value = json!({});
flatten(&value, &mut flat_value, None, true, None)?;
serde_json::to_string(&flat_value)?