在两个MongoDB集合之间比较文档

9

我将根据两个现有集合之间的比较来填充第三个集合,涉及到it技术相关内容。

需要进行比较的两个集合具有以下模式:

// Settings collection:
{
  "Identifier":"ABC123",
  "C":"1",
  "U":"V",
  "Low":116,
  "High":124,
  "ImportLogId":1
}

// Data collection
{
  "Identifier":"ABC123",
  "C":"1",
  "U":"V",
  "Date":"11/6/2013 12AM",
  "Value":128,
  "ImportLogId": 1
}

我是MongoDB和NoSQL的新手,因此很难掌握如何操作。相应的SQL查询可能如下:

SELECT s.Identifier, r.ReadValue, r.U, r.C, r.Date
FROM Settings s
JOIN Reads r
  ON s.Identifier = r.Identifier
  AND s.C = r.C
  AND s.U = r.U
WHERE (r.Value <= s.Low OR r.Value >= s.High)

在这种情况下,使用示例数据,我希望返回一条记录,因为来自Data集合的值大于来自setting集合的高值。是否可以使用Mongo查询或MapReduce实现此目标,或者是不良的集合结构(即可能应该将所有内容放入一个集合中)?
还有几个附加说明: Settings集合每个“标识符”应该只有1条记录。Data集合将有许多记录每个“标识符”。此过程可能会一次扫描数十万个文档,因此需要考虑资源问题。

1
为什么不使用diff命令? - myildirim
3
这种类型的事情并不是 MongoDB 真正设计用来处理的。 - WiredPrairie
4个回答

4

使用 MongoDB 没有好的方式来执行这样的操作。如果您想要一个不好的方式,可以使用以下代码:

db.settings.find().forEach(
    function(doc) {
        data = db.data.find({
            Identifier: doc.Idendtifier,
            C: doc.C,
            U: doc.U,
            $or: [{Value: {$lte: doc.Low}}, {Value: {$gte: doc.High}}]
        }).toArray();
        // Do what you need
    }
) 

但是不要期望它能像任何一个优秀的关系数据库管理系统一样表现得好。

你可以重建你的架构并嵌入从数据集合中提取的文档,如下:

{
    "_id" : ObjectId("527a7f4b07c17a1f8ad009d2"),
    "Identifier" : "ABC123",
    "C" : "1",
    "U" : "V",
    "Low" : 116,
    "High" : 124,
    "ImportLogId" : 1,
    "Data" : [
        {
            "Date" : ISODate("2013-11-06T00:00:00Z"),
            "Value" : 128
        },
        {
            "Date" : ISODate("2013-10-09T00:00:00Z"),
            "Value" : 99
        }
    ]
}

如果嵌入式文档数量较少,则可能有效,但说实话,处理文档数组远非愉快的体验。更不用说随着数据数组大小的增长,您很容易达到文档大小限制。

如果这种操作在您的应用程序中很常见,我建议使用不同的解决方案。尽管我很喜欢MongoDB,但它仅适用于特定类型的数据和访问模式。


1

Mongo 4.4 开始,我们可以使用新的 $unionWith 聚合阶段和传统的 $group 阶段来实现这种类型的“join”:

// > db.settings.find()
//   { "Identifier" : "ABC123", "C" : "1", "U" : "V", "Low" : 116 }
//   { "Identifier" : "DEF456", "C" : "1", "U" : "W", "Low" : 416 }
//   { "Identifier" : "GHI789", "C" : "1", "U" : "W", "Low" : 142 }
// > db.data.find()
//   { "Identifier" : "ABC123", "C" : "1", "U" : "V", "Value" : 14 }
//   { "Identifier" : "GHI789", "C" : "1", "U" : "W", "Value" : 43 }
//   { "Identifier" : "ABC123", "C" : "1", "U" : "V", "Value" : 45 }
//   { "Identifier" : "DEF456", "C" : "1", "U" : "W", "Value" : 8  }
db.data.aggregate([
  { $unionWith: "settings" },
  { $group: {
      _id: { Identifier: "$Identifier", C: "$C", U: "$U" },
      Values: { $push: "$Value" },
      Low: { $mergeObjects: { v: "$Low" } }
  }},
  { $match: { "Low.v": { $lt: 150 } } },
  { $out: "result-collection" }
])
// > db.result-collection.find()
//   { _id: { Identifier: "ABC123", C: "1", U: "V" }, Values: [14, 45], Low: { v: 116 } }
//   { _id: { Identifier: "GHI789", C: "1", U: "W" }, Values: [43], Low: { v: 142 } }

这个:

  • 通过新的 $unionWith 阶段,将两个集合合并到管道中。

  • 继续使用 $group 阶段:

    • 基于 IdentifierCU 对记录进行分组
    • Value 累加到数组中
    • 通过 $mergeObjects 操作累加 Low,以获取不为 nullLow 值。因为使用 $first 可能会先取到 null(对于来自数据集合的元素)。而 $mergeObjects 在合并包含非空值的对象时会丢弃 null 值。
  • 然后丢弃连接记录中 Low 值大于 150 的记录。

  • 最后通过 $out 阶段将结果记录输出到第三个集合中。


1

如果没有JOIN的概念,您必须改变方法并反规范化。

在您的情况下,看起来您正在进行数据日志验证。我的建议是循环设置集合,并使用每个设置中的findAndModify运算符,以便在匹配的数据集合记录上设置验证标志;之后,您可以只使用数据集合上的查找运算符,通过新标志进行过滤。


0
我们开发了一个名为数据比较与同步的功能,可能能够在这里提供帮助。
它可以让您比较两个MongoDB集合并查看差异(例如,发现相同、缺失或不同的字段)。
然后,您可以将这些比较结果导出到CSV文件中,并使用该文件创建新的第三个集合。

Export differences in two MongoDB collections to a CSV file

声明:我们是MongoDB GUI Studio 3T的创建者。


我们可以比较不同名称的集合吗? - Kumar Vikramjeet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接