【问题标题】:Partition data around a match query during aggregation在聚合期间围绕匹配查询对数据进行分区
【发布时间】:2020-02-18 16:59:47
【问题描述】:

我一直想弄清楚的是在 mongo 查询中执行某种分区(按谓词分割)。我当前的查询如下所示:

db.posts.aggregate([
 {"$match": { $and:[ {$or:[{"toggled":false},{"toggled":true, "status":"INACTIVE"}]}  ,  {"updatedAt":{$gte:1549786260000}} ] }},
 {"$unwind" :"$interests"},
 {"$group" : {"_id": {"iid": "$interests", "pid":"$publisher"}, "count": {"$sum" : 1}}},
 {"$project":{ _id: 0, "iid": "$_id.iid", "pid": "$_id.pid", "count": 1 }}
])

这会产生以下输出:

{
    "count" : 3.0,
    "iid" : "INT456",
    "pid" : "P789"
}
{
    "count" : 2.0,
    "iid" : "INT789",
    "pid" : "P789"
}
{
    "count" : 1.0,
    "iid" : "INT123",
    "pid" : "P789"
}
{
    "count" : 1.0,
    "iid" : "INT123",
    "pid" : "P123"
}

到目前为止一切顺利,但后来我意识到对于匹配特定过滤器{"toggled":true, "status":"INACTIVE"} 的文档,我宁愿减少计数 (-1)。 (考虑到最终值也可能是负数。)

有没有办法以某种方式对match 之后的数据进行分区,以确保对两个文档集合执行不同的grouping 操作?

听起来与我正在寻找的东西相似的是 $mergeObjects,或者可能是 $reduce,但我无法从文档示例中获得太多信息。

注意:我能感觉到,处理此问题的一种直接方法是执行两个查询,但我正在寻找一个查询来执行操作。


上述输出的示例文档为:

/* 1 */
{
    "_id" : ObjectId("5d1f7******"),
    "id" : "CON123",
    "title" : "Game",
    "content" : {},
    "status" : "ACTIVE",
    "toggle":false,
    "publisher" : "P789",
    "interests" : [ 
        "INT456"
    ],
    "updatedAt" : NumberLong(1582078628264)
}

/* 2 */
{
    "_id" : ObjectId("5d1f8******"),
    "id" : "CON456",
    "title" : "Home",
    "content" : {},
    "status" : "INACTIVE",
    "toggle":true,
    "publisher" : "P789",
    "interests" : [ 
        "INT456",
        "INT789"
    ],
    "updatedAt" : NumberLong(1582078628264)
}

/* 3 */
{
    "_id" : ObjectId("5d0e9******"),
    "id" : "CON654",
    "title" : "School",
    "content" : {},
    "status" : "ACTIVE",
    "toggle":false,
    "publisher" : "P789",
    "interests" : [ 
        "INT123",
        "INT456",
        "INT789"
    ],
    "updatedAt" : NumberLong(1582078628264)
}

/* 4 */
{
    "_id" : ObjectId("5d207*******"),
    "id" : "CON789",
    "title":"Stack",
    "content" : { },
    "status" : "ACTIVE",
    "toggle":false,
    "publisher" : "P123",
    "interests" : [ 
        "INT123"
    ],
    "updatedAt" : NumberLong(1582078628264)
}

我期待的结果是

{
    "count" : 1.0, (2-1)
    "iid" : "INT456",
    "pid" : "P789"
}
{
    "count" : 0.0, (1-1)
    "iid" : "INT789",
    "pid" : "P789"
}
{
    "count" : 1.0,
    "iid" : "INT123",
    "pid" : "P789"
}
{
    "count" : 1.0,
    "iid" : "INT123",
    "pid" : "P123"
}

【问题讨论】:

  • 您可以尝试使用$facet管道阶段。
  • @prasad_ 考虑回答?同时,我将使用 whoami 提出的示例文档更新问题。
  • 我使用方面发布了一个答案。但是,稍后我可能会进一步完善它并更新查询(就代码而言,而不是函数而言)。

标签: mongodb mongodb-query aggregate partitioning mongo-java


【解决方案1】:

这种聚合给出了预期的结果。

db.posts.aggregate( [
{ $match:  { updatedAt: { $gte: 1549786260000 } } },
{ $facet: {
        FALSE: [
            { $match: { toggle: false } },
            { $unwind : "$interests" },
            { $group : { _id : { iid: "$interests", pid: "$publisher" }, count: { $sum : 1 } } },
        ],
        TRUE: [
            { $match: { toggle: true, status: "INACTIVE" } },
            { $unwind : "$interests" },
            { $group : { _id : { iid: "$interests", pid: "$publisher" }, count: { $sum : -1 } } },
        ]
} },
{ $project: { result: { $concatArrays: [ "$FALSE", "$TRUE" ] } } },
{ $unwind: "$result" },
{ $replaceRoot: { newRoot: "$result" } },
{ $group : { _id : "$_id", count: { $sum : "$count" } } },
{ $project:{ _id: 0, iid: "$_id.iid", pid: "$_id.pid", count: 1 } }
] )


[编辑添加]

使用来自问题帖子的输入数据的查询输出:

{ "count" : 1, "iid" : "INT123", "pid" : "P789" }
{ "count" : 1, "iid" : "INT123", "pid" : "P123" }
{ "count" : 0, "iid" : "INT789", "pid" : "P789" }
{ "count" : 1, "iid" : "INT456", "pid" : "P789" }



[编辑添加 2]

此查询使用不同的方法(代码)得到相同的结果:

db.posts.aggregate( [
  { 
      $match:  { updatedAt: { $gte: 1549786260000 } } 
  },
  { 
      $unwind : "$interests" 
  },
  { 
      $group : { 
          _id : { 
              iid: "$interests", 
              pid: "$publisher" 
          }, 
          count: { 
              $sum: {
                  $switch: {
                      branches: [
                        { case: { $eq: [ "$toggle", false ] },
                           then: 1 },
                        { case: { $and: [ { $eq: [ "$toggle", true] },  { $eq: [ "$status", "INACTIVE" ] } ] },
                           then: -1 }
                      ]
                  }          
              } 
          }
      } 
  },
  { 
      $project:{
           _id: 0, 
           iid: "$_id.iid", 
           pid: "$_id.pid", 
           count: 1 
      } 
  }
] )


[编辑添加 3]

注意:

构面查询在同一组文档上运行两个构面(TRUE 和 FALSE);这就像两个并行运行的查询。但是,存在一些重复的代码以及额外的阶段,用于将文档沿管道调整以获得所需的输出。

第二个查询避免了代码重复,并且聚合管道中的阶段要少得多。当输入数据集有大量文档要处理时,这将产生影响——就性能而言。一般来说,更少的阶段意味着更少的文档迭代(因为一个阶段必须扫描从前一个阶段输出的文档)。

【讨论】:

  • 您对count 的评论是正确的。我发布了错误版本的代码。我将答案更新为更正后的答案。
  • 我添加了另一个版本的代码,结果相同。这个不使用构面。
  • 更新后的查询有效。我会花更多时间学习facets。与正常的matchunwindgroupproject 流相比,是否会影响性能?
  • 检查我的新版本代码;它比第一个版本更有效。
  • 对于未指定的情况,"default": 0 可以。此外,它是文档。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-27
  • 2016-06-13
  • 1970-01-01
相关资源
最近更新 更多