mongoose different ways to reference subdocuments? - mongodb

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'
},
});

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.

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' })

Difference is pretty simple. The former one where you are just defining schema for child won't create a separate collection for the children in the database instead you will embed the whole child document in the parent.
And in the later one you are defining a model for the child schema by calling mongoose.model which creates a separate collection of children in the database and you can then reference child documents in the parent document without embedding the whole child document in the parent by just adding the child _id.

Related

How to remove _id from collection in mongodb using mongoose

Tried to remove _id from collection but not working. I do not know how to resolve this issue. Need help anyone.
model.js:
const schemmaodel = new mongoose.Schema({
"name": { type: string}
}, { _id: false, versionKey: false });
data.controller.js:
var newModel = require(path.resolve('./models/model.js'))(collectionName);
newModel.create({}, function(err, doc) {
});
_id is like a primary key for the collection. If _id is not specified in the Schema then Mongoose will by default _id of type ObjectId.
As per the mongoose documentation you can disable _id on subdocuments only. So in your case, schemmaodel is considered as parent schema and since you have set { _id: false } option on parent schema while saving it will throw an error related to missing _id

How to define mongoose schema for nested documents

I need to define the mongoose schema for for nested documents which is given below.
Documents:
"Options":[{"Value":["28","30","32","34","36","38","40","42","44","46"],"_id":{"$oid":"5de8427af55716115dd43c8f"},"Name":"Size"},{"Value":["White"],"_id":{"$oid":"5de8427af55716115dd43c8e"},"Name":"Colour"}]
I was declaring like below but its not working.
const Product = new Schema(
{
Options: [{ value: { _id: ObjectId, Name: String } }]
},
{
timestamps: {
createdAt: "createdAt",
updatedAt: "updatedAt"
},
collection: "products"
}
);
Here I need the schema where if i will directly add/update the same document then it will be added.
You need to modify your schema like this :
{
Options: [ new Schema ({ value: [...], _id: Schema.Types.ObjectId, Name: String })]
}
This is the way to create an array of subdocuments with Mongoose. If you don't use the "new Schema" key words, you are actually creating a field with type "Mixed", which needs a different way to handle updates.
You can also omit the _id, it should be added automatically.
You can find more information on subdocument on this page :
https://mongoosejs.com/docs/subdocs.html
...and on mixed type fields : https://mongoosejs.com/docs/schematypes.html#mixed
...which will explain shortly the problem.
{
Options: [ new Schema ({ _id: mongoose.Types.ObjectId(),value: [String], Name: String } })]
}

MongoDB Using Mongoose Filter Based on SubDocuments

I have a one to many relationship between Dish and Review. One dish can have many reviews. Here is the Mongoose Schema for Dish and Review:
const mongoose = require('mongoose')
const Review = require('./reviewSchema')
// defining the structore of your document
let dishSchema = mongoose.Schema({
name : String,
price :Number,
imageURL :String,
reviews : [Review.schema]
})
// convert the schema into a model class which you can use in your code
const Dish = mongoose.model('Dish',dishSchema)
module.exports = Dish
const mongoose = require('mongoose')
let reviewSchema = mongoose.Schema({
title : String,
description :String
})
const Review = mongoose.model('Review',reviewSchema)
module.exports = Review
The problem I am having is that I want to fetch all the dishes if they have at least one review. Here is the code I wrote which returns an empty array.
Dish.find({
"reviews.length" : { $gt : 0 }
},function(error,dishes){
console.log(dishes)
})
What am I missing?
You can't explicitly refer to length property of an array. To check if array is not empty you can check if it's of $type "array" and if its $size is $not 0.
Dish.find({ reviews: { $type: "array", $not: { $size: 0 } } },
function(error,dishes){
console.log(dishes)
})

How Mongoose generate an ObjectId on non-collection field? [duplicate]

