gpt4 book ai didi

node.js - 每天或每月间隔的交易总和

转载 作者:太空宇宙 更新时间:2023-11-04 00:10:08 25 4
gpt4 key购买 nike

在我的收藏中,我记录了交易记录。

{
"_id":{
"$oid":"5ae77296c780351beadc5518"
},
"payment_amount":10000,
"store_id":{
"$oid":"5aa6babce7c97f0875556ae6"
},
"operator_id":{
"$oid":"5aa6ba95e7c97f0875556ae3"
},
"cashier_id":{
"$oid":"5acd4144c94ba7250da4af78"
},
"player_id":{
"$oid":"5ae75fccc780351beadc5493"
},
"payment_type":"deposit",
"payment_time":{
"$date":"2018-04-30T19:46:30.055+0000"
},
"__v":0
}


现在,我需要以某种方式聚合数据。


在过去的12个月中,我每个月需要12个结果。
在每个结果中,应该有所有 payment_amount的总和,其中 payment_typedeposit和所有 payment_type == withdraw的总和
在过去31天内,我每天需要存入和提取相同的金额。


我正在使用猫鼬,这是我的模式

//schema definition
var Sch = new Schema({
store_id: Schema.Types.ObjectId,
operator_id: Schema.Types.ObjectId, //users._id
cashier_id: {type: Schema.Types.ObjectId, ref: 'usersMD'}, //users._id
player_id: {type: Schema.Types.ObjectId, ref: 'playersMD'},
payment_type: {type: String, enum: ['deposit', 'withdraw']},
payment_amount: {type: Number, default: 0},
payment_time: {type: Date, default: Date.now}
}, opts);


解决这个问题的最佳方法是什么?

我正在考虑在API的log_transaction模块中创建两个方法。

一个会要求1,另一个会要求2。

最佳答案

我主要在这里讨论“实现”的细节,因为一旦您了解了它的实际实现方式,便可以得出一个合理的结论,即如何围绕此实现方法。

在理想情况下,您实际上是在存储时以“月”或“日”之类的时间间隔“预先累积”写入数据。通常,这是我们在大容量环境中通过写累积的总数来做到的方式。

否则,您将求助于现有数据的聚合,而聚合框架正是您应该使用的数据。通常,您至少会有一些代码来报告不同的时间间隔,而您实际上并没有对其进行“预累加”。

您在这里寻找的基本内容是$cond运算符。这是一个“三进制”或if/then/else条件,它允许为if表示条件,该条件会分支逻辑以返回值为true的条件,或者条件为true的条件为then

这是运算符,它使我们可以查看else并确定在使用"payment_type"进行累加时该值是具有“正”还是“负”的数字表示。因此,这里的基本声明是:

 "$sum": {
"$cond": {
"if": { "$eq": [ "$payment_type", "deposit" ] },
"then": "$payment_amount",
"else": { "$subtract": [ 0, "$payment_amount" ] }
}
}


这适用于这里的基本数学,因此累加的其余任务全部是关于“按时间间隔”收集的,并且有许多不同的方法可以做到这一点:

每月一次

使用MongoDB 3.6 $dateFromParts

Model.aggregate([
{ "$match": {
"payment_time": { "$gte": start_date, "$lt": end_date }
}},
{ "$group": {
"_id": {
"$dateFromParts": {
"year": { "$year": "$payment_time" },
"month": { "$month": "$payment_time" }
}
},
"balance": {
"$sum": {
"$cond": {
"if": { "$eq": [ "$payment_type", "deposit" ] },
"then": "$payment_amount",
"else": { "$subtract": [ 0, "$payment_amount" ] }
}
}
}
}}
])


使用日期数学

Model.aggregate([
{ "$match": {
"payment_time": { "$gte": start_date, "$lt": end_date }
}},
{ "$group": {
"_id": {
"$add": [
{ "$subtract": [
{ "$subtract": ["$payment_time", new Date(0)] },
{ "$mod": [
{ "$subtract": ["$payment_time", new Date(0)] },
1000 * 60 * 60 * 24
]}
]},
{ "$multiply": [
{ "$subtract": [{ "$dayOfMonth": "$payment_time" }, 1] },
-1000 * 60 * 60 * 24
]},
new Date(0)
]
},
"balance": {
"$sum": {
"$cond": {
"if": { "$eq": [ "$payment_type", "deposit" ] },
"then": "$payment_amount",
"else": { "$subtract": [ 0, "$payment_amount" ] }
}
}
}
}}
])


普通日期运算符

Model.aggregate([
{ "$match": {
"payment_time": { "$gte": start_date, "$lt": end_date }
}},
{ "$group": {
"_id": {
"year": { "$year": "$payment_time" },
"month": { "$month": "$payment_time" }
},
"balance": {
"$sum": {
"$cond": {
"if": { "$eq": [ "$payment_type", "deposit" ] },
"then": "$payment_amount",
"else": { "$subtract": [ 0, "$payment_amount" ] }
}
}
}
}}
])


