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

Unexpected behavior using different kinds of select #14115

Closed
1 task done
damir-minkashev opened this issue Nov 23, 2023 · 6 comments
Closed
1 task done

Unexpected behavior using different kinds of select #14115

damir-minkashev opened this issue Nov 23, 2023 · 6 comments
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@damir-minkashev
Copy link

damir-minkashev commented Nov 23, 2023

Prerequisites

  • I have written a descriptive issue title

Mongoose version

6.x.x

Node.js version

20.9.0

MongoDB version

5.10.19

Hey guys

I have an issue with using different kinds of .select() query

First case. The code below returns the expected behavior

const selectFields = {
  _id: 1,
  livemode: 1,
  'payment.clientPaymentMethod': 1,
  'donation.frequency': 1,
};
User.findOne().select(selectFields) 
/* result   
  livemode: true,
  donation: { frequency: 'once' },
  payment: { clientPaymentMethod: 'paypal' },
  */

Second case. If I use another kind of selection we could see different behaviour

const selectFields = {
  _id: 1,
  livemode: 1,
  'payment': {
     clientPaymentMethod: 1
   },
  'donation': {
     frequency: 1
   },
};
User.findOne().select(selectFields) 
/* result   
  livemode: true,
  donation: { frequency: 'once', amount: 0, isMobile: false, },  <- here extra fields are selected and set by default
  payment: { clientPaymentMethod: 'paypal', id: '' },
  */

I am not sure that the second case is valid for use, but it works. There are no rules for selecting nested fields in docs(maybe I missed)

I expected that the second case would work like the first case. Is it valid behavior?

@damir-minkashev damir-minkashev added help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary help wanted labels Nov 23, 2023
@pshaddel
Copy link
Contributor

In MongoDB projection, both are valid and behave the same:
https://mongoplayground.net/p/OllrRw9WSxj

But in Mongoose projection it behaves differently. It is a bug.

@vkarpov15 vkarpov15 added this to the 6.12.4 milestone Nov 25, 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 Nov 25, 2023
@IslandRhythms IslandRhythms added can't reproduce Mongoose devs have been unable to reproduce this issue. Close after 14 days of inactivity. and removed help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary help wanted has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue labels Nov 27, 2023
@IslandRhythms
Copy link
Collaborator

Please specify the mongoose version, unable to reproduce on 6.11.4

const mongoose = require('mongoose');

const testSchema = new mongoose.Schema({
  liveMode: Number,
  payment: {
    clientPaymentMethod: String,
    id: String
  },
  donation: {
    frequency: Number,
    amount: Number,
    isMobile: Boolean
  }
});

const Test = mongoose.model('Test', testSchema);

async function run() {
  console.log('mongoose version', mongoose.version);
  await mongoose.connect('mongodb://localhost:27017');
  await mongoose.connection.dropDatabase();

  await Test.create({
    liveMode: 99,
    payment: {
      clientPaymentMethod: 'a payment method',
      id: ''
    },
    donation: {
      frequency: 5,
      amount: 20,
      isMobile: false
    }
  });

  const selectFields = {
    _id: 1,
    livemode: 1,
    'payment': {
       clientPaymentMethod: 1
     },
    'donation': {
       frequency: 1
     },
  };

  const v2SelectFields = {
    _id: 1,
    livemode: 1,
    'payment.clientPaymentMethod': 1,
    'donation.frequency': 1,
  };

  const v1 = await Test.findOne().select(selectFields);
  console.log('what is v1', v1);
  console.log('====================================================');

  const v2 = await Test.findOne().select(v2SelectFields)
  console.log('what is v2', v2);
}


run();

@pshaddel
Copy link
Contributor

pshaddel commented Nov 27, 2023

