Do you want to request a feature or report a bug?
To me it seems like a bug, but probably it will end up being a feature request.
What is the current behavior?
Trying to define the same discriminator for two different models on two different collections throws an OverwriteModelError: Cannot overwrite
[model name]model once compiled.
If the current behavior is a bug, please provide the steps to reproduce.`
const mongoose = require('mongoose');
const { Schema } = mongoose;
const DB = '7100';
const URI = `mongodb://localhost:27017/${DB}`;
const OPTS = { family: 4, useNewUrlParser: true };
const DocumentSchema = new Schema({
});
const InvoiceSchema = new Schema({
});
const DeliveryNoteSchema = new Schema({
});
const FactSchema = new Schema({
});
const InvoiceFactSchema = new Schema({
});
const DeliveryNoteFactSchema = new Schema({
});
const Document = mongoose.model('Document', DocumentSchema);
const Invoice = Document.discriminator('Invoice', InvoiceSchema);
const DeliveryNote = Document.discriminator('DeliveryNote', DeliveryNoteSchema);
const Fact = mongoose.model('Fact', FactSchema);
const InvoiceFact = Fact.discriminator('Invoice', InvoiceFactSchema);
const DeliveryNoteFact = Fact.discriminator('DeliveryNote', DeliveryNoteFactSchema);
mongoose.connect(URI, OPTS);
What is the expected behavior?
No error
Please mention your node.js, mongoose and MongoDB version.
node v8.10.0
mongoose 5.3.2
MongoDB 3.2
You have 2 typos which are causing this error.
The 2 Models which are discriminated from Fact
need to be named like this:
const Fact = mongoose.model('Fact', FactSchema);
const InvoiceFact = Fact.discriminator('InvoiceFact', InvoiceFactSchema);
const DeliveryNoteFact = Fact.discriminator('DeliveryNoteFact', DeliveryNoteFactSchema);
I call it "typo" because you named them correct on the Document-Discriminators above.
With this minimal correction the error disappears :)
It's not a typo. It's the way the system was built and cannot easily be changed without modifying both servers and a variety of clients, not to mention millions of records of historical data. I don't understand why mongoose would prevent me from using the same discriminator in two different models. They are in separate collections so there should be no conflict.
Ok, or you misread the documentation.
You are not specifying and discriminator-key in your code.
The first parameter in the discriminator
-function is the name of the model.
So this line here
Fact.discriminator('Invoice', InvoiceFactSchema)
tries to create a model with the name "Invoice", regardless of how you name the variable.
But a a model (not "variable", i am talking about the mongoose-internally model-name) with the name "Invoice" does already exist because of this line
Document.discriminator('Invoice', InvoiceSchema)
You can still give a 3rd parameter to the discriminator
-function which then speficies the value of the discriminator-key.
Maybe this link here clarifies things: https://mongoosejs.com/docs/api.html#model_Model.discriminator
If you have further questions, feel free to ask :)
Oh, I understand now, thank you!
Related issues: https://github.com/Automattic/mongoose/issues/4148 , https://github.com/Automattic/mongoose/issues/2596, https://github.com/Automattic/mongoose/commit/10efec326a0cbcc04054ed7f56cb4886de1009cd .
Worth thinking about more for the next backwards breaking release. We have a few options for this:
1) Make discriminator names dotted, so Document.discriminator('Invoice')
would compile a model with name Document.Invoice
2) Don't put discriminators in top-level models()
at all. To access a discriminator, you'd instead have to get the model and then the discriminator, something like mongoose.model('Document').discriminator('Invoice').find()
.
I'm not thrilled about either of these solutions honestly. Anyone have any strong thoughts on this - @joniba ? It looks like the value
argument to Model.discriminator()
should solve your problem.
Also the main page for discriminators in the doc says This function takes 2 parameters
when it takes 3 actually: https://mongoosejs.com/docs/discriminators.html#the-model-discriminator-function
I had a similar problem, which is solved by using the 3rd parameter, but it was hard to debug because no error was thrown due the way I defined and used my models. The create
call hang without doing anything. I am posting here just as reference for anyone that may have created the environment the same way as I did.
const mongoose = require('mongoose')
mongoose.connect("...")
const { Schema } = mongoose;
const S1 = new Schema({}, {discriminatorKey: 'kind' })
const S1_1 = new Schema({})
const S1_2 = new Schema({})
const M1 = mongoose.model('m1', S1)
M1.discriminator('M2', S1_1)
M1.discriminator('M3', S1_2)
// This is necessary to avoid model compilation errors in watch mode
// see https://github.com/Automattic/mongoose/issues/1251
if (mongoose.modelNames().includes('M3')) {
mongoose.deleteModel('M3')
}
const S2 = new Schema({}, {discriminatorKey: 'kind'})
const M3 = mongoose.model('M3', S2)
M1.create([{kind: 'M2}]) // Promise hangs, no error thrown.
Most helpful comment
Ok, or you misread the documentation.
You are not specifying and discriminator-key in your code.
The first parameter in the
discriminator
-function is the name of the model.So this line here
tries to create a model with the name "Invoice", regardless of how you name the variable.
But a a model (not "variable", i am talking about the mongoose-internally model-name) with the name "Invoice" does already exist because of this line
You can still give a 3rd parameter to the
discriminator
-function which then speficies the value of the discriminator-key.Maybe this link here clarifies things: https://mongoosejs.com/docs/api.html#model_Model.discriminator
If you have further questions, feel free to ask :)