1

I wasn't lucky finding any help on this anywhere else.

Basically I want to update only the few documents that do not have a given attribute already.

And the value for the update comes from a field that is already on the document.

This is what I tried but it didn't like the "from a field already on the document" part. Saying Cn doesn't exist.

db.getCollection('test').update(
    // query 
    { "id2" : { $exists: false } },

    // update 
    { id2: Cn },

    // options 
    {
        "multi" : true,  // update all documents
        "upsert" : false  // don't insert new documents
    }
);

Here is my test data

/* 1 */
{
    "_id" : ObjectId("5912132c4a58677726d37168"),
    "Cn" : "CA",
    "id2" : "CAAB",
    "Prov" : "AB"
}

/* 2 */
{
    "_id" : ObjectId("591213404a58677726d37172"),
    "Cn" : "CA",
    "id2" : "CANZ",
    "Prov" : "NZ"
}

/* 3 */
{
    "_id" : ObjectId("591213534a58677726d37180"),
    "Cn" : "CA",
    "id2" : "CAMB",
    "Prov" : "MB"
}

/* 4 */
{
    "_id" : ObjectId("591213674a58677726d3718c"),
    "Cn" : "US"
}

/* 5 */
{
    "_id" : ObjectId("591213894a58677726d371a3"),
    "Cn" : "MX"
}

All this should do is create a id2 on US & MX and give those new id2 attributes the corresponding values 'US' & 'MX'.

This would not be a big deal but I have able 144 countries & 10,000+ documents to add id2 to.

user5903880
  • 102
  • 8

3 Answers3

0

You can try this :

db.your_collection.aggregate( [
    { $match : { id2 : {  $exists: false }}},
    { $addFields: {
        id2 : "$Cn"
        }
    },
    { $out : "your_collection" }
]);

This will remove all documents in collection which have id2 field.

You can do this in mongoose in a longer way :

db.getCollection('test').find({ "id2" : { $exists: false } }, function(err, docs){
    if(err){
        //handle errror
    }
    else if(!docs){
        // no docs found
    }
    else{
        for(var i=0; i< docs.length; i++){
            db.getCollection('test')
               .findByIdAndUpdate(
                   docs[i]._id
                  , { $set : { id2 : docs[i].Cn}}
                  , { new : true}
                  , function(err, doc){
                if(err){
                    //handle error
                }
                else{
                    //doc was updated
                }
            });
        }
    }
});
Mihir Bhende
  • 8,677
  • 1
  • 30
  • 37
  • I played with it and a few modification even. All it could give me is "Featched 0 record(s)". And nothign was changed in the data. But it didn't error – user5903880 May 09 '17 at 19:37
  • hey have updated my answer, i accidently wrote _id, where it should have been id2 in match. Can you try now? I am trying at my system too. – Mihir Bhende May 09 '17 at 19:40
  • I already did. Featched 0 records. Also you have a in "$Cn" – user5903880 May 09 '17 at 19:42
  • Ohh man, there is a space after $cn, remove it and it will work. I just checked. have updated my answer, – Mihir Bhende May 09 '17 at 19:42
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/143814/discussion-between-mihir-bhende-and-user5903880). – Mihir Bhende May 09 '17 at 19:43
  • Your collection is called "collection"? – user5903880 May 09 '17 at 19:43
  • yes, Sorry if it was confusing. Basically you need to replace collection with your collection name – Mihir Bhende May 09 '17 at 19:45
  • Did you notice your query also deletes all the documents that already have the attribute `id2`. Seeing ti now I'd expect it would given the $ out & $exists. This is getting interesting. – user5903880 May 09 '17 at 19:51
  • Apparently : If the collection specified by the $out operation already exists, then upon completion of the aggregation, the $out stage atomically replaces the existing collection with the new results collection. The $out operation does not change any indexes that existed on the previous collection. If the aggregation fails, the $out operation makes no changes to the pre-existing collection. – Mihir Bhende May 09 '17 at 20:03
  • I have updated the answer and have added mongoose part using for loop – Mihir Bhende May 09 '17 at 20:11
  • I wouldn't know that you could even do that. How is it you know this? – user5903880 May 09 '17 at 20:55
  • You need a ':' semicolon after $set. After that it threw some error I've not gone into yet. But it looks to be getting closeer – user5903880 May 09 '17 at 21:10
  • error is: >'projection' field must be of BSON type object.", – user5903880 May 09 '17 at 21:17
  • I guess try id2 in quotes – Mihir Bhende May 09 '17 at 21:18
  • it has to do with the outer .find(). just for having included the callback in the find generates the error – user5903880 May 09 '17 at 21:20
  • Duh. Your callback sets in a parameter to .find() where a projections object belongs – user5903880 May 09 '17 at 21:25
  • I just posted the answer. Gave you a great mention. Thanks. I could not have done it if you hadn't kept trying also. – user5903880 May 09 '17 at 21:43
0

You can't update multiple items with an operator that only contains expressions, and self referencing doesn't work either.

Check out this question for work around solutions to get what you need done: Update MongoDB field using value of another field

Community
  • 1
  • 1
Z. Bagley
  • 8,942
  • 1
  • 40
  • 52
0

I would not have gotten to this without Mihir Bhende pushing. Thanks for that.

var c = db.getCollection('test').find(
    { "id2" : { $exists: false }}
);
c.forEach(function(myDoc) { 
    print("doc", myDoc.Cn);

    db.getCollection('test').update( {_id: myDoc._id}, {$set: { "id2": myDoc.Cn }}, function (err) {
        if (err) { print("err"); }
    });
});
user5903880
  • 102
  • 8