Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mongoose different ways to reference subdocuments?

This syntax is straight from the mongoose documentation on subtypes. However, I've also seen this alternate reference to subdocs. What is the difference?

https://mongoosejs.com/docs/subdocs.html

var childSchema = new Schema({ name: 'string' });

var parentSchema = new Schema({
  // Array of subdocuments
  children: [childSchema],
  // Single nested subdocuments. Caveat: single nested subdocs only work
  // in mongoose >= 4.2.0
  child: childSchema
});

An alternate type of reference to subdocs

var childSchema = new Schema({ name: 'string' });

mongoose.model('children', childSchema);

var parentSchema = new Schema({
  children: {
    type: Schema.Types.ObjectId,
    ref: 'children'
  },
});
like image 635
Bill Avatar asked Mar 11 '19 06:03

Bill


Video Answer


2 Answers

The above two syntax are totally different, In one the actual sub-document(children ) is stored in the parent document, but in the other one, a new document is stored in the children collection and only its reference is stored in the parent document.

Case 1:

var childSchema = new Schema({ name: 'string' });

var parentSchema = new Schema({
  // Array of subdocuments
  children: [childSchema],
  // Single nested subdocuments. Caveat: single nested subdocs only work
  // in mongoose >= 4.2.0
  child: childSchema
});

In this given syntax, parent document will have the child document stored in the parent document as well.

A sample document in the parent sollection will look like this:

{ 
  _id : "parent_random_generated_id"
  children :[{ _id : "childid1" , name : "Captain America"},
             { _id : "childid2" , name : "Iron Man"},...],
  child : {_id : "childid3" , name : "Thor Odinson"},
  ...
}

Case 2:

var childSchema = new Schema({ name: 'string' });

mongoose.model('children', childSchema);

var parentSchema = new Schema({
  children: {
    type: Schema.Types.ObjectId,
    ref: 'children'
  },
});

In this syntax, child documents will be stored separately, and they reference id (_id) will be stored in the parent document.

Sample documents in this case will look something like this:

// Children documents
{ _id : "childid1" , name : "Captain America"}
{ _id : "childid2" , name : "Iron Man"}
{ _id : "childid3" , name : "Thor Odinson"}

//parent document
{ 
  _id : "parent_random_generated_id"
  children :["childid1","childid2",...],
  child : "childid3",
  ...
}

In the second case you can use Mongodb $lookup operator to populate the sub documents, whenever needed, using mongodb aggregation pipeline, or use .populate('children') or .populate('child') to pupulate the specific child document.

I hope this clarifies your doubt.

like image 73
Ravi Shankar Bharti Avatar answered Sep 28 '22 00:09

Ravi Shankar Bharti


I have completed Ravi Shankar Bharti Case 2 with some data writing example. Let's use a book-author scenario:

const authorSchema = new Schema({ name: 'string' });
const authorModel = mongoose.model('authors', authorSchema);
    
const bookSchema = new Schema({
  title: String,
  author: {
    type: Schema.Types.ObjectId,
    ref: 'author'
  },
});

const bookModel = mongoose.model('books', bookSchema)

const authorData = { name: "John Doe" }

// Check if author does exists. Insert if not or find if yes
const options = {
  upsert: true,
  new: true,
  setDefaultsOnInsert: true
};
const anAuthor = await authorModel.findOneAndUpdate(authorData, authorData, options)

// Insert a new book with reference to `anAuthor`
const aBook = new bookModel({ title: "MyBook" })
aBook.set({ author: anAuthor })
await aBook.save()

In this syntax, child documents will be stored separately, and they reference id (_id) will be stored in the parent document.

Sample documents in this case will look something like this:

// authors documents
{ _id : "authorId1" , name : "John Doe"}
{ _id : "authorId2" , name : "Iron Man"}
{ _id : "authorId3" , name : "Thor Odinson"}

//books document
{ 
  _id : "parent_random_generated_id"
  title: "MyBook",
  author : "authorId1",
  ...
}

And for reading, you can use populate:

let result = await bookModel.find()
result = await authorModel.populate(result, { path: 'author' })

like image 43
gazdagergo Avatar answered Sep 28 '22 02:09

gazdagergo