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

When using a Map of Mixed (mongoose.Schema.Types.mixed) types in a subschema, a TypeError is thrown when trying to update or assign a value to a nested property of that mixed type using updateOne() or findOneAndUpdate(). #12530

Closed
1 task done
dl3061 opened this issue Oct 6, 2022 · 1 comment · Fixed by #12605
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@dl3061
Copy link

dl3061 commented Oct 6, 2022

Prerequisites

  • I have written a descriptive issue title

Mongoose version

6.6.4

Node.js version

18.8.0

MongoDB version

4.2.20

Operating system

No response

Operating system version (i.e. 20.04, 11.3, 10)

No response

Issue

When using a Map of Mixed (mongoose.Schema.Types.mixed) types in a subSchema, a TypeError is thrown when trying to update or assign a value to a nested property of that mixed typo using updateOne() or findOneAndUpdate().

One field I have in a schema is a generic 'properties', that's a type Map of Schema.Types.Mixed.

When this 'properties' field is on a top-level schema, I can $set values in it without issue, including nested values:

$set: {
  'properties.hello': 'world',
  'properties.foo.bar': 1,
  'properties.foo.baz': 2,
},

However, when this 'properties' field is in a nested child schema, I'm seeing an error when trying to $set nested values within that properties.

$set: {
  'child.properties.hello': 'world', // works if it's just this
  'child.properties.foo.bar': 1, // added this nested property causes the error below
  'child.properties.foo.baz': 2,
},

The issue I'm seeing is:

%PROJECT_DIR%\node_modules\mongoose\lib\schema.js:2289
      foundschema = schema.path(trypath);

TypeError: Cannot read properties of undefined (reading 'path')
    at search (%PROJECT_DIR%\node_modules\mongoose\lib\schema.js:2289:28)
    at search (%PROJECT_DIR%\node_modules\mongoose\lib\schema.js:2339:23)
    at Schema._getSchema (%PROJECT_DIR%\node_modules\mongoose\lib\schema.js:2358:10)
    at walkUpdatePath (%PROJECT_DIR%\node_modules\mongoose\lib\helpers\query\castUpdate.js:313:27)
    at castUpdate (%PROJECT_DIR%\node_modules\mongoose\lib\helpers\query\castUpdate.js:97:7)
    at model.Query._castUpdate (%PROJECT_DIR%\node_modules\mongoose\lib\query.js:5104:10)
    at Query._findAndModify (%PROJECT_DIR%\node_modules\mongoose\lib\query.js:3989:29)
    at model.Query.<anonymous> %PROJECT_DIR%\node_modules\mongoose\lib\query.js:3470:8)
    at model.Query._wrappedThunk [as _findOneAndUpdate] (%PROJECT_DIR%\node_modules\mongoose\lib\helpers\query\wrapThunk.js:29:8)
    at %PROJECT_DIR%\node_modules\kareem\index.js:426:25

I included a script that could reproduce the issue:
The parent schema includes one of these 'properties' fields and an instance of a child schema which in turn also includes one of these 'properties' fields.

  • The script will create a document using the parent schema.
  • Then, it will use $set a few fields within that parent's properties map, including one with a nested value.
  • Then, it will try to $set a few fields within that child's properties map, including one with a nested value. (I see the error being thrown here)
import mongoose from 'mongoose';

const childSchema = new mongoose.Schema({
  properties: {
    type: Map,
    of: mongoose.Schema.Types.Mixed,
    required: true,
    default: {},
  },
});

const parentSchema = new mongoose.Schema({
  username: {
    type: String,
    required: true,
  },
  properties: {
    type: Map,
    of: mongoose.Schema.Types.Mixed,
    required: true,
    default: {},
  },
  child: {
    type: childSchema,
    required: true,
  },
});

async function main() {
  const mongooseConnection = mongoose.createConnection('mongodb://0.0.0.0:27017/test');
  const TestModel = mongooseConnection.model('Test', parentSchema);

  await TestModel.deleteMany({});

  const username = 'TestUser';
  await TestModel.create({
    username,
    child: {},
  });

  console.log(`beforeUpdate:\n ${JSON.stringify(await TestModel.findOne({ username }).lean(), null, 2)}\n`);

  // Update parent.properties
  await TestModel.updateOne(
    { username },
    {
      $set: {
        'properties.age': 'eleven',
        'properties.foo.bar': 1,
      },
    },
    { runValidators: true },
  );
  console.log(`afterUpdateParent:\n ${JSON.stringify(await TestModel.findOne({ username }).lean(), null, 2)}\n`);

  // Update child properties
  await TestModel.findOneAndUpdate(
    {
      username,
    },
    {
      $set: {
        'child.properties.test': 'ing',
        'child.properties.foo.bar': 'ing',
      },
    },
    { runValidators: true },
  );
  console.log(`afterUpdateChild:\n ${JSON.stringify(await TestModel.findOne({ username }).lean(), null, 2)}\n`);
}

main();

Is this a bug, or is there a better way I should be setting up this schema and making calls to update values within that schema, including that properties value?

Thanks in advance!

@dl3061 dl3061 added help This issue can likely be resolved in GitHub issues. No bug fixes, features, or docs necessary help wanted labels Oct 6, 2022
@hasezoey hasezoey added the has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue label Oct 7, 2022
@IslandRhythms IslandRhythms added confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. 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 Oct 10, 2022
@IslandRhythms
Copy link
Collaborator

const mongoose = require('mongoose');

const childSchema = new mongoose.Schema({
  properties: {
    type: Map,
    of: mongoose.Schema.Types.Mixed,
    required: true,
    default: {},
  },
});

const parentSchema = new mongoose.Schema({
  username: {
    type: String,
    required: true,
  },
  properties: {
    type: Map,
    of: mongoose.Schema.Types.Mixed,
    required: true,
    default: {},
  },
  child: {
    type: childSchema,
    required: true,
  },
});

async function main() {
  const mongooseConnection = mongoose.createConnection('mongodb://0.0.0.0:27017/test');
  mongoose.connection.dropDatabase();
  const TestModel = mongooseConnection.model('Test', parentSchema);

  await TestModel.deleteMany({});

  const username = 'TestUser';
  await TestModel.create({
    username,
    child: {},
  });

  console.log(`beforeUpdate:\n ${JSON.stringify(await TestModel.findOne({ username }).lean(), null, 2)}\n`);

  // Update parent.properties
  await TestModel.updateOne(
    { username },
    {
      $set: {
        'properties.age': 'eleven',
        'properties.foo.bar': 1,
      },
    },
    { runValidators: true },
  );
  console.log(`afterUpdateParent:\n ${JSON.stringify(await TestModel.findOne({ username }).lean(), null, 2)}\n`);

  // Update child properties
  await TestModel.findOneAndUpdate(
    {
      username,
    },
    {
      $set: {
        'child.properties.test': 'ing',
        'child.properties.foo.bar': 'ing',
      },
    },
    { runValidators: true },
  );
  console.log(`afterUpdateChild:\n ${JSON.stringify(await TestModel.findOne({ username }).lean(), null, 2)}\n`);
}

main();

@vkarpov15 vkarpov15 added this to the 6.6.8 milestone Oct 24, 2022
vkarpov15 added a commit that referenced this issue Oct 28, 2022
Correctly find paths underneath single nested document with an array of mixed
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

Successfully merging a pull request may close this issue.

4 participants