2016-03-15 10 views
10

Ho bisogno di recuperare l'intera gerarchia di oggetti dal database come JSON. In realtà la proposta su qualsiasi altra soluzione per ottenere questo risultato sarebbe molto apprezzata. Ho deciso di utilizzare MongoDB con il suo supporto per la ricerca $.Ricerca nested MongoDB con 3 livelli

Così ho tre collezioni:

partito

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

indirizzo

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

Ho bisogno di recuperare tutte le parti con tutti gli indirizzi corrispondenti e commenti di indirizzo come parte del record. La mia aggregazione:

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" 
    } 
}]) 

Il risultato è piuttosto strano. Alcuni record sono ok. Ma manca Party con _id 4 (non c'è un indirizzo per questo). Inoltre ci sono due Party _id 1 nel set di risultati (ma con indirizzi diversi):

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

Please help me with this. Sono abbastanza nuovo per MongoDB ma sento che può fare quello che mi serve.

risposta

19

La causa dei tuoi "problemi" è la seconda fase di aggregazione - { $unwind: "$address" }. Rimuove record per party con _id: 4 (poiché l'array di indirizzi è vuoto, come si menziona) e produce due record per le parti _id: 1 e _id: 5 (perché ognuno di essi ha due indirizzi).

  • Per impedire la rimozione di parti senza indirizzi si dovrebbe impostare preserveNullAndEmptyArrays possibilità di $unwind palco per true.

  • Per evitare la duplicazione delle parti per i suoi diversi indirizzi, è necessario aggiungere la fase di aggregazione $group alla pipeline. Inoltre, utilizzare lo stage con l'operatore $filter per escludere i record di indirizzi vuoti nell'output.

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

serbatoio si Shad! C'è un piccolo problema con il record di 4 però: '{ \t "_id": "4", \t "name": "Party4", \t "indirizzo": [{ \t \t "addressComment": [] } } Come potete vedere - l'indirizzo deve essere nullo ma è invece un record vuoto ... Possiamo saltare l'indirizzo se il suo indirizzoComment è vuoto? In altri casi questo indirizzo sarà considerato un record. – Yuriy

+1

In realtà vedo che la soluzione fornita funziona come previsto in base alla descrizione del nuovo campo "preserveNullAndEmptyArrays" per l'operazione $ unwind (a partire dalla 3.2). Ora possiamo saltare il passaggio "$ project" e specificare questo "$ unwind" invece di quello semplice: '$ unwind: {percorso:" $ address ", preserveNullAndEmptyArrays: true}'.Accetterò la tua risposta, grazie per una risposta rapida e chiara! – Yuriy

+0

@Shad ho una domanda abbastanza simile. Il codice dell'OP ha solo una proprietà chiamata 'name' nella raccolta' party' e quindi hai usato '$ first' per ottenerlo in' $ group'. Supponiamo che io abbia 10+ proprietà, quindi esiste un modo per ottenere automaticamente tutte le proprietà senza menzionarle singolarmente? – Xyroid

Problemi correlati