2014-05-05 15 views
11

Zastanawiam się, jak wykonać rodzaj związku w agregacie w MongoDB. Załóżmy obrazowania poniższy dokument w kolekcji (struktura jest przez wzgląd na przykładzie):Wykonuj połączenie w mongoDB

{ 
    linkedIn: { 
    people : [ 
    { 
     name : 'Fred' 
    }, 
    { 
     name : 'Matilda' 
    } 
    ] 
    }, 
    twitter: { 
    people : [ 
    { 
     name : 'Hanna' 
    }, 
    { 
     name : 'Walter' 
    } 
    ] 
    } 
} 

Jak zrobić agregat, który zwraca zjednoczenie ludzi w Twitter i LinkedIn?

{ 
{ name :'Fred', source : 'LinkedIn'}, 
{ name :'Matilda', source : 'LinkedIn'}, 
{ name :'Hanna', source : 'Twitter'}, 
{ name :'Walter', source : 'Twitter'}, 
} 

Odpowiedz

12

Istnieje kilka podejść do tego, że można użyć metody aggregate dla

db.collection.aggregate([ 
    // Assign an array of constants to each document 
    { "$project": { 
     "linkedIn": 1, 
     "twitter": 1, 
     "source": { "$cond": [1, ["linkedIn", "twitter"],0 ] } 
    }}, 

    // Unwind the array 
    { "$unwind": "$source" }, 

    // Conditionally push the fields based on the matching constant 
    { "$group": { 
     "_id": "$_id", 
     "data": { "$push": { 
      "$cond": [ 
       { "$eq": [ "$source", "linkedIn" ] }, 
       { "source": "$source", "people": "$linkedIn.people" }, 
       { "source": "$source", "people": "$twitter.people" } 
      ] 
     }} 
    }}, 

    // Unwind that array 
    { "$unwind": "$data" }, 

    // Unwind the underlying people array 
    { "$unwind": "$data.people" }, 

    // Project the required fields 
    { "$project": { 
     "_id": 0, 
     "name": "$data.people.name", 
     "source": "$data.source" 
    }} 
]) 

Albo z innego podejścia z wykorzystaniem niektórych operatorów z MongoDB 2.6:

db.people.aggregate([ 
    // Unwind the "linkedIn" people 
    { "$unwind": "$linkedIn.people" }, 

    // Tag their source and re-group the array 
    { "$group": { 
     "_id": "$_id", 
     "linkedIn": { "$push": { 
      "name": "$linkedIn.people.name", 
      "source": { "$literal": "linkedIn" } 
     }}, 
     "twitter": { "$first": "$twitter" } 
    }}, 

    // Unwind the "twitter" people 
    { "$unwind": "$twitter.people" }, 

    // Tag their source and re-group the array 
    { "$group": { 
     "_id": "$_id", 
     "linkedIn": { "$first": "$linkedIn" }, 
     "twitter": { "$push": { 
      "name": "$twitter.people.name", 
      "source": { "$literal": "twitter" } 
     }} 
    }}, 

    // Merge the sets with "$setUnion" 
    { "$project": { 
     "data": { "$setUnion": [ "$twitter", "$linkedIn" ] } 
    }}, 

    // Unwind the union array 
    { "$unwind": "$data" }, 

    // Project the fields 
    { "$project": { 
     "_id": 0, 
     "name": "$data.name", 
     "source": "$data.source" 
    }} 
]) 

I oczywiście, jeśli po prostu nie obchodzi, co było źródłem:

db.collection.aggregate([ 
    // Union the two arrays 
    { "$project": { 
     "data": { "$setUnion": [ 
      "$linkedIn.people", 
      "$twitter.people" 
     ]} 
    }}, 

    // Unwind the union array 
    { "$unwind": "$data" }, 

    // Project the fields 
    { "$project": { 
     "_id": 0, 
     "name": "$data.name", 
    }} 

]) 
2

Nie wiem, czy za pomocą agregatu zaleca nad mapą-zredukować do tego rodzaju operacji, ale po co robi prosisz (dunno jeśli $ const może być używany bez problemu w ogóle w funkcja .aggregate()):

aggregate([ 
    { $project: { linkedIn: '$linkedIn', twitter: '$twitter', idx: { $const: [0,1] }}}, 
    { $unwind: '$idx' }, 
    { $group: { _id : '$_id', data: { $push: { $cond:[ {$eq:['$idx', 0]}, { source: {$const: 'LinkedIn'}, people: '$linkedIn.people' } , { source: {$const: 'Twitter'}, people: '$twitter.people' } ] }}}}, 
    { $unwind: '$data'}, 
    { $unwind: '$data.people'}, 
    { $project: { _id: 0, name: '$data.people.name', source: '$data.source' }} 
]) 
Powiązane problemy