Home > Software engineering >  Rust update mongo document
Rust update mongo document

Time:11-09

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)?
  • Related