猫鼬虚拟场一对多

时间:2017-07-01 05:58:56

标签: node.js mongoose

我的Mongo数据库中有两个模型,例如<link href='<?php base_url("foo bar") ?>'>Child。我会将多个孩子分配给单亲。它们基本上是这样定义的:

Parent

我将它用于可以将多个childSchema = new mongoose.Schema({ // fields }); parentSchema = new mongoose.Schema({ // fields ones: [{ type: mongoose.Schema.ObjectId, ref: 'Child' }] }); 保存到单个Child的位置,并且所有内容都可以保存,而且一切都很好。您还可以将每个Parent保存到多个Child

我想要的是Parent上的Child虚拟字段,并填充了分配给它的parents个字段。这很复杂,因为每个Parent可以有多个Child,每个Parent可以分配多个Parent

我采取的路线是:

Child

我之前用商店及其评论做过这个,所以一个商店有多个评论,所以改为多对一的关系,以及上面的工作。但是,使用此功能,childSchema.virtual('parents', { ref: 'Parent', localField: '_id', foreignField: 'children' }); 虚拟字段始终返回null。

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

修改

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

我的猜测是因为parents模型具有Parent属性,它会被一个无限循环捕获,在那里它获取子进程并填充父进程,填充子进程填充子进程父母......有没有办法限制这个?

2 个答案:

答案 0 :(得分:3)

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

我认为你可能有这个但是忘了在定义“虚拟”的“子”模式中定义模式选项:

mongoose.set('debug', true)

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

此外,始终设置调试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(); } ); 。例如,如果您忘记为“虚拟”设置序列化选项,那么至少您会看到正在执行对数据库的单独调用以实现填充。

作为一个完整的例子:

[
  {
    "_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"
  }
]

产生预期的输出:

Child

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

我将在此注意到,如果您期望以下其中一项,那么您必须将这些视为人口中“虚拟”的预期用法的“免责声明”:

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

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

  3. 但总的来说,这个概念按设计运作。因此,只要您了解如何使用它,那么它肯定具有自己的价值。

答案 1 :(得分:0)

Neil Lunn的答案深入解释了这些关系,你也应该在寻找答案时阅读。就我而言,我遇到的问题是:

我发现,让孩子的电话永远不会从服务器回来的原因是我已经设置了所有findfindOne来自动填充父和子模型。因此,当访问Child时,它会填充父母,这些父母填充了孩子,这些父母填充了父母......等等。它只是陷入无限循环的人口。我取出了自动填充,只在需要时调用它,一切都按预期工作。

再次,看看这里的其他一些答案肯定,但这就是为什么我遇到问题,而其他人可能遇到同样的问题。