2014-05-05 6 views
11

Ich frage mich, wie man eine Art Union in einem Aggregat in MongoDB durchführen. Lassen Sie uns die Abbildung des folgenden Dokument in einer Sammlung (die Struktur ist im Interesse des Beispiels):Führen Sie Union in MongoDB

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

Wie ein Aggregat machen, dass die Vereinigung der Menschen in Twitter und LinkedIn zurückkehrt?

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

Antwort

12

Es gibt ein paar Ansätze dazu, dass Sie die aggregate Methode für

verwenden können
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" 
    }} 
]) 
Einige Betreiber von MongoDB 2.6 10

Oder mit einem anderen Ansatz:

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

Und natürlich, wenn Sie scherte einfach nicht, was die Quelle war:

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

Nicht sicher, ob Aggregat mit über ein empfohlenes Karten reduziert für diese Art von Operation, aber die folgende tut, was Sie für Fragen (weiß nicht, ob $ const kann in der überhaupt ohne Problem verwendet werden .aggregate() Funktion):

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