3

I'm trying to add a new or to update an existing SubDocument based on it's existence.

Given is the following Schema:

var providerSchema = new Schema({
  provider: String,
  id: String,
  accessToken: Object
})

var userSchema = new Schema({
  email: { type: String },
  provider: [providerSchema],
  created_at: Date,
  updated_at: Date
});

The following Document is stored:

    {
    "_id": {
        "$oid": "5629f57b13bc066c17b88a0d"
    },
    "created_at": {
        "$date": "2015-10-23T08:53:15.684Z"
    },
    "updated_at": {
        "$date": "2015-10-23T08:53:15.684Z"
    },
    "email": "123123123@gmail.com",
    "provider": [
        {
            "provider": "facebook",
            "id": "123123",
            "accessToken": {
                "expires_in": 123123,
                "token_type": "bearer",
                "access_token": "123123123"
            },
            "_id": {
                "$oid": "5629f57b13bc066c17b88a0e"
            }
        },
        {
            "accessToken": {
                "access_token": "123123",
                "token_type": "bearer",
                "uid": "123123"
            },
            "id": "123123",
            "provider": "dropbox",
            "_id": {
                "$oid": "5629f58813bc066c17b88a12"
            }
        }
    ],
    "__v": 0
}

I'm trying to update or to add the new SubDocument with the following code:

User.findOneAndUpdate({email: email},
    {
      "$push" : {
        "provider": {
          "provider": "dropbox",
          "id": "123441212",
          "accessToken": "sdasddad"
        }
      }
    },  function (error, doc) { 
         .... 
}});

Unfortunately the existing SubDocuments aren't updated but new SubDocuments are created.

Could you please help me?

Thanks in advance!

P.s. Is it possible to have named SubDocuments? So for example to have a SubDocument called "facebook" which is accessible directly via name?

--- Solution ---

User.findOne(
    {"email": email})
    .populate("provider")
    .exec(function (err, data) {
        if(data === null && typeof data === "object"){
          // Create User
          user.save(function (error) {
            if(error) return callback(new Error(error), null);
            return callback(null, user);
          })
        } else {
          //
          if(err) return callback(new Error(err), null);

          var prov = _.findWhere(data.provider, { "provider": provider });

          if(typeof variable_here === 'undefined'){
              data.provider.push(update);
          } else {
            _.extend(prov, update);
          }

          data.save(function (error) {
            if(error) {
              return callback(error, null);
            } else {
              return callback(null, data);
            }
          });
        }
      }
    );

Thanks, Hossein Gerami! Still a lot of fallbacks ...

(I struggled at first with "_.findWhere"; it's underscore; just install via "npm install underscore --save" and put the following line in your file "var _ = require('underscore');")

Community
  • 1
  • 1
mbecker
  • 1,663
  • 3
  • 19
  • 24

3 Answers3

1

Consider the following document in the students collection whose grades element value is an array of embedded documents:

    {
      _id: 4,
      grades: [
         { grade: 80, mean: 75, std: 8 },
         { grade: 85, mean: 90, std: 5 },
         { grade: 90, mean: 85, std: 3 }
      ]
    }

Use the positional $ operator to update the value of the std field in the embedded document with the grade of 85:

    db.students.update(
       { _id: 4, "grades.grade": 85 },
       { $set: { "grades.$.std" : 6 } }
    )

Below code is the complete syntax for update in the following if you define upsert as true it will insert new record other wise it will be updated. Default Value : false

            db.collection.update(
               <query>,
               <update>,
               {
                 upsert: <boolean>,
                 multi: <boolean>,
                 writeConcern: <document>
               }
            )
Alok Deshwal
  • 1,128
  • 9
  • 20
0

You can try this. I think this may work.

 User.update({email: email,provider.provider:'dropbox'},{$set:{"provider.$": {"provider": {
              "provider": "dropbox",
              "id": "123441212",
              "accessToken": "sdasddad"
            }}}},function(error, doc){
    ...
    }});
Ketha Kavya
  • 558
  • 6
  • 18
  • Hi Ketha, thank you very much for your answer! Your solution works only if provider.provider = dropbox and the SubDocument exists. It doesn't work if SubDocument provider.provider = "facebook" doesn't exist but should be created. – mbecker Oct 23 '15 at 15:47
0

--- Solution ---

User.findOne(
    {"email": email})
    .populate("provider")
    .exec(function (err, data) {
        if(data === null && typeof data === "object"){
          // Create User
          user.save(function (error) {
            if(error) return callback(new Error(error), null);
            return callback(null, user);
          })
        } else {
          //
          if(err) return callback(new Error(err), null);

          var prov = _.findWhere(data.provider, { "provider": provider });

          if(typeof variable_here === 'undefined'){
              data.provider.push(update);
          } else {
            _.extend(prov, update);
          }

          data.save(function (error) {
            if(error) {
              return callback(error, null);
            } else {
              return callback(null, data);
            }
          });
        }
      }
    );

Thanks, Hossein Gerami! Still a lot of fallbacks ...

(I struggled at first with "_.findWhere"; it's underscore; just install via "npm install underscore --save" and put the following line in your file "var _ = require('underscore');")

mbecker
  • 1,663
  • 3
  • 19
  • 24