折腾了一晚上,文档看完了,谷歌操作一顿,没搞定。只能求助大神啦。
我有一个数据表 Schema 大概如下:
// PostCountry 表 const PostCountrySchema = new Schema({ uid: { type: ObjectId, ref: 'User', index: true, required: true }, pid: { type: String, ref: 'Product', index: { unique: true }, // 设为唯一的 required: true }, country_list: [ { country_name: { type: String, required: true }, country_code: String, fourPx_code: { type: String, default: '' }, declared: { // 申报单价 type: Number, default: 0 }, fourPx_send_name: { type: String, default: '' } } ] }
数据示例如下:
// PostCountry 表 "country_list": [ { "country_name": "China", "declared": 3, "fourPx_send_name": "中国邮政" }, { "country_name": "Ecuador", "declared": 21, "fourPx_send_name": "中国邮政" }, { "country_name": "Argentina", "declared": 14, "fourPx_send_name": "中国邮政" } ], "pid": "5380578836639", "uid": "5f7205b75e08ebr1dab5abfa"
我现在需要更新 PostCountry 表中嵌套数组的,fourPx_send_name 数据,如果数据存在则更新,如果不存在则在这个表内增加新的数组。
我 post 填充的数据是:
{ "country_name": "Canada", "declared": 123, "fourPx_send_name": "中国邮 31231 政", "pid":"5380578836639" }
我用过的方法 1:
const Data = await PostCountrySchema.findOneAndUpdate({ uid: ctx.cookies.get('uid'), pid: psotData.pid, country_list: { $elemMatch: { country_name: psotData.country_name } } }, { $set: { 'country_list.$.fourPx_send_name': psotData.fourPx_send_name, 'country_list.$.declared': psotData.declared } }, { new: true, upsert: true, setDefaultsOnInsert: true }) .then((data) => { return data }) .catch(err => console.error('返回错误' + err))
这样操作会返回一个错误 MongoError: Updating the path 'country_list.$.fourPx_send_name' would create a conflict at 'country_list'
我用过的方法 2:
const Data = await PostCountrySchema.findOneAndUpdate({ uid: ctx.cookies.get('uid'), pid: psotData.pid, 'country_list.country_name': { $ne: psotData.country_name } }, { $addToSet: { country_list: { country_name: psotData.country_name, fourPx_send_name: psotData.fourPx_send_name, declared: psotData.declared } } }, { new: true, upsert: true, setDefaultsOnInsert: true }) .then((data) => { return data }) .catch(err => console.error('返回错误' + err))
这样操作,如果 country_name 字段名字没搜索到,是可以添加成功的,然而再次提交修改就会报错了:
MongoError: E11000 duplicate key error collection: shopify.post_country index: pid_1 dup key: { pid: "5380578836639" }
操作 3,将 上面的 $addToSet 改成$push 结果也是一样,没有数据可以填充,有数据就无法填充了。
折磨了半天了,求大神支个招吧。
![]() | 1 libook 2020-09-29 13:31:24 +08:00 https://docs.mongodb.com/manual/reference/operator/update/addToSet/#value-to-add-is-a-document $addToSet 必须得是字段和值完全一样才会认为不需要插入,也就是说顶多做插入,跟更新数据没关系。 你要是非要按照现有的 schema 来做,只能用程序先遍历 country_list,找到一样的 fourPx_send_name 就修改数据,然后整个 document save 。 MongoDB 操作的最小单位是文档(Document),你现在操作的是子文档,所以会很麻烦。 对于需要频繁对你现在操作的是子文档操作的场景,应该把你现在操作的是子文档抽出来做成一个新的 Collection,然后用 ref 和 populate 来与 PostCountry 建立关系。 |
![]() | 2 libook 2020-09-29 13:32:49 +08:00 ![]() 对于需要频繁操作子文档的场景,应该把子文档抽出来做成一个新的 Collection,然后用 ref 和 populate 来与 PostCountry 建立关系。 |
![]() | 5 libook 2020-09-30 11:01:02 +08:00 // const body = { // "country_name": "Canada", // "declared": 123, // "fourPx_send_name": "中国邮 31231 政", // "pid": "5380578836639" // } // const postCountry = await PostCountry.findOne({ "pid": body.pid }); // if (postCountry !== null) { // let isCountryExists = false; // for (const index in postCountry.country_list) { // if (postCountry.country_list[index].fourPx_send_name === body.fourPx_send_name) { // postCountry.country_list[index] = body; // isCountryExists = true; // break; // 不需要继续循环了 // } // } // if (!isCountryExists) { // // 如果没有找到相同的 country 信息,就插入 // postCountry.country_list.push(body); // // 如果 country_list 部分的 schema 用了 Mixed,就得需要调用 markModified,如果 schema 里声明的就是对象数组就不需要 // } // await postCountry.save(); // } else { // // pid 没找到相关 document // } 就是查出来,然后判断数组和修改,再 save 进去。 另外因为 findOne 和 save 是两步操作,如果是分布式高并发系统可能在这个过程中这个 document 就已经被其他实例修改过了,所以需要用两段提交或事务来保证整个过程的原子性,MongoDB 都支持。 |
![]() | 6 libook 2020-09-30 11:02:25 +08:00 ![]() 以为改成注释格式就不会乱了,没想到 V2 直接把行内多个空格删掉了,你自己贴到编辑器里,反注释再格式化一下看吧 |