2015-06-25 7 views
6

Supposto che disponga di due raccolte, A e B.Ottenere la differenza di due raccolte strutturate in modo diverso

A contiene documenti semplici della forma seguente:

{ _id: '...', value: 'A', data: '...' } 
{ _id: '...', value: 'B', data: '...' } 
{ _id: '...', value: 'C', data: '...' } 
… 

B contiene oggetti nidificati come questo:

{ _id: '...', values: [ 'A', 'B' ]} 
{ _id: '...', values: [ 'C' ]} 
… 

Ora ciò che può accadere è che ci sono documenti in A che non fanno riferimento qualsiasi documento in B o che ci siano documenti di riferimento in B che non sono presenti in A.

Chiamiamoli "orfani".

La mia domanda ora è: come trovo quei documenti orfani, in un modo più efficiente? Alla fine, quello di cui ho bisogno è il loro campo _id.

Finora ho usato unwind a "appiattire" A, e calcolato la differenza con il differenceWith function of Ramda, ma questo richiede molto tempo ed è di sicuro non è realmente efficace, come faccio io tutto il lavoro sul client invece che nel database.

Ho visto che c'è un operatore $setDifference in MongoDB, ma non l'ho fatto funzionare.

Qualcuno può indicarmi la giusta direzione, come risolvere questi problemi utilizzando Node.js e eseguire la maggior parte (tutti?) Del lavoro nel database? Ogni suggerimento è apprezzato :-)

+3

'Provalo al comando:' var myCursor = db.B.find(); var B = myCursor.hasNext()? myCursor.next(): null; db.A.find ({valore: {$ nin: B.valori}}); Speriamo che questo aiuti – karthick

+0

@ karthick.k Basta postare la mia risposta lunga e dettagliata! La tua soluzione sembra molto meglio! Lascio il mio dentro, dal momento che mostra come farlo con la pipeline di aggregazione ... – cessor

+0

@cessor Diverse idee e soluzioni sono sempre buone :) – karthick

risposta

6

In MongoDb è possibile utilizzare la pipeline di aggregazione per ciò che si sta tentando. Se questo non ti aiuta puoi usare MapReduce ma è un po 'più complicato.

Per questo esempio ho chiamato le due raccolte "Tag" e "Papers", dove Tag si chiama "B" nel tuo esempio, e Papers sarebbe "A".

Per prima cosa otteniamo l'insieme di valori effettivamente esistenti e fanno riferimento a documenti. Per questo, appiattiamo ogni valore nella raccolta di tag e li ricomponiamo. Unwinding crea un documento con _id originale per ogni valore nella matrice 'values'. Questa lista piatta viene quindi ricordata e i loro id ignorati.

var referenced_tags = db.tags.aggregate(
    {$unwind: '$values'}, 
    {$group: { 
     _id: '', 
     tags: { $push: '$values'} 
    } 
}); 

Ciò restituisce:

{ "_id" : "", "tags" : [ "A", "B", "C"] } 

Questa lista è una raccolta di tutti i valori in tutti i documenti.

Quindi, si crea una raccolta simile, contenente il set di tag dei documenti disponibili. Questo non ha bisogno del passo di svolgimento, dal momento che la _id è un valore scalare (= non una lista)

var papers = db.papers.aggregate(
    {$group: { 
     _id: '', 
     tags: {$push: '$value'} 
    } 
}); 

cedendo

{ "_id" : "", "tags" : [ "A", "B", "C", "D"] } 

Come si può già vedere, dal set che ho messo in nel database, sembra esserci un Documento (Carta) in A con l'id "D", che non è referenziato nella collezione di tag ed è quindi un orfano.

È ora possibile calcolare la differenza impostato in qualsiasi modo tu voglia, questo potrebbe essere lento, ma è adatto come un esempio:

var a = referenced_tags.tags; 
var b = tags.tags; 
var delta = a.filter(function (v) { return b.indexOf(v) < 0; }); 

Come passo successivo, è possibile trovare gli ID, cercando di questi valori in delta, e sporgenti solo loro iD:

db.papers.find({'value' : {'$in': delta}}, {'_id': 1}) 

Tornando:

{ "_id" : ObjectId("558bd2...44f6a") } 

MODIFICA: Mentre questo mostra chiaramente come affrontare questo problema con il framework di aggregazione, questa non è una soluzione fattibile. Uno non ha nemmeno bisogno di aggregazione, dal momento che MongoDb è molto intelligente:

db.papers.find({'value' : {'$nin': tags.values }}, {'_id': 1}) 

Dove tag è

var cursor = db.tags.find(); 
var tags = cursor.hasNext() : cusor.next() : null; 

Come sottolineato da @ karthick.k

+0

Prima di tutto: grazie mille per la tua risposta dettagliata :-). Fondamentalmente ho capito, tranne il motivo per cui si calcola la differenza di set sul client, non nel database. Ciò è effettivamente possibile? –

+0

Lo stavo facendo sulla riga di comando. Vedo che non è assolutamente necessario farlo in questo modo, come ha mostrato l'ultimo commento op. Se dovessi eseguirlo automaticamente da qualche parte, proverei a implementarlo in map/ridurre (supponendo che tu stia solo dando un picco a ciò che stai facendo in realtà ...) - Dal momento che hai chiesto informazioni sull'aggregazione su Twitter, ero pronto per fai in questo modo, un sacco di client/server coinvolti ... Non è una soluzione fattibile, ma forse un riferimento all'apprendimento ... – cessor

+0

Risposta impressionante +1 – karthick

Problemi correlati