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

时间:2013-11-06 16:22:09

标签: mongodb

我有两个现有的集合,需要根据两个现有集合之间的比较来填充第三个集合。需要比较的两个集合具有以下模式:

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集合中的值大于设置集合中的高值。这可能是使用Mongo查询或map reduce,还是这个糟糕的集合结构(也许所有这些都应该在一个集合中)?

还有一些补充说明: Settings集合中每个“Identifier”实际上只有1条记录。每个“标识符”的数据集合将包含许多记录。这个过程可能同时扫描数十万个文档,因此资源考虑有点重要

4 个答案:

答案 0 :(得分:3)

没有使用MongoDB执行此类操作的好方法。如果您想要 BAD 方式,可以使用以下代码:

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
    }
) 

但不要指望它会像任何体面的RDBMS一样远程执行。

您可以重建架构并从数据收集中嵌入文档,如下所示:

{
    "_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 :(得分:1)

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

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

答案 2 :(得分:1)

Mongo 4.4开始,我们可以通过新的$unionWith聚合阶段以及经典的$group阶段来实现这种“联接”:

// > 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 s累积到一个数组中
    • 通过$mergeObjects操作累积Low,以获得不是Low的{​​{1}}值。使用null无效,因为这可能首先需要$first(对于数据收集中的元素)。合并包含非空值的对象时,null会丢弃$mergeObjects值。
  • 然后丢弃null值大于150的联接记录。

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

答案 3 :(得分:0)

我们开发的名为Data Compare & Sync的功能可能会对此有所帮助。

它允许您比较两个MongoDB集合并查看差异(例如,发现相同,缺失或不同的字段)。

然后,您可以将这些比较结果导出到CSV文件,并使用它创建新的第三个集合。

Export differences in two MongoDB collections to a CSV file

披露:我们是MongoDB GUI的创建者,Studio 3T。