Git Product home page Git Product logo

Comments (7)

vkarpov15 avatar vkarpov15 commented on September 21, 2024

What specifically about the middleware callback is confusing? Our middleware docs show examples of using next vs using async functions, which seems to cover most of what I can think of with the middleware callback.

from mongoose.

adelbke avatar adelbke commented on September 21, 2024

I'll explain it to you by giving examples of situations I found myself in when working on a task related to this.

Goal:
I am trying to propagate Mongoose schema events to an event-emitter. A one-to-one mapping would be straightforward. Of each event in a given schema to an event name in the event emitter.

Situations:

  • Post Hook of save events (a document middleware) fire twice on each save, and in the first instance, when I access the "this" keyword, it is not a proper document instance as this.constructor.modelName is undefined.
  • in the post Hook of save events, I expect to pass a function that looks like the following
function (doc, next) { }

This is expected and documented, but there's no indication of what happens when I pass an async function; it seems to be working, but I still have to call next somehow (which is not bad; I would prefer if I could have the choice not to do see and return a promise).

  • In Query Middleware, the "pre" function passes everything regarding the query in the this keyword. But Queries are different; sometimes, there can be an update object, a filter Object, or even an Options Object. They are accessible through functions that may or may not be defined depending on the event. If I try to do what I am doing and react to many different events, I would need to do investigative work to determine which type of query this is.

In this case, I think it would be better if the this keyword were more strictly typed to understand what type of object I am manipulating (is this an update or insertMany ? ). If there is already a way for me to type the Query strictly (I believe there is) that I do not understand, then it means it's either hard to find in the documentation or undocumented.
As to my knowledge, the blog post linked in the docs explains clearly that Query is "ambiguous" in middleware and I think that should be updated

  • I also noticed something weird in insertMany mentioned in this issue #11531. The pre and post middleware functions have different orders of parameters.

  • In all the other document events, I had to add an if statement checking if this.constructor.modelName is defined because of occasional double event firing like the save case mentioned above.

I hope this was clear; thank you and apologies for the delayed response.

Edit: updated paragraph structure

from mongoose.

vkarpov15 avatar vkarpov15 commented on September 21, 2024

Post Hook of save events (a document middleware) fire twice on each save, and in the first instance, when I access the "this" keyword, it is not a proper document instance as this.constructor.modelName is undefined. This is incorrect. The following script shows that post save hooks only execute once, there's only one "post save" printed to the console:

'use strict';

const mongoose = require('mongoose');

mongoose.connect('mongodb://127.0.0.1:27017/mongoose_test');

const schema = mongoose.Schema({ name: String });
schema.post('save', function() {
  console.log('post save');
});
const TestModel = mongoose.model('Test', schema);
const doc = new TestModel({ name: 'test' });
doc.save().then(() => console.log('done'));

This is expected and documented, but there's no indication of what happens when I pass an async function; it seems to be working, but I still have to call next somehow (which is not bad; I would prefer if I could have the choice not to do see and return a promise).

That's fair, we don't have an example in our docs of using async functions with post hooks, just with pre hooks. But post hooks work the same way, and you don't need to call next if you have an async function. For example, the following script shows a post save hook with an async function that waits for 1 sec before completing

'use strict';

const mongoose = require('mongoose');

mongoose.connect('mongodb://127.0.0.1:27017/mongoose_test');

const schema = mongoose.Schema({ name: String });
schema.post('save', async function() {
  console.log('Post save start');
  const start = Date.now();
  await new Promise(resolve => setTimeout(resolve, 1000));
  console.log('post save end', Date.now() - start);
});
const TestModel = mongoose.model('Test', schema);
const doc = new TestModel({ name: 'test' });
doc.save().then(() => console.log('done'));

In this case, I think it would be better if the this keyword were more strictly typed to understand what type of object I am manipulating (is this an update or insertMany ? ). If there is already a way for me to type the Query strictly (I believe there is) that I do not understand, then it means it's either hard to find in the documentation or undocumented.
As to my knowledge, the blog post linked in the docs explains clearly that Query is "ambiguous" in middleware and I think that should be updated

That's why we have schema.pre('updateOne') and schema.pre('insertMany') instead of just schema.pre(/.*/). insertMany isn't even query middleware. I guess you mean just for TypeScript types, not runtime behavior?

from mongoose.

adelbke avatar adelbke commented on September 21, 2024

Hi @vkarpov15, thanks for responding.

Post Hook of save events (a document middleware) fire twice on each save, and in the first instance, when I access the "this" keyword, it is not a proper document instance as this.constructor.modelName is undefined. This is incorrect. The following script shows that post save hooks only execute once, there's only one "post save" printed to the console:

I just ran that script and it adds up, thank you for pointing that out. I'll check the other dependencies I had... I may have made a mistake

That's why we have schema.pre('updateOne') and schema.pre('insertMany') instead of just schema.pre(/.*/). insertMany isn't even a query middleware. I guess you mean just for TypeScript types, not runtime behavior?

I was referring to typescript types, not runtime behavior. Especially the meaning and behavior of generics in mongoose that I have a hard time grasping (I don't know if this is my fault or the docs tbh).
at the time of writing the issue I tried to set up a single hook for all queries and set up separation logic myself. But resorted to defining a hook for each operation. As it made more sense. Therefore this comment is moot

from mongoose.

vkarpov15 avatar vkarpov15 commented on September 21, 2024

Yeah I would recommend defining a hook for each operation, or at least a hook for each general class of operations like pre(['find', 'findOne']), pre(['updateOne', 'updateMany']), etc.

However, Query instances are the same for all operations, so there isn't anything wrong with a pre('find') hook that adds details to this.getUpdate() other than the trivial perf overhead of updating an object. find() just won't use the update.

Which generics are you specifically confused about? With automatic schema type inference, you frequently don't need generics at all.

from mongoose.

github-actions avatar github-actions commented on September 21, 2024

This issue is stale because it has been open 14 days with no activity. Remove stale label or comment or this will be closed in 5 days

from mongoose.

github-actions avatar github-actions commented on September 21, 2024

This issue was closed because it has been inactive for 19 days and has been marked as stale.

from mongoose.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.