Получить «данные из коллекции b, а не из коллекции a» в запросе оболочки MongoDB - PullRequest
27 голосов
/ 08 января 2012

У меня есть две коллекции MongoDB, которые имеют общий _id.Используя оболочку mongo, я хочу найти все документы в одной коллекции, у которых нет совпадающего _id в другой коллекции.

Пример:

> 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 }

Теперь я хочу запрос или запросыкоторый возвращает два документа в Test, где _id не соответствуют ни одному документу в Test2:

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

Я пробовал различные комбинации $ not, $ ne, $ или, $ in, но просто не могуполучить правильную комбинацию и синтаксис.Кроме того, я не против, если сначала выполняется db.Test2.find({}, {"_id": 1}), сохраняемый в некоторой переменной, которая затем используется во втором запросе (хотя я не могу заставить это работать также).

Обновление : ответ Захария, указывающий на нин, ответил на ключевую часть вопроса.Например, это работает:

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

Но (и признание того, что это не масштабируемо, но в любом случае пытаюсь сделать это, потому что это не проблема в этой ситуации), я все еще не могу объединить два запроса вместе в оболочке,Это самое близкое, что я могу получить, что явно не идеально:

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

Есть ли способ вернуть только значения в команде поиска, чтобы vals можно было использовать непосредственно как вход массива для $нин

Ответы [ 4 ]

37 голосов
/ 24 июня 2016

В Mongo 3.2 следующий код работает

db.collectionb.aggregate([
    {
      $lookup:
        {
          from: "collectiona",
          localField: "collectionb_fk",
          foreignField: "collectiona_fk",
          as: "matched_docs"
        }
   },
   {
      $match: { "matched_docs": { $eq: [] } }
   }
]);

на основании этого https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#use-lookup-with-an-array примера

31 голосов
/ 08 января 2012

Ответ на ваше наблюдение.Я бы использовал map ().

Учитывая это:

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

Все, что вам нужно:

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

, который возвращает

{ "_id" : ObjectId("4f08c60d6b5e49fa3f6b46c1"), "i" : 2 }
16 голосов
/ 08 января 2012

Вам нужно будет сохранить _ids из коллекции A, чтобы не извлекать их снова из коллекции B, но вы можете сделать это, используя $nin. См. Расширенные запросы для всех операторов MongoDB.

Ваш конечный запрос, используя приведенный вами пример, будет выглядеть примерно так:

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

Обратите внимание, что этот подход не будет масштабироваться. Если вам нужно решение, которое масштабируется, вы должны установить флаг в коллекциях A и B, указывающий, находится ли _id в другой коллекции, а затем запросить его вместо этого.

Обновлено для второй части:

Вторая часть невозможна. MongoDB не поддерживает объединения или какие-либо перекрестные запросы между коллекциями в одном запросе. Запрос из одной коллекции, сохранение результатов, а затем запрос из второй - ваш единственный выбор, если только вы не внедрили данные в сами строки, как я упоминал ранее.

2 голосов
/ 03 декабря 2015

Я сделал скрипт, отмечающий все документы во второй коллекции, которая появляется в первой коллекции. Затем обрабатывается вторая коллекция документов.

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

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

... обработать вторую коллекцию без метки

db.secondCollection.find({"firstCollectionField":{$exists:false}})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...