Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node, Mongoose, problems saving multiple-depths of nested schema

I'm having trouble working out how to insert multiple-depths of nested schema in MongoDB, via Mongoose and node.js.

The example below is a bit contrived but should hopefully explain my problem. As for why each schema is defined as a full model but not used in the example, that's just because in my real-world problem, they are actual, usable models and I wanted this example to be realistic in case it's relevant.

So here are the example schema definitions in reverse order, ie. smallest Russian-doll first:

// Define pen model
var PenSchema = new Schema({
  color: String // black, blue or red
});
var Pen = mongoose.model('Pen', PenSchema);

// Define ruler model
var RulerSchema = new Schema({
  units: String // inches or millimetres
});
var Ruler = mongoose.model('Ruler', RulerSchema);

// --------

// Define drawing tools model
var DrawingToolsSchema = new Schema({
  label: String,
  pens: [Pen]
});
var DrawingTools = mongoose.model('DrawingTools', DrawingToolsSchema);

// Define measuring tools model
var MeasuringToolsSchema = new Schema({
  label: String,
  ruler: [Ruler]
});
var MeasuringTools = mongoose.model('MeasuringTools', MeasuringToolsSchema);

// --------

// Define stationery box model
//  It has a label and two compartments - tools for drawing and measuring
var StationeryBoxSchema = new Schema({
  label: String,
  drawingTools: [DrawingToolsSchema],
  measuringTools: [MeasuringToolsSchema]
});
var StationeryBox = mongoose.model('StationeryBox', StationeryBoxSchema);

Hopefully you can tell from this that there is a main model, StationeryBox, which has a label and contains two compartments for DrawingTools and MeasuringTools which are nested schema. They in turn have their own labels and contain nested schemas for Pens and Rulers. The problem I'm having is inserted the 2nd level nesting, ie. pens/rulers. So based on the mongoose docs, creating the top level model, and pushing in the first nested objects works fine, then trouble strikes. For example:

// To create my stationery box - this works
var stationery = new StationeryBox({ label: 'My Stationery Box' });

// To add the nested compartments - this works
stationery.drawingTools.push({ label: 'My Pens' });
stationery.measuringTools.push({ label: 'My Rulers' });

// But this is wrong as 'stationery.drawingTools.pens' is undefined
stationery.drawingTools.pens.push({ color: 'red' });
stationery.drawingTools.pens.push({ color: 'black' });

And if I go back one step and try to insert the pens at the same time as the drawing tools:

// Also wrong - presumably the second level of nesting is the problem
stationery.drawingTools.push({
  label: 'My Pens',
  pens: [ // These object represent second levels of nested schema
    { color: 'red' },
    { color: 'black' }
  ]
});

I know this isn't a super-realistic example, but it's a simplified example of a real-world system I'm building and this was a easiest way to illustrate that.

The actual saving happens after this of course and I've left that out, but do I need to add these next levels in the save callback perhaps?

If anyone can tell me where I'm going wrong on this or point me in the right direction I'll buy you a nice cake (imaginary cake only I'm afraid, unless you live near me).

like image 748
tdous Avatar asked Nov 23 '11 13:11

tdous


1 Answers

You are extremely close, the problem is actually in your Schema definitions. It all comes down to the difference between a Schema object and a Model object. When specifying mongoose Schema with embedded documents, you can only point to other Schema.

var DrawingToolsSchema = new Schema({
  label: String,
  pens: [Pen] // uh-oh, broken! Pen is a Model.
});

However, you do have this correct for your first level of embedded documents defined in StationeryBoxSchema.

var StationeryBoxSchema = new Schema({
  label: String,
  drawingTools: [DrawingToolsSchema], // yes! DrawingToolsSchema is a Schema
  measuringTools: [MeasuringToolsSchema] // this one too.
});

This difference accounts for all of your unexpected behaviour later on.

like image 149
Daniel Mendel Avatar answered Oct 27 '22 01:10

Daniel Mendel