【发布时间】:2017-05-04 12:25:13
【问题描述】:
我有两个 mongo 集合,一个是指生产环境,另一个是测试环境。
如何比较我的两个集合之间的差异?
我尝试将它们转储到 bson,然后转换为 json。但我不能只对它们进行简单的比较,因为排序可能会有所不同,而且 json 文件太大而无法排序。
【问题讨论】:
标签: mongodb collections diff
我有两个 mongo 集合,一个是指生产环境,另一个是测试环境。
如何比较我的两个集合之间的差异?
我尝试将它们转储到 bson,然后转换为 json。但我不能只对它们进行简单的比较,因为排序可能会有所不同,而且 json 文件太大而无法排序。
【问题讨论】:
标签: mongodb collections diff
在 shell 中尝试以下操作,它将遍历集合中的每个项目并尝试根据 ID 匹配每个文档。
假设我们有 2 个集合 db.col1 和 db.col2:
> db.col1.find()
{ "_id" : 1, "item" : 1 }
{ "_id" : 2, "item" : 2 }
{ "_id" : 3, "item" : 3 }
{ "_id" : 4, "item" : 4 }
> db.col2.find()
{ "_id" : 1, "item" : 1 }
{ "_id" : 2, "item" : 2 }
{ "_id" : 3, "item" : 3 }
{ "_id" : 4, "item" : 4 }
然后我们可以创建一个 javascript 函数来比较 2 个集合
function compareCollection(col1, col2){
if(col1.count() !== col2.count()){
return false;
}
var same = true;
var compared = col1.find().forEach(function(doc1){
var doc2 = col2.findOne({_id: doc1._id});
same = same && JSON.stringify(doc1)==JSON.stringify(doc2);
});
return same;
}
然后调用如下:
> compareCollection(db.col1, db.col2)
true
如果我们有第三个集合db.col3
> db.col3.find()
{ "_id" : 1, "item" : 1 }
比较一下这个
> compareCollection(db.col1, db.col3)
false
我们会得到预期的结果。
如果我们还有第 4 个集合,它有匹配的文档但不同的数据 db.col4
> db.col4.find()
{ "_id" : 1, "item" : 10 }
{ "_id" : 2, "item" : 2 }
{ "_id" : 3, "item" : 3 }
{ "_id" : 4, "item" : 4 }
这也将返回false
> compareCollection(db.col1, db.col4)
false
【讨论】:
如果您只需要比较字段的子集(例如,您不需要比较 id),您可以通过以下方式进行。将集合导出到 csv,指定要比较的字段 (source):
mongoexport -d <db_name> -c <col_name> --fields "field1,field2" --type=csv | sort > export.csv
然后对 csv 文件执行简单的diff。请注意,csv 文件中的列顺序对应于--field 选项。
优点:
缺点:
mongoexport 对于大型数据库可能会很慢。要获取集合中所有文档的所有字段,请参阅this answer。
【讨论】:
使用 Studio 3T 对比 mongodb。 您也可以比较集合、数据库、单个记录。 只需要下载并连接mongo。 这是下载链接https://studio3t.com/
【讨论】:
【讨论】:
使用Kevin Smith response,我有一个新版本,仅用于比较并返回collectionB 没有与collectionA 比较的那些ID。当您有很多记录时,将结果保存在collectionC 中。
db.collectionA.find().forEach(function(doc1){
var doc2 = db.collectionB.findOne({_id: doc1._id});
if (!(doc2)) {
db.collectionC.insert(doc1);
}
});
【讨论】:
doc1.__v 和 doc2.__v,然后再对它们进行字符串化。
delete doc1.__v 的行
mongoexport 现在有一个--sort 选项:
例如:
$ mongo
test> db.coll.insertMany([
{ _id: 0, name: 'Alex' },
{ _id: 1, name: 'Bart' },
{ _id: 2, name: 'Maria' },
{ _id: 3, name: 'Aristotle' },
]);
{
"acknowledged": true,
"insertedIds": [
0,
1,
2,
3
]
}
导出:
mongoexport -d test -c coll --sort "{name: 1}"
2018-10-25T15:50:07.210+0300 connected to: localhost
{"_id":0.0,"name":"Alex"}
{"_id":3.0,"name":"Aristotle"}
{"_id":1.0,"name":"Bart"}
{"_id":2.0,"name":"Maria"}
2018-10-25T15:50:07.210+0300 exported 4 records
mongoexport -d test -c coll --sort "{name: -1}"
2018-10-25T15:49:42.010+0300 connected to: localhost
{"_id":2.0,"name":"Maria"}
{"_id":1.0,"name":"Bart"}
{"_id":3.0,"name":"Aristotle"}
{"_id":0.0,"name":"Alex"}
2018-10-25T15:49:42.011+0300 exported 4 records
导出集合后,您可以在命令行中执行 diff 或使用 Beyond Compare 等图形用户界面 (GUI) 工具。
仅供参考:如果您的文档可能具有相同的数据但具有不同的 _id 值:
导出时可以排除 _id 字段,如下所示:https://stackoverflow.com/a/49895549/728287
【讨论】:
从Mongo 4.4 开始,聚合框架提供了一个新的$unionWith 阶段,执行两个集合的并集(来自两个集合的组合管道结果到一个结果集中)。
更容易找到两个集合之间的差异:
// > db.test.find()
// { "a" : 9, "b" : 2 }
// { "a" : 4, "b" : 12 }
// { "a" : 3, "b" : 5 }
// { "a" : 0, "b" : 7 }
// { "a" : 7, "b" : 12 }
// > db.prod.find()
// { "a" : 3, "b" : 5 }
// { "a" : 4, "b" : 12 }
// { "a" : 3, "b" : 5 }
// { "a" : 0, "b" : 7 }
db.test.aggregate(
{ $unset: "_id" },
{ $project: { from: "test", doc: "$$ROOT" } },
{ $unionWith: {
coll: "prod",
pipeline: [
{ $unset: "_id" },
{ $project: { from: "prod", doc: "$$ROOT" } }
]
}},
{ $group: {
_id: "$doc",
test: { $sum: { $cond: [ { $eq: ["$from", "test"] }, 1, 0 ] } },
prod: { $sum: { $cond: [ { $eq: ["$from", "prod"] }, 1, 0 ] } }
}},
{ $match: { $expr: { $ne: ["$test", "$prod"] } } }
)
// { "_id" : { "a" : 7, "b" : 12 }, "test" : 1, "prod" : 0 }
// { "_id" : { "a" : 9, "b" : 2 }, "test" : 1, "prod" : 0 }
// { "_id" : { "a" : 3, "b" : 5 }, "test" : 1, "prod" : 2 }
这个:
$unsets _id 以便后者能够在不考虑 _id 的情况下自行处理 $group 文档(因为它在其他集合中可能不同)。$projects 字段 from 的值是文档来自的集合(test 或 prod),以便在我们合并两个集合时跟踪文档的来源。李>
$projects 字段doc,其值为文档本身(感谢$$ROOT 变量)。这是将用于$group 文档的字段。$unionWith prod 集合,以便将两个集合中的文档合并到同一个聚合管道中。 pipeline 参数是一个可选的聚合管道,在将文档插入下游管道之前,应用于正在合并的集合 (prod) 中的文档。我们正在应用与 test 文档相同的 $unset/$project 阶段。$groups test 和 prod 文档基于我们为表示实际文档而创建的 doc 字段。我们将test 和prod 这两个字段累积为来自一个或另一个集合的分组文档的$sum(计数)(通过$cond if 表达式)。$matches 通过仅保留测试和产品文档数量不同的项目来生成分组元素:两个集合之间的实际差异。【讨论】: