我有两个共享一个公共_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 }
现在我想要一些查询或查询返回Test中的两个文档,其中_id与Test2中的任何文档都不匹配:
{ "_id" : ObjectId("4f08a767306b428fb9d8bb30"), "foo" : 3 }
{ "_id" : ObjectId("4f08a769306b428fb9d8bb31"), "foo" : 4 }
我尝试了各种$ 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 }
但是(并且承认这不是可扩展的,但无论如何都要尝试它,因为它在这种情况下不是问题)我仍然无法在shell中将两个查询组合在一起。这是我能得到的最接近的,这显然不太理想:
vals = db.Test2.find({}, {"_id": 1}).toArray()
db.Test.find({"_id": {"$nin": [ObjectId(vals[0]._id), ObjectId(vals[1]._id)]}})
有没有办法只返回find命令中的值,以便val可以直接用作$ nin的数组输入?
答案 0 :(得分:37)
在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示例
答案 1 :(得分:30)
回答你的后续行动。我使用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 }
答案 2 :(得分:16)
您必须将集合A中的_ids保存为不再从集合B中提取它们,但您可以使用$nin
执行此操作。有关所有MongoDB运算符,请参阅Advanced Queries。
您的最终查询,使用您提供的示例,如下所示:
db.Test.find({"_id": {"$nin": [ObjectId("4f08a75f306b428fb9d8bb2e"),
ObjectId("4f08a766306b428fb9d8bb2f")]}})`
请注意,此方法无法扩展。如果你需要一个可扩展的解决方案,你应该在集合A和B中设置一个标志,指示_id是否在另一个集合中,然后再查询它。
第二部分已更新:
第二部分是不可能的。 MongoDB不支持单个查询中的集合之间的连接或任何类型的交叉查询。从一个集合中查询,保存结果然后从第二个查询是唯一的选择,除非您将数据嵌入到行中,如前所述。
答案 3 :(得分:2)
我制作了一个脚本,标记第一个集合中出现的第二个集合上的所有文档。然后处理了第二个收集文件。
NSString *searchedString = @"<p style=\"margin-bottom: 0pt;\"> <a data-mce-href=\"NewdownloadSingle? db_value=Test_2.jpg%23ms_attach_delimiter%23784123161&id=102003&proc=1085821&processInstanceId=1085821&attach_seq=203&oper=R\" href=\"javascript:void(0)\"onclick=\"parent.openRTFAttachment(784123161,link);\"title=\"Test_2.jpg\" alt=\"Test_2.jpg\" target=\"_blank\">Test_2.jpg</a></p>";
NSRange searchedRange = NSMakeRange(0, [searchedString length]);
NSString *pattern = @"RTFAttachment[(]([0-9]+)";
NSError *error = nil;
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern: pattern options:0 error:&error];
NSArray* matches = [regex matchesInString:searchedString options:0 range: searchedRange];
for (NSTextCheckingResult* match in matches) {
NSLog(@"match: %@", [searchedString substringWithRange:[match range]]);
NSLog(@"RTFAttachment number: %@", [searchedString substringWithRange:[match rangeAtIndex:1]]);
}
...处理没有标记的第二个集合
var first = db.firstCollection.aggregate([ {'$unwind':'$secondCollectionField'} ])
while (first.hasNext()){ var doc = first.next(); db.secondCollection.update( {_id:doc.secondCollectionField} ,{$set:{firstCollectionField:doc._id}} ); }