Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving nested objects in mongoose

Coming from a PHP/ MySQL background, I'm struggling with best practices in terms of structuring and saving my data.

I'm trying to create a small application where I can add recipes with multiple ingredients. I have a bunch of pre-populated ingredients as seed data, the schema for which looks like:

var IngredientSchema = new Schema({
    name: {
        type: String,
        trim: true,
        required: true,
        index: {
            unique: true
        }
    },
    created: {
        type: Date,
        default: Date.now
    }
});

var Ingredient = mongoose.model('Ingredient', IngredientSchema);

A recipe currently looks like this:

var RecipeSchema = new Schema({
    name: {
      type: String,
      trim: true
    },
    ingredients: [
      {
        type: Schema.Types.ObjectId,
        ref: 'RecipeIngredient'
      }
    ],
    created: {
      type: Date,
      default: Date.now
    }
});

var Recipe = mongoose.model('Recipe', RecipeSchema);

Finally, I have a RecipeIngredientSchema. Now, this is where my MySQL background might be creeping in; the reason I've done it this way is because I want the one to many relationship between Recipes and Ingredients, but I also want to be able to specify a unit:

var RecipeIngredientSchema = new Schema({
    recipe: {
      type: Schema.Types.ObjectId,
      ref: 'Recipe'
    },
    ingredient: {
      type: Schema.Types.ObjectId,
      ref: 'Ingredient'
    },
    unit: {
      type: Schema.Types.ObjectId,
      ref: 'Unit'
    },
    created: {
      type: Date,
      default: Date.now
    }
});

var RecipeIngredient = mongoose.model('RecipeIngredient', RecipeIngredientSchema);

My question comes in two parts:

  • Am I going about this in a sensible way in terms data modeling or am I way off?
  • What would the process of saving a recipe with multiple ingredients actually look like?

I'm currently thinking the following:

exports.create = function(req, res) {

  var recipe = new Recipe(req.body);

  recipe.save(function(err, recipe) {
    if (err) {
      return res.jsonp(err);
    } else {

      // Loop ingredients
      if (req.body.ingredients) {
        for(var prop in req.body.ingredients) {
          var recipeIngredient = new RecipeIngredient({
            recipeId: recipe._id,
            ingredientId: mongoose.Types.ObjectId(req.body.ingredients[prop])
          });

          recipeIngredient.save(function(err, recipeIngredient) {
            if (err) {
              return res.jsonp(err);
            } else {
              recipe.recipeIngredients.push(recipeIngredient._id);
              recipe.save(function(err, recipe) {
                return res.jsonp(recipe);
              });
            }
          });
        };
      }
    }
  });
}

I feel this is convoluted and generally wrong, so would appreciate some guidance!

like image 313
James Avatar asked Jan 20 '14 20:01

James


People also ask

What does save () do in Mongoose?

Mongoose | save() Function The save() function is used to save the document to the database. Using this function, new documents can be added to the database.

How do I change the value of nested objects in MongoDB?

To update more than one array item matching the criteria in this way, then you need to issue the update several times until the document is no longer modified. So the index position is 0 for the first element, 1 for the second element and so on.


2 Answers

The beauty of NoSQL databases (or document stores in general) is that you don't have to split your data into multiple tables/collections. You can store all the related data into a single entity so that your read operations happen in one shot.

There is no "right" approach to do this, but if you are going to use NoSQL I would consider saving the entire recipe (recipe and ingredients and directions) as a single document rather than splitting the data into 3 or 4 tables a-la relational model.

For example, I would save a single recipe as follows:

recipe = {
    name: 'name of recipe'
    time: '45 minutes'
    ingredients : [
       {qty: 3, unit: 'item', name: 'tomatoes'},
       {qty: 0.5, unit: 'tbs', name: 'salt'},
       {qty: 1, name: 'item', name: 'avocado'} 
    ]
}

Now, this is not pixie dust. There will be times where having the data split into multiple tables/collections and having a relational query language (like SQL) will be beneficial to query data. For example, if you wanted to query all recipes that use 'tomatoes' having a relational database with a join table for the recipe/ingredients relations would make this much simpler than the NoSQL approach.

That's a decision that you'll need to make at one point: Are you better of with a NoSQL or with a RBMS for your application?

like image 171
Hector Correa Avatar answered Nov 15 '22 23:11

Hector Correa


If you want to save nested objects in DB you have two options: as embedded schema or with ref. If you save them with ref them the nested schema is saved in a separate collection.

Start taking a look at this example mongo-many-to-many and this one section: saving-refs

like image 31
maxi-code Avatar answered Nov 15 '22 21:11

maxi-code