1

I want to define a method on my model that involves searching the documents of the same model, here is what I tried:

var mongoose = require('mongoose');
var Author = require('./author.js'); 

var bookSchema = mongoose.Schema({
    author : { type: mongoose.Schema.Types.ObjectId, ref: 'author' },
    genre: String,
});

bookSchema.methods.findSimilar = function(callback) {
    bookSchema.find({'genre': this.genre}).exec(function doThings(err, doc){ 
        /* ... */ 
    }); 
}; 
module.exports = mongoose.model('book', bookSchema, 'book');

However, I get TypeError: bookSchema.find is not a function.

I also tried bookSchema.methods.find(), same result. How can I fix this?

Thanks,

Edit: Inspired by this answer, I also tried this.model('Book').find(), but I get a similar error: TypeError: this.model is not a function

Community
  • 1
  • 1
jeff
  • 13,055
  • 29
  • 78
  • 136
  • 1
    I believe u need to first define a model to use the function `find()`. [API DOCs reference](https://www.npmjs.com/package/mongoose#accessing-a-model) – Daniel Netto Jan 15 '17 at 09:01

2 Answers2

1

Change your method to: (I assume you have already exported model Book from your schema module.

bookSchema.methods.findSimilar = function(callback) {

    this.model('Book').find({'genre': this.genre}).exec(function doThings(err, doc){ 
        /* ... */ 

    }); 

    // Or if Book model is exported in the same module
    // this will work too:
    // Book.find({'genre': this.genre}).exec(function doThings(err, doc){ 
    //     /* ... */ 
    //    
    // }); 
};

The method will be available on the instance of your model:

var book = new Book({ author: author_id, genre: 'some_genre' });
// Or you could use a book document retrieved from database

book.findSimilarTypes(function(err, books) {
    console.log(books);
});

See documentation here.

EDIT (Complete Schema/Model Code)

The complete schema/model code will be as follows:

var mongoose = require('mongoose');
var Author = require('./author.js'); 

var BookSchema = mongoose.Schema({
    author : { type: Schema.Types.ObjectId, ref: 'Author' },
    genre: String,
});

BookSchema.methods.findSimilar = function(callback) {
    Book.find({genre: this.genre}).exec(function doThings(err, doc){ 
        /* ... */ 
    }); 
}; 

const Book = module.exports = mongoose.model('Book', BookSchema);

Usage example:

var book = new Book({ author: author_id, genre: 'some_genre' });
// Or you could use a book document retrieved from database

book.findSimilarTypes(function(err, books) {
    console.log(books);
});
Santanu Biswas
  • 4,699
  • 2
  • 22
  • 21
  • Did you create a model for this schema? If you exported a model for this schema from the same module (named `Book`), you can use `Book.find({'genre': this.genre}).exec(function doThings(err, doc) { // ... // })` – Santanu Biswas Jan 15 '17 at 10:01
  • I thought this file was the model :) In other files, I include this like `var Book = require('./models/thisfile');` and then do `Book.find(..)`. I can define my methods on external files (like controllers) but I just wonder if I can make it part of the model. **Edit:** I forgot the last line where I do `module.exports = ...`. But it's confusing. Should I define the function before I export the model, or after? Neither seems logical right now :D – jeff Jan 15 '17 at 10:02
  • I got confused with PascalCasing & CamelCasing in your code. Usually we use PascalCasing for schema & models and camelCasing for instances. See my edit above. I have included complete code for how your schema/model module should be. Hope this helps. – Santanu Biswas Jan 15 '17 at 10:16
  • Thank you so much! This is still confusing me, the `Book` inside `findSimilar` is defined after the function definition. How does it work? And what does the `const` do? Again, thanks a lot. – jeff Jan 15 '17 at 10:28
  • Even though `Book` is defined after the function definition, the module is loaded before you call the function and so `Book` gets already defined. `const` is constant (ES6). You could use `var` if you prefer. – Santanu Biswas Jan 15 '17 at 10:32
  • OK, thanks. This works. However, I have another problem. I cannot do `doc.save` inside `findSimilarTypes` because `doc.save` is not a function. Anyways, this will be another question :) – jeff Jan 15 '17 at 10:35
  • When you are inside the method, you have to do *this.save()*. If you are inside the callback function then, before calling *findSimilarTypes*, you must do a *var doc = this;*, and inside the callback you can *doc.save()*. Hope this helps. – Santanu Biswas Jan 15 '17 at 10:42
  • You can also do **book.save()** where book is the instance of the document. – Santanu Biswas Jan 15 '17 at 10:44
  • Sorry I meant to do `doc.save` inside `doThings`, i.e. I want to update the document found by it (I'm sure it will find only one) – jeff Jan 15 '17 at 11:03
  • Are you getting a valid **doc** and there is no error? – Santanu Biswas Jan 15 '17 at 11:09
  • Yes, I print it and everything looks fine. – jeff Jan 15 '17 at 11:12
  • 1
    Ah ok... You are doing **Book.find()** and that will return array of doc. So you must call **doc[0].save()**. You have another option of using **Book.findOne()** which returns single doc and in that case you can call **doc.save()** – Santanu Biswas Jan 15 '17 at 11:17
0

You forgot to write new keyword, so do this :

var bookSchema = new mongoose.Schema({
    author : { type: mongoose.Schema.Types.ObjectId, ref: 'author' },
    genre: String,
});

Cheers:)

Codesingh
  • 3,316
  • 1
  • 11
  • 18
  • Strange, I defined all my models without `new`, and everything works. I wonder if there is a difference. **P.S.** adding it didn't solve the issue. – jeff Jan 15 '17 at 10:00