node.js - Mongoose 虚拟场一对多

标签 node.js mongoose

例如,我的 Mongo 数据库中有两个模型,ChildParent。我会将多个 child 分配给单亲家长。它们本质上是这样定义的:

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'
});

我之前曾对一家商店及其评论做过此操作,因此一家商店有多个评论,因此采用多对一关系,并且上述方法有效。然而,这样一来,parents 虚拟字段总是返回 null。

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

编辑

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

我的猜测是,因为 Parent 模型具有 children 属性,它会陷入无限循环,在该循环中它获取子级并填充父级,然后填充父级 child ,填充 parent ......有什么方法可以限制这个吗?

最佳答案

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

我认为您可能有这个,但忘记在定义“虚拟”的“子”架构上定义架构选项:

{
  toJSON: { virtuals: true },
  toObject: { virtuals: true }
}

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

此外,始终设置调试 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();
  }
);

这会产生预期的输出:

[
  {
    "_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 的“ child ”。自从我们从 child 开始以来,这确实是我的常识,再次填充“虚拟”是没有意义的。

我将在此指出,如果您期望出现以下情况之一,那么您必须将这些视为关于“虚拟”在人口中的预期用途的“免责声明”:

  1. 如果您不想将“子项”作为真实字段包含在父项中。这根本不可能,因为它们至少需要在一侧成为引用。典型的关系是父级->子级,您可以避免在父级中创建数组并使用虚拟数组。另一方面,父级有“许多”,因此您可以在那里存储“许多”引用,或者存储对子级的“许多”父级引用。但在“多”关系的情况下,某处需要有一个列表。

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

但总的来说,这个概念按照设计发挥作用。因此,只要您了解如何使用它,那么它肯定有其自身的值(value)。

关于node.js - Mongoose 虚拟场一对多,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44857850/

相关文章:

node.js - Travis 构建未正确完成

node.js - Express.js CRUD 应用程序无法删除

node.js - mongoose 不使用 findById 返回输出

node.js - 如何编辑 Mongoose 中的嵌套对象

node.js - 我想在使用 node.js 完成所有 promise 后将数据插入 mongodb

mongodb - 用于 Lambda 的 NestJS 外部的 Mongoose 连接

javascript - 如何比较数组中的连续日期/时间项并根据特定时间进行过滤

node.js - 使用busboy(nodejs)上传多个文件并发出相关的响应对象

javascript - 如何使用给定 ID 进行快速重定向?

javascript - 带有 Node 、ES7、puppeteer 的页面对象模式