Mongo $sort 和 $group,顺序有保证吗?

huangapple go评论93阅读模式
英文:

Mongo $sort and then $group, is order guaranteed?

问题

我有一堆表示游戏中得分的文件。我想按用户ID对得分进行分组并求和,但有一个要注意的地方:得分可能是负数,但累积总数不应低于零。例如,如果用户1按以下顺序得分:

1
2
-5
3

然后他们的最终得分应为3,因为-5只能将他们的总得分降至0,而不是-2

我有以下聚合管道,据我所知,它可以正确实现此目标:

[
  {
    $sort: {
      earnedDate: 1,
    },
  },
  {
    $group: {
      _id: '$userId',
      pointDeltas: {
        $push: '$points',
      },
      userId: {
        $first: '$userId',
      },
    },
  },
  {
    $project: {
      userId: 1,
      points: {
        $reduce: {
          input: '$pointDeltas',
          initialValue: 0,
          in: {
            $max: [
              0,
              {
                $add: ['$$value', '$$this'],
              },
            ],
          },
        },
      },
    },
  }
]

这依赖于按正确顺序(按earnedDate升序)将points推送到pointDeltas中。

根据我的实验,似乎确实会按正确顺序处理。但这是否被保证了呢?

我注意到$first操作符的官方文档中有这样的说明:

如果文档已排序,则仅定义顺序。

这似乎是一个提示,表明文档是按顺序处理的,尽管不太清楚这个原则是否也适用于使用$push。在同一页上$push的文档没有类似的注释。

这种行为是被保证的还是巧合的?我还没有找到确凿的答案。

英文:

I have a bunch of documents representing points scored in a game. I want to group the points by user ID and sum them, with a gotcha: it's possible to score negative points, but the running sum should never go below zero. So for example if user 1 scored the following points in this order:

1
2
-5
3

Then their final score should be 3, because the -5 only takes them down to 0 total, rather than -2.

I have this aggregation pipeline which, as far as I can tell, correctly accomplishes this:

[
  {
    $sort: {
      earnedDate: 1,
    },
  },
  {
    $group: {
      _id: '$userId',
      pointDeltas: {
        $push: '$points',
      },
      userId: {
        $first: '$userId',
      },
    },
  },
  {
    $project: {
      userId: 1,
      points: {
        $reduce: {
          input: '$pointDeltas',
          initialValue: 0,
          in: {
            $max: [
              0,
              {
                $add: ['$$value', '$$this'],
              },
            ],
          },
        },
      },
    },
  }
]

This depends on the points being pushed onto pointDeltas in the right order (by earnedDate, ascending).

Experimentally, this does seem to happen. But is this guaranteed?

I noticed the official documentation for the $first operator states:

> Returns a value from the first document for each group. Order is only defined if the documents are sorted.

This seems like a hint that the documents are processed in order, though it's not 100% clear if this principle applies to using $push too. The documentation for $push on the same page does not have a similar note.

Is this behavior guaranteed or coincidental? I haven't been able to find any bulletproof answer either way.

答案1

得分: 1

根据$push中的示例,文档是有序的。

我建议在https://jira.mongodb.org/上提出一个案例。在那里提出您的问题和/或请求文档的扩展。

如果您想要绝对确定,那么您可以使用以下内容:

[
   {
      $group: {
         _id: '$userId',
         pointDeltas: {
            $push: { points: "$points", earnedDate: "$earnedDate" }
         },
         userId: { $first: '$userId' },
      },
   },
   {
      $set: {
         pointDeltas: {
            input: "$pointDeltas",
            sortBy: { earnedDate: 1 }
         }
      }
   },
   {
      $project: {
         userId: 1,
         points: {
            $reduce: {
               input: '$pointDeltas',
               initialValue: 0,
               in: {
                  $max: [
                     0,
                     { $add: ['$$value.points', '$$this.points'], }
                  ]
               }
            }
         }
      }
   }
]
英文:

According the examples in $push the documents are ordered.

I would suggest to open a case in https://jira.mongodb.org/. Ask your question there and/or request an extension in the documentation.

if you like to be absolutely sure, then you can use this one:

[
   {
      $group: {
         _id: '$userId',
         pointDeltas: {
            $push: { points: "$points", earnedDate: "$earnedDate" }
         },
         userId: { $first: '$userId' },
      },
   },
   {
      $set: {
         pointDeltas: {
            input: "$pointDeltas",
            sortBy: { earnedDate: 1 }
         }
      }
   },
   {
      $project: {
         userId: 1,
         points: {
            $reduce: {
               input: '$pointDeltas',
               initialValue: 0,
               in: {
                  $max: [
                     0,
                     { $add: ['$$value.points', '$$this.points'], }
                  ]
               }
            }
         }
      }
   }
]

huangapple
  • 本文由 发表于 2023年7月28日 06:20:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76783750.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定