【问题标题】:How to reference and populate nested schemas?如何引用和填充嵌套模式?
【发布时间】:2018-09-11 16:01:36
【问题描述】:

TL;DR 您如何引用(并因此填充)同一集合中的子文档?

我一直在尝试在我的 Mongoose 架构中填充对子文档的引用。我有一个包含位置和联系人数组的主架构(MainSchema)。这些位置引用了这些联系人。

在我的位置数组中,我通过联系人的 _id 引用这些联系人。见下文。

import mongoose from 'mongoose';

const LocationSchema = new mongoose.Schema({
  city: {type: String},
  contact: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Contact' //alternative tried: refPath: 'contacts'
  } 
});
const Location = mongoose.model('Location', LocationSchema);

const ContactSchema = new mongoose.Schema({
  firstName: {type: String},
  lastName: {type: String}
});
const Contact = mongoose.model('Contact', ContactSchema );

const MainSchema = new mongoose.Schema({
  name: {type: String},
  locations: [LocationSchema],
  contacts: [ContactSchema]    
});

export.default mongoose.model('Main', 'MainSchema');

现在,当我想填充位置的联系人时,我得到 null 或者只是返回的普通 _id 字符串。下面是我的填充代码。我已经尝试了所有我能找到的组合,包括让嵌套文档成为自己的模型并尝试不同的方式来引用它们。

MainSchema.statics = {
  get(slug) {
    return this.findOne({name})
      .populate('locations.contact')
      .exec()
      .then((company) => {
        if (company) {
          return company;
        }
        const err = 'generic error message';
        return Promise.reject(err);
      });
    }
  };

我也尝试了新的方法,但无济于事:

populate({
  path: 'locations',
  populate: {
    path: 'contacts',
    model: 'Contact'
  }
});

我一定在这里遗漏了什么。但是什么?

编辑了问题以按要求显示完整的查询语句

【问题讨论】:

  • 你能把整个查询写下来吗?不只是最后一个 sn-p 中的populate?所以我可以更好地理解你想要达到的目标
  • .populate({path : 'locations', populate : {path: 'contacts'}}) 应该可以工作,同样在你的第一个 sn-p 示例中,我没有看到其他两个模型的 export 函数,你有定义吗?
  • 您的数组 locations: [LocationSchema]contacts: [ContactSchema] 是否包含引用 _id ?从其他模式
  • @Sabbin 感谢 cmets。我已经添加了模型语句。我的位置数组包含对contact._id 的引用。当我尝试您的建议时,查询按原样返回此引用,因此尚未填充。
  • 在您的MainSchema 中有locations: [LocationSchema],是否有来自LocationSchema 的参考ID?在数据库中,您应该有一个带有MongoDB objectId 的数组

标签: javascript node.js mongodb mongoose


【解决方案1】:

在搜索了更多内容后,我发现在 Mongoose github issue tracker 上发布了一个完全相同的案例。

根据 Mongoose 的主要维护者的说法,这种填充形式是不可能的:

如果您要嵌入子文档,您将无法运行 populate() 在数组上,因为子文档存储在文档中 本身而不是在单独的集合中。

【讨论】:

    【解决方案2】:

    因此,以您的架构示例为例,我将执行以下操作。请注意,我并不是说我的方法是最好的方法,但我的情况与您完全相似。

    猫鼬模型

    import mongoose from 'mongoose';
    
    const LocationSchema = new mongoose.Schema({
        city: {type: String},
        contact: { type: Schema.Types.ObjectId, ref: 'Contact'}
    });
    
    const ContactSchema = new mongoose.Schema({
        firstName: {type: String},
        lastName: {type: String}
    });
    
    
    const MainSchema = new mongoose.Schema({
        name: {type: String},
        locations: [{ type: Schema.Types.ObjectId, ref: 'Location' }],
    });
    
    const Main = mongoose.model('Main', MainSchema);
    const Location = mongoose.model('Location', LocationSchema);
    const Contact = mongoose.model('Contact', ContactSchema );
    

    注意:在您的主要架构中,我已经删除了联系人,因为我从您的示例中了解到每个位置都有自己的联系人,因此实际上在 MainSchema 中您不需要 ContactSchema

    插入数据的控制器

    这里的想法是您必须将每个文档的引用 _id 传递给另一个文档,下面的示例是模型,请对其进行调整以适合您的 app。我使用了一个data 对象,我假设它有一个位置和一个联系人

    async function addData(data) {
    
        //First you create your Main document
        let main = await new Main({
            name: data.name
        }).save();
    
        //Create the contact document in order to have it's _id to pass into the location document
        let contact = await new Contact({
            firstName: data.fistName,
            lastName: data.lastName
        });
    
        //Create the location document with the reference _id from the contact document
        let location = await new Location({
            city: data.city,
            contact: contact._id
        }).save();
    
        //Push the location document in you Main document location array
        main.locations.push(location);
        main.save();
    
        //return what ever you need
        return main;
    }
    

    查询

    let mainObj = await Main.findOne({name})
        .populate({path: 'locations', populate: {path: 'contact'}})
        .exec();
    

    这种方法对我有用,希望它也对你有用

    【讨论】:

    • 感谢扩展示例。这里的问题是它要求联系人住在他们自己的收藏中。我的目标是在主集合中同时存在联系人和位置。这减少了我前端的代码量,并且应该使事情更加包含和易于管理。如果我能从同一个集合中的另一个数组项中引用一个数组项就好了。
    • 所以对我来说更好地理解......联系人在位置上是否可靠?我的意思是您还需要在没有位置的情况下拥有Main 集合中的所有联系人?
    • 在我的示例中,您有Main -> Location -> Contacts,您还需要在MainLocation 中拥有联系人?
    • 我想从我的主集合中的位置引用我的主集合中的联系人,这样我就不必复制任何数据,同时将它们全部保存在主集合中。我会有更多相互引用的项目,这似乎是最节省代码和无开销的解决方案
    • 据我理解上面的评论,上面的例子就是这样做的。您已从Location 集合中引用了您的Main 集合中的联系人
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-04
    • 2019-02-24
    • 2017-03-01
    • 1970-01-01
    • 2019-12-27
    • 2015-06-04
    • 2016-06-07
    相关资源
    最近更新 更多