【问题标题】:Mongodb: Aggregation/Pipilines to join two schema's data based on nested documentsMongodb:聚合/管道基于嵌套文档连接两个模式数据
【发布时间】:2018-12-26 00:14:03
【问题描述】:

这是我的架构:

var mongoose = require('mongoose');

var EventSchema = new mongoose.Schema({
    name: {type:String},
    start_date: {type:Date},
    duration: {type:String},
    event_id:{type: mongoose.Schema.Types.ObjectId}
});
var Project = new mongoose.Schema({
    name: {
        type: String, 
        required: '{PATH} is required!'
    },
    user_id:{
        type: mongoose.Schema.Types.ObjectId,
        required:'{PATH} is required!'
    },
    client: {type: Object},
    no_of_events: {type: String, required: '{PATH} is required!'},
    start_date:{type:Date, required: '{PATH} is required!'},
    end_date:{type:Date, required: '{PATH} is required!'},
    budget:{type: String},
    groom:{type:Object},
    bride:{type:Object},
    events:[EventSchema],
    status:{type:Number,Default:1}, //0=Inactive, 1=Active, 2=Completed
    created_at: { type: Date, default: Date.now },
    updated_at: { type: Date, default: Date.now }
});

module.exports = mongoose.model('Project', Project);

在创建项目时,我正在添加多个事件。因此,在“事件”中有对象数组。这是示例数据:

{
    "_id": {
        "$oid": "5b43582a716d9a4e96345f4a"
    },
    "bride": {
        "city": "Hyderabad",
        "phone": "09876543211",
        "dob": "1993-05-06T18:30:00.000Z",
        "name": "Shriya Bhupal"
    },
    "groom": {
        "city": "Hyderabad",
        "phone": "09876543211",
        "dob": "1993-08-09T18:30:00.000Z",
        "name": "Anindith Reddy"
    },
    "client": {
        "name": "Apoorva Pagar"
    },
    "end_date": {
        "$date": "2018-07-22T18:30:00.000Z"
    },
    "start_date": {
        "$date": "2018-07-10T18:30:00.000Z"
    },
    "no_of_events": "4",
    "user_id": {
        "$oid": "5b126966bcc8072e526346ad"
    },
    "name": "Big Fat Wedding",
    "updated_at": {
        "$date": "2018-07-09T12:42:18.263Z"
    },
    "created_at": {
        "$date": "2018-07-09T12:42:18.263Z"
    },
    "events": [
        {
            "name": "Engagement",
            "event_id": {
                "$oid": "5b3b232914cdec23c19c034c"
            },
            "start_date": {
                "$date": "2018-07-11T18:30:00.000Z"
            },
            "duration": "1 Day",
            "_id": {
                "$oid": "5b43582a716d9a4e96345f4b"
            }
        },
        {
            "name": "Mehndi",
            "event_id": {
                "$oid": "5b3b23b314cdec23c19c034d"
            },
            "start_date": {
                "$date": "2018-07-12T18:30:00.000Z"
            },
            "duration": "1 Day",
            "_id": {
                "$oid": "5b43582a716d9a4e96345f4c"
            }
        },
        {
            "name": "Sangeet",
            "event_id": {
                "$oid": "5b3b232914cdec23c19c034c"
            },
            "start_date": {
                "$date": "2018-07-17T18:30:00.000Z"
            },
            "duration": "2 Days",
            "_id": {
                "$oid": "5b43582a716d9a4e96345f4d"
            }
        }
    ],
    "__v": 0
}

这里的情况是,如果我从默认事件中添加事件,则有 event_id,否则没有 event_id。因此,如果我添加了保存在另一个名为“defaultevents”的模式中的默认事件。现在,如果数据中存在 event_id,我想在每个事件对象中添加 eventdetail。

    {"events": [
            {
// it should include event_detail:{ details about event from defaultevents}
                "name": "Engagement",
                "event_id": {
                    "$oid": "5b3b232914cdec23c19c034c"
                },
                "start_date": {
                    "$date": "2018-07-11T18:30:00.000Z"
                },
                "duration": "1 Day",
                "_id": {
                    "$oid": "5b43582a716d9a4e96345f4b"
                }
            },
            {
// it should not include event_details as there is no event_id
                "name": "Mehndi",
                "start_date": {
                    "$date": "2018-07-12T18:30:00.000Z"
                },
                "duration": "1 Day",
                "_id": {
                    "$oid": "5b43582a716d9a4e96345f4c"
                }
            }
    ]
    }

