鉴于这个简单的示例,项目另存为:
{
"test": {
"a1": "a1"
}
}
当我用
collection.updateOne
和以下对象调用 $set
时:
await collection.updateOne(doc, {$set: {"test": {"a2": "a2"}}}, {upsert: true});
它将保存的 test
对象替换为:
{
"test": {
"a2": "a2"
}
}
a1
密钥将不再存在,因为它也不存在于发送的对象中。
我正在尝试使用对象中传递的值更新保存的项目**而不**删除不存在的值,我的意思是:
{"test": {"a2": "a2"}}
将使该项目更新为:
{
"test": {
"a1": "a1",
"a2": "a2"
}
}
我编写了一个函数来递归解析对象/数组并为此用例构建查询。
在我看来,它看起来一团糟,我不确定它是否正确:
// The function attempts to parse arrays and objects recursively and recreate the query
function buildUpdateQuery(object, prefix = '')
{
let updateQuery = {};
for (let [key, value] of Object.entries(object))
{
let newPrefix = prefix ? `${ prefix }.${ key }` : key;
if (Array.isArray(value))
{
for (let i = 0; i < value.length; i++)
{
let arrayPrefix = `${newPrefix}.${i}`;
if (typeof value[i] === 'object' && value[i] !== null)
updateQuery = {...updateQuery, ...buildUpdateQuery(value[i], arrayPrefix)};
else
updateQuery[arrayPrefix] = value[i];
}
}
else if (typeof value === 'object' && value !== null)
updateQuery = {...updateQuery, ...buildUpdateQuery(value, newPrefix)};
else
updateQuery[newPrefix] = value;
}
return updateQuery;
}
export const set = async (req, res /*key, object*/) => // Using mongodb and express
{
try
{
const [collection, key] = req.body.key.split("|");
if (collection === undefined || key === undefined)
return res ? res.send('fail') : 'fail'
const doc = buildUpdateQuery(req.body.object);
let r = await db.collection(collection).updateOne({ [key]: { $exists: true } }, { $set: doc }, { upsert: true });
r = r.modifiedCount ? 'ok' : 'fail';
return res ? res.send(r) : r
}
catch (error)
{
console.log(`\n[set]\n${ JSON.stringify(req.body, null, 2) }\n\n`, error);
return res ? res.send('fail') : 'fail'
}
}
使用以下方式调用
set
函数:
{"key": "vars|test", "object": {"test": {"a2": "a2"}}}
在
const doc = buildUpdateQuery(req.body.object);
doc
是 {test.a2: 'a2'}
并且确实更新了
{"test": {"a1": "a1"}}
至 {"test": {"a1": "a1"},{"a2":"a2"}}
我担心它有时会弄乱集合,也许解析错误。
我知道我可以首先使用以下方法获取现有对象:
const document = await db.collection(collection).findOne({ [key]: { $exists: true } });
然后用新值修改它,但这不是我想要的。
MongoDB中是否有任何内置方法可以实现这样的事情?
如果不是,buildUpdateQuery
是否正确,或者对于此用例还有其他更好的方法吗?
$set
阶段作为聚合管道阶段传递,您将获得所需的结果。例如,给定数据库中已存在您要更新的文档:
{
test: {
a1: "a1"
}
}
您可以使用 {upsert: true}
发出更新,将属性添加到现有对象,或者如果没有找到匹配文档,则创建一个新对象。
await db.collection.updateOne({
test: {
a1: "a1"
}
},
[
{
$set: {
test: {
a2: "a2"
}
}
}
],
{
upsert: true
});
upsert: true
因此变为活动状态。