数据库中的数据
fieldA : 'aaaaa',
fieldB : {
en : 'aaaaaaaaaa',
de : 'bbbbbbbbbb'
}
新数据
val = {
fieldA : 'aaaaa11',
fieldB : {
en : 'aaaaa1111'
}
}
我试过这个代码
Model.findOneAndUpdate({fieldA : val.fieldA},{ $set : val})
当我运行此命令时,“fieldB.de”丢失。我想知道如何更新,结果如下所示
fieldA : 'aaaaa11',
fieldB : {
en : 'aaaaa1111',
de : 'bbbbbbbbbb'
}
您必须更改传递给
findOneAndUpdate()
的对象,这样您就不会将 fieldB
作为对象传递,而只传递要更新的字段。
可以用这样的东西来完成:
let val = {
fieldA: 'aaaaa11',
fieldB: {
en: 'aaaaa1111',
},
};
if (val.fieldB) {
const newFieldB = Object.entries(val.fieldB).reduce(
(a, [key, value]) => ({ ...a, ['fieldB.' + key]: value }),
{}
);
delete val.fieldB;
val = { ...val, ...newFieldB };
}
Model.findOneAndUpdate({fieldA : val.fieldA},{ $set : val})
这样,传递给
val
的findOneAndUpdate()
的值就是:
{
fieldA: 'aaaaa11',
'fieldB.en': 'aaaaa1111',
}
代替:
{
fieldA: 'aaaaa11',
fieldB: {
en: 'aaaaa1111',
},
}
...以及您未在
fieldB
中指定但在数据库中的 val
属性不会被覆盖。
试试这个:
Model.findOneAndUpdate({fieldA : val.fieldA},{
"$set": {
"fieldA": val.fieldA,
"fieldB.en": val.fieldB.en
}
})
在您的解决方案中,您将文档与其他文档完全更新。要仅更新特定字段,您必须在“$set”对象中指定此字段
我通过首先找到文档并手动编辑对象来解决此问题。
/* Recursive function to update manually an object */
const update = (obj, newValues) => {
Object.entries(newValues).forEach( ([key, value]) => {
if(typeof value === 'object'){
update(obj[key], value);
} else {
obj[key] = value;
}
});
}
/* Find document, update and save */
const infoToUpdate = {};
const doc = await Model.findOne({ field: valueToFind });
if (doc) {
object.update(doc, infoToUpdate);
doc.save();
}
这里有一个解决方案,可以在不覆盖 Mongoose 中其他字段的情况下更新子文档中的数据。此方法区分
PUT
和 PATCH
请求,方法是将请求主体扁平化到名为 flatten 的新字段(对于 PATCH
请求)并保持原样对于 PUT
请求。
首先,为
middleware
请求设置 flatten
到 PATCH
请求正文:
// flatten-mongoose.middleware.ts <---------------- File
import { Request, Response, NextFunction } from 'express';
import flattenizer './flatten-mongoose.helper'
// Middleware to flatten the request body for PATCH requests
export default function flatten(
req: Request,
res: Response,
next: NextFunction
) {
req.body = flatten(req.body);
next();
}
// flatten-mongoose.helper.ts <---------------- File
export function flattenizer(obj: any) {
const result: any = {};
Object.keys(obj).forEach((key: string) => {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === "object" && !Array.isArray(obj[key])) {
const flatObject = flattenizer(obj[key]);
for (const key2 in flatObject) {
if (flatObject.hasOwnProperty(key2)) {
result[`${key}.${key2}`] = flatObject[key2];
}
}
} else {
result[key] = obj[key];
}
}
});
return result;
}
// app.ts or someroute.ts <----------------------------- File
import express from 'express';
import flatten from './flatten-mongoose.middleware'
...
app = express(); // you can assign it to only some routes
app.patch("*", flatten)
...
说明:
app.patch("*", flatten) in the mail app file or routes
将中间件扁平化应用于所有 PATCH 请求。用途:
PATCH
请求:当发出PATCH
请求时,请求正文会被展平,允许您更新子文档中的特定字段,而不会覆盖其他字段,但请记住使用req.body.flatten
。PUT
请求:对于PUT请求,请求正文保持不变,允许完全替换文档。
这种方法确保您可以更新子文档中的特定字段而不影响其他字段,为 Mongoose 中处理部分更新提供了灵活高效的方法。router.patch('/:id', (req: Request, res: Response, next: NextFunction) {
Transaction
.findByIdAndUpdate(
req.params.id,
{
...req.body.flatten // here you need to use flatten
},
{ new: true } // Return the updated document
)
.then((updatedTransaction) => {
if (updatedTransaction) {
res.status(202).json({
...res.locals,
message: `transaction updated with id ${updatedTransaction._id}`,
transaction: updatedTransaction,
};
} else {
res.status(404).json({error: "Error : transaction not found});
}
})
.catch(error => res.status(500).json(error));
});