在MongoDB shell查询中获取"集合b中的数据不在集合中"

Ram*_*man 29 mongodb

我有两个共享一个公共_id的MongoDB集合.使用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 }
Run Code Online (Sandbox Code Playgroud)

现在我想要一些查询或查询返回Test中的两个文档,其中_id与Test2中的任何文档都不匹配:

{ "_id" : ObjectId("4f08a767306b428fb9d8bb30"), "foo" : 3 }
{ "_id" : ObjectId("4f08a769306b428fb9d8bb31"), "foo" : 4 }
Run Code Online (Sandbox Code Playgroud)

我已经尝试了$ not,$ ne,$或$ in的各种组合,但却无法获得正确的组合和语法.另外,我不介意db.Test2.find({}, {"_id": 1})是先执行,保存到某个变量,然后在第二个查询中使用(尽管我无法使其工作).

更新:Zachary的答案指向$ nin回答了问题的关键部分.例如,这有效:

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

但是(并且承认这不是可扩展的,但无论如何都要尝试它,因为它在这种情况下不是问题)我仍然无法在shell中将两个查询组合在一起.这是我能得到的最接近的,显然不太理想:

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

有没有办法只返回find命令中的值,以便val可以直接用作$ nin的数组输入?

Nik*_*kas 42

在mongo 3.2中,以下代码似乎有效

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

基于此https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#use-lookup-with-an-array示例


小智 33

回答你的后续行动.我用map().

鉴于这种:

> b1 = {i: 1}
> db.b.save(b1)
> db.b.save({i: 2})
> db.a.save({_id: b1._id})
Run Code Online (Sandbox Code Playgroud)

所有你需要的是:

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

返回

{ "_id" : ObjectId("4f08c60d6b5e49fa3f6b46c1"), "i" : 2 }
Run Code Online (Sandbox Code Playgroud)

  • `map`只是一个很好的,老式的javascript标准库函数,它运行在从Mongo中拔出的数组上.Mongo shell支持任意JS. (2认同)

Zac*_*ker 17

您必须将集合A中的_ids保存为不再从集合B中提取它们,但您可以使用它$nin.请参阅所有MongoDB运算符的高级查询.

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

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

请注意,此方法不会扩展.如果您需要一个可扩展的解决方案,您应该在集合A和B中设置一个标志,指示_id是否在另一个集合中,然后查询它.

第二部分更新:

第二部分是不可能的.MongoDB不支持单个查询中的集合之间的连接或任何类型的交叉查询.从一个集合中查询,保存结果然后从第二个查询是唯一的选择,除非您将数据嵌入行中,如前所述.