'this' is empty during Mongoose middleware - javascript

long time Java programmer here trying to figure out Node.js
I am trying to cascade a delete to child objects using the pre middleware, but no child delete is happening, while the parent deletes without a hitch. Leaving my database full of sad orphans. When I started logging to the console I see that the reference to 'this' is empty. For most cases this seems to be a problem of using ==> to create the function, but I am not doing so:
GameSession(parent):
var mongoose = require('mongoose');
var TimeLineEvent = require('../models/timelineevent');
//Define a schema
var Schema = mongoose.Schema;
var GameSessionSchema = new Schema({
name: {type: String, required: true},
gameMasterId : {type: Schema.Types.ObjectId, ref: 'GameMaster', required: true},
});
GameSessionSchema.pre('findOneAndDelete', function(next) {
console.log('GameSessionSchema.pre findOneAndDelete this ='+this);
console.log('GameSessionSchema.pre findOneAndDelete id ='+this._id);
TimeLineEvent.deleteMany({gameSessionId: this._id}).exec();
next();
});
//Export function to create "SomeModel" model class
module.exports = mongoose.model('GameSessionModel', GameSessionSchema );
TimeLineEvent(child):
//Require Mongoose
var mongoose = require('mongoose');
//Define a schema
var Schema = mongoose.Schema;
var TimeLineEventSchema = new Schema({
name: {type: String, required: true},
gameSessionId: {type: Schema.Types.ObjectId, ref: 'GameSession', required: true},
time: {type: Number, required: true},
nextAction: {type: Number}
});
module.exports = mongoose.model('TimeLineEventModel', TimeLineEventSchema );
This is what the console shows when the pre method is called:
GameSessionSchema.pre findOneAndDelete this =[object Object]
GameSessionSchema.pre findOneAndDelete id =undefined
Can you see what I am missing? Thanks!

From the documentation: "In query middleware functions, this refers to the query."
If you use console.log(this) you will be able to view the full Query object. You might find this.model references what you need.

Related

What is a virtual for an URL in Mongoose?

I am currently following a tutorial on implementing a database (Mongoose) in a simple website created using Express-framework. I do not have any problem understanding the concept of models, but I fail to make sense of the lines following the comment "Virtual for book's URL" in the code attached. How do these lines operate, and what role does having a virtual property have in this context?
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var BookSchema = new Schema(
{
title: {type: String, required: true},
author: {type: Schema.Types.ObjectId, ref: 'Author', required: true},
summary: {type: String, required: true},
isbn: {type: String, required: true},
genre: [{type: Schema.Types.ObjectId, ref: 'Genre'}]
}
);
// Virtual for book's URL
BookSchema
.virtual('url')
.get(function () {
return '/catalog/book/' + this._id;
});
//Export model
module.exports = mongoose.model('Book', BookSchema);
Virtuals are typically used for computed properties on documents.
BookSchema
// 'url' is the name of the virtual
.virtual('url')
.get(function () {
// you are computing dynamic url
return '/catalog/book/' + this._id;
});
Important thing about virtuals is it does not store anything on database. Imagine you stored "name" and "lastname" but you want to display fullname without having "fullname" column. You would be using repeated data on database if you add fullname property to your schema, this will slow down and will also cost you. Instead you write virtual:
YourSchema.get(function(){
return this.firstname + ' ' + this.lastname
})
You can also go one step further and return fullname property as it is in a column in database. mongoose Schema takes options object
let options={
toJSON:{
virtuals: true
}
}
set the virtual
let YourSchemaVirtual=YourSchema.virtual('fullname')
now when querying the database, get the collection
YourModel.findOne({firstname:"name"}).exec(function(err,user){
res.send(user)
})
In return value you will see fullname field computed as this.firstname + ' ' + this.lastname even though you have no fullname stored in database.

Populating an Object with Information from a Child Object with Different Model (JS/Mongoose)

Here is what I have. I created a project model that references a user model for an array of members.
var ProjectSchema = new mongoose.Schema(
{title: {
type: String,
required: true
},
members: [
{user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users'
}
}],
});
User Schema (I have code that creates a model from both of these schemas)
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
}
});
In a separate file, I want to export the JSON of a found project and include the information of the users in the members array, but I am not sure how to do that. This is the code I have right now.
const project = await Project.findById(req.params.proj_id).populate(
'members'
);
res.json(project);
It has no trouble finding the project but the only information I can get on the members is their id. I tried using for loops to gather the information from members separately using the id that I can get from the project, but the code gets messy and I am hoping to find a simpler way to do it.
You can use mongoose populate query to get all members associated with a project. It should populate array of objects users associated to a project. You should be doing something like this:
const project = await Project.findById(req.params.proj_id)
await project.populate('members').execPopulate()
console.log(project.members)
Mongoose docs for the reference: Populate
You can give the model to your mongoose populate.
const project = await Project.findById(req.params.proj_id)
.populate({
'members',
model: UserModel
})
.exec()
res.json(project);
keep in mind you've to create UserModel just like
const UserModel = mongoose.model('User', userSchema);

Nested elements in mongoose modelling

