猫鼬虚拟场一对多

pjl*_*b12 1 mongoose node.js

我有两个型号在我蒙戈的数据库,Child并且Parent,例如。我会将多个孩子分配给一个父母。它们基本上是这样定义的:

childSchema = new mongoose.Schema({
    // fields
});

parentSchema = new mongoose.Schema({
    // fields
    ones: [{
        type: mongoose.Schema.ObjectId,
        ref: 'Child'
    }]
});
Run Code Online (Sandbox Code Playgroud)

我让它工作到我可以将多个Childs保存到单个 s 的地方Parent,并且一切都保存了,并且在那里似乎一切都很好。您还可以将每个保存Child到多个Parents。

我要的是一个虚拟的领域上Childparents,并填充与Parent其所分配的S。这很复杂,因为每个都Child可以有多个Parents,并且每个都Parent可以Child分配多个s。

我走的路线是:

childSchema.virtual('parents', {
    ref: 'Parent',
    localField: '_id',
    foreignField: 'children'
});
Run Code Online (Sandbox Code Playgroud)

我之前在一家商店及其评论中做过这个,所以一个商店有多个评论,所以是多对一的关系,上面的方法奏效了。但是,有了这个,parents虚拟字段总是返回空值。

这是最好的方法吗?或者有没有更好的方法我应该研究?

编辑

我意识到我没有像我应该的那样手动填充虚拟字段,但是当我尝试这样做时,它只会让孩子的调用挂起并且永远不会从服务器返回。

我的猜测是,因为Parent模型具有该children属性,它会陷入无限循环,在那里它获取孩子并填充父母,填充孩子,填充父母......有没有办法限制这一点?

Nei*_*unn 5

实际上这工作得很好。您当然可以定义一个链接到孩子的多个父母作为虚拟。一个非常常见的例子是“家庭”,其中所有孩子都有多个父母。

我想你可能有这个,但忘记在定义“虚拟”的“子”模式上定义模式选项:

{
  toJSON: { virtuals: true },
  toObject: { virtuals: true }
}
Run Code Online (Sandbox Code Playgroud)

如果您没有包含这些,则在转换为 JSON 或 Object 时不会显示“虚拟”。

此外,始终设置调试mongoose.set('debug', true)。例如,如果您忘记为“虚拟”设置序列化选项,那么至少您会看到对数据库的单独调用正在执行以完成填充。

作为一个完整的例子:

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


mongoose.set('debug',true);
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/family');

const childSchema = new Schema({
  name: String
},{
  toJSON: { virtuals: true },
  toObject: { virtuals: true }
});

childSchema.virtual('parents', {
  ref: 'Parent',
  localField: '_id',
  foreignField: 'children'
});

const Child = mongoose.model('Child',childSchema);

const parentSchema = new Schema({
  name: String,
  title: String,
  children: [{ type: Schema.Types.ObjectId, ref: 'Child' }]
});

const Parent = mongoose.model('Parent',parentSchema);

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

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

    (callback) =>
      async.waterfall(
        [
          (callback) => Child.create(
            ['Bill','Ted'].map(name => ({ name })),callback),

          (children,callback) => Parent.create(
            [{ name: 'Jill', title: 'Mom'},{ name: 'Jack', title: 'Dad' }]
              .map( p => Object.assign(p, { children })),callback),

        ],
        callback
      ),

    (callback) =>
      Child.find()
        .populate({ path: 'parents', populate: { path: 'children' }})
        .exec((err,children) => {
        if (err) callback(err);
        log(children);
        callback()
      }),
  ],
  (err) => {
    if (err) throw err;
    mongoose.disconnect();
  }
);
Run Code Online (Sandbox Code Playgroud)

产生预期的输出:

[
  {
    "_id": "59575832c8af766505a55a24",
    "name": "Bill",
    "__v": 0,
    "parents": [
      {
        "_id": "59575832c8af766505a55a26",
        "name": "Jill",
        "title": "Mom",
        "__v": 0,
        "children": [
          {
            "_id": "59575832c8af766505a55a24",
            "name": "Bill",
            "__v": 0,
            "parents": null,
            "id": "59575832c8af766505a55a24"
          },
          {
            "_id": "59575832c8af766505a55a25",
            "name": "Ted",
            "__v": 0,
            "parents": null,
            "id": "59575832c8af766505a55a25"
          }
        ]
      },
      {
        "_id": "59575832c8af766505a55a27",
        "name": "Jack",
        "title": "Dad",
        "__v": 0,
        "children": [
          {
            "_id": "59575832c8af766505a55a24",
            "name": "Bill",
            "__v": 0,
            "parents": null,
            "id": "59575832c8af766505a55a24"
          },
          {
            "_id": "59575832c8af766505a55a25",
            "name": "Ted",
            "__v": 0,
            "parents": null,
            "id": "59575832c8af766505a55a25"
          }
        ]
      }
    ],
    "id": "59575832c8af766505a55a24"
  },
  {
    "_id": "59575832c8af766505a55a25",
    "name": "Ted",
    "__v": 0,
    "parents": [
      {
        "_id": "59575832c8af766505a55a26",
        "name": "Jill",
        "title": "Mom",
        "__v": 0,
        "children": [
          {
            "_id": "59575832c8af766505a55a24",
            "name": "Bill",
            "__v": 0,
            "parents": null,
            "id": "59575832c8af766505a55a24"
          },
          {
            "_id": "59575832c8af766505a55a25",
            "name": "Ted",
            "__v": 0,
            "parents": null,
            "id": "59575832c8af766505a55a25"
          }
        ]
      },
      {
        "_id": "59575832c8af766505a55a27",
        "name": "Jack",
        "title": "Dad",
        "__v": 0,
        "children": [
          {
            "_id": "59575832c8af766505a55a24",
            "name": "Bill",
            "__v": 0,
            "parents": null,
            "id": "59575832c8af766505a55a24"
          },
          {
            "_id": "59575832c8af766505a55a25",
            "name": "Ted",
            "__v": 0,
            "parents": null,
            "id": "59575832c8af766505a55a25"
          }
        ]
      }
    ],
    "id": "59575832c8af766505a55a25"
  }
]
Run Code Online (Sandbox Code Playgroud)

现在,我正在将其作为嵌套的群体Child来获取它的“虚拟” Parent,然后甚至填充每个Parent“孩子”。自从我们从孩子开始以来,这真的是常识带给我的,再次填充“虚拟”是没有意义的。

我将在此指出,如果您期待以下其中一项,那么您必须将这些视为“免责声明”,说明“虚拟”在人群中的预期用途:

  1. 如果您不想将“子项”作为父项中的真实字段包含在内。这根本是不可能的,因为他们需要至少在一方面成为参考。典型的关系是 Parent->Child,您避免在 Parent 中创建数组并使用虚拟。Parent 的另一种方式有“many”,所以你可以在那里存储“many”引用,或者在 child 上存储“many” Parent 引用。但是在“多”关系的情况下需要有一个列表。

  2. 如果您想在这里进行无限递归,那么这可能不是解决问题的方法。您可以通过不同的方式对“树结构”进行建模,这通常涉及在每个节点上保留一个“实体化”路径的数组,表示它在树上的路径。这本身就是一个完整的主题,超出了人口的范围。

但总的来说,这个概念按设计工作。所以只要你了解如何使用它,那么它肯定有它自己的价值。