前两种方法之间的区别与最后一种方法的区别在于,这些初始方法实际上返回BSON Date对象,该对象将在NodeJS下表示为标准JavaScript $sum对象。使用 Date和“ Math”方法本质上都返回代表月初的“四舍五入”日期。

诸如“银行对帐单”之类的事情实际上是在“每月的特定日期”发布的,这是很普遍的。这实际上只需要扩展提出的调整逻辑,只需简单地“修补”返回的日期即可。

假设每个月的“第15天”:

    "_id": {
"$add": [
{ "$subtract": [
{ "$subtract": ["$payment_time", new Date(0)] },
{ "$mod": [
{ "$subtract": ["$payment_time", new Date(0)] },
1000 * 60 * 60 * 24
]}
]},
{ "$multiply": [
{ "$subtract": [{ "$dayOfMonth": "$payment_time" }, 1] },
-1000 * 60 * 60 * 24
]},
1000 * 60 * 60 * 24 * (15-1), // n-1 days adjusting
new Date(0)
]
},


这是我偏爱“数学”方法的一个坚实原因,因为它比其他形式灵活得多。您可以对那些依赖于其他“日期运算符”的用户执行此操作,但最终他们会在一个月的某天应用“范围条件”,而这需要更多的逻辑来实现。但是,要在数组中增加一个数字!有什么比这更容易的?

当然,对于Modern MongoDB,还有 $dateFromParts$dateFromString之类的方法可以作为其他方法,但是强制转换为“字符串”是一项昂贵的操作。您将更多的操作(实际上是数字的内容)强制转换为字符串形式以进行操作,最终会产生累积效果,这对性能有很大影响。在现代支付计算周期和数据传输的世界中,这等于$ money $$。而且我是个小气鬼,所以我希望事情变得高效。

日常

与以前大致相同,但如何获得“每日”间隔略有不同

MongoDB 3.6 $dateFromParts

Model.aggregate([
{ "$match": {
"payment_time": { "$gte": start_date, "$lt": end_date }
}},
{ "$group": {
"_id": {
"$dateFromParts": {
"year": { "$year": "$payment_time" },
"month": { "$month": "$payment_time" },
"day": { "$dayOfMonth": "$payment_time" }
}
},
"balance": {
"$sum": {
"$cond": {
"if": { "$eq": [ "$payment_type", "deposit" ] },
"then": "$payment_amount",
"else": { "$subtract": [ 0, "$payment_amount" ] }
}
}
}
}}
])


使用日期数学

Model.aggregate([
{ "$match": {
"payment_time": { "$gte": start_date, "$lt": end_date }
}},
{ "$group": {
"_id": {
"$add": [
{ "$subtract": [
{ "$subtract": ["$payment_time", new Date(0)] },
{ "$mod": [
{ "$subtract": ["$payment_time", new Date(0)] },
1000 * 60 * 60 * 24
]}
]},
new Date(0)
]
},
"balance": {
"$sum": {
"$cond": {
"if": { "$eq": [ "$payment_type", "deposit" ] },
"then": "$payment_amount",
"else": { "$subtract": [ 0, "$payment_amount" ] }
}
}
}
}}
])


普通日期运算符

Model.aggregate([
{ "$match": {
"payment_time": { "$gte": start_date, "$lt": end_date }
}},
{ "$group": {
"_id": {
"year": { "$year": "$payment_time" },
"month": { "$month": "$payment_time" },
"day": { "$dayOfMonth": "$payment_time" }
},
"balance": {
"$sum": {
"$cond": {
"if": { "$eq": [ "$payment_type", "deposit" ] },
"then": "$payment_amount",
"else": { "$subtract": [ 0, "$payment_amount" ] }
}
}
}
}}
])


在此值得注意的是,其他方法“添加”了 $dateToString运算符,而“数学”方法实际上从该考虑中删除了该运算符。这样做的原因是,由于基本的事实是每个月的天数会有所不同,因此在数学运算中很难处理“月”。因此,我们在“每月”四舍五入过程中所做的主要工作是考虑当前日期,以查找月初。

这里要说明的是,相同的“数学”实际上适用于任何时间间隔,无论是天还是年,小时或秒。您唯一需要更改和调整的时间就是累积“天”,该天的天数不一致。

还要注意此处的“重复”,因为所有内容都非常相同。最多您应该有两种方法,但实际上唯一需要更改的部分是月份与任何其他时间间隔之间的累加器,尤其是如果您使用数学方法。因此,说实话,您真正需要的只是一种识别“每月”与“其他所有东西”之间差异的方法,因为聚合管道无论如何都只是数据结构。

因此,“操纵”它们。就像其他任何数据结构一样。




  注意:除了“日期”以外,该问题实际上没有提及任何要为其累积任何字段的字段。如果需要每个 $dayOfMonth之类的内容,则只需将其作为复合键的一部分添加到 "player_id"阶段的 "_id"中。在上面的每个“普通日期运算符”部分中,都将演示“复合键”。

关于node.js - 每天或每月间隔的交易总和,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50162713/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com