# 鑒頻器(discriminators)
> 原文:[The model.discriminator() function](http://mongoosejs.com/docs/discriminators.html)
## The `model.discriminator()` function
鑒別器是一個模式繼承機制。他們使你重疊模式上同一標的MongoDB集合有多個模型。
假設你想在一個集合中跟蹤不同類型的事件。每一件事件都會有一個時間戳,但事件表示點擊鏈接應該有一個URL。你可以使用`model.discriminator()`函數。這個函數需要2個參數,一個模型的名字和一個鑒頻器模式。它返回一個模型是基礎模式的結合和鑒頻器模式。
```
var options = {discriminatorKey: 'kind'};
var eventSchema = new mongoose.Schema({time: Date}, options);
var Event = mongoose.model('Event', eventSchema);
// ClickedLinkEvent is a special type of Event that has
// a URL.
var ClickedLinkEvent = Event.discriminator('ClickedLink',
new mongoose.Schema({url: String}, options));
// When you create a generic event, it can't have a URL field...
var genericEvent = new Event({time: Date.now(), url: 'google.com'});
assert.ok(!genericEvent.url);
// But a ClickedLinkEvent can
var clickedEvent =
new ClickedLinkEvent({time: Date.now(), url: 'google.com'});
assert.ok(clickedEvent.url);
```
### 鑒別器保存事件模型的集合
假設你創建一個鑒別器跟蹤事件,新用戶注冊。這些`SignedUpEvent`實例將被存儲在相同的集合作為通用的事件和`ClickedLinkEvent`實例。
```
var event1 = new Event({time: Date.now()});
var event2 = new ClickedLinkEvent({time: Date.now(), url: 'google.com'});
var event3 = new SignedUpEvent({time: Date.now(), user: 'testuser'});
var save = function (doc, callback) {
doc.save(function (error, doc) {
callback(error, doc);
});
};
async.map([event1, event2, event3], save, function (error) {
Event.count({}, function (error, count) {
assert.equal(count, 3);
});
});
```
### 鑒別器的鍵
mongoose講述不同的鑒別模型之間的差異是由“鑒頻器的鍵”,默認是`__t`。Mongoose添加一個叫做`__t`字符串路徑到你的模式中,它采用追蹤鑒別本文檔實例。
```
var event1 = new Event({time: Date.now()});
var event2 = new ClickedLinkEvent({time: Date.now(), url: 'google.com'});
var event3 = new SignedUpEvent({time: Date.now(), user: 'testuser'});
assert.ok(!event1.__t);
assert.equal(event2.__t, 'ClickedLink');
assert.equal(event3.__t, 'SignedUp');
```
### 鑒別器添加鑒別鍵查詢
鑒別器模型是特殊的;他們重視鑒別鍵查詢。換句話說,`find(), count(), aggregate(),`等等,有足夠的智慧來解釋鑒別器。
```
var event1 = new Event({time: Date.now()});
var event2 = new ClickedLinkEvent({time: Date.now(), url: 'google.com'});
var event3 = new SignedUpEvent({time: Date.now(), user: 'testuser'});
var save = function (doc, callback) {
doc.save(function (error, doc) {
callback(error, doc);
});
};
async.map([event1, event2, event3], save, function (error) {
ClickedLinkEvent.find({}, function (error, docs) {
assert.equal(docs.length, 1);
assert.equal(docs[0]._id.toString(), event2._id.toString());
assert.equal(docs[0].url, 'google.com');
});
});
```
### 鑒別器復制的前置和后置鉤子
作者也使用他們的基礎模式的前置和后置的中間件。然而,你也可以把中間件來鑒別模式不影響基礎模式。
```
var options = {discriminatorKey: 'kind'};
var eventSchema = new mongoose.Schema({time: Date}, options);
var eventSchemaCalls = 0;
eventSchema.pre('validate', function (next) {
++eventSchemaCalls;
next();
});
var Event = mongoose.model('GenericEvent', eventSchema);
var clickedLinkSchema = new mongoose.Schema({url: String}, options);
var clickedSchemaCalls = 0;
clickedLinkSchema.pre('validate', function (next) {
++clickedSchemaCalls;
next();
});
var ClickedLinkEvent = Event.discriminator('ClickedLinkEvent',
clickedLinkSchema);
var event1 = new ClickedLinkEvent();
event1.validate(function () {
assert.equal(eventSchemaCalls, 1);
assert.equal(clickedSchemaCalls, 1);
var generic = new Event();
generic.validate(function () {
assert.equal(eventSchemaCalls, 2);
assert.equal(clickedSchemaCalls, 1);
});
});
```
### 處理自定義\_id字段
鑒別器的字段是基礎模式的字段和鑒別器模式的字段的結合,并且鑒頻器模式的字段優先。這種行為變得古怪當你有一個自定義`_id`字段。一個模式默認情況下得有`_id`字段,所以基礎模式的`_id`字段將得到由鑒別器模式的默認`_id`字段覆蓋。
你可以通過設置`_id`選項為false 在鑒別器的模式如下圖所示。
```
var options = {discriminatorKey: 'kind'};
// Base schema has a String _id...
var eventSchema = new mongoose.Schema({_id: String, time: Date},
options);
var Event = mongoose.model('BaseEvent', eventSchema);
var clickedLinkSchema = new mongoose.Schema({url: String}, options);
var ClickedLinkEvent = Event.discriminator('ChildEventBad',
clickedLinkSchema);
var event1 = new ClickedLinkEvent();
// Woops, clickedLinkSchema overwrote the custom _id
assert.ok(event1._id instanceof mongoose.Types.ObjectId);
// But if you set `_id` option to false...
clickedLinkSchema = new mongoose.Schema({url: String},
{discriminatorKey: 'kind', _id: false});
ClickedLinkEvent = Event.discriminator('ChildEventGood',
clickedLinkSchema);
// The custom _id from the base schema comes through
var event2 = new ClickedLinkEvent({_id: 'test'});
assert.ok(event2._id.toString() === event2._id);
```