question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Improve support for partially denormalized subdocs, including populating denormalized subdocs

See original GitHub issue

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

What is the current behavior? When a schema is set as strict, it is not possible to append different fields to the model OR save those fields in the database. When a schema is set as not strict, it is possible to append different fields AND save those fields in the database

What is the proposed behavior? It would be nice if these two features could be split into two distinct features/flags. I have a use case where I would like to append/set additional properties to a model, and have this returned to the client when using toJSON, but I don’t want these fields to be saved to the database.

Currently, this doesn’t seem possible unless I disable strict for the schema altogether, and create a manual pre-save hook to clean up data that I don’t want when saving. Needless to say this adds overhead and is prone to bugs if new fields are added.

Use case The main use case is being able to populate denormalised data easily, which currently is not possible if using strict schemas.

For example:

//Bar model, has a name property and some other properties that we are interested in
const BarSchema = new Schema({
  name: String,
  more: String,
  another: Number,
})
mongoose.model('Bar', BarSchema)

//Denormalised Bar schema with just the name, for use on the Foo model
const BarNameSchema = new Schema({
  _id: {
    type: Schema.Types.ObjectId,
    ref: 'Bar',
  },
  name: String,
})

//Foo model, which contains denormalized bar data (just the name
const FooSchema = new Schema({
  something: String,
  other: Number,
  bar: BarNameSchema,
})
mongoose.model('Foo', FooSchema)

//Now for some routes I want to be able to load the full `Bar` model, and set it on the `bar` property of a loaded `foo` document
const _id = ...
const foo = mongoose.model('Foo').findOne({_id})
const bar = mongoose.model('Bar').findOne({foo.bar._id})

//This won't work, mongoose will drop the additional properties because the schema is set to `strict`
foo.bar = bar 

//I could make the schema not-strict, but then saving foo will save redundant properties in the database
await foo.save() //don't want the full bar data to be saved on the model

So ideally, these two behaviours of strict could be separated somehow into a separate flag/setting. However, I am also open to other suggestions that would solve our problem.

To be honest this has been a pet peeve of mine for a few years of working with Mongoose now. It’s so easy to populate data, and it’s so easy to make and manage denormalized data in your database, but it seems the combination of the two still causes unnecessary friction.

Maybe there’s a use case for defining a new schema type, which is a “Denormalized” type, which can refer to another model and which will store a select number of fields alongside the parent document, but which can also easily be populated with additional fields for output as JSON/object, while not saving those fields in the database. 🤔

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:11 (7 by maintainers)

github_iconTop GitHub Comments

2reactions
vkarpov15commented, Oct 19, 2021

This does make sense. I’m thinking the syntax will look like this:

const FooSchema = new Schema({
  something: String,
  other: Number,
  bar: {
    type: BarNameSchema,
    ref: 'Bar'
  }
})

So if you put a ref on a single nested subdoc, Mongoose will be smart enough to replace bar with a populated doc if you call populate('bar').

The tricky part is what happens if you overwrite certain properties of bar? For example, if bar is populated, what should doc.bar.name = 'new name'; await doc.save(); do? Should it just set doc.bar.name, should it update both doc.bar.name and the populated doc’s name?

1reaction
adamreisnzcommented, Aug 2, 2022

@vkarpov15 I think I can confirm that all cases are working now, thanks! 👍🏼

Read more comments on GitHub >

github_iconTop Results From Across the Web

What is denormalization and how does it work? - TechTarget
Denormalization improves read performance of a normalized database. Find out what denormalization is and how it differs from normalization, as well as pros ......
Read more >
Comparing MongoDB vs PostgreSQL
MongoDB allows any field of a document, including those deeply nested in arrays and subdocuments, to be indexed and efficiently queried. The following...
Read more >
Extracting a Relational Database Schema from a Document ...
with a more denormalized structure [Mei13A]. There is no formalized method for transforming data from a document database into a relational schema [Goyal16] ......
Read more >
Release Notes - Hackolade
New features in v6.7.0 [25-Nov-2022] - Workgroup: added support for GitLab Cloud ... relationships in denormalized models - Delta Lake/Databricks: throttled ...
Read more >
Mongoose Monthly Feb 2022: Mongoose 6.2, Pro Subscription
Populating Partially Embedded Subdocs ... feature is a meaningful first step toward supporting denormalization as a core part of Mongoose.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found