Mongoose: {$exists: false} not working for nested fields in $pull statement

Created on 15 Sep 2019  路  1Comment  路  Source: Automattic/mongoose

I have a collection documents with a document that looks like this:

{
    "_id" : ObjectId("5d7ddedf92264a43ceee9f48"),
    "name" : "test123",
    "predictedEntityValues" : [ 
        {
            "status" : "DONE",
            "values" : [ 
                {
                    "text" : "123"
                }
            ]
        }
    ]
}

I have the following query where documentIds = [ ObjectId("5d7ddedf92264a43ceee9f48") ]

await models.documents
    .updateMany(
      { _id: { $in: documentIds } },
      {
        $pull: {
          predictedEntityValues: { 'values.0.coords': { $exists: false } },
        },
      }
    )

The above query should should remove the value in the array 'predictedEntityValues', but it doesn't.
My query is correct because when I execute it into the mongo shell, it works.

I turned on debug mode, and I saw that the library parsed my query to this query:

Mongoose: documents.updateMany({ _id: { '$in': [ ObjectId("5d7ddedf92264a43ceee9f48") ] } }, { '$setOnInsert': { createdAt: new Date("Sun, 15 Sep 2019 06:42:51 GMT") }, '$set': { updatedAt: new Date("Sun, 15 Sep 2019 06:42:51 GMT") }, 
'$pull': {  predictedEntityValues: { 'values.0.coords': {} } } }, {})

As you can see, it removes the $exists false in the parsed query: { predictedEntityValues: { 'values.0.coords': {} } } }

confirmed-bug

Most helpful comment

Fix will be in 5.7.3. As a workaround, add coords to your schema. For example:

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

const GITHUB_ISSUE = `gh8166`;
const connectionString = `mongodb://localhost:27017/${ GITHUB_ISSUE }`;
const { Schema } = mongoose;

run().then(() => console.log('done')).catch(error => console.error(error.stack));

async function run() {
  await mongoose.connect(connectionString, { useNewUrlParser: true });
  await mongoose.connection.dropDatabase();

  const Model = mongoose.model('Test', Schema({
    name: String,
    predictedEntityValues: [{
      status: String,
      values: [{ text: String, coords: [Number] }]
    }]
  }));

  await Model.create({
    "_id" : new mongoose.Types.ObjectId("5d7ddedf92264a43ceee9f48"),
    "name" : "test123",
    "predictedEntityValues" : [
        {
            "status" : "DONE",
            "values" : [
                {
                    "text" : "123"
                }
            ]
        }
    ]
  });

  await Model.updateMany(
      {},
      {
        $pull: {
          predictedEntityValues: { 'values.0.coords': { $exists: false } },
        },
      }
    )
}

>All comments

Fix will be in 5.7.3. As a workaround, add coords to your schema. For example:

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

const GITHUB_ISSUE = `gh8166`;
const connectionString = `mongodb://localhost:27017/${ GITHUB_ISSUE }`;
const { Schema } = mongoose;

run().then(() => console.log('done')).catch(error => console.error(error.stack));

async function run() {
  await mongoose.connect(connectionString, { useNewUrlParser: true });
  await mongoose.connection.dropDatabase();

  const Model = mongoose.model('Test', Schema({
    name: String,
    predictedEntityValues: [{
      status: String,
      values: [{ text: String, coords: [Number] }]
    }]
  }));

  await Model.create({
    "_id" : new mongoose.Types.ObjectId("5d7ddedf92264a43ceee9f48"),
    "name" : "test123",
    "predictedEntityValues" : [
        {
            "status" : "DONE",
            "values" : [
                {
                    "text" : "123"
                }
            ]
        }
    ]
  });

  await Model.updateMany(
      {},
      {
        $pull: {
          predictedEntityValues: { 'values.0.coords': { $exists: false } },
        },
      }
    )
}
Was this page helpful?
0 / 5 - 0 ratings