MongoDB - 子文档外键的一对多关系

4

我正在使用mongodb v.2.6.11和mongoose。

我有两个具有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数组的结构。但是我不能使用populate,因为TA没有任何对子对象的引用(因为作为子文档保存的数量可能太多)。那么这怎样才能实现呢?我想错了吗?或者我应该怎么做才能像示例输出中一样输出指定的查询结果?

1
通过给定的条件选择您的TB文档并用TA填充。通过按TA分组重新格式化TB响应数组。您可以尝试使用$group请求来实现这一点,但使用JavaScript处理文档会更容易,请求速度也会更快。 - vmkcom
1个回答

2

为了实现这个目标,您可以尝试使用聚合框架和$match以及$group管道步骤。使用从管道操作返回的结果来填充TA字段,但需要更改TA模型的架构设计。这是为了填充聚合结果而必须添加一个TB模式的引用数组到您的TA模式中:

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(例如名称)中的数据填充到聚合对象中? - Victor Axelsson
@VictorAxelsson 还没有测试过,但你可以使用TBModel上的populate方法从TA中填充数据,就像答案中所示。更多信息请参阅文档 - chridam
我不太明白路径的作用,也不知道如何将TA的名称包含在“outer”对象中(该对象具有TB数组)。 - Victor Axelsson
populate 方法中的 path 选项指的是要填充的路径,即填充后的路径不再设置为其原始 _id,而是通过在返回结果之前在底层执行单独的查询来替换其值为从数据库返回的 mongoose 文档。如果您只想返回填充文档的 TA 名称,则可以通过将字段名称语法作为 populate 方法的第二个参数中的 select 属性值传递来实现此目的。 - chridam
我不知道为什么,但 populate 函数似乎对我没有任何作用。无论我在路径值中输入什么,都没有任何反应,选择也没有任何作用。使用 populate 在 TA 或 TB 上也没有区别。 - Victor Axelsson
一个模式的更改将使这成为可能,因为populate方法需要mongoose文档,我已经更新了我的答案以反映这一点。 - chridam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接