2016-03-15 3 views
10

J'ai besoin de récupérer la hiérarchie entière d'objet unique de la base de données en tant que JSON. En fait, la proposition concernant toute autre solution pour réaliser ce résultat serait très appréciée. J'ai décidé d'utiliser MongoDB avec son support de recherche $.Recherche imbriquée MongoDB avec 3 niveaux

J'ai donc trois collections:

partie

{ "_id" : "2", "name" : "party2" } 
{ "_id" : "5", "name" : "party5" } 
{ "_id" : "4", "name" : "party4" } 
{ "_id" : "1", "name" : "party1" } 
{ "_id" : "3", "name" : "party3" }  

Adresse

{ "_id" : "a3", "street" : "Address3", "party_id" : "2" } 
{ "_id" : "a6", "street" : "Address6", "party_id" : "5" } 
{ "_id" : "a1", "street" : "Address1", "party_id" : "1" } 
{ "_id" : "a5", "street" : "Address5", "party_id" : "5" } 
{ "_id" : "a2", "street" : "Address2", "party_id" : "1" } 
{ "_id" : "a4", "street" : "Address4", "party_id" : "3" } 

addressComment

{ "_id" : "ac2", "address_id" : "a1", "comment" : "Comment2" } 
{ "_id" : "ac1", "address_id" : "a1", "comment" : "Comment1" } 
{ "_id" : "ac5", "address_id" : "a5", "comment" : "Comment6" } 
{ "_id" : "ac4", "address_id" : "a3", "comment" : "Comment4" } 
{ "_id" : "ac3", "address_id" : "a2", "comment" : "Comment3" } 

Je dois récupérer toutes les parties avec toutes les adresses correspondantes et les commentaires d'adresse dans le cadre de l'enregistrement. Mon agrégation:

db.party.aggregate([{ 
    $lookup: { 
     from: "address", 
     localField: "_id", 
     foreignField: "party_id", 
     as: "address" 
    } 
}, 
{ 
    $unwind: "$address" 
}, 
{ 
    $lookup: { 
     from: "addressComment", 
     localField: "address._id", 
     foreignField: "address_id", 
     as: "address.addressComment" 
    } 
}]) 

Le résultat est assez étrange. Certains enregistrements sont ok. Mais Party with _id 4 est manquant (il n'y a pas d'adresse pour cela). Il y a aussi deux _id 1 Party dans le jeu de résultats (mais avec des adresses différentes):

{ 
    "_id": "1", 
    "name": "party1", 
    "address": { 
     "_id": "2", 
     "street": "Address2", 
     "party_id": "1", 
     "addressComment": [{ 
      "_id": "3", 
      "address_id": "2", 
      "comment": "Comment3" 
     }] 
    } 
}{ 
    "_id": "1", 
    "name": "party1", 
    "address": { 
     "_id": "1", 
     "street": "Address1", 
     "party_id": "1", 
     "addressComment": [{ 
      "_id": "1", 
      "address_id": "1", 
      "comment": "Comment1" 
     }, 
     { 
      "_id": "2", 
      "address_id": "1", 
      "comment": "Comment2" 
     }] 
    } 
}{ 
    "_id": "3", 
    "name": "party3", 
    "address": { 
     "_id": "4", 
     "street": "Address4", 
     "party_id": "3", 
     "addressComment": [] 
    } 
}{ 
    "_id": "5", 
    "name": "party5", 
    "address": { 
     "_id": "5", 
     "street": "Address5", 
     "party_id": "5", 
     "addressComment": [{ 
      "_id": "5", 
      "address_id": "5", 
      "comment": "Comment5" 
     }] 
    } 
}{ 
    "_id": "2", 
    "name": "party2", 
    "address": { 
     "_id": "3", 
     "street": "Address3", 
     "party_id": "2", 
     "addressComment": [{ 
      "_id": "4", 
      "address_id": "3", 
      "comment": "Comment4" 
     }] 
    } 
} 

Aidez-moi s'il vous plaît. Je suis assez nouveau sur le MongoDB mais je pense qu'il peut faire ce dont j'ai besoin.

Répondre

19

La cause de vos «problèmes» est la deuxième étape d'agrégation - { $unwind: "$address" }. Il supprime l'enregistrement pour la partie avec _id: 4 (parce que son tableau d'adresses est vide, comme vous le mentionnez) et produit deux enregistrements pour les parties _id: 1 et _id: 5 (car chacun d'eux a deux adresses).

  • Pour éviter la suppression des parties sans adresses, vous devez définir l'option preserveNullAndEmptyArrays de $unwind étape à true.

  • Pour éviter la duplication de parties pour ses différentes adresses, vous devez ajouter l'étape d'agrégation $group à votre pipeline. Utilisez également l'opérateur $project avec l'opérateur $filter pour exclure les enregistrements d'adresses vides en sortie.

db.party.aggregate([{ 
    $lookup: { 
    from: "address", 
    localField: "_id", 
    foreignField: "party_id", 
    as: "address" 
    } 
}, { 
    $unwind: { 
    path: "$address", 
    preserveNullAndEmptyArrays: true 
    } 
}, { 
    $lookup: { 
    from: "addressComment", 
    localField: "address._id", 
    foreignField: "address_id", 
    as: "address.addressComment", 
    } 
}, { 
    $group: { 
    _id : "$_id", 
    name: { $first: "$name" }, 
    address: { $push: "$address" } 
    } 
}, { 
    $project: { 
    _id: 1, 
    name: 1, 
    address: { 
     $filter: { input: "$address", as: "a", cond: { $ifNull: ["$$a._id", false] } } 
    } 
    } 
}]); 
+0

réservoir vous Shad! Il y a un petit problème avec le disque 4 si: '{ \t "_id": "4", \t "name": "party4", \t "adresse": [{ \t \t "addressComment": [] }] } ' } Comme vous pouvez le voir - adresse devrait être nulle mais c'est un enregistrement vide à la place ... Pouvons-nous passer l'adresse si son adresse est vide? Dans les autres cas, cette adresse sera considérée comme un enregistrement. – Yuriy

+1

En fait, je vois que la solution fournie fonctionne comme prévu selon la description du nouveau champ "preserveNullAndEmptyArrays" pour l'opération $ unwinding (depuis 3.2). Maintenant, nous pouvons passer l'étape "$ project" et spécifier cette "$ unwind" au lieu de la simple: '$ unwind: {path:" $ address ", preserveNullAndEmptyArrays: true}'.J'accepterai votre réponse, merci pour une réponse rapide et claire! – Yuriy

+0

@Shad j'ai une question assez similaire. Ici, le code d'OP n'a qu'une seule propriété appelée 'name' dans la collection' party' et vous avez donc utilisé '$ first' pour l'obtenir dans' $ group'. Supposons que j'ai plus de 10 propriétés, alors est-il possible d'obtenir automatiquement toutes les propriétés sans en mentionner chacune individuellement? – Xyroid