Mongodb: агрегация / пипилины для объединения двух данных схемы на основе вложенных документов

Вот моя схема:

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". Теперь я хочу добавить eventdetail в каждый объект события, если в данных существует event_id.

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

3 ответа

Решение

Вы можете использовать ниже агрегации в 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"}}
])

Вы можете сделать это с $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" }
  }}
])

Simerjit, так как mongo не предоставляет $ (оператор позиционирования) для поиска и обновления в массиве объектов (если мы попытаемся сделать это, только первый объект будет обновлен, подробнее здесь), нам нужно сделать это следующим образом:

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

Возможно, вам придется внести некоторые изменения в код, но я надеюсь, что это даст вам представление о том, как вы можете достичь того, что вы ищете.

Другие вопросы по тегам