DefaultEvents 相同数据:

{
    "_id": {
        "$oid": "5b3b232914cdec23c19c034c"
    },
    "image": "https://dostbucket.s3.us-east-2.amazonaws.com/events/1530601666360mehndi.svg",
    "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
    "name": "Sangeet",
    "updated_at": {
        "$date": "2018-07-03T07:18:01.166Z"
    },
    "created_at": {
        "$date": "2018-07-03T07:18:01.166Z"
    },
    "__v": 0
}

mongod 版本:3.4.7 (MMAPv1)

【问题讨论】:

  • 请发布 defaultevents 架构的示例集合以及您使用的 mongodb 版本/
  • @AnthonyWinzlet jsut 添加了默认事件和猫鼬版本的示例数据。希望有帮助
  • 我认为这里的问题是更新产品文档以具有 event_details 字段,如果他们在事件数组中有 event_id?正确的?请相应地更新问题。
  • 我不想在文档中保存 event_details 但需要这个连接来渲染数据@Jack

标签: node.js mongodb mongoose aggregation-framework nested-documents


【解决方案1】:

您可以在 3.6 中使用以下聚合。

ProjectModel.aggregate([
  {"$unwind":"$events"},
  {"$lookup":{
    "from": "event", //name of the foreign collection not model or schema name
    "localField": "events.event_id",
    "foreignField": "_id",
    "as": "events.eventdetail"
  }},
  {"$unwind":{"path":"$events.eventdetail", "preserveNullAndEmptyArrays":true }}, // Keep non matching events
  {"$group":{
    "_id": "$_id",
    "events": { "$push": "$events" },
    "data": { "$first": "$$ROOT" } // $$ROOT to keep the entire data
  }},
  {"$addFields":{"data.events":"$events", "events":0}}, // Replace the events  with grouped events.
  {"$replaceRoot":{"newRoot":"$data"}}
])

【讨论】:

  • 你能告诉我$unwind的用途吗?我也阅读了文档,但不明白。 @Veeram
  • $unwind 将数组 [] 转换为文档 {}。我们展开将事件数组转换为查找阶段的文档。同样,当查找返回数组时,我们将返回的数组转换回文档。
【解决方案2】:

您可以使用 $lookup 聚合来做到这一点

db.colletion.aggregate([
  { "$unwind": "$events" },
  { "$lookup": {
    "from": DefaultEventSchema.collection.name,
    "localField": "events.event_id",
    "foreignField": "_id",
    "as": "events.event_id"
  }},
  { "$unwind": { "path": "$events.event_id", "preserveNullAndEmptyArrays": true } },
  { "$group": {
    "_id": "$_id",
    "events": { "$push": "$events" },
    "bride": { "$first": "$bride" },
    "groom": { "$first": "$groom" },
    "client": { "$first": "$client" },
    "end_date": { "$first": "$end_date" },
    "no_of_events": { "$first": "$no_of_events" },
    "name": { "$first": "$name" },
    "created_at": { "$first": "$created_at" },
    "updated_at": { "$first": "$updated_at" }
  }}
])

【讨论】:

  • 它不返回架构的其他字段。此外,如果没有事件 ID,它不会返回任何内容
  • 问题改变了吗?
  • 不,它返回:{ "_id": "5b3c8e6d6283e24f522adaef", "events": [ [], [], [] ], "otherFields": null } 如果 event_id 不存在
  • 检查更新的答案...如果您的 localField 和 foreignField 都相同,那么它应该可以工作...而且您的集合名称(DefaultEventSchema.collection.name)应该是正确的...跨度>
  • 为什么要替换 events.event_id?我想从事件对象以及另一个模式中获取数据作为详细信息
【解决方案3】:

Simerjit,因为 mongo 不提供 $(定位运算符)来搜索和更新对象数组(如果我们尝试这样做,只有第一个对象会更新read more here),我们需要按照以下方式进行:

var products = Product.find();

products.forEach(product => {
    product.events.forEach(event => {
        if ('event_id' in event) {
            var product = product.events[event];
            product.event.event_details = 'my details';
            product.save(product);
        }
    });
});

您可能需要对代码进行一些调整,但我希望它能让您了解如何实现所需的目标。

【讨论】:

  • 我不想在文档中保存详细信息。我希望这些细节能够在前端呈现数据。
猜你喜欢
  • 1970-01-01
  • 2018-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-08
  • 2016-05-03
相关资源
最近更新 更多