I'm fairly new to using Node.js and MongoDB with Mongoose. Currently, I'm facing an rather annoying issue, for which I'm certain there's a better solution than the ones I've come up with so far!
The problem is, that I can find no "good" way for updating embedded documents. This is my scenario:
This SOULD be pretty straight-forward, but I've been all out of luck - with simplicity, anyways.
Here's what I have so far. This is 2 solutions, that both work, but both require to iterate through ListItems at some point, which I'd really like to NOT do.
ShoppingListItem = new Schema({
listID : ObjectId
,ingredient : [Ingredient]
,count : Number
});
ShoppingList = new Schema({
name : String
,userID : ObjectId
/* Embedded documents solution */
,items : [ShoppingListItem]
/* No embedded documents solution */
/*,items : [{
ingredient : [Ingredient]
,count : Number
}]*/
});
ShoppingList.methods.addItem = function addItem(newItem, cb){
var __self = this;
var _id = __self._id;
/**
* Embedded documents solution (Need to both maintain the embedded document source PLUS manually update the actual embedded document)
**/
ShoppingListItem.findOne({'listID' : _id, 'ingredient.name' : newItem.ingredient.name}, function(err, item){
if(!item){
var item = new ShoppingListItem({
listID : _id
,ingredient : [newItem.ingredient]
,count : newItem.count
});
item.save(function(err, saved){
__self.items.push(saved);
__self.save(cb);
});
}else{
item.count += newItem.count;
item.save(function(err, saved){
//Iterate through all ListItems to update the embedded document to reflect the changed "source" document
for(var i = 0; i < __self.items.length; ++i){
if(__self.items[i]._id.equals(saved._id)){
__self.items[i] = saved;
}
}
__self.markModified('items');
__self.save(cb);
});
}
});
/**
* No embedded documents solution (Only need to maintain the internal objects)
**/
/*
var existing = __self.items.filter(function(item){ return item.ingredient.name.equals == newItem.ingredient.name;});
if(existing.length > 0){
existing[0].count += newItem.count;
}else{
__self.items.push(newItem)
}
__self.markModified('items');
__self.save(cb);
*/
}
Of the 2 solutions above, I'd say the latter is the more "elegant" solution, since it has the least code. But I'd really like to be able to use the first method, because I'd say, that MongoDB's querying is faster at finding documents, than filtering an array. My last thought was to try to utilize the DBRef features of Mongoose, but I just cannot figure out how to apply that in my solution.
So, long question short: Is there a way to "link" documents, rather than to "embed a copy" (which is how I see the embedded documents) ?
Thanks a lot in advance!
Edit: Both of the above solutions do work - but I just can't see them being very efficient in a larger scale (say 1.000.000+ items in a list)