The problem emerges when you have some default values, I was also looking at this issue and added two tests that fail to document.test.js

  it('sets defaults on subdocs with subdoc projection (gh-14115)', async function() {
    const userSchema = new mongoose.Schema({
      name: String,
      account: {
        amount: Number,
        owner: { type: String, default: 'OWNER' },
        IBAN: String,
        BIC: String,
        churchTax: Number,
        taxIds: [
          {
            /**
             * This is a sub document, we do not want it to have a `_id`
             */
            _id: false,
            type: Schema.Types.Mixed,
            country: {
              type: String,
              required: true
            },
            taxId: { type: String, required: true }
          }
        ]
      }
    });
    const User = db.model('User', userSchema);
    await User.insertMany([{ name: 'user', account: { amount: 100, owner: 'John Doe', taxIds: [{ country: 'DE', taxId: '123456789' }] } }]);
    const nestedProjectionDocFormat1 = await User.findOne({ name: 'user' }, { name: 1, address: { street: 1 } });
    const nestedProjectionDocFormat2 = await User.findOne({ name: 'user' }, { name: 1, 'address.street': 1 });
    // only address should be in the address subdoc
    assert.strictEqual(nestedProjectionDocFormat1.account.amount, 100);
    assert.strictEqual(nestedProjectionDocFormat2.account.amount, 100);
    // both should not have other members
    assert.strictEqual(nestedProjectionDocFormat1.account.owner, undefined);
    assert.strictEqual(nestedProjectionDocFormat2.account.owner, undefined);
    assert.strictEqual(nestedProjectionDocFormat1.account.taxIds, undefined);
    assert.strictEqual(nestedProjectionDocFormat2.account.taxIds, undefined);
  });

  it('sets defaults on subdocs with subdoc selection (gh-14115)', async function() {
    const userSchema = new mongoose.Schema({
      name: String,
      account: {
        amount: Number,
        owner: { type: String, default: 'OWNER' },
        IBAN: String,
        BIC: String,
        churchTax: Number,
        taxIds: [
          {
            /**
             * This is a sub document, we do not want it to have a `_id`
             */
            _id: false,
            type: Schema.Types.Mixed,
            country: {
              type: String,
              required: true
            },
            taxId: { type: String, required: true }
          }
        ]
      }
    });
    const User = db.model('User', userSchema);
    await User.insertMany([{ name: 'user', account: { amount: 100, owner: 'John Doe', taxIds: [{ country: 'DE', taxId: '123456789' }] } }]);
    const nestedProjectionDocFormat1 = await User.findOne({ name: 'user' }).select({ name: 1, account: { amount: 1 } });
    const nestedProjectionDocFormat2 = await User.findOne({ name: 'user' }).select({ name: 1, 'account.amount': 1 });
    console.log(nestedProjectionDocFormat1, nestedProjectionDocFormat2);
    // only address should be in the address subdoc
    assert.strictEqual(nestedProjectionDocFormat1.account.amount, 100);
    assert.strictEqual(nestedProjectionDocFormat2.account.amount, 100);
    // both should not have other members
    assert.strictEqual(nestedProjectionDocFormat1.account.owner, undefined);
    assert.strictEqual(nestedProjectionDocFormat2.account.owner, undefined);
    assert.strictEqual(nestedProjectionDocFormat1.account.taxIds, undefined);
    assert.strictEqual(nestedProjectionDocFormat2.account.taxIds, undefined);
  });

@pshaddel
Copy link
Contributor

This issue might be related to this one:
#11293

@vkarpov15 vkarpov15 added has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue and removed can't reproduce Mongoose devs have been unable to reproduce this issue. Close after 14 days of inactivity. labels Nov 29, 2023
@IslandRhythms IslandRhythms added confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. and removed has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue labels Nov 29, 2023
@IslandRhythms
Copy link
Collaborator

There's no address property on your schema. You also don't create it in the insert many. I'm assuming that's a typo and you meant account and account.amount

const mongoose = require('mongoose');


const userSchema = new mongoose.Schema({
  name: String,
  account: {
    amount: Number,
    owner: { type: String, default: 'OWNER' },
    IBAN: String,
    BIC: String,
    churchTax: Number,
    taxIds: [
      {
        /**
         * This is a sub document, we do not want it to have a `_id`
         */
        _id: false,
        type: mongoose.Schema.Types.Mixed,
        country: {
          type: String,
          required: true
        },
        taxId: { type: String, required: true }
      }
    ]
  }
});
const User = mongoose.model('User', userSchema);
async function run() {
  await mongoose.connect('mongodb://localhost:27017');
  await mongoose.connection.dropDatabase();
  await User.insertMany([{ name: 'user', account: { amount: 100, owner: 'John Doe', taxIds: [{ country: 'DE', taxId: '123456789' }] } }]);
  const nestedProjectionDocFormat1 = await User.findOne({ name: 'user' }, { name: 1, account: { amount: 1 } });
  const nestedProjectionDocFormat2 = await User.findOne({ name: 'user' }, { name: 1, 'account.amount': 1 });
  console.log(nestedProjectionDocFormat1.account.amount);
  console.log(nestedProjectionDocFormat2.account.amount);

  console.log(nestedProjectionDocFormat1.account.owner);
  console.log(nestedProjectionDocFormat2.account.owner);
  console.log(nestedProjectionDocFormat1.account.taxIds);
  console.log(nestedProjectionDocFormat2.account.taxIds);
}

run()

@IslandRhythms
Copy link
Collaborator

Bug is also present on mongoose 8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Projects
None yet
Development

No branches or pull requests

4 participants