是的,这是很有可能的,也是一个经过深思熟虑的问题。我对该方法所做的唯一变化是将“时间”值计算为真实Date
对象(在 MongoDB 中非常有用,并且也可以操作),而只是用基本的日期数学“四舍五入”这些值。您可以使用“moment.js”获得相同的结果,但我发现数学很简单。
这里的另一个主要考虑因素是将数组“推送”操作与可能的“updsert”文档操作混合可能是一个真正的问题,因此最好使用“多个”更新语句来处理这个问题,其中只有您想要的条件会改变任何事物。
最好的方法是使用 MongoDB Bulk Operations。
考虑到您的数据是这样的:
{ "timestamp": 1439381722531, "action": "action1" }
其中“时间戳”是精确到毫秒的纪元时间戳值。所以这个处理看起来像:
// Just adding for the listing, assuming already defined otherwise
var payload = { "timestamp": 1439381722531, "action": "action1" };
// Round to hour
var hour = new Date(
payload.timestamp - ( payload.timestamp % ( 1000 * 60 * 60 ) )
);
// Init transaction
var bulk = db.collection.initializeOrderedBulkOp();
// Try to increment where array element exists in document
bulk.find({
"time": hour,
"log.action": payload.action
}).updateOne({
"$inc": { "log.$.count": 1 }
});
// Try to upsert where document does not exist
bulk.find({ "time": hour }).upsert().updateOne({
"$setOnInsert": {
"log": [{ "action": payload.action, "count": 1 }]
}
});
// Try to "push" where array element does not exist in matched document
bulk.find({
"time": hour,
"log.action": { "$ne": payload.action }
}).updateOne({
"$push": { "log": { "action": payload.action, "count": 1 } }
});
bulk.execute();
因此,如果您查看那里的逻辑,那么您会发现,对于文档的任何给定状态,无论是否存在,这些陈述中的“一个”永远可能为真。从技术上讲,带有“upsert”的语句实际上可以匹配一个存在的文档,但是所$setOnInsert
使用的操作确保没有进行任何更改,除非该操作实际上“插入”了一个新文档。
由于所有操作都是以“批量”方式触发的,因此唯一一次联系服务器是在.execute()
通话中。因此,尽管有多个操作,但对服务器只有“一个”请求,并且只有“一个”响应。它实际上是“一个”请求。
这样,条件都满足了:
为不存在的当前期间创建一个新文档,并将初始数据插入到数组中。
在当前“动作”分类不存在的数组中添加一个新项目,并添加一个初始计数。
在执行语句时增加数组中指定操作的计数属性。
总而言之,是的,也是一个存储的好主意,只要动作分类在一段时间内不会变得太大(应该使用 500 个数组元素作为最大指导)并且更新非常有效且自包含每个时间样本的单个文档。
该结构也很好,非常适合其他查询和可能的附加聚合目的。