2014-05-05 7 views
11

Je me demande comment effectuer une sorte d'union dans un agrégat dans MongoDB. Nous allons imager le document suivant dans une collection (la structure est pour le bien de l'exemple):Effectuer une union dans mongoDB

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

Comment faire un agrégat qui retourne l'union du peuple dans Twitter et LinkedIn?

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

Répondre

12

Il y a deux approches à ce que vous pouvez utiliser la méthode aggregate pour

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

ou avec une approche différente en utilisant certains opérateurs de 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" 
    }} 
]) 

Et bien sûr, si vous simplement ne se souciaient pas ce que la source était:

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

Je ne sais pas si vous utilisez ensemble est recommandé sur une carte-reduce pour ce genre d'opération, mais ce qui suit est en train de faire ce que vous demandez (je sais pas si const $ peut être utilisé sans problème du tout dans la fonction .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' }} 
]) 
Questions connexes