2017-08-11 2 views
1

Je souhaite combiner les données d'une collection en utilisant les ID des deux baies.Comment joindre des tableaux dans le même document?

Un exemple est illustré ci-dessous.

{ 
    "_id": ObjectId ("5976fd2eb0adec0a32fa9831"), 
     "People": [ 
      { 
      "_id": 1,  <--- ID 
      "Name": "jane" 
      }, 
      { 
      "_id": 2,  <--- ID 
      "Name": "Mark" 
      } 
     ], 
     "Contents": [ 
      { 
      "userID": 2, <--- People ID 
      "Text": "111" 
      }, 
      { 
      "userID": 1, <--- People ID 
      "Text": "Hi" 
      } 
     ] 
} 

Je veux rendre le document ci-dessus comme ci-dessous.

{ 
    "_id": ObjectId ("5976fd2eb0adec0a32fa9831"), 
    "People": [ 
     { 
      "_id": 1, 
      "Name" : "Jane" 
     }, 
     { 
      "_id": 2, 
      "Name": "Mark" 
     } 
    ], 
    "Contents": [ 
     { 
      "userID": 2, 
      "Name": "Mark", <-- Adding 
      "Text": "111", 

     }, 
     { 
      "userID": 1, 
      "Name": "Jane", <-- Adding 
      "Text": "Hi", 

     } 
    ] 
} 

J'ai essayé différentes choses comme $lookup ou $unwind de .aggregate() mais je ne peux pas obtenir le résultat.

+0

Avez-vous essayé ce liens ?? J'espère que cela aiderait, https://stackoverflow.com/questions/37086387/join-two-collections-on-mutiple-field-using-lookup et https://www.sitepoint.com/using-joins-in- mongodb-nosql-databases/ – Tehmina

+0

@Tehmina Il ne devrait pas être nécessaire de "s'auto-joindre" entre les éléments d'un même document. Ce serait vraiment inefficace et il existe des moyens très efficaces de le faire, sans avoir besoin de «re-interroger» efficacement la collection. –

Répondre

0

Vous voulez $map et $indexOfArray idéalement:

db.collection.aggregate([ 
    { "$addFields": { 
    "Contents": { 
     "$map": { 
     "input": "$Contents", 
     "as": "c", 
     "in": { 
      "userID": "$$c.userID", 
      "Name": { 
      "$arrayElemAt": [ 
       "$People.Name", 
       { "$indexOfArray": [ "$People._id", "$$c.userID" ] } 
      ] 
      }, 
      "Text": "$$c.Text" 
     } 
     } 
    } 
    }} 
]) 

qui prend essentiellement la valeur de l'autre réseau via $arrayElemAt pour le "index" correspondant retourné par $indexOfArray.

Si votre MongoDB doit se replier une version sans cet opérateur, vous pouvez utiliser $filter à la place:

db.collection.aggregate([ 
    { "$addFields": { 
    "Contents": { 
     "$map": { 
     "input": "$Contents", 
     "as": "c", 
     "in": { 
      "userID": "$$c.userID", 
      "Name": { 
      "$arrayElemAt": [ 
       { "$map": { 
       "input": { 
        "$filter": { 
        "input": "$People", 
        "as": "p", 
        "cond": { "$eq": [ "$$p._id", "$$c.userID" ] } 
        } 
       }, 
       "as": "p", 
       "in": "$$p.Name" 
       }}, 
       0 
      ] 
      }, 
      "Text": "$$c.Text" 
     } 
     } 
    } 
    }} 
]) 

Si fondamentalement, vous $filter les résultats vers le bas de l'autre tableau en comparaison et il suffit de retourner le premier l'élément correspondant par l'index 0 avec $arrayElemAt.

Dans les deux cas, il n'y a pas besoin de "s'auto-joindre" en utilisant $lookup, et c'est juste une surcharge inutile.

D'après le document dans la question que vous obtenez ce qui suit:

/* 1 */ 
{ 
    "_id" : ObjectId("5976fd2eb0adec0a32fa9831"), 
    "People" : [ 
     { 
      "_id" : 1.0, 
      "Name" : "jane" 
     }, 
     { 
      "_id" : 2.0, 
      "Name" : "Mark" 
     } 
    ], 
    "Contents" : [ 
     { 
      "userID" : 2.0, 
      "Name" : "Mark", 
      "Text" : "111" 
     }, 
     { 
      "userID" : 1.0, 
      "Name" : "jane", 
      "Text" : "Hi" 
     } 
    ] 
} 

D'une manière générale cependant, il n'y a pas de raison pour tous les opérateurs d'agrégation du tout, comme ce genre d'opération est généralement préférable gauche à post-traitement dans le curseur. En fait, puisque vous "ajoutez" des données au document à retourner, il est préférable d'effectuer une modification après l'envoi du document sur le réseau.

Comme un idiome commun de ce qui précède montre que JavaScript pour le shell:

db.collection.find().map(d => 
    Object.assign(
    d, 
    { 
     "Contents": d.Contents.map(c => 
     Object.assign(c, 
      { "Name": d.People.map(p => p.Name)[d.People.map(p => p._id).indexOf(c.userID)] } 
     ) 
    ) 
    } 
) 
) 

produit le même résultat à, et est généralement un peu plus facile sur les yeux pour lire et interpréter

+0

merci, je reçois référence, alors merci –