Nodejs mongoose怎么populate别的集合的子文档?

Nodejs mongoose怎么populate别的集合的子文档?

ASchema = new mongoose.Schema({
name: String,
B: [BSchema]
});

var BSchema = new Schema({ name: String });

var CSchema = new Schema({ name: String, B: { type: Schema.ObjectId, ref: ‘BSchema’ } });

//This is OK, but not one time mongoose search: CSchema.find({ name: ‘C_xxx’ }).exec(function(err, docC) {

docC.forEach(function(o) { var BId = o.B; A.findOne({ ‘B._id’: BId }, { ‘B.$’: 1 } ).exec(function(err, docA) { var Bname = docA.B[0].name; var Cname = docA.name; });

}); });

//wrong in populate: CSchema.find({ name: ‘C_xxx’ }) .populate(‘ASchema.B’) .exec(function(err, docC) { docC.forEach(function(o) { var Bname = o.B.name; //ERROR:o.B is a objectId,o.B.name is undefined //Aname can’t find }); });

I can’t find it with populate, o.B is a objectId,o.B.name is undefined.

BSchema is a subdocument of ASchema, CSchema has a ref connection of BSchema.

Data like this:

ASchema = {
  name: "A_xxx",
  B: [{
      _id: 1,
      name: "B_xxx"
    }, {
      _id: 2,
      name: "B_xxx"
    }]
}
CSchema = [{
    name: "C_xxx",
    B: 1
  }, {
    name: "C_xxx",
    B: 2
  }]

I want to find all CSchema which CSchema’s name is “c_xxx”, and shows CSchema’s B’s name and its A’s name.

Like this result: {CName:“c_xxx”,BName:“b_xxx”,AName:“a_xxx”}


4 回复

为了实现你的需求,即通过 populate 方法来查询 CSchema 并获取关联的 B 文档以及其父文档 A 的信息,你需要正确地设置引用关系,并使用嵌套的 populate 操作。以下是详细的步骤和示例代码。

数据结构定义

首先,确保你的数据模型定义正确:

const mongoose = require('mongoose');

const BSchema = new mongoose.Schema({
  name: String
});

const ASchema = new mongoose.Schema({
  name: String,
  B: [BSchema]
});

const CSchema = new mongoose.Schema({
  name: String,
  B: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'B'  // 注意这里使用的是 'B' 而不是 'BSchema'
  }
});

const A = mongoose.model('A', ASchema);
const B = mongoose.model('B', BSchema);
const C = mongoose.model('C', CSchema);

module.exports = { A, B, C };

查询并填充数据

接下来,你需要使用 populate 方法来填充关联的数据。由于 CSchema 中的 B 字段是一个引用到 B 集合的 ObjectId,我们需要先填充 B 字段,然后再填充 A 字段。可以使用嵌套的 populate 操作来实现这一点:

const { A, B, C } = require('./models');  // 假设你已经设置了正确的路径

CSchema.find({ name: 'C_xxx' })
  .populate({
    path: 'B',
    model: 'B'  // 使用模型名称而不是模式名称
  })
  .then(docC => {
    return Promise.all(docC.map(cDoc => {
      return cDoc.populate({
        path: 'B.A',  // 假设你有一个字段指向A的子文档
        model: 'A'
      });
    }));
  })
  .then(finalDocs => {
    finalDocs.forEach(doc => {
      console.log({
        CName: doc.name,
        BName: doc.B.name,
        AName: doc.B.A ? doc.B.A.name : 'Not found'  // 确保处理可能的未找到情况
      });
    });
  })
  .catch(err => {
    console.error('Error:', err);
  });

注意事项

  1. 模型名称:在 populate 方法中,你应该使用模型名称(例如 'B')而不是模式名称(例如 'BSchema')。
  2. 嵌套引用:如果 B 文档中有指向 A 的引用,确保你在 populate 方法中正确地指定路径。
  3. 错误处理:确保处理所有可能的错误,特别是当某些引用不存在时。

通过这种方式,你可以正确地查询 CSchema 并填充相关的 BA 文档。


CSchema.find().populate(‘B’).exec();

CSchema.find().populate(‘B’, ‘name’).exec();

我很好奇,既然都能用英文提问,为何不用英文搜索?或者看文档?

B没有单独保存到一个集合里, 是A的子文档, 所以你那样是找不到的 我本来是写成CSchema.find().populate(‘A.B’),但是也不行

要实现通过 populate 方法来查询 CSchema 并获取关联的 BSchemaASchema 的信息,需要进行一些结构调整。根据你的描述,BSchemaASchema 的子文档,而 CSchema 中有一个引用到 BSchema

首先,你需要调整 CSchema 中对 B 字段的定义。假设 BSchemaASchema 的结构如你所描述,我们需要确保 CSchema 中的 B 字段是一个引用到 BSchema_id

调整后的模型定义

const mongoose = require('mongoose');

const BSchema = new mongoose.Schema({
  name: String
});

const ASchema = new mongoose.Schema({
  name: String,
  B: [BSchema]
});

const CSchema = new mongoose.Schema({
  name: String,
  B: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'BSchema'
  }
});

const A = mongoose.model('A', ASchema);
const B = mongoose.model('B', BSchema);
const C = mongoose.model('C', CSchema);

module.exports = { A, B, C };

查询并填充数据

使用 populate 方法来查询 CSchema 并填充 B 引用的数据:

const { C } = require('./models'); // 导入模型

C.find({ name: 'C_xxx' })
  .populate({
    path: 'B',
    populate: {
      path: 'B',
      model: 'A'
    }
  })
  .exec((err, docs) => {
    if (err) {
      console.error(err);
      return;
    }

    docs.forEach(doc => {
      console.log({
        CName: doc.name,
        BName: doc.B.name,
        AName: doc.B.B[0].name // 注意这里的路径
      });
    });
  });

解释

  • CSchema 中的 B 字段现在是一个 ObjectId,引用到 BSchema
  • 使用 .populate 方法填充 B 引用的数据。
  • 使用嵌套的 populate 填充 B 文档中的 B 字段(即 A 的数据)。

这样,你就可以通过 populate 方法来获取 CSchema 中的所有相关信息,并输出期望的结果。

回到顶部