【问题标题】:Populating Mongoose objects from id to new field将 Mongoose 对象从 id 填充到新字段
【发布时间】:2019-06-23 01:12:29
【问题描述】:

我正在使用 mongoose 将 id 字段及其各自的文档填充到一个新字段中。我的问题是假设我的购物车模型是 -

let CartSchema = new mongoose.Schema({
    userId: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    },
    productIds: [
        {
            type: mongoose.Schema.Types.ObjectId,
            ref: 'Product'
        }
    ]
});

我想填充产品,所以我使用了

Cart.find({}).populate("products").exec(function (err, cart) {
    console.log(cart)
}

但这会将文档填充到相同的字段名称 productIds 中,我想将这些字段填充到一个名为“products”的新字段名称中,所以我尝试了这个

let CartSchema = new mongoose.Schema({
        userId: {
            type: mongoose.Schema.Types.ObjectId,
            ref: 'User'
        },
        productIds: [
            {
                type: String
            }
        ]
    }, { toJSON: { virtuals: true } });

CartSchema.virtual('products', {
    ref: 'Product',
    localField: 'productIds',
    foreignField: '_id',
});

Cart.find({}).populate("products").exec(function (err, cart) {
    console.log(cart)
}

但返回了名为 products.so 的空数组,那么我如何将 productIds 数组填充到具有各自文档数组的新字段名称 products 中。

谢谢。

【问题讨论】:

    标签: node.js express mongoose mongoose-populate


    【解决方案1】:

    有一种方法可以做到这一点 - 它称为 Virtuals(请参阅 docs)。 这个想法是创建一个“虚拟属性”,它实际上并没有保存到数据库中,而是作为一个计算属性。根据相关github问题上的示例provided by qinshenxue

    // declare your ID field as a regular string
    var countrySchema = new mongoose.Schema({
        capitalId: {type:String}
    });
    
    // create a virtual field which links between the field you've just declared 
    // and the related collection. 
    // localField is the name of the connecting field, 
    // foreign field is a corresponding field in the connected collection
    // justOne says that it'll populate a single connected object, 
    // set it to false if you need to get an array
    countrySchema.virtual('capital',{
        ref: 'City',
        localField: 'capitalId',
        foreignField: '_id',
        justOne: true
    });
    
    // tell Mongoose to retreive the virtual fields
    countrySchema.set('toObject', { virtuals: true });
    countrySchema.set('toJSON', { virtuals: true });
    
    // now you can populate your virtual field like it actually exists
    // the following will return a Country object in the 'capital' field
    Country.find().populate('capital') 
    

    【讨论】:

    • 我想这可以解决问题,但是它会破坏 Mongoose 模型。如果 OP 完全使用了松散类型的技术堆栈(例如 NodeJS),那么这永远不会成为问题。如果你有一个强类型的前端,那么这里正确的解决方案是在强类型的前端找到一个解决方案,而不是在松散类型的后端上混改 Mongoose 模型。
    • @skyzaDev 请定义“混蛋”。在我看来,无论你是否使用类型化,拥有一个具有意外属性的对象(你永远不知道它包含什么)是一个极易出错的解决方案,永远不应该使用。
    • Mongoose populate 会被有意调用,而不是随机调用。例如。如果您有一个 API 端点,那么该端点将始终返回填充或未填充的数据库数据,而不是随机返回。然后对前端进行编码。
    • @skyzaDev 所以如果我有 3 个可填充的属性,我应该有 9 种类型的对象?对于已填充/未填充属性的每种组合?这确实确​​实看起来像私生子。
    【解决方案2】:

    方法是正确的,您应该会在产品字段中看到您的数据。确保您拥有正确的数据和模型 n

    【讨论】:

      【解决方案3】:

      从技术上讲,做你想做的事违反了使用 Mongoose 的惯例。您只需将“productIds”字段重命名为“products”即可让事情变得简单:

      如果您考虑一下,产品数组可以是产品 id 值的数组,也可以是实际文档。属性名称“products”正确适用于所有场景,“productIds”则不适用。

      考虑到填充的文档在每个文档上也都有“_id”属性,没有必要仅仅为 id 值使用新的虚拟属性来膨胀 JSON - 你已经有了它们!

      您在期望文档时获得 id 或在期望 id 时获得文档的可能性极小,因为您始终知道选择填充属性的时间以及何时不填充。示例:如果您正在与 API 端点通信,那么该 API 端点将始终返回填充或未填充的产品,而不是随机返回。然后你的前端会被编码!

      【讨论】:

      • 是的,你是对的,但是如果我想将产品添加到我的购物车中,我会将产品的 ID 放入 ID 中,当我拿到购物车时,我希望将 ID 填充为产品和在 ios 中编程我的模型时,如果我将一个字段用于两者,我将需要创建 2 个不同的模型来发送和接收购物车信息
      • @NatnaelGetachew 我假设您完全拥有一个松散类型的技术堆栈(例如 NodeJS)。如果你不这样做,那么你就不能总是在强类型前端完全重用松散类型的模型。我建议你在强类型的前端找到解决方案,而不是在松散类型的后端上混搭你的 Mongoose 模型。
      猜你喜欢
      • 2023-04-01
      • 2012-01-22
      • 2017-05-21
      • 1970-01-01
      • 2018-06-11
      • 2021-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多