0

Based on mongoose's documentation, I tried to create a chainable byYear query helper method as follows:

import * as mongoose from 'mongoose'
import { IMovie } from '../interfaces/movies'

export interface MovieDocument extends IMovie, mongoose.Document {}

const schema = new mongoose.Schema({
    id: {
        type: String,
        required: [true, 'Missing ID'],
        index: { unique: true },
    },
    title: { type: String, required: [true, 'Missing title'] },
    year: Number,
    director: String,
    actor: [String],
})

schema.query.byYear = function (year) {
    return this.find({ year: year })
}

export const Movie = mongoose.model<MovieDocument>(
    'Movies',
    schema,
    'Movies'
)

where IMovie is a simple interface having the same fields as the schema.

Then I tried using my query helper:

Movie.find().byYear(year).then(movies => {
    console.log('found these movies:', movies)
}).catch(err => {
    console.log('error:' + err)
})

But I get Property 'byYear' does not exist on type 'Query<MovieDocument[], MovieDocument, {}>' for the Movie.find().byYear() part. Nevertheless, the code seems to work, but it seems that due to insufficient typing VS Code does not realize that the Query returned by Movie.find() actually does have a byYear method. How do I do this properly?


UPDATE 2021-05-02: I should have emphasized more that the code I present works beautifully. My problem and the matter of the question is more geared towards how to correctly 'type' this in TypeScript or simply why the error occurs.

bp99
  • 338
  • 3
  • 15
  • Does this answer your question? [How to define custom query helper in mongoose model with typescript?](https://stackoverflow.com/questions/52856264/how-to-define-custom-query-helper-in-mongoose-model-with-typescript) – OfirD Apr 29 '21 at 09:37
  • Not reallly. When trying exactly what is in the answer to that question, I get `Type '{ byYear(this: mg.DocumentQuery, year: number): mg.Query; }' is not assignable to type '{ [name: string]: (this: T, ...args: any[]) => any; }'. Property 'byYear' is incompatible with index signature.` (this is my signature: `byYear(this: mg.DocumentQuery, year: number)`). The linked PR was merged in and I have the latest version. May be related: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/23132 – bp99 May 01 '21 at 11:25

2 Answers2

0

You should do

Movie.find().byYear(year).exec((err, movies) => {
  if (err) {
        console.log('error:' + err)
  } else {
        console.log('found these movies:', movies)
  }
})

Basically you should use .exec and not .then method.
You can find more reference here https://mongoosejs.com/docs/guide.html#query-helpers

NetanMangal
  • 309
  • 1
  • 9
  • Thank you, but unfortunately this does not help. I don't see why `.then` should not be used (despite the linked documentation giving an example of usage with `.exec`), but anyway, the error I descibed is about the `byYear` property of the object returned by `find()` not being found. It is irrelevant, what happens after the `.byYear()` call. Additionally, as I said, the code works, it's just an error that VS Code gives me for my code. – bp99 May 01 '21 at 11:11
-2

You need to export it like below

  export interface MovieDocument extends IMovie, mongoose.Document {
    }
    
    const schema = new mongoose.Schema({
        id: {
            type: String,
            required: [true, 'Missing ID'],
            index: { unique: true },
        },
        title: { type: String, required: [true, 'Missing title'] },
        year: Number,
        director: String,
        actor: [String],
    })
    
    schema.query.byYear = function(year){
        return this.find({ year: year });
    }
    
    const Movie = mongoose.model<MovieDocument>(
        'Movies',
        schema
    );

module.exports = Movie; // this file will i have saved it as movie.ts extension

And Access in helper like this Make Sure to use require while importing, I have tested this and its working fine

const Movie = require('./models/movie'); 

Movie.find().byYear(2020).then((movies: any) => {
    console.log('found these movies:', movies)
}).catch((err: any) => {
    console.log('error:' + err)
})
Sagar Kale
  • 97
  • 1
  • 7
  • Have you tested your answer? It seems to make very little sense to me that you are extending the document interface with the `byYear` method that operates on the collection level (should be a method called on the model, not an instance of the model, ie a document). I don't understand how you want to execute a query on a single MovieDocument instance... Furthermore, the documentation clearly shows a very different example usage, which _does_ in fact work (what you present fails with `byYear is not a function` but I think it's logically flawed to begin with). – bp99 May 02 '21 at 09:33