Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow passing subdocument with { defaults: false } to query filter #13512

Closed
2 tasks done
g-ongenae opened this issue Jun 15, 2023 · 3 comments
Closed
2 tasks done

Allow passing subdocument with { defaults: false } to query filter #13512

g-ongenae opened this issue Jun 15, 2023 · 3 comments
Labels
enhancement This issue is a user-facing general improvement that doesn't fix a bug or add a new feature
Milestone

Comments

@g-ongenae
Copy link

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Mongoose version

7.1.1

Node.js version

18.16.0

MongoDB server version

5.4.0

Typescript version (if applicable)

4.2.4

Description

Sub-document in filter is converted to model and gets default values set.

So a filter: { bookHolder: {} } // works also with { bookHolder: { isReading: true } }
Get converted: { bookHolder: { comments: [] } } // a mongoose document

This conversion ends up returning incorrect results from the find.

Steps to Reproduce

On a collection Book

{
  "bookHolder": {
    "isReading": boolean,
    "bookedAt": Date,
    "maxBookingDate": Date,
    "comments": { "postedAt": Date, "message": string }[] // default: []
  }
}

Do:

await this.collection.find({ bookHolder: { isReading: true } }).exec();

(I will create a sample app to reproduce it later.)

Expected Behavior

The request filter is not converted to a mongoose object.
Default values are not set in the filter.
The result of the request is not impacted by default values of the schema/collection.

@vkarpov15 vkarpov15 added this to the 7.3.1 milestone Jun 15, 2023
@vkarpov15 vkarpov15 added the has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue label Jun 15, 2023
@vkarpov15
Copy link
Collaborator

This is currently expected behavior. Mongoose will cast and apply defaults when you do an exact equality match on a subdocument, because otherwise you would have to explicitly define defaults in your query filter yourself.

Keep in mind that collection.find({ bookHolder: { isReading: true } }) means "find all documents where bookHolder is exactly deep equal to the object { isReading: true }". You created a document through Mongoose with defaults set, you would need the defaults in your query filter.

More often than not, you want to do collection.find({ 'bookHolder.isReading': true }), which means "find all documents where bookHolder is an object with an isReading property that is equal to true".

That being said, it is problematic that Mongoose doesn't have a functioning workaround for skipping applying defaults on subdocuments. My recommended workaround would be to explicitly create an instance of the bookHolder subdocument with { defaults: false } as follows:

'use strict';

const mongoose = require('mongoose');
mongoose.set('debug', true);

run().catch(err => console.log(err));

async function run() {
  const schema = mongoose.Schema({
    title: String,
    bookHolder: mongoose.Schema({
      isReading: Boolean,
      tags: [String]
    })
  });
  const Test = mongoose.model('Test', schema);

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

  const BookHolder = schema.path('bookHolder').caster;

  await Test.create({ title: 'foo', bookHolder: { isReading: true } });

  // Create a new BookHolder subdocument, skip applying defaults
  const bookHolder = new BookHolder(
    { isReading: true },
    null,
    null,
    { defaults: false }
  );
  const doc = await Test.findOne({ bookHolder });
  console.log(doc);
}

But, unfortunately, that doesn't currently work because of some internal logic Mongoose uses for copying query filters. We're working on a fix.

@vkarpov15 vkarpov15 changed the title Sub-document in filter is converted to model and gets default values set Allow passing subdocument with { defaults: false } to query filter Jun 18, 2023
@vkarpov15 vkarpov15 added enhancement This issue is a user-facing general improvement that doesn't fix a bug or add a new feature and removed has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue labels Jun 18, 2023
@vkarpov15 vkarpov15 modified the milestones: 7.3.1, 7.4.0 Jun 18, 2023
vkarpov15 added a commit that referenced this issue Jun 18, 2023
…ion, allow querying subdocuments with defaults disabled

Fix #13512
vkarpov15 added a commit that referenced this issue Jun 19, 2023
feat(query): delay converting documents into POJOs until query execution, allow querying subdocuments with defaults disabled
@ghost91-
Copy link
Contributor

@vkarpov15 The problem still exists for us in 7.4.0 (and 7.3.4): There are still circular references in the filter object that dd-trace tries to stringify.

This is now a major problem for us, since there is no version of mongoose we can upgrade to, to avoid GHSA-9m93-w8w6-76hh, that does not cause our service to crash.

@vkarpov15
Copy link
Collaborator

@ghost91- I don't see how your issue relates to OP's, or even what your issue is. Can you please open a new issue and follow the issue template?

@Automattic Automattic locked as resolved and limited conversation to collaborators Jul 21, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement This issue is a user-facing general improvement that doesn't fix a bug or add a new feature
Projects
None yet
Development

No branches or pull requests

3 participants