2016-05-07 29 views
5

Ecco la mia collezione:

collection1:

{ 
    user1: 1, 
    user2: 2, 
    percent: 0.56 
} 

Collection2:

{ 
    user1: 1, 
    user2: 2, 
    percent: 0.3 
} 

voglio unire i due raccolta da 'user1' e ' user2' .

Il risultato come questo:

{ 
    user1: 1, 
    user2: 2, 
    percent1: 0.56, 
    percent2: 0.3 
} 

Come faccio a scrivere il gasdotto?

+0

No, non è possibile unire utilizzando 'user1' e 'user2' questo semplicemente non è possibile in quanto sia 'localField' che' foreignField' prendono solo il nome di un campo. – styvane

+0

Ho trovato un modo per farlo. raccolta in una nuova raccolta e raggrupparla – user6148078

risposta

20

È possibile eseguire più condizioni di join con l'operatore di pipeline di aggregazione $lookup nella versione 3.6 e successive.

Abbiamo bisogno di assegnare i valori dei campi alla variabile usando il campo opzionale let; si accede quindi a tali variabili nelle fasi di campo pipeline in cui si specifica la pipeline da eseguire sulle raccolte.

Si noti che nella fase $match, si utilizza l'operatore di query di valutazione $expr per confrontare il valore dei campi.

L'ultima tappa in cantiere è la fase gasdotto $replaceRoot aggregazione dove abbiamo semplicemente fondiamo il risultato $lookup con parte del documento $$ROOT utilizzando l'operatore $mergeObjects.

db.collection2.aggregate([ 
     { 
      $lookup: { 
      from: "collection1", 
      let: { 
       firstUser: "$user1", 
       secondUser: "$user2" 
      }, 
      pipeline: [ 
       { 
        $match: { 
         $expr: { 
         $and: [ 
          { 
           $eq: [ 
            "$user1", 
            "$$firstUser" 
           ] 
          }, 
          { 
           $eq: [ 
            "$user2", 
            "$$secondUser" 
           ] 
          } 
         ] 
         } 
        } 
       } 
      ], 
      as: "result" 
      } 
     }, 
     { 
      $replaceRoot: { 
      newRoot: { 
       $mergeObjects:[ 
        { 
         $arrayElemAt: [ 
         "$result", 
         0 
         ] 
        }, 
        { 
         percent1: "$$ROOT.percent1" 
        } 
       ] 
      } 
      } 
     } 
    ] 
) 

Questa rendimenti gasdotti qualcosa che assomigliano a questo:

{ 
    "_id" : ObjectId("59e1ad7d36f42d8960c06022"), 
    "user1" : 1, 
    "user2" : 2, 
    "percent" : 0.3, 
    "percent1" : 0.56 
} 

Se non si è in versione 3.6 e versioni successive, è possibile prima aderire utilizzando uno del vostro campo diciamo "user1" poi da lì si svolge la matrice del documento corrispondente utilizzando l'operatore di pipeline di aggregazione $unwind. La fase successiva della pipeline è la fase $redact in cui si escludono quei documenti in cui il valore di "utente2" dalla raccolta "unita" e il documento di input non sono uguali utilizzando le variabili di sistema $$KEEP e $$PRUNE. È quindi possibile rimodellare il documento nello stage $project.

db.collection1.aggregate([ 
    { "$lookup": { 
     "from": "collection2", 
     "localField": "user1", 
     "foreignField": "user1", 
     "as": "collection2_doc" 
    }}, 
    { "$unwind": "$collection2_doc" }, 
    { "$redact": { 
     "$cond": [ 
      { "$eq": [ "$user2", "$collection2_doc.user2" ] }, 
      "$$KEEP", 
      "$$PRUNE" 
     ] 
    }}, 
    { "$project": { 
     "user1": 1, 
     "user2": 1, 
     "percent1": "$percent", 
     "percent2": "$collection2_doc.percent" 
    }} 
]) 

che produce:

{ 
    "_id" : ObjectId("572daa87cc52a841bb292beb"), 
    "user1" : 1, 
    "user2" : 2, 
    "percent1" : 0.56, 
    "percent2" : 0.3 
} 

Se i documenti nelle raccolte hanno la stessa struttura e ti ritrovi di eseguire questa operazione spesso, allora si dovrebbe considerare di fondere le due collezioni in uno o inserire i documenti in queste raccolte in una nuova raccolta.

db.collection3.insertMany(
    db.collection1.find({}, {"_id": 0}) 
    .toArray() 
    .concat(db.collection2.find({}, {"_id": 0}).toArray()) 
) 

Poi $group i documenti di "user1" e "user2"

db.collection3.aggregate([ 
    { "$group": { 
     "_id": { "user1": "$user1", "user2": "$user2" }, 
     "percent": { "$push": "$percent" } 
    }} 
]) 

che produce:

{ "_id" : { "user1" : 1, "user2" : 2 }, "percent" : [ 0.56, 0.3 ] } 
+1

Questo funziona ma è molto lento – user6148078

+0

@ user6148078 Il palco '$ unwind' è probabilmente il colpevole qui.Anche nota che' $ redact' esegue una scansione di raccolta, ma se non è possibile fare unire quindi, penso che questo sia il meglio che si possa ottenere – styvane

+0

Guardare questo mi fa apprezzare molto di più SQL rispetto a Mongo. – user2223059

1

Si può fare mutiple campo corrisponde utilizzando $ partita e $ progetto condotte. (Vedi risposta dettagliata qui - mongoDB Join on multiple fields)

db.collection1.aggregate([ 
        {"$lookup": { 
        "from": "collection2", 
        "localField": "user1", 
        "foreignField": "user1", 
        "as": "c2" 
        }}, 
        {"$unwind": "$c2"}, 

        {"$project": { 
        "user2Eq": {"$eq": ["$user2", "$c2.user2"]}, 
        "user1": 1, "user2": 1, 
        "percent1": "$percent", "percent2": "$c2.percent" 
        }}, 

        {"$match": { 
        {"user2Eq": {"$eq": True}} 
        }}, 

        {"$project": { 
        "user2Eq": 0 
        }} 

        ]) 
0

Se stai cercando di modellare i dati, ed è venuto qui per verificare se MongoDB può effettuare si unisce su più campi prima di decidere di farlo, si prega di leggere su.

Mentre MongoDB può eseguire join, è inoltre possibile modellare i dati in base al proprio modello di accesso dell'applicazione. Se i dati è semplice come presentato nella domanda, possiamo semplicemente mantenere un'unica collezione che assomiglia a questo:

{ 
    user1: 1, 
    user2: 2, 
    percent1: 0.56, 
    percent2: 0.3 
} 

Ora è possibile eseguire tutte le operazioni di questa collezione si sarebbe eseguite unendo. Perché stiamo cercando di evitare i join? Perché non sono supportati da raccolte più grandi (docs), che ti impediranno di ridimensionare quando necessario. La normalizzazione dei dati (con tabelle/raccolte separate) funziona molto bene in SQL, ma quando si tratta di Mongo, evitare i join può offrire vantaggi senza conseguenze nella maggior parte dei casi. Usa la normalizzazione in MongoDB solo quando non hai altra scelta. Dalla docs:

In generale, utilizzare modelli di dati normalizzati:

  • quando incorporamento comporterebbe duplicazione dei dati, ma non fornirebbe sufficienti vantaggi prestazionali lettura per controbilanciare le implicazioni della duplicazione.
  • per rappresentare relazioni più complesse molti-a-molti.
  • per modellare grandi insiemi di dati gerarchici.

Controllare here per leggere di più su embedding e perché si dovrebbe scegliere sopra la normalizzazione.