Is it possible to have nested schemas in mongoose and have a required validator on the children? Something like this:
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
}
});
const eventSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
host: {
type: userSchema,
required: true
}
});
I can't find anything in the documentation. Thanks.
Yes, your schema is correct.
The docs for mongoose nested schema (SubDocuments) can be found here
i suppose you'll update eventSchema with subdocuments of type user model.
you can use { runValidators: true} for update.
eventModel.update({ name: 'YOUR NAME' }, { $push: { host: user } }, { runValidators: true}, function(err) {
})
You can use the nested schema in mongoose.
It will also give you he Object Id on each sub schema values as well.
Doc: Here
Example: Here
required is a validator added to a schema or subschema in Mongoose (from docs)
so yes, you can set the required field to true ( it is false by default) for your subschema or subdocument in Mongoose.
The example schema you have created is correct.
Related
Here is my code:
const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost:27017/miniRDS",{
useNewUrlParser:true,
useCreateIndex:true,
useUnifiedTopology: true
},(err)=>{
if(!err){
console.log("Connected")
}else{
console.log("Couldn't connect!");
}
});
const tests = new mongoose.Schema({
subject:{
type:String,
required:true,
default:"ict"
},
date:{
type:String,
required:true,
default:"01-01-2021"
}
});
const testsModel = mongoose.model("classTests",tests);
const s = new testsModel({
subject:"english",
date:"12-01-2021"
});
s.save();
I am using mongoose version 5.11.11. And I am facing difficulty when I try to create a collection with camelCase name using mongoose model.
In the above codes, It should create a collection name "classTests", instead it creates "classtests". how can I achieve "classTests"? Thanks
you can use it as:
module.exports = mongoose.model("user_notification_preference", NotificationPreference, "userNotificationPreferences");
three parameters:(the name you want to use as camel, schema, the actual name)
Mongoose automatically looks for the plural, lowercased version of your model name,
please check this documentation, so you can't create a collection with camelCase in mongoose
Solution :
To Resolve this, We can use a Plural name in the collection field
const mongoose = require('mongoose');
const AssessmentAttemptSchema = new mongoose.Schema(
{
userId: {
type: mongoose.Types.ObjectId,
required: true,
},
verificationToken: {
type: String,
required: true,
unique: true,
},
},
{
timestamps: true,
collection: "assessmentAttempts",
}
);
module.exports = mongoose.model('AssessmentAttempt', AssessmentAttemptSchema);
So for this case, we are using assessmentAttempts to save the collection name as CamelCase.
End Result:
Mongo DB : Sample Collection
MongoDB Collection Name Restrictions Docs : Reference Link
Here is my Game Schema :
var mongoose = require('mongoose');
const GameSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
publishers: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Publisher'
}
]
});
var GameModel = mongoose.model('Game', GameSchema);
module.exports = GameModel;
Here is my Publisher Schema :
var mongoose = require('mongoose');
const PublisherSchema = new mongoose.Schema({
companyName: {
type: String,
required: true
},
firstParty: {
type: Boolean,
required: true
},
website: {
website: String,
}
});
var PublisherModel = mongoose.model('Publisher', PublisherSchema);
module.exports = PublisherModel;
I have a picture of what you can find in my collection "games" in mongoDB :
When I use this route :
router.get('/games', async function(req, res) {
const games = await Game
.find()
.populate('publishers')
.select('title publishers')
res.json(games);
})
I have empty arrays as result for the publishers. If I don't use an array in Schema, this is correcly populated and I got data of publisher into each game. So why mongoose doesn't populate when it is an array?
Check below by modifying the schema definition as below
I think the below could fix your issue, please give a try by redefining the publishers as below
Publishers: [
publisher: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Publisher'
}
]
I think the definition of schema is more about to define the structure of the object that we would like to process, rather about the number of objects that we want to process.
The Schema definition with what I know is defining the semantics of the model and to take advantage of the middle ware functionalities.
You can save multiple objects of which meet the same semantic definition but the definition itself cannot be an array
Thanks
Pavan
I have a mongoose model that looks something like this
var LogSchema = new Schema({
item: {
type: ObjectId,
ref: 'article',
index:true,
},
});
But 'item' could be referenced from multiple collections. Is it possible to do something like this?
var LogSchema = new Schema({
item: {
type: ObjectId,
ref: ['article','image'],
index:true,
},
});
The idea being that 'item' could be a document from the 'article' collection OR the 'image' collection.
Is this possible or do i need to manually populate?
Question is old, but maybe someone else still looks for similar issues :)
I found in Mongoose Github issues this:
mongoose 4.x supports using refPath instead of ref:
var schema = new Schema({
name:String,
others: [{ value: {type:mongoose.Types.ObjectId, refPath: 'others.kind' } }, kind: String }]
})
In #CadeEmbery case it would be:
var logSchema = new Schema({
item: {type: mongoose.Types.ObjectId, refPath: 'kind' } },
kind: String
})
But I did't try it yet...
First of all some basics
The ref option says mongoose which collection to get data for when you use populate().
The ref option is not mandatory, when you do not set it up, populate() require you to give dynamically a ref to him using the model option.
#example
populate({ path: 'conversation', model: Conversation }).
Here you say to mongoose that the collection behind the ObjectId is Conversation.
It is not possible to gives populate or Schema an array of refs.
Some others Stackoverflow people asked about it.
Soluce 1: Populate both (Manual)
Try to populate one, if you have no data, populate the second.
Soluce 2: Change your schema
Create two link, and set one of them.
var LogSchema = new Schema({
itemLink1: {
type: ObjectId,
ref: 'image',
index: true,
},
itemLink2: {
type: ObjectId,
ref: 'article',
index: true,
},
});
LogSchema.find({})
.populate('itemLink1')
.populate('itemLink2')
.exec()
Dynamic References via refPath
Mongoose can also populate from multiple collections based on the value of a property in the document. Let's say you're building a schema for storing comments. A user may comment on either a blog post or a product.
body: { type: String, required: true },
on: {
type: Schema.Types.ObjectId,
required: true,
// Instead of a hardcoded model name in `ref`, `refPath` means Mongoose
// will look at the `onModel` property to find the right model.
refPath: 'onModel'
},
onModel: {
type: String,
required: true,
enum: ['BlogPost', 'Product']
}
});
const Product = mongoose.model('Product', new Schema({ name: String }));
const BlogPost = mongoose.model('BlogPost', new Schema({ title: String }));
const Comment = mongoose.model('Comment', commentSchema);
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;
I'm using Mongoose version 3 with MongoDB version 2.2. I've noticed a __v field has started appearing in my MongoDB documents. Is it something to do with versioning? How is it used?
From here:
The versionKey is a property set on each document when first created
by Mongoose. This keys value contains the internal revision of the
document. The name of this document property is configurable. The
default is __v.
If this conflicts with your application you can configure as such:
new Schema({..}, { versionKey: '_somethingElse' })
Well, I can't see Tony's solution...so I have to handle it myself...
If you don't need version_key, you can just:
var UserSchema = new mongoose.Schema({
nickname: String,
reg_time: {type: Date, default: Date.now}
}, {
versionKey: false // You should be aware of the outcome after set to false
});
Setting the versionKey to false means the document is no longer versioned.
This is problematic if the document contains an array of subdocuments. One of the subdocuments could be deleted, reducing the size of the array. Later on, another operation could access the subdocument in the array at it's original position.
Since the array is now smaller, it may accidentally access the wrong subdocument in the array.
The versionKey solves this by associating the document with the a versionKey, used by mongoose internally to make sure it accesses the right collection version.
More information can be found at: http://aaronheckmann.blogspot.com/2012/06/mongoose-v3-part-1-versioning.html
For remove in NestJS need to add option to Schema() decorator
#Schema({ versionKey: false })
It is possible to disable the version key if you don't need it.
See this example:
var User = new mongoose.Schema({
FullName:{
type :String,
},
BirthDay:{
type :String,
},
Address:{
type :String,
},
Gender:{
type:String,
},
PhoneNumber:{
type:Number,
ref:'Account'
},
AccountID:{
type: Schema.Types.ObjectId,
ref: 'Account'
},
UserName:{
type:String,
ref:'Account'
}
},{collection:'User',
versionKey: false //here
});
It is the version key.It gets updated whenever a new update is made. I personally don't like to disable it .
Read this solution if you want to know more
[1]: Mongoose versioning: when is it safe to disable it?
the '__v' field in our 'document' serves 'optimisticConcurrency' concerns.
This term basically means in mongoose :
let, you grabed a document by 'findOne, findById' but not used save() method of mongoose yet. and what if at this interval, any other code grabed same document and used .save() method before the first document instance.
at this use case, if we want to (mongoose specific) throw a version error kinda thing, we use optimisticConcurrency: true option in schema.
and then mongoose will use '__v1' to compare these two document.
without optimisticConcurrency: true option. '__v' has no has no effect. and mongoose will not increase it by 1.
Note : in 'findOneAndUpdate' kinda operations, will not update '__v'. (only save() updates)
The __v field is called the version key. It describes the internal revision of a document. This __v field is used to track the revisions of a document. By default, its value is zero (__v:0).
If you don't want to use this version key you can use the versionKey: false as mongoose.Schema parameter.
You can follow this example...
const mongoose = require('mongoose');
const userSchema = mongoose.Schema(
{
name: {
type: String,
require: true
},
email: {
type: String,
unique: true
},
password: {
type: String,
}
},
{
timestamps: true,
versionKey: false, // Here You have to add.
}
)
module.exports = mongoose.model('tbl_user', userSchema)
We can use versionKey: false in Schema definition
'use strict';
const mongoose = require('mongoose');
export class Account extends mongoose.Schema {
constructor(manager) {
var trans = {
tran_date: Date,
particulars: String,
debit: Number,
credit: Number,
balance: Number
}
super({
account_number: Number,
account_name: String,
ifsc_code: String,
password: String,
currency: String,
balance: Number,
beneficiaries: Array,
transaction: [trans]
}, {
versionKey: false // set to false then it wont create in mongodb
});
this.pre('remove', function(next) {
manager
.getModel(BENEFICIARY_MODEL)
.remove({
_id: {
$in: this.beneficiaries
}
})
.exec();
next();
});
}
}