【问题标题】:MongoDB - OneToMany with foreign key in childMongoDB - OneToMany 与子外键
【发布时间】:2016-02-25 07:45:48
【问题描述】:

我正在使用 mongodb v.2.6.11 和猫鼬。

我有两个具有 OneToMany 关系的模型。在我的父母 TypeA(简称 TA)中,我没有提到孩子。在我的孩子 TypeB(缩写为 TB)中,我有一个对父母的 id 引用。

示例架构:

var TASchema = {
    name: {type: String, required: "TASchema.name is required", index: {unique: true, dropDups: true}}
}; 

var TBSchema  = {
    start: {type: Number, required: "TBSchema.language is required"},
    stop: {type: Number, required: "TBSchema.stop is required"},
    TA: {type: Schema.Types.ObjectId, ref: 'tbschema'},
}

我想要做的:选择在两个变量“ts_start”和“ts_stop”(它们是时间戳)的时间跨度内具有“start”变量的所有 TB。比如:start : {$gte: ts_min, $lte : ts_max}

示例输出:

[
    {
       name: "Ta object 1",
       tbs: [{start : 1, stop2}, {start: 2, stop: 3}] 
    },
    {
       name: "Ta object 2",
       tbs: [{start : 1, stop2}, {start: 2, stop: 3}] 
    }
]

我想保留查询返回 TA 数组的结构,其中每个 TA 都包含一个 TB:s 数组。但我不能使用populate,因为 TA 没有对孩子的任何引用(因为可能有太多无法将它们保留为子文档)。

那么这是如何实现的呢?我是不是想错了,或者我应该怎么做才能像我的示例输出那样输出指定的查询?

【问题讨论】:

  • 通过填充 TA 的给定标准选择您的 TB 文档。通过按 TA 分组重新格式化 TB 的响应数组。您可以尝试使用 $group 请求来实现此目的,但使用 javascript 处理文档更容易,请求会更快

标签: node.js mongodb mongoose reverse populate


【解决方案1】:

要搭载@vmkcom,请尝试使用聚合框架通过 $match$group 管道步骤来实现此目的,使用从管道操作返回的结果然后填充 TA 字段,但更改 TA 模型的架构设计。这对于从聚合中填充结果是必要的,因此在您的 TA 架构中添加一个引用数组到 TB 架构:

var TASchema = {
    name: { 
        type: String, 
        required: "TASchema.name is required", 
        index: {unique: true, dropDups: true}
    },
    tbs: [{ type: Schema.Types.ObjectId, ref: 'tbschema' }]
};

实现如下(未经测试):

var pipeline = [
    { 
        "$match": { 
            "start": { "$gte": ts_min, "$lte": ts_max }
        }
    },
    { 
        "$group": {
            "_id": "$TA",
            "tbs": {
                "$push": {  "start": "$start", "stop": "$stop" }
            }           
        }
    }
];
TBModel.aggregate(pipeline,
    function(err, results) {
        if (err) throw err; 
        var results = result.map(function(doc) { return new TAModel(doc) });        
        TBModel.populate(results, {"path": "TA"}, function(err, docs) {
            if (err) throw err;
            console.log(JSON.stringify(docs, undefined, 4));
        });
    }
);

【讨论】:

  • 这几乎是完美的。但是如何将 TBA 中的数据(例如名称)填充到聚合对象中?
  • @VictorAxelsson 尚未对其进行测试,但您可以使用 TBModel 上的填充方法从 TA 填充数据,如答案所示。更多关于documentation
  • 我不太明白路径的作用以及我如何将 TA 的名称包含在对象的“外部”对象中(具有 TB:s 数组)
  • 填充方法中的路径选项是指要填充的路径,即填充的路径不再设置为它们原来的_id,它们的值被替换为从数据库返回的猫鼬文档通过在返回结果之前在后台执行单独的查询。如果您只想为填充的文档返回 TA 的名称,您可以通过将字段名称语法作为第二个参数中的 select 属性值传递给填充方法来完成此操作。
  • 我不知道为什么,但填充功能似乎没有为我做任何事情。无论我在路径值中放置什么,都不会发生任何事情,并且选择也不做任何事情。如果我在 TA 或 TB 上使用 populate 也没有区别。
猜你喜欢
  • 2012-03-20
  • 1970-01-01
  • 2019-09-23
  • 2022-07-29
  • 1970-01-01
  • 2017-04-20
  • 2020-08-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多