我有一个非常大的聚合,它为每个用户(在集合中)汇总了一些活动。此聚合返回数百万个结果(每个用户活动)。使用这种格式:
Array ( [_id] => Array ( [user] =>
MongoId Object ( [$id] => 52050d48e654f6342c002d42 )
[send] => 1
[open] => 1
[click] => 2 )
现在我需要做一些事情:
- 使用这些结果更新用户(增加现有值)
- 更新后计算每个用户的平均值(打开/发送)
- 将每个平均值与一个数字进行比较,并根据结果为用户添加一个标志
在不循环使用聚合结果并一次更新每个结果的情况下,如何使用聚合结果更新用户?
如果我理解正确,您分享的示例结果是:
{
user: ObjectId("52050d48e654f6342c002d42"),
send: 1,
open: 1,
click: 2
}
将需要执行以下操作:
- 将该用户的
send
和open
字段分别增加1
- 将增量后的
open
/send
比率(未存储)与某个值进行比较,并根据比较结果在用户文档上设置布尔标志
充其量,我认为您可以预先聚合将接收类似更新的用户,并发布多文档更新,其中_id
是值数组中的$in
;然而,这仍然需要MongoDB单独查询每个用户。$in
数组参数的大小是有限制的,但此策略的主要好处是,您将在驱动程序和服务器之间发送更少的操作,并且您将收到单个GLE(即获取最后一个错误)响应。
open
/send
比率的计算将更加棘手,因为这将需要从所有文档中获取这些字段。在更新步骤之后,您可能能够针对用户集合发布第二个聚合,并在两个字段上使用$divide
来计算该值。然后,您可以在比较运算符中使用商和其他值。这将允许您创建一个仅包含用户ID和布尔值的聚合结果。从那时起,您仍然需要从驱动程序发出更多的更新语句,尽管将这些语句分组为多文档更新会简单得多,因为标志字段只有两个可能的值。
我绝对建议将标志字段设置为始终存在的布尔值,因为这将使您可以更改其值,而不用担心创建新字段,也不用担心在文档超出其分配空间时无意中需要在磁盘上移动文档。您应该尽可能地进行就地更新。
最后,PHP驱动程序的另一个选项是更新操作的写入问题。通过使用零写问题,您可能会获得一些性能(以牺牲错误检查为代价),因为这将允许您尽可能快地向MongoDB发送更新,而无需等待错误响应。