Search code examples
node.jsmongodbmongooseaggregation-frameworknested-documents

Mongodb: Aggregation/Pipilines to join two schema's data based on nested documents


Here is my schema:

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

While creating Project, I am adding multiple events. Therefore in 'events' there is array of objects. Here is the sample data:

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

Here the case is, If I add event from the default events, then there is event_id, otherwise there is no event_id. So if I added, default events that are saved in another schema called 'defaultevents'. Now I want to add eventdetail in each event object if event_id exists in data.

    {"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 Sameple Data:

{
    "_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 version: 3.4.7 (MMAPv1)


Solution

  • You can use below aggregation in 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"}}
    ])