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

Base models do not honor select: false for discriminators #4991

Closed
sobafuchs opened this issue Feb 16, 2017 · 3 comments
Closed

Base models do not honor select: false for discriminators #4991

sobafuchs opened this issue Feb 16, 2017 · 3 comments
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@sobafuchs
Copy link
Contributor

Do you want to request a feature or report a bug?
Bug

What is the current behavior?
When a base model is used to retrieve a discriminator model document, it does not honor select: false.

If the current behavior is a bug, please provide the steps to reproduce.

const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
const co = require('co');
const chalk = require('chalk');

const GITHUB_ISSUE = `test-constructor-emit-bug`;

exec()
  .then(() => {
    console.log(chalk.green(`Successfully ran program`));
    process.exit(0);
  })
  .catch(error => {
    console.log(chalk.red(`Error: ${ error }\n${ error.stack }`));
    process.exit(2);
  });

function exec() {
  return co(function* () {
    const db = mongoose.createConnection(`mongodb://localhost:27017/${ GITHUB_ISSUE }`);
    const { Base, Discriminator } = createModels(db);
    const { baseDoc, discrimDoc } = yield seedDb({ Base, Discriminator });

    const docFromDb = yield Base.findById(discrimDoc._id) // does not honor select: false
    // const docFromDb = yield Discriminator.findById(discrimDoc._id); // honors select: false
    console.log(docFromDb.internal); // should not log `internal.password`
  });
}

function seedDb(models) {
  return co(function*() {
    const { Base, Discriminator } = models;
    
    yield [Base.remove({}), Discriminator.remove({})];

    const baseDoc = yield Base.create({ internal: { diseases: ['Malaria'] }});
    const discrimDoc = yield Discriminator.create({ internal: {
      diseases: ['MS'],
      password: 'plain_test_password_ftw'
    }});

    return { baseDoc, discrimDoc };
  });
}

function createModels(db) {
  const baseSchema = new mongoose.Schema({
    internal: {
      diseases: [{ type: String }]
    }
  });

  const Base = db.model('Base', baseSchema);
  const discriminatorSchema = new mongoose.Schema({
    internal: {
      password: { type: String, select: false }
    }
  });
  const Discriminator = Base.discriminator('Discriminator', discriminatorSchema);

  return { Base, Discriminator };
}

What is the expected behavior?
We should probably unselect those fields manually after fetching from the database, since mongoose doesn't know ahead of time which discriminators to select for.

Please mention your node.js, mongoose and MongoDB version.
node 6.9.2, mongoose 4.8.3, mongodb 3.4

@sobafuchs sobafuchs added the confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. label Feb 16, 2017
@sobafuchs sobafuchs modified the milestones: 4.9, 4.8.4 Feb 16, 2017
@vkarpov15 vkarpov15 modified the milestones: 4.8.5, 4.8.4 Feb 20, 2017
vkarpov15 added a commit that referenced this issue Feb 21, 2017
@bruun
Copy link

bruun commented Aug 24, 2017

I seem to be experiencing unexpected side effects from this change:

I have a base model A, with a discriminator model B.
When I query A for documents using a $slice array projection, resulting documents of type A return all properties (excluding those marked as select: false), while resulting documents of type B only return properties explicitly marked as select: true or those with a default set. Those with a default set return the default value, not the value actually stored in the database. If no properties are marked with select: true, only properties with default values are returned (again with the default value, not the store value).

EDIT: Disregard my comment, the issue was that mongoose adds the discriminator key to the query under the hood. The solution was to use the slice function on the Query.

@bruun
Copy link

bruun commented Nov 23, 2017

I might have spoken too soon. Testing with both version 4.13.4 and the latest master commit, I am seeing the same behaviour when only using the slice projection.

I have taken the original example in this issue and modified to reproduce the behaviour:

const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
const co = require('co');
const chalk = require('chalk');
mongoose.set('debug', true);
const GITHUB_ISSUE = `default-values-on-discriminator-documents`;

exec()
    .then(() => {
        console.log(chalk.green(`Successfully ran program`));
        process.exit(0);
    })
    .catch(error => {
        console.log(chalk.red(`Error: ${error}\n${error.stack}`));
        process.exit(2);
    });

function exec() {
    return co(function* () {
        const db = mongoose.createConnection(`mongodb://localhost:27017/${GITHUB_ISSUE}`);
        const { Base, Discriminator } = createModels(db);
        const { baseDoc, discrimDoc } = yield seedDb({ Base, Discriminator });

        const baseDocsFromDbWithSlice = yield Base.find().slice('array', 1)
        const baseDocsFromDb = yield Base.find()
        console.log(baseDocsFromDbWithSlice); // Discriminator document returns default value for propA (and not propB), base document returns stored value - unexpected
        console.log(baseDocsFromDb); // Discriminator and base document returns stored value for propA - expected
    });
}

function seedDb(models) {
    return co(function* () {
        const { Base, Discriminator } = models;

        yield [Base.remove({}), Discriminator.remove({})];

        const baseDoc = yield Base.create({
            propA: 'Hi',
            array: ["a", "b"]
        });
        const discrimDoc = yield Discriminator.create({
            propA: 'Hi',
            propB: 'Hello',
            array: ["a", "b"]
        });

        return { baseDoc, discrimDoc };
    });
}

function createModels(db) {
    const baseSchema = new mongoose.Schema({
        propA: { type: String, default: 'default value' },
        array: [{type: String}],
    });

    const Base = db.model('Base', baseSchema);
    const discriminatorSchema = new mongoose.Schema({
        propB: { type: String}
    });
    const Discriminator = Base.discriminator('Discriminator', discriminatorSchema);

    return { Base, Discriminator };
}

This behaviour changed in 4.11.13, which is why I suspected the resolution for this issue to be the cause. If not, I'll be happy to create a new issue

@vkarpov15 vkarpov15 reopened this Dec 5, 2017
@vkarpov15 vkarpov15 modified the milestones: 4.8.5, 4.13.7 Dec 5, 2017
@vkarpov15
Copy link
Collaborator

Thanks for reporting @bruun , will investigate asap 👍

vkarpov15 added a commit that referenced this issue Dec 6, 2017
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

3 participants