2012-01-07 20 views
17

Ho due raccolte MongoDB che condividono un _id comune. Usando la shell mongo, voglio trovare tutti i documenti in una collezione che non hanno un _id corrispondente nell'altra collezione.Ottieni "i dati dalla raccolta b non nella raccolta a" in una query della shell MongoDB

Esempio:

> db.Test.insert({ "_id" : ObjectId("4f08a75f306b428fb9d8bb2e"), "foo" : 1 }) 
> db.Test.insert({ "_id" : ObjectId("4f08a766306b428fb9d8bb2f"), "foo" : 2 }) 
> db.Test.insert({ "_id" : ObjectId("4f08a767306b428fb9d8bb30"), "foo" : 3 }) 
> db.Test.insert({ "_id" : ObjectId("4f08a769306b428fb9d8bb31"), "foo" : 4 }) 
> db.Test.find() 
{ "_id" : ObjectId("4f08a75f306b428fb9d8bb2e"), "foo" : 1 } 
{ "_id" : ObjectId("4f08a766306b428fb9d8bb2f"), "foo" : 2 } 
{ "_id" : ObjectId("4f08a767306b428fb9d8bb30"), "foo" : 3 } 
{ "_id" : ObjectId("4f08a769306b428fb9d8bb31"), "foo" : 4 } 
> db.Test2.insert({ "_id" : ObjectId("4f08a75f306b428fb9d8bb2e"), "bar" : 1 }); 
> db.Test2.insert({ "_id" : ObjectId("4f08a766306b428fb9d8bb2f"), "bar" : 2 }); 
> db.Test2.find() 
{ "_id" : ObjectId("4f08a75f306b428fb9d8bb2e"), "bar" : 1 } 
{ "_id" : ObjectId("4f08a766306b428fb9d8bb2f"), "bar" : 2 } 

Ora voglio un po 'di query o le query che restituisce i due documenti di prova dove i _id di non corrispondono qualsiasi documento in Test2:

{ "_id" : ObjectId("4f08a767306b428fb9d8bb30"), "foo" : 3 } 
{ "_id" : ObjectId("4f08a769306b428fb9d8bb31"), "foo" : 4 } 

Ho provato varie combinazioni di $ not, $ ne, $ o, $ in ma non riescono a ottenere la combinazione e la sintassi giuste. Inoltre, non mi interessa se prima viene eseguito db.Test2.find({}, {"_id": 1}), salvato in una variabile, che viene poi utilizzata in una seconda query (anche se non riesco a farlo funzionare).

Aggiornamento: la risposta di Zachary che indicava $ nin ha risposto alla parte fondamentale della domanda. Ad esempio, questo funziona:

> db.Test.find({"_id": {"$nin": [ObjectId("4f08a75f306b428fb9d8bb2e"), ObjectId("4f08a766306b428fb9d8bb2f")]}}) 
{ "_id" : ObjectId("4f08a767306b428fb9d8bb30"), "foo" : 3 } 
{ "_id" : ObjectId("4f08a769306b428fb9d8bb31"), "foo" : 4 } 

Ma (e riconoscendo questo non è scalabile, ma cercando di comunque perché il suo non è un problema in questa situazione) non riesco ancora a combinare le due interrogazioni insieme nel guscio. Questo è il più vicino che posso ottenere, che è ovviamente meno ideale:

vals = db.Test2.find({}, {"_id": 1}).toArray() 
db.Test.find({"_id": {"$nin": [ObjectId(vals[0]._id), ObjectId(vals[1]._id)]}}) 

C'è un modo per restituire solo i valori nel comando find in modo che vals possono essere utilizzati direttamente come ingresso array per $ nin?

risposta

12

È necessario salvare gli _id dalla raccolta A per non estrarli nuovamente dalla raccolta B, ma è possibile farlo utilizzando $nin. Vedi Advanced Queries per tutti gli operatori MongoDB.

Vostri criteri fine, utilizzando l'esempio che ha dato sarebbe simile:

db.Test.find({"_id": {"$nin": [ObjectId("4f08a75f306b428fb9d8bb2e"), ObjectId("4f08a766306b428fb9d8bb2f")]}})

Si noti che questo approccio non scala. Se hai bisogno di una soluzione che ridimensiona, dovresti impostare un flag nelle raccolte A e B che indicano se _id si trova nell'altra raccolta e quindi eseguire una query al di fuori di quella.

aggiornato per la seconda parte:

La seconda parte è impossibile. MongoDB non supporta join o qualsiasi tipo di query incrociata tra le raccolte in una singola query. Interrogare da una raccolta, salvare i risultati e quindi eseguire query dal secondo è la tua unica scelta, a meno che tu non abbia incorporato i dati nelle righe come menzionato prima.

+1

Minor reclamo: il concetto è giusto, ma hai ottenuto il Test e il Test2 all'indietro nella risposta. "Ora desidero alcune query o query che restituiscano i due documenti in Test in cui _id non corrisponde a nessun documento in Test2" –

+0

'> db.Test.find ({" _ id ": {" $ nin ": [ObjectId (" 4f08a75f306b428fb9d8bb2e "), ObjectId (" 4f08a766306b428fb9d8bb2f ")]}});' dà '{ "_id": ObjectId (" 4f08a767306b428fb9d8bb30 "), "foo": 3} { "_id": ObjectId (" 4f08a769306b428fb9d8bb31") , "foo": 4} ' –

+0

Grazie, ha risposto alla parte fondamentale della domanda, ma questo non è molto utile senza rispondere anche alla seconda parte. Ho aggiornato la domanda per riflettere. – Raman

26

Risposta al follow-up. Io userei map().

Dato questo:

> b1 = {i: 1} 
> db.b.save(b1) 
> db.b.save({i: 2}) 
> db.a.save({_id: b1._id}) 

Tutto ciò che serve è:

> vals = db.a.find({}, {id: 1}).map(function(a){return a._id;}) 
> db.b.find({_id: {$nin: vals}}) 

che restituisce

{ "_id" : ObjectId("4f08c60d6b5e49fa3f6b46c1"), "i" : 2 } 
+0

Ahh, 'map', dolce! Funziona perfettamente. Vorrei poter accettare sia la risposta di Zachary che la tua. – Raman

+0

BTW, sono tutti i possibili metodi del cursore documentati da qualche parte? Non ho visto la funzione 'map' menzionata su http://www.mongodb.org/display/DOCS/Queries+and+Cursors, né su http://www.mongodb.org/display/DOCS/Advanced+ query # AdvancedQueries-CursorMethods. – Raman

+2

'map' è solo una buona, vecchia libreria di javascript standard che gira sulla matrice estratta da Mongo. La shell di Mongo supporta JS arbitrari. –

0

ho fatto uno script, che segna tutti i documenti sulla seconda collezione che appare nella prima raccolta. Quindi elaborato i documenti della seconda raccolta.

var first = db.firstCollection.aggregate([ {'$unwind':'$secondCollectionField'} ]) 

while (first.hasNext()){ var doc = first.next(); db.secondCollection.update({_id:doc.secondCollectionField} ,{$set:{firstCollectionField:doc._id}}); } 

... processo la seconda collezione che non ha alcun segno di

db.secondCollection.find({"firstCollectionField":{$exists:false}}) 
0

db.bar.find ({_ id: {$ nin: db.foo.find ({}, {_ id: 1}). toArray()}})

+0

Questa domanda è stata risposta più tempo fa. Aggiungi ulteriori informazioni sulla tua risposta o cancellala –

Problemi correlati