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

Different behaviour with set() when using subdocuments #9046

Closed
saveman71 opened this issue May 22, 2020 · 1 comment
Closed

Different behaviour with set() when using subdocuments #9046

saveman71 opened this issue May 22, 2020 · 1 comment
Labels
docs This issue is due to a mistake or omission in the mongoosejs.com documentation
Milestone

Comments

@saveman71
Copy link
Contributor

saveman71 commented May 22, 2020

I noticed a different behavior of set() when using a sub-schema vs using just a more nested schema. Until now, I had assumed that except for the added _id property, using one of the other was pretty much equivalent and just opened more flexibility when using a sub-schema. Am I wrong to assume that?

Anyway, after moving a "location" field to a sub-schema (LocationSchema in the following repro code), I notice a different behaviour when using .set() with a Document which already has some data in these paths.

Without the subschema, it appears to "merge" the nested props, but with the subschema it replaces the whole path with the new one.

Is that documented somewhere? Is that a bug?

'use strict';

const _ = require('lodash');
const assert = require('assert');
const Promise = require('bluebird');
const mongoose = require('mongoose');
mongoose.Promise = Promise;

// DB
let connect = mongoose.connect('mongodb://localhost:27018/test', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

const LocationSchema = new mongoose.Schema({
  type: {
    type: String,
    default: 'Point',
  },
  coordinates: [Number],
  properties: {
    name: String,
  },
}, {
  toJSON: { virtuals: true },
  toObject: { virtuals: true },
  _id: false,
  id: false,
});

const UserSchema = new mongoose.Schema({
  location1: {
    type: LocationSchema,
  },
  location2: {
    type: {
      type: String,
      default: 'Point',
    },
    coordinates: [Number],
    properties: {
      name: String,
    },
  },
});

const User = mongoose.model('User', UserSchema);

// Create test data
connect.then(async () => {
  mongoose.connection.db.dropDatabase();

  let user = await User.create({
    location1: {
      coordinates: [ 2.3522219, 48.856614 ],
      properties: {
        name: 'Paris, France',
      }
    },
    location2: {
      coordinates: [ 2.3522219, 48.856614 ],
      properties: {
        name: 'Paris, France',
      }
    }
  });

  _.assign(user, {
    location1: {coordinates: [0, 0]},
    location2: {coordinates: [0, 0]},
  })

  // both have lost properties.name
  console.log(user);

  let user2 = new User({
    location1: {
      coordinates: [ 2.3522219, 48.856614 ],
      properties: {
        name: 'Paris, France',
      }
    },
    location2: {
      coordinates: [ 2.3522219, 48.856614 ],
      properties: {
        name: 'Paris, France',
      }
    }
  });

  user2.set(_.omit(user.toObject(), '_id'))

  // location1 lost properties.name, but location2 still has it
  console.log(user2);
});

What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.

Mongoose 5.9.8 and also tested on 4.13.19. Node 8.

@vkarpov15 vkarpov15 added this to the 5.9.18 milestone Jun 2, 2020
@vkarpov15 vkarpov15 added the has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue label Jun 2, 2020
@vkarpov15
Copy link
Collaborator

I took a closer look and this is behavior we assert on for nested paths, so we should document this quirk. And consider changing it for a future release - .set(obj) merging at top-level makes sense, but recursively merging seems a bit strange.

Using a single nested schema is slightly different than using a nested path in a few ways. For example, defaults. new Schema({ nested: { path: { type: String, default: 'foo' } } }) vs new Schema({ nested: new Schema({ path: { type: String, default: 'foo' } }) }) - the former gets nested.path = 'foo' if you create an empty object, the latter does not.

@vkarpov15 vkarpov15 added docs This issue is due to a mistake or omission in the mongoosejs.com documentation and removed has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue labels Jun 3, 2020
@vkarpov15 vkarpov15 modified the milestones: 5.9.18, 5.9.19 Jun 5, 2020
vkarpov15 added a commit that referenced this issue Sep 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs This issue is due to a mistake or omission in the mongoosejs.com documentation
Projects
None yet
Development

No branches or pull requests

2 participants