If you have subdocument arrays, Mongoose automatically creates ids for each one. Example:
{
_id: "mainId"
subDocArray: [
{
_id: "unwantedId",
field: "value"
},
{
_id: "unwantedId",
field: "value"
}
]
}
Is there a way to tell Mongoose to not create ids for objects within an array?
It's simple, you can define this in the subschema :
var mongoose = require("mongoose");
var subSchema = mongoose.Schema({
// your subschema content
}, { _id : false });
var schema = mongoose.Schema({
// schema content
subSchemaCollection : [subSchema]
});
var model = mongoose.model('tablename', schema);
You can create sub-documents without schema and avoid _id. Just add _id: false to your subdocument declaration.
var schema = new mongoose.Schema({
field1: {
type: String
},
subdocArray: [{
_id: false,
field: { type: String }
}]
});
This will prevent the creation of an _id field in your subdoc.
Tested in Mongoose v5.9.10
Additionally, if you use an object literal syntax for specifying a sub-schema, you may also just add _id: false to supress it.
{
sub: {
property1: String,
property2: String,
_id: false
}
}
I'm using mongoose 4.6.3 and all I had to do was add _id: false in the schema, no need to make a subschema.
{
_id: ObjectId
subDocArray: [
{
_id: false,
field: "String"
}
]
}
You can use either of the one
var subSchema = mongoose.Schema({
//subschema fields
},{ _id : false });
or
var subSchema = mongoose.Schema({
//subschema content
_id : false
});
Check your mongoose version before using the second option
If you want to use a predefined schema (with _id) as subdocument (without _id), you can do as follow in theory :
const sourceSchema = mongoose.Schema({
key : value
})
const subSourceSchema = sourceSchema.clone().set('_id',false);
But that didn't work for me. So I added that :
delete subSourceSchema.paths._id;
Now I can include subSourceSchema in my parent document without _id.
I'm not sure this is the clean way to do it, but it work.
NestJS example for anyone looking for a solution with decorators
#Schema({_id: false})
export class MySubDocument {
#Prop()
id: string;
}
Below is some additional information from the Mongoose Schema Type definitions for id and _id:
/**
* Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field
* cast to a string, or in the case of ObjectIds, its hexString.
*/
id?: boolean;
/**
* Mongoose assigns each of your schemas an _id field by default if one is not passed into the Schema
* constructor. The type assigned is an ObjectId to coincide with MongoDB's default behavior. If you
* don't want an _id added to your schema at all, you may disable it using this option.
*/
_id?: boolean;

Mongoose inserts extra _id in array of objects corresponding to related entity [duplicate]

If you have subdocument arrays, Mongoose automatically creates ids for each one. Example:
{
_id: "mainId"
subDocArray: [
{
_id: "unwantedId",
field: "value"
},
{
_id: "unwantedId",
field: "value"
}
]
}
Is there a way to tell Mongoose to not create ids for objects within an array?
It's simple, you can define this in the subschema :
var mongoose = require("mongoose");
var subSchema = mongoose.Schema({
// your subschema content
}, { _id : false });
var schema = mongoose.Schema({
// schema content
subSchemaCollection : [subSchema]
});
var model = mongoose.model('tablename', schema);
You can create sub-documents without schema and avoid _id. Just add _id: false to your subdocument declaration.
var schema = new mongoose.Schema({
field1: {
type: String
},
subdocArray: [{
_id: false,
field: { type: String }
}]
});
This will prevent the creation of an _id field in your subdoc.
Tested in Mongoose v5.9.10
Additionally, if you use an object literal syntax for specifying a sub-schema, you may also just add _id: false to supress it.
{
sub: {
property1: String,
property2: String,
_id: false
}
}
I'm using mongoose 4.6.3 and all I had to do was add _id: false in the schema, no need to make a subschema.
{
_id: ObjectId
subDocArray: [
{
_id: false,
field: "String"
}
]
}
You can use either of the one
var subSchema = mongoose.Schema({
//subschema fields
},{ _id : false });
or
var subSchema = mongoose.Schema({
//subschema content
_id : false
});
Check your mongoose version before using the second option
If you want to use a predefined schema (with _id) as subdocument (without _id), you can do as follow in theory :
const sourceSchema = mongoose.Schema({
key : value
})
const subSourceSchema = sourceSchema.clone().set('_id',false);
But that didn't work for me. So I added that :
delete subSourceSchema.paths._id;
Now I can include subSourceSchema in my parent document without _id.
I'm not sure this is the clean way to do it, but it work.
NestJS example for anyone looking for a solution with decorators
#Schema({_id: false})
export class MySubDocument {
#Prop()
id: string;
}
Below is some additional information from the Mongoose Schema Type definitions for id and _id:
/**
* Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field
* cast to a string, or in the case of ObjectIds, its hexString.
*/
id?: boolean;
/**
* Mongoose assigns each of your schemas an _id field by default if one is not passed into the Schema
* constructor. The type assigned is an ObjectId to coincide with MongoDB's default behavior. If you
* don't want an _id added to your schema at all, you may disable it using this option.
*/
_id?: boolean;