Mongoose: Can't use $exists with Array

Created on 28 Jan 2017  路  8Comments  路  Source: Automattic/mongoose

Mongoose 4.7.9

Just started having this Error on code that has been working for months:

Error: Can't use $exists with Array. at SchemaArray.castForQuery (/app/node_modules/mongoose/lib/schema/array.js:210:13)

Could it be related to #4933 ?

confirmed-bug

All 8 comments

Confirmed rolling back to 4.7.8 works.

Just checked mongoose schemas and this error is indeed related to #4933. Those schema types that inherit defaults $conditionalHandlers work since they will also have the $exists handler that is now part of schametype.js.

The string schema for example is OK:

SchemaString.prototype.$conditionalHandlers =
    utils.options(SchemaType.prototype.$conditionalHandlers, {
      $all: handleArray,
      $gt: handleSingle,
      $gte: handleSingle,
      $lt: handleSingle,
      $lte: handleSingle,
      $options: handleSingle,
      $regex: handleSingle,
      $not: handleSingle
    });

However, array doesn't do the same:

var handle = SchemaArray.prototype.$conditionalHandlers = {};

handle.$all = cast$all;
handle.$options = String;
handle.$elemMatch = cast$elemMatch;
// and so on

Now this can also affects LOT of plugins, I actually ended up here because a working code with mongoose-moment started failing without any change.

@vkarpov15 , as explained, I think this is a potential breaking change for lot of plugins, not just a patch bump version. What if it is rolled back and applied for the next major version?

Thanks for opening an issue, I've created a simple repro script here:

const mongoose = require('mongoose');
const co = require('co');

mongoose.Promise = global.Promise;

// Constants
const GITHUB_ISSUE = `gh-4937`;

exec();

function exec() {
  connectToDb()
    .then(() => co(function*(){
      const schema = new mongoose.Schema({
        name: String,
        petElephants: [String]
      });

      const Model = mongoose.model('Model', schema);

      const doc = yield Model.create({
        name: 'Elephant Master',
        petElephants: ['Dumbo']
      });

      const foundDoc = yield Model.find({
        petElephants: { $exists: true }
      });

      assert.ok(true);

    }))
    .catch(error => {
      console.error(error);
      process.exit(2);
    })
}

function connectToDb() {
  return co(function*() {
    mongoose.connect(`mongodb://localhost:27017/${ GITHUB_ISSUE }`);
  })
}

Hi @gastonelhordoy sorry about this, this is definitely a bug and due to some minor refactoring that went wrong. Will push 4.8.1 with a fix tomorrow.

Hey @vkarpov15 np, thanks for the quick fix. BTW, this change will remain in the 4.7.x version? What about plugins with schema types that do not extends the default $conditionalHandler hash?

@gastonelhordoy yeah for now the change will stay in 4.7.x since 4.8.x is already out, I'd rather not release another 4.7.

If your type extends from the mongoose SchemaType you should be fine. The problem was that the Array type overwrote the built-in $conditionalHandler hash and so didn't include a $exists handler. If you don't do that you should be fine, feel free to let me know in this thread if you have any more problems :+1:

Ya the plugin I was using was doing the same thing, I already fixed it.

However, I feel like if other plugins do the same this change can have an impact on production apps requiring [email protected] (not yet 4.8), like in my case.

Anyways... thanks for the great work!

I suppose the other alternative is to deprecate 4.7.9 in npm to avoid this side effect. Do you think that's worth doing?

Was this page helpful?
0 / 5 - 0 ratings