# 鉴频器（discriminators）

> 原文：[The model.discriminator() function](http://mongoosejs.com/docs/discriminators.html)\
> 翻译：小虾米（QQ:509129）

## The `model.discriminator()` function

鉴别器是一个模式继承机制。他们使你重叠模式上同一标的MongoDB集合有多个模型。

假设你想在一个集合中跟踪不同类型的事件。每一件事件都会有一个时间戳，但事件表示点击链接应该有一个URL。你可以使用`model.discriminator()`函数。这个函数需要2个参数，一个模型的名字和一个鉴频器模式。它返回一个模型是基础模式的结合和鉴频器模式。

```javascript
 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`实例。

```javascript
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`字符串路径到你的模式中，它采用追踪鉴别本文档实例。

```javascript
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(),`等等，有足够的智慧来解释鉴别器。

```javascript
 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');
      });
    });
```

### 鉴别器复制的前置和后置钩子

作者也使用他们的基础模式的前置和后置的中间件。然而，你也可以把中间件来鉴别模式不影响基础模式。

```javascript
 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 在鉴别器的模式如下图所示。

```javascript
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);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mongoose.shujuwajue.com/guide/discriminators.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
