1

I have a document like this:

{
    wid: 1,
    items: [
        {nid: 1, sent: 900},
        {nid: 2, sent: 12},
        {nid: 3, sent: 0},
        {nid: 4, sent: 1}
    ]
}

Now consider I want to update this document with an object I compute from somewhere else:

# All for wid:1, a mapping of nid:sent
# These values should be added to values already in the DB
{
    2: 37,
    3: 58,
    4: 172
}

so after the update, I want to have something like:

{
    wid: 1,
    items: [
        {nid: 1, sent: 900},
        {nid: 2, sent: 49},
        {nid: 3, sent: 58},
        {nid: 4, sent: 173}
    ]
}

Is there anyway to do this with only one update query?

What I know works->

db.test.update_one({wid: 1, 'items.nid': 2}, {$inc: {'items.$.sent': 37}})
db.test.update_one({wid: 1, 'items.nid': 3}, {$inc: {'items.$.sent': 37}})
db.test.update_one({wid: 1, 'items.nid': 4}, {$inc: {'items.$.sent': 172}})

But that's O(n) update queries issued and this is really contradicting all reasons I chose to stick to MongoDB.

Thanks!

  • What do you think about going the bulkWrite() route? Like bulkWrite() + updateOne + ordered:false – Marcos Zolnowski Oct 1 '18 at 5:58
  • 1
    @MarcosZolnowski That would hit the DB once, but still O(n) update queries are issued. I'm not sure if that scales well. (we process about 20K "wids" each with about 100 to 1000 items inside, every minute, that way it would be about 2-20M update operations per minute which I'm not sure the DB's gonna keep up, doing it the otherway round, it's gonna issue 20K update queries, which is definitely feasible) – SpiXel Oct 1 '18 at 6:02
1

Use the arrayFilters property:

db.getCollection("test").updateOne(
    {
        "wid": 1
    },
    {
        $inc: {
            "items.$[aa].sent": 37,
            "items.$[bb].sent": 58,
            "items.$[cc].sent": 172
        }
    },
    {
        upsert: false,            
        arrayFilters: [
            { "aa.nid": 2 },
            { "bb.nid": 3 },
            { "cc.nid": 4 }
        ]
    }
);

For each named identifier you create (aa, bb, cc in the example), should be created a matching document in the arrayFilters array.

  • Cool but that's a 3.6+ version addition, I'll still wait for a solution for at most the 3.4 version of mongodb, if any exists. Otherwise I have to update a production sharded cluster of nearly 32+ nodes to just use this! talk about a pain. – SpiXel Oct 1 '18 at 5:45
  • Ouch! Well, if you ever plan to upgrade, I advise not to lag behind too much. – Marcos Zolnowski Oct 1 '18 at 5:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.