I have to create a database in this format:
I have tried to do it like this :
var mongoose = require("mongoose")
, Schema = mongoose.Schema;
var categorySchema = new Schema({
_id: String,
subcategory: {
type: Schema.Types.ObjectId,
ref: "Subcategory"
},
id: String,
name: String,
page_description: String,
page_title: String,
parent_category: String,
c_showInMenu: Boolean
});
module.exports = mongoose.model("Category", categorySchema);
And I did the same for every subcategory, but I named them "Subcategory" and "SubsubCategory". I'm pretty much a newbie, I've read the documentation on mongoose and I've also followed an online course. I feel like I know something, but I don't understand it properly or that my logic behind it is wrong.
Also, I've managed to find this about recursive elements in Schemas, but I don't fully understand it and I don't know how to implement it to my database: Recursive elements in Schema : Mongoose modelling
I checked your code and collection image.
Here's what you are doing wrong.
You don't need to declare _id field, it's auto-generated.
Keep the column names same as they are in the given collection image.
This should be your schema declaration. Note that, this schema is for the whole collection and not just for categories array.
var mongoose = require("mongoose"),
Schema = mongoose.Schema;
var mainSchema = new Schema({
categories: [ {
id: String,
image: String,
name: String,
page_description: String,
page_title: String,
parent_category_id: String,
c_showInMenu: Boolean
}]
});
This is just an example of the schema, please add necessary changes to it.

Mongoose create test data and populate without a database connection

I am writing a unit test that uses a mongoose model which has nested object. I want to populate the main model and the referenced model without calling 'populate' and fetching anything from the database. Here is an example in coffeescript
CarSchema = new mongoose.Schema
name:
type: String
required: true
engine:
type: ObjectId
ref: 'Engine'
required: true
Car = mongoose.model('Car', CarSchema)
EngineSchema = new mongoose.Schema
name:
type:String
required: true
Engine = mongoose.model('Engine', EngineSchema)
engine1 = new Engine({name: 'test'})
car1 = new Car({engine: engine1, name: 'car'})
assert.equal (car1.engine.name, 'test') #this fails
What happens is that car1.engine is set to an id and not to the engine object.
Is there a way to get this working?
calling setValue will retain the hydrated document:
car1.setValue('engine', engine1)

MissingSchema Error: Schema hasn't been registered for model "User"

I keep getting this error whenever I launch my application:
--MissingSchema Error: Schema hasn't been registered for model "User" --
I'm working a tutorial from the "Mongoose for Application Development" book by Simon Holmes.
I'm at Chapter 5 "Interacting with Data - Creation"
Here's my code:
app.js:
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, project = require('./routes/project')
, http = require('http')
, path = require('path');
db.js:
//Creating the Application Schemas:
//====================================
//User Schema:
//===============
var userSchema = new mongoose.Schema({
name: String,
email: {type: String, unique:true},
createdOn: { type: date, default: date.now },
modifiedOn: Date,
LastLogin: Date
});
//Build the User Model:
//===========================
mongoose.model( 'User', userSchema );
User.js:
var mongoose = require("mongoose");
var User = mongoose.model( 'User' );
ERROR THAT RESULTS:
throw new mongoose.Error.MissingSchemaError(name);
^
MissingSchemaError: Schema hasn't been registered for model "User".
Use mongoose.model(name, schema) at Mongoose.Model (C:\Users\New\Desktop\mongoose_pm_app\
mongoosepm\node_modules\mongoose\lib\index.js.311.13)
at Object. <anonymous> (C:\Users\New\Desktop\mongoose_pm_app\mongoosepm\routes\user.js:2:21)
atModule._compile (module.js:456:26)
atObject.Module._extensions..js (module.js:474:10)
atModule.load (module.js:356:32)
at Function.Module._load (module.js:364:17)
at require (module.js:380:17)
at Object <anonymous> (C:\Users\New\Desktop\mongoose_pm_app\mongoosepm\app.js:8:12)
at Module._compile (module.js:456:26)
25 June 19:52:55 - [nodemon] app crashed waiting for file changes before starting...
I'm young to mongoose and mongodb.
I've been through the books errata pages to
check if I mistyped anything but its all the same as here.
I also tried downloading the sample code from PACKT, the sample code
looks the same.
Any and all assistance would be Greatly appreciated.
Thanks.
You need to require your db.js file someplace, as otherwise the code in it will never run, and the schema will never be registered.
Also, I would recommend that you define your user schema inside of User.js and add the model to exports, like so:
User.js
//User Schema:
//===============
var userSchema = new mongoose.Schema({
name: String,
email: {type: String, unique:true},
createdOn: { type: date, default: date.now },
modifiedOn: Date,
LastLogin: Date
});
//Build the User Model:
//===========================
var User = mongoose.model( 'User', userSchema );
//Exports
//=======
exports = User;
This allows you to do just require the file elsewhere to get a reference to the User model (instead of having to retrieve it through mongoose.model). For example:
var User = require('User');
// ...
var fred = new User({ ... }); // create a new user
fred.save(function (err, user) { ... });
I've not read the book that you referred to, so I'm not sure if there might be a good reason to define all your schemas in one file (which is the direction it looked like you were going in with db.js). From what I've seen, it's more common to have one schema/model per file.
That error comes from one line:
var User = mongoose.model( 'User' );
You need to provide a Schema object to mongoose.model() method. The first param, 'User', just tells mongoose in what will the collection be named. The second one defines the user schema.
So either do something like Sergey has answered, or add these few changes.
To your db.js, add export line at the bottom:
module.exports = userSchema;
Then require it in User.js:
var mongoose = require("mongoose");
var userSchema = require("./db.js");
var User = mongoose.model( 'User', userSchema );
module.exports = User;
Alternatively you can just alter the last line in your Schema definition, by exporting the user model when you build it:
//Build the User Model:
//===========================
module.exports = mongoose.model( 'User', userSchema );
Then you don't need User.js at all.
Although, then your file naming doesn't follow convention, if you care about it.
Advantages of separating Schema and Model are when you require multiple complex schemas, when you have a lot of schema plugins so you want a simpler model file where you require all that. If you only have a simple schema like above, I recommend that you use a single-file version.

Categories