Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mongoose pre validate hook not firing

Tags:

mongoose

I am trying to create a model then create another model and save the reference to the original model using mongoose. I have been looking through mongoose's documentation on middleware and their hooks, but some of those don't seem to fire.

This answer tells me why my init hook would not fire HERE, pre and post init only fire when loading a pre existing model from the db. So I read that validate would run on the creation of a new model. In knowing that I switched from pre init to pre validate.

Here is the code for what I am trying to do:

GroupSchema.pre('validate', function (next, data) {
    console.log("inside pre validate");
    if (data.location) {
        location = new Location(data.location);
        data.location = location._id;
        location.save(function (err) {
            if (err) handleError(err);
        });
    }

    next();
})

I know I can't use this because the document is not populated with data yet. So that is why I have data, but this still does not seem to work, oh I got which parameters I am supposed to pass in from this answer.

Any help would be appreciated.

**************UPDATE**************

To add some clarity, using what was recommended in an answer and changing my pre validate function to this:

GroupSchema.pre('validate', function (next, data) {
    console.log("inside pre validate", this);
    if (this.location) {
        location = new Location(this.location);
        this.location = location._id;
        location.save(function (err) {
            if (err) handleError(err);
        });
    }

    next();
})

I get the following error

service err: CastError: Cast to ObjectId failed for value "[object Object]" at path "location"

Which makes sense because in my model it expects an ObjectId and not [object, Object] which is what I am passing. However, I thought I could save the location, get the ObjectId that was generated and store that in the group model before it threw an error. Hence originally using pre init until finding out that wouldn't work and now find out that pre validate will also not work. Is there any way to do what I am trying?

Here is what I am trying in sequence:

  1. create location
  2. get new ObjectId
  3. store new location ObjectId in the group instead of the Object itself

reason I was trying to add this to a pre hook is so that I could have this code in one spot and it automatically would handle this when newing up a group model.

like image 569
gmaniac Avatar asked Dec 18 '14 20:12

gmaniac


People also ask

How to use pre in mongoose?

pre('save', function(next) { // do stuff next(); }); In mongoose 5. x, instead of calling next() manually, you can use a function that returns a promise. In particular, you can use async/await .

What is pre hook in mongoose?

On my journey to explore MongoDB and Mongoose's powerful features over the years, I encountered something called pre and post hooks, few years back. They are simple yet powerful tools with which you can manipulate model instances, queries, perform validation before or after an intended database operation etc.

Does mongoose have built in validation?

Mongoose has several built-in validators. All SchemaTypes have the built-in required validator. The required validator uses the SchemaType's checkRequired() function to determine if the value satisfies the required validator. Numbers have min and max validators.

What is pre and post hooks?

Pre- and post-execution hooks are Processing scripts that run before and after actual data processing is performed. This can be used to automate tasks that should be performed whenever an algorithm is executed.


2 Answers

What you're trying to do isn't a good fit for validate or any other middleware, because you have a need to supply location data along with what's needed to create the group itself.

A better fit would be to create a static method on GroupSchema that explicitly performs this enhanced create function.

So assuming basic location and group schemas like:

var LocationSchema = new Schema({
    city: String,
    state: String
});

var GroupSchema = new Schema({
    name: String,
    location: {type: ObjectId, ref: 'Location'}
});

Add a static method to the group schema and register the models:

GroupSchema.statics.createWithLocation = function(group, location, callback) {
    Location.create(location, function(err, location) {
        if (err) return callback(err);
        group.location = location._id;
        Group.create(group, callback);
    });
};

var Location = mongoose.model('Location', LocationSchema);
var Group = mongoose.model('Group', GroupSchema);

Call the new method by separately passing the group and location data parameters:

Group.createWithLocation(
    {name: 'group1'}, 
    {city: 'New York', state: 'NY'},
    function(err, group) {
        console.log('group created with location: ', group);
    }
);
like image 148
JohnnyHK Avatar answered Sep 28 '22 17:09

JohnnyHK


pre(validate) triggers before a ".save" and do note that data does not have the current document.The "this" variable will have it.Also note .pre(validate) will trigger whenever we do ".save".Here below is an example of how to do what you are looking for.I worked it out and it works perfectly.

   var mongoose = require('mongoose');
    var Schema = mongoose.Schema;
    //group schema
    var GroupSchema = new Schema({
        location: String,
        pass: String
    });
    //dependant location schema
    var LocationSchema = new Schema({
        city: String
    });

  //here I compile it into a model -groupschema
    var Groupuser = mongoose.model('usertest', GroupSchema, 'usertest');
    //here I compile it into a model-location schema
    var Location = mongoose.model('locationtest', LocationSchema, 'locationtest');


    //here is the pre validate,note this will trigger when we save groupschema realted documents.

    GroupSchema.pre('validate', function(next, data) { //will be triggered before the save
        console.log("inside pre validate", this); //the this will have amsterdam,and test see below.
        if (this.location) {//will be amsterdam
            var location = new Location();
            location.city = this.location//adding amsterdam to the location.city 
            location.save(function(err, data) {//this data will have the location document that we just saved.
            });
        }
        next();
    });

    //now I create a new user for groupschema
    var newuser = new Groupuser();
    newuser.location = 'amsterdam';
    newuser.pass = 'test'
    newuser.save(function(err) { //Before this save function does the save,it triggers the .pre(validatE)
        if (err) return console.log(err);//
    });

I might have not nailed the inner location functionality as you want,but I am sure .pre(validate) triggers only when you do .save. It can be a new document or an existing doucment.Also note that it does not trigger when loading a model.

1)we define a schema (var schema=new Schma({datatypes})
2)we compile it into a model(var model=mongoose.model('collectionname',schema)
3)we instantiate a new variable of the model(var user=new model(); and then define the values
4) finally we save the data(user.save()->its here that the pre.validate triggers
Hope its clear now
like image 26
harryy000 Avatar answered Sep 28 '22 16:09

harryy000