- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我正在开发一个简单的财务应用程序来跟踪收入和结果。
为了简单起见,假设这些是我的一些文档:
{ description: "test1", amount: 100, dateEntry: ISODate("2015-01-07T23:00:00Z") }
{ description: "test2", amount: 50, dateEntry: ISODate("2015-01-06T23:00:00Z") }
{ description: "test3", amount: 11, dateEntry: ISODate("2015-01-09T23:00:00Z") }
{ description: "test4", amount: 2, dateEntry: ISODate("2015-01-09T23:00:00Z") }
{ description: "test5", amount: 12, dateEntry: ISODate("2015-01-09T23:00:00Z") }
{ description: "test6", amount: 4, dateEntry: ISODate("2015-01-09T23:00:00Z") }
我现在想要的是根据这些数据绘制一个“余额”图表:
{ day: "2015-01-06", amount: 50 }
{ day: "2015-01-07", amount: 150 }
{ day: "2015-01-09", amount: 179 }
换句话说,我需要按天对我的所有交易进行分组,并且每天我需要汇总我之前的所有交易(从世界之初开始)。
我已经知道如何按天分组了:
$group: {
_id: {
y: {$year:"$dateEntry"},
m: {$month:"$dateEntry"},
d: {$dayOfMonth:"$dateEntry"}
},
sum: ???
}
但我不知道如何返回并汇总所有金额。
假设我需要显示每月余额报告:我是否应该运行 31 次查询,每天一次,将除后几天之外的所有交易金额相加?当然可以,但不要认为这是最好的解决方案。
最佳答案
其实更适合mapReduce比聚合框架,至少在最初的问题解决中。聚合框架没有先前文档的值的概念,或者文档的先前“分组”值的概念,所以这就是它不能这样做的原因。
另一方面,mapReduce 有一个“全局范围”,可以在处理阶段和文档时在它们之间共享。这将在您需要的一天结束时为您提供当前余额的“运行总计”。
db.collection.mapReduce(
function () {
var date = new Date(this.dateEntry.valueOf() -
( this.dateEntry.valueOf() % ( 1000 * 60 * 60 * 24 ) )
);
emit( date, this.amount );
},
function(key,values) {
return Array.sum( values );
},
{
"scope": { "total": 0 },
"finalize": function(key,value) {
total += value;
return total;
},
"out": { "inline": 1 }
}
)
这将按日期分组求和,然后在“最终确定”部分中计算每天的累积总和。
"results" : [
{
"_id" : ISODate("2015-01-06T00:00:00Z"),
"value" : 50
},
{
"_id" : ISODate("2015-01-07T00:00:00Z"),
"value" : 150
},
{
"_id" : ISODate("2015-01-09T00:00:00Z"),
"value" : 179
}
],
从长远来看,您最好有一个单独的集合,每天都有一个条目,然后使用 $inc
更改余额。在更新中。也可以做一个 $inc
upsert在每天开始时创建一个新的文档来结转前一天的余额:
// increase balance
db.daily(
{ "dateEntry": currentDate },
{ "$inc": { "balance": amount } },
{ "upsert": true }
);
// decrease balance
db.daily(
{ "dateEntry": currentDate },
{ "$inc": { "balance": -amount } },
{ "upsert": true }
);
// Each day
var lastDay = db.daily.findOne({ "dateEntry": lastDate });
db.daily(
{ "dateEntry": currentDate },
{ "$inc": { "balance": lastDay.balance } },
{ "upsert": true }
);
虽然确实由于最初的写作有更多的运算符被引入聚合框架,但这里提出的问题仍然不是实际在聚合语句中做的。
同样的基本规则适用于聚合框架不能从以前的“文档”引用值,也不能存储“全局变量”。 “黑客” 通过将所有结果强制转换为数组:
db.collection.aggregate([
{ "$group": {
"_id": {
"y": { "$year": "$dateEntry" },
"m": { "$month": "$dateEntry" },
"d": { "$dayOfMonth": "$dateEntry" }
},
"amount": { "$sum": "$amount" }
}},
{ "$sort": { "_id": 1 } },
{ "$group": {
"_id": null,
"docs": { "$push": "$$ROOT" }
}},
{ "$addFields": {
"docs": {
"$map": {
"input": { "$range": [ 0, { "$size": "$docs" } ] },
"in": {
"$mergeObjects": [
{ "$arrayElemAt": [ "$docs", "$$this" ] },
{ "amount": {
"$sum": {
"$slice": [ "$docs.amount", 0, { "$add": [ "$$this", 1 ] } ]
}
}}
]
}
}
}
}},
{ "$unwind": "$docs" },
{ "$replaceRoot": { "newRoot": "$docs" } }
])
这既不是一个高性能的解决方案,也不是“安全”,考虑到更大的结果集运行违反 16MB BSON 限制的非常真实的可能性。作为“黄金法则”,任何建议将所有内容放在单个文档的数组中的东西:
{ "$group": {
"_id": null,
"docs": { "$push": "$$ROOT" }
}}
那么这是一个基本缺陷,因此不是解决方案。
解决这个问题的更有说服力的方法通常是对结果的运行光标进行后处理:
var globalAmount = 0;
db.collection.aggregate([
{ $group: {
"_id": {
y: { $year:"$dateEntry"},
m: { $month:"$dateEntry"},
d: { $dayOfMonth:"$dateEntry"}
},
amount: { "$sum": "$amount" }
}},
{ "$sort": { "_id": 1 } }
]).map(doc => {
globalAmount += doc.amount;
return Object.assign(doc, { amount: globalAmount });
})
所以总的来说,最好:
使用游标迭代和跟踪变量进行总计。 mapReduce
示例是上述简化过程的人为示例。
使用预先汇总的总数。可能与游标迭代一致,具体取决于您的预聚合过程,无论是间隔总计还是“结转”运行总计。
聚合框架应该真正用于“聚合”,仅此而已。通过诸如操作到数组之类的过程来强制对数据进行强制转换只是为了处理您想要的方式既不明智也不安全,最重要的是客户端操作代码更清洁、更高效。
让数据库做它们擅长的事情,因为您的“操作”在代码中处理得更好。
关于mongodb - 如何使用聚合计算运行总数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27995085/
我有一个 Cassandra 集群,里面有 4 个表和数据。 我想使用聚合函数(sum,max ...)发出请求,但我在这里读到这是不可能的: http://www.datastax.com/docu
我有以下两张表 Table: items ID | TITLE 249 | One 250 | Two 251 | Three 我投票给这些: Table: votes VID | IID | u
这个问题在这里已经有了答案: Update MongoDB field using value of another field (12 个答案) 关闭 3 年前。 我想根据另一个“源”集合的文档中
我的收藏包含以下文件。我想使用聚合来计算里面有多少客户,但我遇到了一些问题。我可以获得总行数,但不能获得总(唯一)客户。 [{ _id: "n001", channel: "Kalip
我有下表 Id Letter 1001 A 1001 H 1001 H 1001 H 1001 B 1001 H 1001 H 1001
得到一列的表 ABC。 “创建”的日期列。所以样本值就像; created 2009-06-18 13:56:00 2009-06-18 12:56:00 2009-06-17 14:02:0
我有一个带有数组字段的集合: {[ name:String buyPrice:Int sellPrice:Int ]} 我试图找到最低和最高买入/卖出价格。在某些条目中,买入或卖出价格为零
我有以下问题: 在我的 mongo db 中,我有以下结构: { "instanceId": "12", "eventId": "0-1b", "activityType":
下面给出的是我要在其上触发聚合查询的 Elasticsearch 文档。 { "id": 1, "attributes": [ { "fieldId": 1,
我正在使用 Django 的 aggregate query expression总计一些值。最终值是一个除法表达式,有时可能以零作为分母。如果是这种情况,我需要一种方法来逃避,以便它只返回 0。 我
我正在学习核心数据,特别是聚合。 当前我想要做的事情:计算表中在某些条件上具有逆关系的多对关系的记录数。 目前我正在这样做: NSExpression *ex = [NSExpression expr
我需要有关 Delphi 中的 ClientDatasets 的一些帮助。 我想要实现的是一个显示客户的网格,其中一列显示每个客户的订单数量。我将 ClientDataset 放在表单上并从 Delp
我的集合有 10M 个文档,并且有一个名为 movieId 的字段;该文档具有以下结构: { "_id" : ObjectId("589bed43e3d78e89bfd9b779"), "us
这个问题已经有答案了: What is the difference between association, aggregation and composition? (21 个回答) 已关闭 9
我在 elasticsearch 中有一些类似于这些示例的文档: { "id": ">", "list": [ "a", "b", "c" ] } { "id"
我正在做一些聚合。但是结果完全不是我所期望的,似乎它们没有聚合索引中与我的查询匹配的所有文档,在这种情况下 - 它有什么好处? 例如,首先我做这个查询: {"index":"datalayer","t
假设我在 ES 中有这些数据。 | KEY | value | |:-----------|------------:| | A |
可能在我的文档中,我有一个被分析的文本字段。我只是在ElasticSearch AggregationAPI中迷路了。我需要2种不同情况的支持: 情况A)结果是带有计数标记(条款)的篮子下降。 情况B
我正在为网上商店构建多面过滤功能,如下所示: Filter on Brand: [ ] LG (10) [ ] Apple (5) [ ] HTC (3) Filter on OS: [ ] Andr
我有一个父/子关系并且正在搜索 child 。 是否可以在父属性上创建聚合? 例如parent 是 POST,children 是 COMMENT。如果父项具有“类别”属性,是否可以搜索 COMMEN
我是一名优秀的程序员,十分优秀!