首页 > 解决方案 > Rust 更新 mongo 文档

问题描述

我正在尝试更新我的文档,如果我没有传递一个字段,它将被删除。

我的文件是

{
   "_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"
   }
}

我试图更新

"pet":{
      "name":"Hello",
      "bio":"Hello",
      "gender":"Male",
   },
   "status":"Pending",
   "created_at":{
      "$date":"2021-11-06T22:30:41.977Z"
   }

他正在删除 "can_live_with_other_cats":true 和 "can_live_with_other_dogs":true

如何在不删除我的字段的情况下更新?

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()))
    }

我要更新的结构

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>,
}

非常感谢

标签: mongodbrust

解决方案


$set使用根据示例提供的值覆盖键,传递对象将pet使用提供的不包含键的对象覆盖键。

"pet":{
  "name":"Hello",
  "bio":"Hello",
  "gender":"Male"
},
"status":"Pending",
"created_at":{
  "$date":"2021-11-06T22:30:41.977Z"
}

要在不覆盖现有密钥的情况下进行更新,您可以使用以下格式显示

例如:

"pet.name":"Hello",
"pet.bio":"Hello",
"pet.gender":"Male"
"pet.status":"Pending",
"pet.created_at.$date": "2021-11-06T22:30:41.977Z"

正如@Renato 在评论中所说,我们也可以使用 serterename属性

pub struct PetDataModel {
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "pet.name")]
    pub name: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "pet.bio")]
    pub bio: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "pet.gender")]
    pub gender: Option<GenderDataModel>,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "pet.can_live_with_other_cats")]
    pub can_live_with_other_cats: Option<bool>,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "pet.can_live_with_other_dogs")]
    pub can_live_with_other_dogs: Option<bool>,
}

或者

您可以使用此代码rust-flatten-json来展平 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)?

推荐阅读