2017-06-22 136 views
3

我有两个集合(coll_1,coll_2),每个集合有数百万个文档。如何比较两个集合中的所有文档和数百万个文档,并在第三个集合中将差异写入MongoDB中

这两个集合实际上是通过运行来自同一数据源的代码的两个版本创建的,因此两个集合将具有相同数量的文档,但两个集合中的文档可能缺少一个字段或子文档或具有不同的值,但这两个集合的文档将具有相同的primary_key_id,这被索引。

我保存在数据库这个javascript函数来获得差异

db.system.js.save({ 
    _id: "diffJSON", value: 
    function(obj1, obj2) { 
     var result = {}; 
     for (key in obj1) { 
      if (obj2[key] != obj1[key]) result[key] = obj2[key]; 
      if (typeof obj2[key] == 'array' && typeof obj1[key] == 'array') 
       result[key] = arguments.callee(obj1[key], obj2[key]); 
      if (typeof obj2[key] == 'object' && typeof obj1[key] == 'object') 
       result[key] = arguments.callee(obj1[key], obj2[key]); 
     } 
     return result; 
    } 
}); 

其中运行良好这样

diffJSON(testObj1, testObj2); 

问题:如何在coll1和coll2运行diffJSON,和将diffJSON结果与primary_key_id一起输出到coll3。

我是MongoDB的新手,我知道JOINS不像RDBMS那样工作,所以我想知道是否必须在一个集合中复制两个比较文档,然后运行diffJSON函数。

此外,大部分时间(比如说90%)的文档在两个集合中都是相同的,我只需要知道只有10%的文档有任何差异。

下面是一个简单的例子文件: (但真正的DOC约为15K的大小,只是让你知道的比例)

var testObj1 = { test:"1",test1: "2", tt:["td","ax"], tr:["Positive"] ,tft:{test:["a"]}}; 
var testObj2 = { test:"1",test1: "2", tt:["td","ax"], tr:["Negative"] }; 

如果你知道一个更好的方式来diff的文件,请随时建议。

回答

3

你可以使用一个简单的shell脚本来实现这一点。首先创建一个名为script.js文件,并在粘贴此代码:

// load previously saved diffJSON() function 
db.loadServerScripts(); 

// get all the document from collection coll1 
var cursor = db.coll1.find(); 

if (cursor != null && cursor.hasNext()) { 
    // iterate over the cursor 
    while (cursor.hasNext()){ 
    var doc1 = cursor.next(); 
    // get the doc with the same _id from coll2 
    var id = doc1._id; 
    var doc2 = db.coll2.findOne({_id: id}); 
    // compute the diff 
    var diff = diffJSON(doc2, doc1); 
    // if there is a difference between the two objects 
    if (Object.keys(diff).length > 0) { 
    diff._id = id; 
    // insert the diff in coll3 with the same _id 
    db.coll3.insert(diff); 
    } 
    } 
} 

在这个剧本我以为你primary_key_id场。

然后从您的壳像这样执行:

mongo --host hostName --port portNumber databaseName < script.js 

其中databaseName是来到含集合coll1coll2数据库。

这个样本文件(只是增加了一个_id场对您的文档):

var testObj1 = { _id: 1, test:"1",test1: "2", tt:["td","ax"], tr:["Positive"] ,tft:{test:["a"]}}; 
var testObj2 = { _id: 1, test:"1",test1: "2", tt:["td","ax"], tr:["Negative"] }; 

脚本将保存在coll3以下文档:

{ "_id" : 1, "tt" : { }, "tr" : { "0" : "Positive" } } 
0

该解决方案基于提出的一个通过felix(我没有必要的评价他的名声)。我做了带来了重要的性能改进一些小的改动,以他的剧本:

// load previously saved diffJSON() function 
db.loadServerScripts(); 

// get all the document from collection coll1 and coll2 
var cursor1 = db.coll1.find().sort({'_id': 1}); 
var cursor2 = db.coll2.find().sort({'_id': 1}); 

if (cursor1 != null && cursor1.hasNext() && cursor2 != null && cursor2.hasNext()) { 
    // iterate over the cursor 
    while (cursor1.hasNext() && cursor2.hasNext()){ 
    var doc1 = cursor1.next(); 
    var doc2 = cursor2.next(); 
    var pk = doc1._id 
    // compute the diff 
    var diff = diffJSON(doc2, doc1); 
    // if there is a difference between the two objects 

    if (Object.keys(diff).length > 0) { 
    diff._id = pk; 
    // insert the diff in coll3 with the same _id 
    db.coll3.insert(diff); 
    } 
    } 
} 

两个光标用于获取由主键分类数据库所有条目。这是一个非常重要的方面,带来了大部分的性能改进。通过检索按主键排序的文档,我们确保通过主键正确匹配它们。这是基于这两个集合拥有相同数据的事实。

这样我们可以避免对coll1中的每个文档调用coll2。这看起来可能是微不足道的,但我们正在谈论的是100万次调用,这给数据库带来了很大的压力。

另一个重要的假设是主键字段是_id。如果不是这种情况,在主键字段上有一个唯一的索引是至关重要的。否则,该脚本可能会使文档与相同的主键不匹配。

相关问题