mongo add to nested array if entry does not contain two fields that match

穿精又带淫゛_ 提交于 2019-12-24 03:10:10

问题


I have a mongo document that contains an array called history:

{
    "_id" : ObjectId("575fe85bfe98c1fba0a6e535"),
    "email" : "email@address",
    "__v" : 0,
    "history" : [ 
        {
            "name" : "Test123",
            "organisation" : "Rat",
            "field" : 4,
            "another": 3
        }
    ]
}

I want to add fields to each history object or update fields IF the name AND organisation match, however if they don't, I want to add a new object to the array with the queried name and organisation and add/update the other fields to the object when necessary.

So:

This query, finds one that matches:

db.users.find({ 
    email:"email@address",
    $and: [
        { "history.name": "Test123", "history.organisation": "Rat"}
    ]
})

However, I'm struggling to get the update/upsert to work IF that combination of history.name and history.organisation dont exist in the array.

What I think I need to do is a :

"If this history name does not equal 'Test123' AND the history organisation does not equal 'Rat' then add an object to the array with those fields and any other field provided in the update query."

I tried this:

db.users.update({ 
    email:"email@address",
    $and: [
        { "history.name": "Test123", "history.organisation": "Rat"}
    ]
}, {
    history: { name: "Test123"},
    history: { organisation: "Rat"}
}, {upsert:true})

But that gave me E11000 duplicate key error index: db.users.$email_1 dup key: { : null }

Any help greatly appreciated.

Thanks community!


回答1:


Not possible with a single atomic update I'm afraid, you would have to do a couple of update operations that satisfy both conditions.

Break down the update logic into two distinct update operations, the first one would require using the positional $ operator to identify the element in the history array you want and the $set to update the existing fields. This operation follows the logic update fields IF the name AND organisation match

Now, you'd want to use the findAndModify() method for this operation since it can return the updated document. By default, the returned document does not include the modifications made on the update.

So, armed with this arsenal, you can then probe your second logic in the next operation i.e. update IF that combination of "history.name" and "history.organisation" don't exist in the array. With this second update operation, you'd need to then use the $push operator to add the elements.

The following example demonstrates the above concept. It initially assumes you have the query part and the document to be updated as separate objects.

Take for instance when we have documents that match the existing history array, it will just do a single update operation, but if the documents do not match, then the findAndModify() method will return null, use this logic in your second update operation to push the document to the array:

var doc = {
        "name": "Test123",
        "organisation": "Rat"
    }, // document to update. Note: the doc here matches the existing array
    query = { "email": "email@address" }; // query document

query["history.name"] = doc.name; // create the update query
query["history.organisation"] = doc.organisation;
var update = db.users.findAndModify({
    "query": query,
    "update": { 
        "$set": { 
            "history.$.name": doc.name,
            "history.$.organisation": doc.organisation
        }
    }
}); // return the document modified, if there's no matched document update = null

if (!update) {
    db.users.update(
        { "email": query.email },
        { "$push": { "history": doc } }
    );
}

After this operation for documents that match, querying the collection will yield the same

db.users.find({ "email": "email@address" });

Output:

{
    "_id" : ObjectId("575fe85bfe98c1fba0a6e535"),
    "email" : "email@address",
    "__v" : 0,
    "history" : [ 
        {
            "name" : "Test123",
            "organisation" : "Rat",
            "field" : 4,
            "another" : 3
        }
    ]
}

Now consider documents that won't match:

var doc = {
        "name": "foo",
        "organisation": "bar"
    }, // document to update. Note: the doc here does not matches the current array
    query = { "email": "email@address" }; // query document

query["history.name"] = doc.name; // create the update query
query["history.organisation"] = doc.organisation;
var update = db.users.findAndModify({
    "query": query,
    "update": { 
        "$set": { 
            "history.$.name": doc.name,
            "history.$.organisation": doc.organisation
        }
    }
}); // return the document modified, if there's no matched document update = null

if (!update) {
    db.users.update(
        { "email": query.email },
        { "$push": { "history": doc } }
    );
}

Querying this collection for this document

db.users.find({ "email": "email@address" });

would yield

Output:

{
    "_id" : ObjectId("575fe85bfe98c1fba0a6e535"),
    "email" : "email@address",
    "__v" : 0,
    "history" : [ 
        {
            "name" : "Test123",
            "organisation" : "Rat",
            "field" : 4,
            "another" : 3
        }, 
        {
            "name" : "foo",
            "organisation" : "bar"
        }
    ]
}


来源:https://stackoverflow.com/questions/37815233/mongo-add-to-nested-array-if-entry-does-not-contain-two-fields-that-match

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!