如何使用不同类型的引用和填充

时间:2017-07-06 11:36:02

标签: node.js mongodb mongoose mongodb-query mongoose-populate

所以我有两个模式,文章和事件 两者都有一个图像区域。

对于文章,

featured_image: {
    type: String,
    default: '',
}

对于活动,

featured_image: {
    type: Schema.ObjectId,
    ref: 'Medium'
}

我有另一个架构,卡,像这样

 type: {
    type: String,
    enum: ['Article', 'Event']
 },
 data: {
    type: Schema.ObjectId,
    refPath: 'type'    
 }

我正在尝试填充卡片,就像这样

Card
    .find(query)
    .populate({
            path: 'data',
            populate: [{
                path: 'featured_image',
                model: 'Medium',
                select: 'source type'
            }]
    };)

然而,它一直给我一个强制转换错误,因为当card是Event类型时,它会填充正常,但是当它是'Article'类型时,featured_image字段是字符串类型,因此无法填充。

如果card类型为Event或者它是引用ID而不是字符串,我如何填充featured_image字段。

1 个答案:

答案 0 :(得分:0)

您应该使用" discriminators"而不是您尝试做的事情,这实际上是处理对象类型在给定引用中变化的关系的正确方法。

您可以通过定义模型的不同方式使用鉴别器,而不是根据"基础模型构建#34;和架构如:

const contentSchema = new Schema({
  name: String
});

const articleSchema = new Schema({
  image: String,
});

const eventSchema = new Schema({
  image: { type: Schema.Types.ObjectId, ref: 'Medium' }
});

const cardSchema = new Schema({
  name: String,
  data: { type: Schema.Types.ObjectId, ref: 'Content' }
});

const Medium = mongoose.model('Medium', mediumSchema);
const Card = mongoose.model('Card', cardSchema )

const Content = mongoose.model('Content', contentSchema);
const Article = Content.discriminator('Article', articleSchema);
const Event = Content.discriminator('Event', eventSchema);

所以你要定义一个"基础模型"例如Content,您实际上将引用指向Event

下一部分是不同的模式实际上是通过基本模型中的.discriminator()方法注册到此模型的,而不是.model()方法。这会使模式与常规Content模型一起注册,当您引用使用.discriminator()定义的任何模型实例时,该数据中隐含存在特殊__t字段,注册的型号名称。

除了在不同类型上启用mongoose到.populate()之外,这还有一个优点:"完整架构"附在不同类型的物品上。因此,如果您愿意,您可以使用不同的验证和其他方法。确实是多态性"在数据库上下文中工作,附加有用的模式对象。

因此,我们可以展示各种各样的"加入&#34>完成后,您现在可以使用ArticleEvent的单个模型,这些模型仅处理所有查询和操作中的项目。并且您不仅可以单独使用"而且由于其机制实际上将数据存储在同一个集合中,因此还有一个Content模型可以访问这两种类型。这实际上主要关系在Event模式的定义中是如何工作的。

完整列表

const async = require('async'),
      mongoose = require('mongoose'),
      Schema = mongoose.Schema;

mongoose.set('debug',true);
mongoose.Promise = global.Promise;

mongoose.connect('mongodb://localhost/cards');

const mediumSchema = new Schema({
  title: String
});

const contentSchema = new Schema({
  name: String
});

const articleSchema = new Schema({
  image: String,
});

const eventSchema = new Schema({
  image: { type: Schema.Types.ObjectId, ref: 'Medium' }
});

const cardSchema = new Schema({
  name: String,
  data: { type: Schema.Types.ObjectId, ref: 'Content' }
});

const Medium = mongoose.model('Medium', mediumSchema);
const Card = mongoose.model('Card', cardSchema )

const Content = mongoose.model('Content', contentSchema);
const Article = Content.discriminator('Article', articleSchema);
const Event = Content.discriminator('Event', eventSchema);

function log(data) {
  console.log(JSON.stringify(data, undefined, 2))
}

async.series(
  [
    // Clean data
    (callback) =>
      async.each(mongoose.models,(model,callback) =>
        model.remove({},callback),callback),

    // Insert some data
    (callback) =>
      async.waterfall(
        [
          (callback) =>
            Medium.create({ title: 'An Image' },callback),

          (medium,callback) =>
            Content.create(
              [
                { name: "An Event", image: medium, __t: 'Event' },
                { name: "An Article", image: "A String", __t: 'Article' }
              ],
              callback
            ),

          (content,callback) =>
            Card.create(
              [
                { name: 'Card 1', data: content[0] },
                { name: 'Card 2', data: content[1] }
              ],
              callback
            )
        ],
        callback
      ),

    // Query and populate
    (callback) =>
      Card.find()
        .populate({
          path: 'data',
          populate: [{
            path: 'image'
          }]
        })
        .exec((err,cards) => {
        if (err) callback(err);
        log(cards);
        callback();
      }),

    // Query on the model for the discriminator
    (callback) =>
      Article.findOne({},(err,article) => {
        if (err) callback(err);
        log(article);
        callback();
      }),

    // Query on the general Content model
    (callback) =>
      Content.find({},(err,contents) => {
        if (err) callback(err);
        log(contents);
        callback();
      }),


  ],
  (err) => {
    if (err) throw err;
    mongoose.disconnect();
  }
);

以及不同查询的示例输出

Mongoose: cards.find({}, { fields: {} })
Mongoose: contents.find({ _id: { '$in': [ ObjectId("595ef117175f6850dcf657d7"), ObjectId("595ef117175f6850dcf657d6") ] } }, { fields: {} })
Mongoose: media.find({ _id: { '$in': [ ObjectId("595ef117175f6850dcf657d5") ] } }, { fields: {} })
[
  {
    "_id": "595ef117175f6850dcf657d9",
    "name": "Card 2",
    "data": {
      "_id": "595ef117175f6850dcf657d7",
      "name": "An Article",
      "image": "A String",
      "__v": 0,
      "__t": "Article"
    },
    "__v": 0
  },
  {
    "_id": "595ef117175f6850dcf657d8",
    "name": "Card 1",
    "data": {
      "_id": "595ef117175f6850dcf657d6",
      "name": "An Event",
      "image": {
        "_id": "595ef117175f6850dcf657d5",
        "title": "An Image",
        "__v": 0
      },
      "__v": 0,
      "__t": "Event"
    },
    "__v": 0
  }
]
Mongoose: contents.findOne({ __t: 'Article' }, { fields: {} })
{
  "_id": "595ef117175f6850dcf657d7",
  "name": "An Article",
  "image": "A String",
  "__v": 0,
  "__t": "Article"
}
Mongoose: contents.find({}, { fields: {} })
[
  {
    "_id": "595ef117175f6850dcf657d6",
    "name": "An Event",
    "image": "595ef117175f6850dcf657d5",
    "__v": 0,
    "__t": "Event"
  },
  {
    "_id": "595ef117175f6850dcf657d7",
    "name": "An Article",
    "image": "A String",
    "__v": 0,
    "__t": "Article"
  }
]