假设我有两个集合,A
和B
。
A
包含以下格式的简单文档:
{ _id: '...', value: 'A', data: '...' }
{ _id: '...', value: 'B', data: '...' }
{ _id: '...', value: 'C', data: '...' }
…
B
包含这样的嵌套对象:
{ _id: '...', values: [ 'A', 'B' ]}
{ _id: '...', values: [ 'C' ]}
…
现在可能发生的是A
中的文档未被B
中的任何文档引用,或者B
中的参考文档在A
中不存在{ {1}}。
让我们称他们为#34;孤儿"。
我现在的问题是:如何以最有效的方式找到这些孤立的文档?最后,我需要的是他们的_id
字段。
到目前为止,我已使用unwind
来#34;展平" A
,并使用differenceWith
function of Ramda计算差异,但这需要相当长的时间,并且肯定不是非常有效,因为我在客户端而不是在数据库中完成所有工作。
我已经看到MongoDB中有一个$setDifference
运算符,但我没有让它工作。
有人能指出我正确的方向,如何使用Node.js解决这个问题,并在数据库中运行大部分(全部?)工作?任何提示都表示赞赏: - )
答案 0 :(得分:6)
在MongoDb中,您可以使用聚合管道来处理您正在尝试的内容。如果这没有用,你可以使用MapReduce,但它有点复杂。
对于这个例子,我将两个集合命名为“Tags”和“Papers”,其中Tags在您的示例中命名为“B”,Papers将为“A”。
首先,我们获取实际存在的值集并引用文档。为此,我们将标签集合中的每个值展平并将其打包在一起。展开为'values'数组中的每个值创建一个包含原始_id的文档。然后重新收集此平面列表并忽略它们的ID。
var referenced_tags = db.tags.aggregate(
{$unwind: '$values'},
{$group: {
_id: '',
tags: { $push: '$values'}
}
});
返回:
{ "_id" : "", "tags" : [ "A", "B", "C"] }
此列表是所有文档中所有值的集合。
然后,您创建一个类似的集合,其中包含可用文档的标记集。这不需要展开步骤,因为_id是标量值(=不是列表)
var papers = db.papers.aggregate(
{$group: {
_id: '',
tags: {$push: '$value'}
}
});
产生
{ "_id" : "", "tags" : [ "A", "B", "C", "D"] }
正如您已经看到的那样,从我放入数据库的集合中,A中似乎有一个文档(Paper),其ID为“D”,未在tags集合中引用,因此是一个孤儿。
您现在可以以任何您喜欢的方式计算差异集,这可能会很慢但适合作为示例:
var a = referenced_tags.tags;
var b = tags.tags;
var delta = a.filter(function (v) { return b.indexOf(v) < 0; });
下一步,您可以通过在delta中查找这些值并仅投影其ID来找到ID:
db.papers.find({'value' : {'$in': delta}}, {'_id': 1})
返回:
{ "_id" : ObjectId("558bd2...44f6a") }
修改强> 虽然这很好地展示了如何使用聚合框架来解决这个问题,但这不是一个可行的解决方案。人们甚至不需要聚合,因为MongoDb非常聪明:
db.papers.find({'value' : {'$nin': tags.values }}, {'_id': 1})
标签在哪里
var cursor = db.tags.find();
var tags = cursor.hasNext() : cusor.next() : null;
正如@ karthick.k所指出的