MongoDB Aggregation Query-переименование полей, возвращаемых из встроенных документов


В настоящее время я использую оператор aggregate для возврата документов, содержащих массив вложенных (вложенных) документов. Я хочу переименовать имя поля для массива, а также переименовать имена полей во вложенных документах массива.

Например, для проекции я хочу переименовать массив из "друзей" в "приятелей", а также переименовать поля во встроенном документе из "имя"в " Ник". Могу ли я сделать это в рамках агрегированной операции, и если да, то как?

Вот пример: пример исходного документа:

[
    {
        _id: ObjectID,
        name: 'Matthew',
        friends: [
            {name: 'Slim', age: '32'},
            {name: 'buba', age: '36'}
        ]
    }
]

Вот как должны выглядеть результаты:

[
    {
        _id: ObjectID,
        name: 'Matthew',
        buddies: [
            {nickName: 'Chris', age: '32'},
            {nickName: 'Jim', age: '36'}
        ]
    }
]

Заранее благодарю за помощь.

2 4

2 ответа:

Есть несколько подходов к этому, но это во многом зависит от вашей версии MongoDB. Более поздние версии от 2.6 и выше поддерживают $map оператор, который вы можете использовать в $project чтобы делать то, что вы хотите:

db.friend.aggregate([
    { "$project": {
        "name": 1,
        "buddies": {
            "$map": {
                "input": "$friends",
                "as": "el",
                "in": {
                    "nickName": "$$el.name",
                    "age": "$$el.age"
                }
            }
        }
    }}
])

В предыдущих версиях вы бы использовали $unwind для работы с элементами массива и повторного построения через $group:

db.collection.aggregate([
    { "$unwind": "$friends" },
    { "$group": {
        "_id": "$_id",
        "name": { "$first": "$name" },
        "buddies": {
            "$push": {
                "nickName": "$friends.name",
                "age": "$friends.age"
            }
        }
    }}
])

С первой формой быть немного более эффективным, как вы не происходит де-нормализации содержимого массива и создания дополнительных документов в конвейере для обработки.

Я думаю, что понял это следующим агрегированным запросом. Мне было бы интересно, есть ли лучший способ сделать это.

db.friends.aggregate([
    { $unwind: '$friends'},
    { $project: {
        _id: 1,
        name: 1,
        buddy: {
            nickName: '$friends.name',
            age: '$friends.age',
        }
    }},
    { $group: {        
        _id: '$_id',
        name: {'$first': '$name'},
        buddies: {"$push": "$buddy"}
    }}
]);

Ниже я опишу ответ каждого оператора ($unwind, $project, $group)

$unwind вернет что-то вроде этого. Он разбивает каждый внедренный документ на свой собственный документ. Итак, где у нас был 1 документ с 2 друзьями sub document, теперь у нас есть два документа. Оба документа будут иметь одинаковые поля id и name, но СУБД friends документ будет отличаться:

{
    "_id" : ObjectId("539c80d43cb9fe99d183a5f7"),
    "name" : "Matthew",
    "friends" : {"name" : "slim", "age" : "32"}
}, {
    "_id" : : ObjectId("539c80d43cb9fe99d183a5f7"),,
    "name" : "Matthew",
    "friends" : {"name" : "buba", "age" : "36"}
}

Проект $вернет что-то вроде этого. Здесь я создаю новый встроенный документ под названием buddy и замечаю, что я назначаю "$friends.name к "Нику":

{
    "_id" : ObjectId("539c80d43cb9fe99d183a5f7"),
    "name" : "Matthew",
    "buddy" : {"nickName" : "slim","age" : "32"}
}, {
    "_id" : : ObjectId("539c80d43cb9fe99d183a5f7"),,
    "name" : "Matthew",
    "friends" : {"nickName" : "buba","age" : "36"}
}

И тогда $group сделает что-то вроде этого. Здесь я просто группирую все обратно вместе, но устанавливаю имя массива на "buddies":

{
    "_id" : ObjectId("539c80d43cb9fe99d183a5f7"),
    "name" : "Matthew",
    "buddies" : [
        {"nickName" : "slim","age" : "32"},
        {"nickName" : "buba","age" : "36"}
    ]
}

Это, кажется, работает, но если бы массив друзей имел дубликаты, этот процесс удалил бы дубликаты. Это может быть решается, если есть уникальный идентификатор для каждого друга.