Mongoose "Model.find is not a function" - cascading deletes works in one case, fails in another

1,699 views
Skip to first unread message

Patrick Borrelli

unread,
Oct 26, 2016, 1:50:36 PM10/26/16
to Mongoose Node.JS ODM

I have two similar parent-child relationship trees in my data model and I am using Mongoose's middleware to handle cascade of deletes.


The first hierarchy is represented as follows:  




//blog.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = require('./user.js');
var Entry = require('./entry.js');
var Follower = require('./follower.js');

var Blog = new Schema({
    author: {
        type: Schema.Types.ObjectId,
        ref: 'User',
        required: true
    },
    name: {
        type: String,
        required: true
    },
    headerImageUrl: String,
    description:  {
        type: String,
        required: true
    }
}, {
    timestamps: true
});

Blog.pre('remove', function(next) {
    Entry.find({"blogId": this._id})
        .exec(function(err, entries) {
            console.log("found entries " + entries);
            for(var i = entries.length -1; i >= 0; i--) {
                entries[i].remove();
            }
        });
    Follower.remove({"blogId": this._id}).exec();
    next();
});

module.exports = mongoose.model('Blog', Blog);


//entry.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Blog = require('./blog.js');
var Comment = require('./comment.js');

var Entry = new Schema({
    blogId: {
        type: Schema.Types.ObjectId,
        ref: 'Blog'
    },
    title: String,
    thumbnailUrl: String,
    content: String
}, {
    timestamps: true
});

Entry.pre('remove', function(next) {
    Comment.remove({"entryId": this._id}).exec();
    next();
});

module.exports = mongoose.model('Entry', Entry);



//comment.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = require('./user.js');
var Entry = require('./entry.js');
var Post = require('./post.js');

var Comment = new Schema({
    text:  {
        type: String,
        required: true
    },
    postedBy:  {
        type: Schema.Types.ObjectId,
        ref: 'User',
        required: true
    },
    parentComment: this,
    entryId: {
        type: Schema.Types.ObjectId,
        ref: 'Entry'
    },
    postId: {
        type: Schema.Types.ObjectId,
        ref: 'Post'
    },
    type: {
        type: String,
        enum: ['BLOG', 'FORUM', 'COMMENT'],
        required: true
    }
}, {
    timestamps: true
});

module.exports = mongoose.model('Comment', Comment);


In this case, when remove is called on a blog instance, the Blog.pre('remove'... functionality does what it is intended to do and cleans up all children of the blog instance. The calling code is as follows:



blogRouter.route('/:blogId')

//delete a specific blog by blog id:    [OWNER OR ADMIN USER]
.delete(Verify.verifyOrdinaryUser, Verify.verifyBlogOwnerOrAdmin, function(req, res, next) {
    Blog.findById(req.params.blogId, function(err, blog) {
        if(err) return next(err);
        blog.remove();
        res.json(blog);
    });
});


I am doing exactly the same thing for the second hierarchy, visually represented here: 




and with the following code:


//forum.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = require('./user.js');
var Post = require('./post.js');
var Subscription = require('./subscription.js');

var Forum = new Schema({
    title: {
        type: String,
        required: true
    },
    description: String,
    moderators: [
        {
            type: Schema.Types.ObjectId,
            ref: 'User'
        }
    ]
}, {
    timestamps: true
});

Forum.pre('remove', function(next) {
    Post.find({"forumId": this._id})
        .exec(function(err, posts) {
            console.log("found posts " + posts);
            for(var i = posts.length -1; i >= 0; i--) {
                posts[i].remove();
            }
        });
    Subscription.remove({"forumId": this._id}).exec();
    next();
});

module.exports = mongoose.model('Forum', Forum);


//post.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = require('./user.js');
var Forum = require('./forum.js');
var Comment = require('./comment.js');

var Post = new Schema({
    createdBy: {
        type: Schema.Types.ObjectId,
        ref: 'User',
        required: true
    },
    postText: {
        type: String,
        required: true
    },
    forumId: {
        type: Schema.Types.ObjectId,
        ref: 'Forum'
    },
}, {
    timestamps: true
});

Post.pre('remove', function(next) {
    Comment.remove({"postId": this._id}).exec();
    next();
});

module.exports = mongoose.model('Post', Post);


//comment.js [SAME AS INCLUDED ABOVE]



But in this case, when remove is called on a forum instance, the Forum.pre('remove'... functionality causes an error to be thrown: TypeError: Post.find is not a function. The error is thrown at forum.js line 24 (second line in the Forum.pre(...) block)

The calling code is as follows:



forumRouter.route('/:forumId')

//delete forum by id:  [ADMIN]
.delete(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function(req, res, next) {
    Forum.findById(req.params.forumId, function(err, forum) {
        if(err) return next(err);
        forum.remove();
        res.json(forum);
    });
});



While I have found instance of this error being thrown in my online research here and elsewhere, the causes there were incorrectly exported models or incorrectly defined schemas. The Post schema is being used successfully throughout the rest of my code with no issues, it is only this call to Post.find that is causing the error. And in fact, my postRouter code calls the following successfully:



postRouter.route('/')

//retrieve all forum posts:  [ALL USERS]
.get(function(req, res, next) {
    Post.find({})
        .populate({
            path: 'forumId',
            model: 'Forum',
            populate: {
                path: 'moderators',
                model: 'User'
            }
        })
        .populate('createdBy')
        .exec(function(err, posts){
            if(err) return next(err);
            res.json(posts);
        });
});
Reply all
Reply to author
Forward
0 new messages