Discriminator support for Sub-Documents #1856

Closed
ElliotChong opened this Issue Dec 30, 2013 · 47 comments

Projects

None yet
@ElliotChong

Starting a feature request to allow discriminator use within sub-documents.

Playing around with a possible API:

class EmployeeSchema extends Schema
    constructor: ->
        super

        @add
            name: { type: String, required: true }
            salary: { type: Number }

class ManagerSchema extends EmployeeSchema
    constructor: ->
        super

        @add
            subordinates: { type: Number }

        @method
            salaryPerSubordinate: ->
                @salary / @subordinates

# Discriminators can only be set once a Model has been defined; however, sub-documents
# only use Schemas.
# Not sure what the best way to handle this is since defining it at the Model level makes
# sense when not worrying about sub-documents and the distinction between the two
# methods wouldn't be intuitive.
EmployeeSchema.discriminator 'Manager', ManagerSchema

class CompanySchema extends Schema
    @add
        name: { type: String, required: true }
        employees: [ new EmployeeSchema() ]

Company = mongoose.model 'Company', new CompanySchema()
acmeInc = new Company { name: "Acme Inc." }

# Discriminator is inferred through the definition of the dsciriminatorKey '__t'
acmeInc.employees.push { __t: "Manager", name: "Roger Rabbit", salary: 0.01, subordinates: 5 }

# No discriminator is inferred, defaults to Employee
acmeInc.employees.push { name: "Road Runner", salary: 2 }

# Perhaps the 'create' method accepts a discriminator as an optional parameter, or we could
# fall back to requiring the discriminatorKey to be manually defined like in the push example
coyote = acmeInc.employees.create "Manager", { name: "Coyote", salary: 500, subordinates: 3 }
console.log coyote.salaryPerSubordinate()

acmeInc.employees.push coyote

# Sub-documents are cast to their respective discriminated Schema
for employee in acmeInc.employees
    switch employee.__t
        when "Manager"
            console.log employee.name, employee.salary, employee.salaryPerSubordinate()
        else
            console.log employee.name, employee.salary
@dylan-baskind

+1 for this feature!

@pavel-kudinov

+1 for this feature!

@alexahdp

+1 for this feature!

@mmoulton

+1 for this feature!

@scarych
scarych commented May 28, 2014

+1 for this feature!

@xuntaka
xuntaka commented May 28, 2014

+1 for this feature!

@AndreeVG

+1 for this feature!

@cynovg
cynovg commented May 28, 2014

+1 for this feature!

@Bravo13
Bravo13 commented May 28, 2014

+1 for this feature!

@kyaroslav

+1 for this feature!

@demetr1us

+1 for this feature!

@mxmCherry

+1 for this feature!

@equinox7

+1 for this feature!

@TimonKK
TimonKK commented May 28, 2014

+1 for this feature!

@izdesenko

+1 for this feature!

@dionys
dionys commented May 28, 2014

+1 for this feature.

@kevindente

+1

@Coderah
Coderah commented Jun 13, 2014

+1

@MOuli90
MOuli90 commented Aug 6, 2014

+1 for this feature!

@UltCombo

+1 for this feature!

@vkarpov15 vkarpov15 added this to the 4.2 milestone Apr 7, 2015
@derickbailey

@vkarpov15 thanks for putting this on the roadmap! this is something that will make my life significantly easier, as I'm dealing with varying data structures in sub-documents.

@vbogdanov

+1

@unusualbob

+1

@jonstorer

nudge

@vkarpov15 vkarpov15 modified the milestone: 4.3, 4.2 Sep 21, 2015
@mathieug

+1

@ducdigital

+1, this need to be done <3

@ducdigital

By the way any progress for this? Is there any other work around or method that can implement this behavior without touching the mongoose core?

Thanks!

@gflandre

+1 for this feature!

@ducdigital

@vkarpov15: I did take liberty to create a small module to have this feature implement on custom Schema:

Here is the gist: https://gist.github.com/ducdigital/cb0926f1a27127369813 (contains the module and test file)

The sample usage is in the test file.

This works in most of the case except there are some downsides:

  • Cannot have required validation in children attributes
  • Cannot have different type on the same attribute name.

Let me know what do you think about this module.
My main concern right now is to able to have custom validation on different schema. I am thinking of generating .pre('save') to validate those document. Are there any better way?

@vkarpov15
Collaborator

I think pre('save'); is the only way to do it. That's how we do validation for subdocs anyway

@vkarpov15 vkarpov15 modified the milestone: 4.5, 4.4 Dec 11, 2015
@mitsos1os

+1 for this feature

@vkarpov15 vkarpov15 modified the milestone: 4.6, 4.5 Feb 19, 2016
@Fonger
Contributor
Fonger commented Mar 20, 2016

+1

@davebaol

+1
I'd say that this feature is crucial for any non-trivial data model :)

@vkarpov15
Collaborator

@davebaol hahah you and I have very different ideas about what non-trivial means :)

@davebaol

@vkarpov15
Well, in this context non-trivial is clearly a hyperbole ๐Ÿ˜‰
And regardless of the context it's a litotes too :bowtie:

Anyways, figures of speech aside, I really appreciate your hard work on mongoose. ๐Ÿ‘
Just wanted to say that this is likely the most important feature among missing ones. ๐Ÿ˜‡

@vkarpov15
Collaborator

Thanks for the kind words ๐Ÿป it's definitely a priority, just not for the next 2 months.

@davebaol

Great! Sounds like a plan ๐Ÿ˜Ž

@whitef0x0

Is this close to completion?

@vkarpov15
Collaborator

@whitef0x0 no progress thus far, contributions welcome.

@vkarpov15 vkarpov15 added the fixed? label Jan 18, 2017
@vkarpov15 vkarpov15 closed this Jan 29, 2017
@davebaol

@vkarpov15
Sweet!
Is there any documentation on this?

@vkarpov15
Collaborator

Working on a blog post about it to consolidate my thoughts and then going to put that into doc form. https://github.com/Automattic/mongoose/pull/4910/files#diff-391b023b3deb2ee3a2fe9a2ffe802070 has a test case that you can extrapolate the general idea from.

@vkarpov15
Collaborator

Thanks for posting @davebaol

@meammeiam

I tried this tonight from the blog and was wondering how you add another event once the Batch is created?

I tried pushing an event, but it saves an empty item.

`Batch.create(batch).then(function(doc) {

doc.events.push({ kind: 'Purchased', product: 24 });
doc.save().then(function(doc) {
    console.log(doc);
});

});`

I also tried create and it returns an empty item.

console.log(doc.events.create({ kind: 'Purchased', product: 24 }));

@vkarpov15
Collaborator

@meammeiam that should work, if it doesn't that's a bug. Please open up a separate issue

@meammeiam

Okay, well maybe I need to upgrade mongoDB. I'm at version 3.3.6. I'll have to compile it from source. Mongoose is at 4.8.4. If it's still not working, I'll open a bug. Thank you!

@vkarpov15
Collaborator

Don't use mongodb 3.3.x, that's an unstable dev release. Please use 3.2.x or 3.4.x

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment