如何更新集合项而不删除查询中发送的不存在值?

问题描述 投票:0回答:1

鉴于这个简单的示例,项目另存为:

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

是否正确,或者对于此用例还有其他更好的方法吗?

javascript mongodb mongodb-query
1个回答
0
投票
如果将

$set

 阶段作为聚合管道阶段传递,您将获得所需的结果。

例如,给定数据库中已存在您要更新的文档:

{ test: { a1: "a1" } }
您可以使用 

{upsert: true}

 发出更新,将属性添加到现有对象,或者如果没有找到匹配文档,则创建一个新对象。

await db.collection.updateOne({ test: { a1: "a1" } }, [ { $set: { test: { a2: "a2" } } } ], { upsert: true });

    请参阅
  1. 此处,了解将属性添加到现有匹配对象的更新示例。
  2. 请参阅
  3. HERE,了解基于无匹配文档创建新对象的更新示例,并且 upsert: true
     因此变为活动状态。
© www.soinside.com 2019 - 2024. All rights reserved.