获得;来自集合b的数据不在集合a中;在MongoDB shell查询中

获得;来自集合b的数据不在集合a中;在MongoDB shell查询中,mongodb,Mongodb,我有两个MongoDB集合,它们共享一个公共的_id。使用mongo shell,我希望在一个集合中查找在另一个集合中没有匹配的_id的所有文档 例如: > db.Test.insert({ "_id" : ObjectId("4f08a75f306b428fb9d8bb2e"), "foo" : 1 }) > db.Test.insert({ "_id" : ObjectId("4f08a766306b428fb9d8bb2f"), "foo" : 2 }) > db.Tes

我有两个MongoDB集合,它们共享一个公共的_id。使用mongo shell,我希望在一个集合中查找在另一个集合中没有匹配的_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 }
现在,我需要一些查询返回测试中的两个文档,其中_id与测试2中的任何文档都不匹配:

{ "_id" : ObjectId("4f08a767306b428fb9d8bb30"), "foo" : 3 }
{ "_id" : ObjectId("4f08a769306b428fb9d8bb31"), "foo" : 4 }
我尝试了$not、$ne、$in的各种组合,但就是找不到正确的组合和语法。另外,我不介意是否先执行
db.Test2.find({},{u id:1})
,保存到某个变量中,然后在第二个查询中使用该变量(尽管我也无法让它工作)

更新:扎卡里指向$nin的答案回答了问题的关键部分。例如,这项工作:

> db.Test.find({"_id": {"$nin": [ObjectId("4f08a75f306b428fb9d8bb2e"), ObjectId("4f08a766306b428fb9d8bb2f")]}})
{ "_id" : ObjectId("4f08a767306b428fb9d8bb30"), "foo" : 3 }
{ "_id" : ObjectId("4f08a769306b428fb9d8bb31"), "foo" : 4 }
但是(承认这是不可伸缩的,但无论如何都要尝试这样做,因为在这种情况下这不是问题),我仍然无法在shell中将这两个查询组合在一起。这是我能得到的最接近的结果,这显然不太理想:

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

是否有方法仅返回find命令中的值,以便将VAL直接用作$nin的数组输入?

您必须保存集合a中的_id,以避免再次从集合B中提取它们,但您可以使用
$nin
执行此操作。有关所有MongoDB操作符,请参阅

使用您给出的示例,您的最终查询如下所示:

db.Test.find({"_id": {"$nin": [ObjectId("4f08a75f306b428fb9d8bb2e"), 
 ObjectId("4f08a766306b428fb9d8bb2f")]}})`
请注意,这种方法不会扩展。如果您需要一个可扩展的解决方案,那么应该在集合a和集合B中设置一个标志,指示_id是否在另一个集合中,然后将其查询掉

第二部分更新:


第二部分是不可能的。MongoDB不支持单个查询中集合之间的联接或任何类型的交叉查询。从一个集合中查询、保存结果然后从第二个集合中查询是您唯一的选择,除非您像我前面提到的那样将数据嵌入到行中。

回答您的后续问题。我会使用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 }

我制作了一个脚本,标记第一个集合中出现的第二个集合上的所有文档。然后处理第二次收集文件

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

在Mongo3.2中,以下代码似乎可以工作

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

基于此示例

轻微投诉:概念是正确的,但答案中的测试和测试2是反向的。“现在,我需要一些查询返回测试中的两个或多个文档,其中_id与Test2中的任何文档都不匹配”
>db.Test.find({“_id”:{“$nin”:[ObjectId(“4f08a75f306b428fb9d8bb2e”)、ObjectId(“4F08A76306B428FB9D8BB2F”)})
给出了{code>{“_id”:ObjectId(“4f08a76730b428fb9d8bb30”),“foo”:3}{“_id”:ObjectId(“4f08a769306b428fb9d8bb31”),“foo”:4}谢谢,回答了问题的关键部分,但如果不回答第二部分,这不是很有用。我更新了问题以反映。从Mongo3.2开始,您可以在聚合框架中使用$lookup,并使用来自其他集合的数据ah,
map
,sweet!那很好用。希望我能接受Zachary的答案和你的答案。顺便问一下,所有可能的游标方法都记录在什么地方了吗?我没有看到在中提到的
map
函数,也没有在中提到。
map
只是一个很好的老式javascript标准库函数,它运行在从Mongo中取出的数组上。Mongo shell支持任意JS。find命令的结果是游标,而不是数组。那么,在调用map等javascript函数时,shell是否隐式地将光标转换为数组呢?否则,我将不得不执行
find(…).toArray().map(…)
。您是对的,看起来它只是显式定义的:。另外,请查看
(游标中的var键){print(key);}
(我是如何找到它的)。如果我没有弄错的话,这与问题的要求正好相反,对吗?这将返回同时存在于两者中的对象,而不是存在于和非B中的对象。此代码应等效于左外部联接,而不是内部联接。在这里也检查一下@JHixson$match:{“matched_docs:{$eq:[]}}会起作用。因此,它会获取不匹配的记录。您必须是MongoDB专家。我自己永远也无法解决这个问题