LearnBoost/mongoose — pull request #1647 by j (2013-08-15 02:08:15+00:00)
Title: added support for discriminator mapping (closes #1003)

So I gave discriminator mapping a shot. After coding this out, it feels pretty natural and seems to fit well with the current API of mongoose.

Anyway, a quick example of how it works:

```javascript
function BaseSchema() {
Schema.apply(this, arguments);

this.add({
name: String,
createdAt: { type: Date, default: Date.now }
});
}
util.inherits(BaseSchema, Schema);

var EventSchema = new BaseSchema();

var ImpressionEvent = new BaseSchema();

var ConversionEvent = new BaseSchema({
revenue: Number
});

var Base = mongoose.model('Event', EventSchema),
var Impression = Base.discriminator('Impression', ImpressionEvent),
var Conversion = Base.discriminator('Conversion', ConversionEvent);
```

Then assuming we have inserted a single document for each event in the order of Base, Impression, and Conversion:

```javascript
Base.find({}, function(err, events) {
expect.ok(events[0] instanceof Base); // true
expect.ok(events[1] instanceof Impression); // true
expect.ok(events[2] instanceof Conversion); // true
expect.ok(events[2] instanceof Base); // false

// however
expect.ok(events[2].schema instanceof BaseSchema); // true
});
```

Lastly, if you take a the specific discriminator type model, it will query for only the documents of that type:
```javascript
// find all conversion events
Conversion.find({}, function(err, events) {
expect.equal(events.length, 1);
});

// or a single document
Conversion.findOne({}, function(err, event) {
expect.ok(event instanceof Conversion); // true
});

// or querying for a specific id which is an invalid type (produces query { _id: ..., __t: 'Conversion' })
Conversion.findOne({ _id: impression.id }}, function(err, event) {
expect.equal(event, null); // true
});
```

Just one thing before moving too far forward: I introduced a new method "discriminator" to be clear of what you're doing. It creates a model in the same connection and returns it and does some other stuff behind the scenes. We could also make the static method Model.model do the same (or alias / remove discriminator method) if the community would rather have the following instead:

```javascript
var Base = mongoose.model('Event', EventSchema);
var Impression = Base.model('Impression', ImpressionEvent),
var Conversion = Base.model('Conversion', ConversionEvent);
```

Another note, in my example, I'm using util.inherit to simply show that you can have a base model. This is completely optional. You can technically store whatever schema's you want and they don't have to inherit from one another. I'm also +1 for creating an extend method in Schema to shortcut schema inheritance.

Anyway, here you go! :) :surfer: