【问题标题】:How to use different types of References and populate如何使用不同类型的引用和填充
【发布时间】:2017-12-10 09:26:49
【问题描述】:

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

对于文章,

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

但是,它一直给我一个投射错误,因为当卡片是 Event 类型时,它可以很好地填充,但是当它是 'Article' 类型时,featured_image 字段是字符串类型,因此无法填充。

只有当卡片是 Event 类型或者它是参考 id 而不是字符串时,我才如何填充 features_image 字段。

【问题讨论】:

  • 您认为提供的答案中是否有某些内容无法解决您的问题?如果是这样,请对答案发表评论,以澄清究竟需要解决哪些尚未解决的问题。如果它确实回答了您提出的问题,请注意Accept your Answers您提出的问题
  • “凹凸”。还是没有反应?

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


【解决方案1】:

您应该使用“鉴别器”而不是您尝试做的事情,这实际上是处理对象类型在给定引用中不同的关系的正确方法。

您通过定义模型的不同方式使用鉴别器,而是从“基本模型”和架构构造,如下所示:

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() 之外,这还具有附加到不同类型项目的“完整模式”的优势。因此,如果您愿意,您也有不同的验证和其他方法。它确实是在数据库上下文中起作用的“多态性”,附加了有用的模式对象。

因此,我们可以演示已完成的各种“连接”,以及您现在可以使用 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"
  }
]

【讨论】:

    猜你喜欢
    • 2020-05-20
    • 1970-01-01
    • 1970-01-01
    • 2011-05-19
    • 2019-06-02
    • 1970-01-01
    • 2021-10-24
    • 1970-01-01
    • 2017-03-27
    相关资源
    最近更新 更多