1

Suppose I have a document collection

{ "id":1, "arr":[{"a":1, "b":2, "c":3}, {"a":6, "b":0, "c":8},....]}
{ "id":2, "arr":[{"a":7, "b":1, "c":4}, {"a":5, "b":2, "c":6},....]}

Now the user will provide me with an array of unknown size like this

let user_id: 2;
let user_arr = [{"a":7, "b":1, "c":9}, {"a":1, "b":6, "c":3},.....]

Now I want to push user provided arr documents in user's arr for user_id given by user such that (a,b) combination of both values will not be duplicated for him/her.

For e.g - for above case as (a:7, b:1) already exist in arr so It won't get inserted, but for (a:1, b:6) no record contains both of them that's why it {"a":1, "b":6, "c":3} gets inserted in the arr.

Please help me, anyone.

Sudhanshu Gaur
  • 7,486
  • 9
  • 47
  • 94
  • Hey, just to clarify: "such that (a,b) combination of both values will not be duplicated" - you mean duplicated inside an array or duplicated in your collection ? – mickl Nov 19 '18 at 17:03
  • @mickl duplicated inside that array only, Thanks. – Sudhanshu Gaur Nov 19 '18 at 17:04

1 Answers1

1

You can use $elemMatch combined with $not to check if the document you're trying to $push will not duplicate any other subdocument in that array. Try:

db.col.update({ id: user_id, arr: { $not: { $elemMatch: { a: 7, b: 1 } } } }, { $push: { arr: { a:7, b:1, c:9} } })

Additional condition for id will force this operation to modify one or zero documents.

To perform multiple updates you can use bulkWrite (MongoDB 3.2+):

db.col.bulkWrite(
    [
        { 
            updateOne : {
                    "filter" : { id: 2, arr: { $not: { $elemMatch: { a: 7, b: 1 } } } },
                    "update" : { $push: { arr: { a:7, b:1, c:9} } }
                }
        },
        { 
            updateOne : {
                    "filter" : { id: 2, arr: { $not: { $elemMatch: { a: 1, b: 6 } } } },
                    "update" : { $push: { arr: { a:1, b:6, c:3} } }
                }
        }
    ]
);

As a response you'll get:

{
    "acknowledged" : true,
    "deletedCount" : 0,
    "insertedCount" : 0,
    "matchedCount" : 1,
    "upsertedCount" : 0,
    "insertedIds" : {

    },
    "upsertedIds" : {

    }
}

which means that only one of two conditions matched the document with id: 2

mickl
  • 48,568
  • 9
  • 60
  • 89
  • How can second item conflict with document 2? as it is not present it will simply get created. Also is there no workaround to write this in one single query instead of writing update for user_arr size. Thanks for helping :) – Sudhanshu Gaur Nov 19 '18 at 17:13
  • Also from every document, for $elemmatch I would have to first omit `c` from them? – Sudhanshu Gaur Nov 19 '18 at 17:15
  • @SudhanshuGaur sorry, modified my answer, I think I misunderstood your initial question, the best way would be to use bulkWrite and you can build that command dynamically based on `user_arr` – mickl Nov 19 '18 at 17:18
  • Is there any kind of query like this `db.coll.find({ user_id: user_id, arr: { $nin: user_arr}}, {$push: {/*only documets which aren't matched}})` ?? – Sudhanshu Gaur Nov 19 '18 at 17:32
  • Like using aggregate query and pushing document which are not matched in an array and then pushing that array? – Sudhanshu Gaur Nov 19 '18 at 17:34
  • 1
    I don't think so and the reason for that is simply because the document can or cannot match the condition, there's nothing like document "partially" matching the condition, that's why you have to split that array into separate operations – mickl Nov 19 '18 at 17:34
  • But in aggregate query we can put values matched in our new array using `$group` and then we can push that array? any query like that? – Sudhanshu Gaur Nov 19 '18 at 17:35
  • 1
    Yes, but aggregate is used to retrieve the data (mostly) or to replace existing collection (entire) and that's not what you're trying to achieve here :) – mickl Nov 19 '18 at 17:40
  • Hi can you please help me here https://stackoverflow.com/questions/53439102/mongodb-bulkwrite-which-queries-failed-at-match-step – Sudhanshu Gaur Nov 22 '18 at 23:37