rOrlig
rOrlig

Reputation: 2529

Mongoose update subdocument of subdocument

My schema definition as below. UserSchema has embedded Cards which in turn has many transactions..

var TransactionSchema = new Schema({
  merchantName: String,
  transactionTime: Date,
  latitude: Number,
  longitude: Number,
  amount: Number
});

var CardSchema = new Schema({
  cardIssuer: String,
  lastFour: String,
  expirationDate: String,
  transactions : [TransactionSchema]
});

/*
 * ...User Schema... 
 */
var UserSchema = new Schema({
  name: String,
  email: { type: String, lowercase: true },
  role: {
    type: String,
    default: 'user'
  },
  hashedPassword: String,
  provider: String,
  salt: String,
  imageURL: String,
  phoneNumber: String,
  card: [CardSchema]
});

I want to add a transaction to the card already in the userschema but I am not sure how to do this in mongoose / mongodb

I identify the user and card as follows..

The api call goes through the auth middleware first

function isAuthenticated() {
  return compose()
    // Validate jwt
    .use(function(req, res, next) {
      // allow access_token to be passed through query parameter as well
      if(req.query && req.query.hasOwnProperty('access_token')) {
        req.headers.authorization = 'Bearer ' + req.query.access_token;
      }
      validateJwt(req, res, next);
    })
    // Attach user to request
    .use(function(req, res, next) {
      User.findById(req.user._id, function (err, user) {
        if (err) return next(err);
        if (!user) return res.send(401);

        req.user = user;
        next();
      });
    });
}


// This is update based on Neil's answer below...
exports.create = function(req, res) {
  //var userItem = req.user;
  //console.log(userItem._id);
  //console.log(req.params.card);
  Transaction.create(req.body, function(err, transaction){
     console.log(transaction);
          //id = mongoose.Types.ObjectId;

          User.findOneAndUpdate({"card._id":id(req.params.card)},{ 
            // $set : {
            //   role: 'user1'
            // } ---- this update operation works!!
              "$push": {
                  "card.$.transactions": transaction
              } // -- this update operation causes error ...
          }, function(err,user) {
            // updated document here
            console.log('err' + err + " user " + user) ;
            return res.json(200, user);
        }
      )
    // }
   // })
})
};

Upvotes: 1

Views: 1486

Answers (1)

Neil Lunn
Neil Lunn

Reputation: 151112

Adding new elements to an inner array is not difficult, as all you really need to do is match the position of the outer array to update in your query and then apply the positional $ operator in the update portion.

var transaction; // and initialize as a new transaction

User.findOneAndUpdate(
    { "card._id": cardId },
    { 
        "$push": {
            "card.$.transactions": transaction.toObject()
        }
    },
    function(err,user) {
        // updated document here
    }
)

So that is straightforward for $push operations. But be careful that you only ever want to $push or $pull as trying to update at a position of in "inner" array is not possible since the positional operator will only contain the first match, or the position in the "outer" array.

Upvotes: 3

Related Questions