web-dev-qa-db-fra.com

MongoDB - Rembobinez un tableau imbriqué $ unwind après recherche $ en utilisant $ group

L'agrégation MongoDB devient exponentiellement compliquée à la minute!

Je suis dans la mesure de $unwind un tableau imbriqué, puis effectuez un $lookup par le _id de chaque objet du tableau imbriqué déroulé. Ma dernière tentative consiste à inverser le déroulement avec $group. Cependant, je ne suis pas en mesure de reconstruire le tableau intégré d'origine, avec son nom de propriété d'origine, ainsi que le reste des propriétés immédiates d'origine de chaque document.

Voici ma tentative jusqu'à présent:

db.users.aggregate([
    {
        $unwind: "$profile",
        $unwind: {
            path: "$profile.universities",
            preserveNullAndEmptyArrays: true
        }
    },
    {
        $lookup: {
            from: "universities",
            localField: "profile.universities._id",
            foreignField: "_id",
            as: "profile.universities"
        }
    },
    {
        $group: {
            _id: "$_id",
            emails: { "$first": "$emails" },
            profile: { "$first": "$profile" },
            universities: { "$Push": "$profile.universities" }
        }
    }
]).pretty()

Ce que je reçois est quelque chose comme ceci:

{
    "_id" : "A_USER_ID",
    "emails" : [
        {
            "address" : "AN_EMAIL_ADDRESS",
            "verified" : false
        }
    ],
    "profile" : {
        "name" : "NAME",
        "company" : "A COMPANY",
        "title" : "A TITLE",
        "phone" : "123-123-1234",
        "disabled" : false,
        "universities" : [
            {
                "_id" : "ID_1",
                "name" : "UNIVERSITY_NAME_1",
                "code" : "CODE_1",
                "styles" : {AN_OBJECT}
            }
        ]
    },
    "universities" : [
        [
            {
                "_id" : "ID_1",
                "name" : "UNIVERSITY_NAME_1",
                "code" : "CODE_1",
                "styles" : {AN_OBJECT}
            }
        ],
        [
            {
                "_id" : "ID_2",
                "name" : "UNIVERSITY_NAME_2",
                "code" : "CODE_2",
                "styles" : {AN_OBJECT}
            }
        ]
    ]
}

Il y a 2 problèmes avec ce résultat:

  1. Le universities résultant est un tableau de tableaux d'un objet chacun, puisque le $lookup a renvoyé un tableau d'éléments unique pour l'original $profile.universities tableau imbriqué. Ce ne devrait être qu'un tableau d'objets.
  2. Le universities résultant doit prendre sa place d'origine tel qu'il est imbriqué sous profiles. Je sais pourquoi l'original profile.universities est ainsi, car j'utilise le $first opérateur. Mon intention derrière cela est de conserver toutes les propriétés d'origine de profile, en conjonction avec la conservation du tableau universities imbriqué d'origine.

En fin de compte, ce dont j'ai besoin est quelque chose comme ceci:

{
    "_id" : "A_USER_ID",
    "emails" : [
        {
            "address" : "AN_EMAIL_ADDRESS",
            "verified" : false
        }
    ],
    "profile" : {
        "name" : "NAME",
        "company" : "A COMPANY",
        "title" : "A TITLE",
        "phone" : "123-123-1234",
        "disabled" : false,
        "universities" : [
            {
                "_id" : "ID_1",
                "name" : "UNIVERSITY_NAME_1",
                "code" : "CODE_1",
                "styles" : {AN_OBJECT}
            },
            {
                "_id" : "ID_2",
                "name" : "UNIVERSITY_NAME_2",
                "code" : "CODE_2",
                "styles" : {AN_OBJECT}
            }
        ]
    }
}

Y a-t-il un autre opérateur que je peux utiliser au lieu de $group pour y parvenir? Ou est-ce que je comprends le but de $group incorrectement?

Edit: Ceci est le message d'origine, pour le contexte: Si Mongo $ lookup est une jointure externe gauche, alors comment se fait-il qu'il exclut les non documents correspondants?

12
Sun Lee

Parce que le $lookup l'opérateur produit un champ de tableau, vous devez $unwind le nouveau champ avant le $group pipeline pour obtenir le résultat souhaité:

db.users.aggregate([
    { "$unwind": "$profile" },
    { "$unwind": {
        "path": "$profile.universities",
         "preserveNullAndEmptyArrays": true
    } }, 
    { "$lookup": {
        "from": "universities",
        "localField": "profile.universities._id",
        "foreignField": "_id",
        "as": "universities"
    } },    
    { "$unwind": "$universities" },
    { "$group": {
        "_id": "$_id",
        "emails": { "$first": "$emails" },
        "profile": { "$first": "$profile" },
        "universities": { "$Push": "$universities" }
    } },
    { "$project": {
        "emails": 1,  
        "profile.name" : 1,
        "profile.company": 1,
        "profile.title" : 1,
        "profile.phone" : 1,
        "profile.disabled": 1,          
        "profile.universities": "$universities"
    } }
]).pretty()
19
chridam