2014-06-30 25 views
7

Si verificano problemi di rimodellamento del documento durante l'aggregazione per il raggruppamento. Fondamentalmente voglio spingere le voci nei campi a seconda del loro tipo. Ho una struttura come segue:MongoDB aggregato progetto selettivo

_id: P1 
    entities: [{type: A, val: X}, {type: B, val: X}, {type: A, val: Y}] 
    ... 

voglio $ rilassarsi e $ proiettare queste entità in modo che ottenere una struttura come:

_id: P1 
    A: [X] 
    B: [] 
_id: P1 
    A: [Y] 
    B: [] 
_id: P1 
    A: [] 
    B: [X] 

modo che io possa eseguire un raggruppamento da A o B o entrambi , vale a dire

$group: { 
    _id: { 
     A: $A, 
     B: $B 
    } 
    count: {$sum : 1} 

ho pensato che avrei potuto semplicemente fare:

$unwind: $entities 
$project: { 
    id: §id 
    A: {"$cond":[{"$eq":["$type","A"]},"$code"]} 
    B: {"$cond":[{"$eq":["$type","B"]},"$code"]} 
} 
$group: { 
    _id: "$id" 
    A: {$addToSet : "$A"} 
} 

o mancanza che qualcosa di simile

$unwind: $entities 
$group: { 
    _id: "$id" 
    A: {"$cond":[{"$eq":["$type","A"]},$push: "$code", null]} 
    ... 
} 

ma entrambe le versioni falliscono perché non posso fare nulla su altro e non sono riuscito a utilizzare $ spinta all'interno di un condizionale. Il più vicino ho ottenuto è il progetto a seconda del tipo, ma dato che non riuscivo a trovare un modo per non aggiungere nulla al campo quando non c'era alcuna corrispondenza, io alla fine con:

_id: P1 
    A: [X,null,Y] 
    B: [null,X,null] 

che scombina il conteggio. La mia seconda idea era di filtrare gli array per rimuovere null. Ma non ho trovato un modo per rimuovere entità, perché ancora una volta $ cond would non mi ha permesso di specificare un vuoto/"non fare nulla" altro.

Ho la sensazione che potrebbe funzionare con il raggruppamento per tipo e contenuto con la corrispondenza dei tipi richiesti, ma poiché ho molti tipi e raggruppamenti arbitrari risultanti in un albero di raggruppamento, questo potrebbe diventare molto complicato. Idee o suggerimenti per gli errori sarebbero molto graditi.

Grazie

EDIT: La soluzione basata sulla anwser accettato

ho dovuto adattare un po 'esso, per filtrare i casi in cui tutti i contenuti di un tipo è stato nullo, perché altrimenti sarebbe stato perso durante l'abbinamento e perché voglio mantenere questa conoscenza. Grazie!

{$project:{ 
    A: {$cond: [ 
     {$eq: ["$A", [false]]}, 
     ["N/A"], 
     "$A" 
    ]}, 
    B: {$cond: [ 
     {$eq: ["$B", [false]]}, 
     ["N/A"], 
     "$B" 
    ]}, 
}}, 
{ "$unwind": "$A" }, 
{ "$match": { "A": { "$ne": false } } }, 
{ "$group": { 
    "_id": "$_id", 
    "A": { "$push": "$A" }, 
    "B": { "$first": "$B" } 
}}, 
{ "$unwind": "$B" }, 
{ "$match": { "B": { "$ne": false } } }, 
{ "$group": { 
    "_id": "$_id", 
    "A": { "$first": "$A" }, 
    "B": { "$push": "$B" } 
}} 
+2

Qual è il tuo risultato _desired_? –

risposta

13

Sembravi sulla strada giusta, ci sono solo diversi approcci per la rimozione di quei valori di false dalla condizionale. Non puoi far sì che non restituisca nulla, ma non puoi eliminare i valori che non vuoi.

Se vuoi veramente "set" e hai MongoDB 2.6 o superiore disponibile, allora è fondamentalmente filtrare i valori false utilizzando $setDifference:

db.entities.aggregate([ 
    { "$unwind": "$entities" }, 
    { "$group": { 
     "_id": "$_id", 
     "A": { 
      "$addToSet": { 
       "$cond": [ 
        { "$eq": [ "$entities.type", "A" ] }, 
        "$entities.val", 
        false 
       ] 
      } 
     }, 
     "B": { 
      "$addToSet": { 
       "$cond": [ 
        { "$eq": [ "$entities.type", "B" ] }, 
        "$entities.val", 
        false 
       ] 
      } 
     } 
    }}, 
    { "$project": { 
     "A": { 
      "$setDifference": [ "$A", [false] ] 
     }, 
     "B": { 
      "$setDifference": [ "$B", [false] ] 
     } 
    }} 
]) 

O semplicemente come un passo utilizzando l'operatore $map all'interno $project:

db.entities.aggregate([ 
    {"$project": { 
     "A": { 
      "$setDifference": [ 
       { 
        "$map": { 
         "input": "$entities", 
         "as": "el", 
         "in": { 
          "$cond": [ 
           { "$eq": [ "$$el.type", "A" ] }, 
           "$$el.val", 
           false 
          ] 
         } 
        } 
       }, 
       [false] 
      ] 
     }, 
     "B": { 
      "$setDifference": [ 
       { 
        "$map": { 
         "input": "$entities", 
         "as": "el", 
         "in": { 
          "$cond": [ 
           { "$eq": [ "$$el.type", "B" ] }, 
           "$$el.val", 
           false 
          ] 
         } 
        } 
       }, 
       [false] 
      ] 
     } 
    }} 
]) 

o comunque rimanere con generale $unwind e $match operatori per filtrare questi:

db.entities.aggregate([ 
    { "$unwind": "$entities" }, 
    { "$group": { 
     "_id": "$_id", 
     "A": { 
      "$push": { 
       "$cond": [ 
        { "$eq": [ "$entities.type", "A" ] }, 
        "$entities.val", 
        false 
       ] 
      } 
     }, 
     "B": { 
      "$push": { 
       "$cond": [ 
        { "$eq": [ "$entities.type", "B" ] }, 
        "$entities.val", 
        false 
       ] 
      } 
     } 
    }}, 
    { "$unwind": "$A" }, 
    { "$match": { "A": { "$ne": false } } }, 
    { "$group": { 
     "_id": "$_id", 
     "A": { "$push": "$A" }, 
     "B": { "$first": "$B" } 
    }}, 
    { "$unwind": "$B" }, 
    { "$match": { "B": { "$ne": false } } }, 
    { "$group": { 
     "_id": "$_id", 
     "A": { "$first": "$A" }, 
     "B": { "$push": "$B" } 
    }} 
]) 

Utilizzo di $push per i normali array o $addToSet per set univoci.

+1

Awesome awnser ringrazia un milione :) L'operatore $ setDifference sarebbe perfetto (non l'avevo visto), ma purtroppo non stiamo eseguendo mongo 2.6. Grr .. Ho usato la seconda versione con una leggera modifica (vedi la modifica della domanda originale per riferimento futuro) – user1437515

+0

La query diventa così complicata che finisce per essere meglio raggruppare i risultati dopo di che lasciare che mongodb lo faccia. – kappaallday

Problemi correlati