第一周留存 MongoDB
First Week Retention with MongoDB
一个 table 会话开始事件:
注册 - unixtime - 用户注册时(第一次打开应用)
DateTime - unixtime - 发送事件的时间(当玩家安装应用程序并首次打开它时:Registered = DateTime)
PlayerId
- 玩家的唯一 ID(相同的 ID - 总是相同的注册,但相同的注册 - 可能不止一个 PlayerId
)
我需要这样 table:
到目前为止进行了此查询(MongoDB for redash):
{
"collection": "dance",
"aggregate": [
{
"$match": {
"$and": [
{
//---filter for the range of Day0 dates
//---need to build Ret_Day1 - Ret_Day7 for each
"Registered": {
"$lt": "ISODate(\"{{Finish date}}\")"
}
},
{
"Registered": {
"$gt": "ISODate(\"{{Start date}}\")"
}
},
{
"EventType": "Session Start"
}
]
}
},
{
"$group": {
"_id": {
"DayZero": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$Registered"
}
},
"DayActive": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$DateTime"
}
},
"PlayerId": "$PlayerId"
}
}
},
{
"$group": {
"_id": {
"DayZero": "$_id.DayZero",
"DayActive": "$_id.DayActive"
},
"Ret": {
"$sum": 1
}
}
},
{
"$project": {
"_id": "1",
"DayZero": {
"$dateFromString": {
"dateString": "$_id.DayZero"
}
},
"DayActive": {
"$dateFromString": {
"dateString": "$_id.DayActive"
}
},
"Ret": 1
}
},
{
"$project": {
"Days": {
"$divide": [
{
"$subtract": [
"$DayActive",
"$DayZero"
]
},
86400000
]
},
"DayZero": {
"$dateToParts": {
"date": "$DayZero",
"timezone": "+02:00"
}
},
"Ret": 1
}
},
{
"$project": {
"Ret": 1,
"Days": 1,
"DayZero": {
"$concat": [
{
"$toString": "$DayZero.day"
},
".",
{
"$toString": "$DayZero.month"
},
".",
{
"$toString": "$DayZero.year"
}
]
}
}
}
]
}
结果:
在 redash 中我可以构建这样的可视化:
但这还不够好 - 所以我想用用户数量和用户百分比填充新字段 (R_Day1 - R_Day7)。哪种方法最容易计算每天的百分比?
这有点棘手,因为它需要日期数学与数据结构操作相结合。
这是一个快速汇总,可以为您提供令人满意的输出结果,
返回的结构将采用这种形式:
{
"day": string
"newUsers" : number,
"DateTimes" : Array<{day: number, month: number, year: number, users: number, percentage: number}>
}
由于 DateTime
数组已经排序,因此对于 R_1
,您将使用 DateTime[0].percentage
作为当天的用户百分比。
db.collection.aggregate([
{
$group: {
_id: {
day: {$dayOfMonth: "$Registered"},
month: {$month: "$Registered"},
year: {$year: "$Registered"},
dateTimeDay: {$dayOfMonth: "$DateTime"},
dateTimeMonth: {$month: "$DateTime"},
dateTimeYear: {$year: "$DateTime"}
},
users: {$addToSet: "$PlayerId"}
}
},
{
$group: {
_id: {day: "$_id.day", month: "$_id.month", year: "$_id.year"},
totalUsers: {$addToSet: "$users"},
DateTimes: {
$push: {
day: "$_id.dateTimeDay",
month: "$_id.dateTimeMonth",
year: "$_id.dateTimeYear",
users: {$size: "$users"}
}
}
}
},
{
$addFields: {
R1_to_R7: {
$map: {
input: [1, 2, 3, 4, 5, 6, 7],
as: "input",
in: {
$cond: [
{$setIsSubset: [["$_id.month"], [1, 3, 5, 7, 8, 10, 12]]},
{
$cond: [
{
$and: [
{$lt: [{$mod: [{$sum: ["$_id.day", "$$input"]}, 32]}, 7]},
{$gt: ["$_id.day", 20]}
]
},
{
day: {$sum: [{$mod: [{$sum: ["$_id.day", "$$input"]}, 32]}, 1]},
month: {
$cond: [
{$eq: [{$mod: [{$sum: ["$_id.month", 1]}, 13]}, 0]},
1,
{$sum: ["$_id.month", 1]},
]
},
year: {
$cond: [
{$eq: [{$mod: [{$sum: ["$_id.month", 1]}, 13]}, 0]},
{$sum: ["$_id.year", 1]},
"$_id.year"
]
}
},
{
day: {$mod: [{$sum: ["$_id.day", "$$input"]}, 32]},
month: "$_id.month",
year: "$_id.year"
}
]
},
{
$cond: [
{
$eq: ["$_id.month", 2]
},
{
$cond: [
{
$and: [
{$lt: [{$mod: [{$sum: ["$_id.day", "$$input"]}, 29]}, 7]},
{$gt: ["$_id.day", 20]}
]
},
{
day: {$sum: [{$mod: [{$sum: ["$_id.day", "$$input"]}, 29]}, 1]},
month: 3,
year: "$_id.year",
},
{
day: {$mod: [{$sum: ["$_id.day", "$$input"]}, 29]},
month: "$_id.month",
year: "$_id.year"
}
]
},
{
$cond: [
{
$and: [
{$lt: [{$mod: [{$sum: ["$_id.day", "$$input"]}, 31]}, 7]},
{$gt: ["$_id.day", 20]}
]
},
{
day: {$sum: [{$mod: [{$sum: ["$_id.day", "$$input"]}, 31]}, 1]},
month: {$sum: ["$_id.month", 1]},
year: "$_id.year",
},
{
day: {$mod: [{$sum: ["$_id.day", "$$input"]}, 31]},
month: "$_id,month",
year: "$_id.year"
}
]
}
]
}
]
}
}
}
}
},
{
$project: {
_id: 0,
day: {$concat: [{$toString: "$_id.day"}, "/", {$toString: "$_id.month"}, "/", {$toString: "$_id.year"}]},
newUsers: {
$size: {
$reduce: {
input: "$totalUsers",
initialValue: [],
in: {$setUnion: ["$$value", "$$this"]}
}
}
},
DateTimes: {
$map: {
input: "$R1_to_R7",
as: "next_day",
in: {
$cond: [
{
$gt: [
{
$size: {
$filter: {
input: "$DateTimes",
as: "dateTime",
cond: {
$eq: [{
day: "$$dateTime.day",
month: "$$dateTime.month",
year: "$$dateTime.year"
}, "$$next_day"]
}
}
}
},
0
]
},
{
$arrayElemAt: [
{
$filter: {
input: "$DateTimes",
as: "dateTime",
cond: {
$eq: [{
day: "$$dateTime.day",
month: "$$dateTime.month",
year: "$$dateTime.year"
}, "$$next_day"]
}
}
},
0
]
},
{
$mergeObjects: ["$$next_day", {users: 0}]
}
]
}
}
}
}
},
{
$project: {
day: 1,
newUsers: 1,
DateTimes: {
$map: {
input: "$DateTimes",
as: "datetime",
in: {
$mergeObjects: [
"$$datetime",
{percentage: {$multiply: [100, {$divide: ["$$datetime.users", "$newUsers"]}]}}
]
}
}
}
}
}
])
除此之外还有 2 件事需要考虑:
- 第一个
$addFields
阶段用于 "add" 接下来的 7 天。 Mongo 在这种情况下很难做到这一点。如果您可以在代码中完成它会更好,因为存在很多冗余。
- 如您所见,我必须更改接下来 7 天的
day
、month
和 year
值,以防注册时间比方说 31 日。但我没有考虑间隔年。如果你想保持这种方式并使其稳定,你应该添加间隔年检查($mod
应该可以正常工作,因为它每 4 年一次。)到 2
月(二月)的条件).
一个 table 会话开始事件:
注册 - unixtime - 用户注册时(第一次打开应用)
DateTime - unixtime - 发送事件的时间(当玩家安装应用程序并首次打开它时:Registered = DateTime)
PlayerId
- 玩家的唯一 ID(相同的 ID - 总是相同的注册,但相同的注册 - 可能不止一个 PlayerId
)
我需要这样 table:
到目前为止进行了此查询(MongoDB for redash):
{
"collection": "dance",
"aggregate": [
{
"$match": {
"$and": [
{
//---filter for the range of Day0 dates
//---need to build Ret_Day1 - Ret_Day7 for each
"Registered": {
"$lt": "ISODate(\"{{Finish date}}\")"
}
},
{
"Registered": {
"$gt": "ISODate(\"{{Start date}}\")"
}
},
{
"EventType": "Session Start"
}
]
}
},
{
"$group": {
"_id": {
"DayZero": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$Registered"
}
},
"DayActive": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$DateTime"
}
},
"PlayerId": "$PlayerId"
}
}
},
{
"$group": {
"_id": {
"DayZero": "$_id.DayZero",
"DayActive": "$_id.DayActive"
},
"Ret": {
"$sum": 1
}
}
},
{
"$project": {
"_id": "1",
"DayZero": {
"$dateFromString": {
"dateString": "$_id.DayZero"
}
},
"DayActive": {
"$dateFromString": {
"dateString": "$_id.DayActive"
}
},
"Ret": 1
}
},
{
"$project": {
"Days": {
"$divide": [
{
"$subtract": [
"$DayActive",
"$DayZero"
]
},
86400000
]
},
"DayZero": {
"$dateToParts": {
"date": "$DayZero",
"timezone": "+02:00"
}
},
"Ret": 1
}
},
{
"$project": {
"Ret": 1,
"Days": 1,
"DayZero": {
"$concat": [
{
"$toString": "$DayZero.day"
},
".",
{
"$toString": "$DayZero.month"
},
".",
{
"$toString": "$DayZero.year"
}
]
}
}
}
]
}
结果:
在 redash 中我可以构建这样的可视化:
但这还不够好 - 所以我想用用户数量和用户百分比填充新字段 (R_Day1 - R_Day7)。哪种方法最容易计算每天的百分比?
这有点棘手,因为它需要日期数学与数据结构操作相结合。
这是一个快速汇总,可以为您提供令人满意的输出结果, 返回的结构将采用这种形式:
{
"day": string
"newUsers" : number,
"DateTimes" : Array<{day: number, month: number, year: number, users: number, percentage: number}>
}
由于 DateTime
数组已经排序,因此对于 R_1
,您将使用 DateTime[0].percentage
作为当天的用户百分比。
db.collection.aggregate([
{
$group: {
_id: {
day: {$dayOfMonth: "$Registered"},
month: {$month: "$Registered"},
year: {$year: "$Registered"},
dateTimeDay: {$dayOfMonth: "$DateTime"},
dateTimeMonth: {$month: "$DateTime"},
dateTimeYear: {$year: "$DateTime"}
},
users: {$addToSet: "$PlayerId"}
}
},
{
$group: {
_id: {day: "$_id.day", month: "$_id.month", year: "$_id.year"},
totalUsers: {$addToSet: "$users"},
DateTimes: {
$push: {
day: "$_id.dateTimeDay",
month: "$_id.dateTimeMonth",
year: "$_id.dateTimeYear",
users: {$size: "$users"}
}
}
}
},
{
$addFields: {
R1_to_R7: {
$map: {
input: [1, 2, 3, 4, 5, 6, 7],
as: "input",
in: {
$cond: [
{$setIsSubset: [["$_id.month"], [1, 3, 5, 7, 8, 10, 12]]},
{
$cond: [
{
$and: [
{$lt: [{$mod: [{$sum: ["$_id.day", "$$input"]}, 32]}, 7]},
{$gt: ["$_id.day", 20]}
]
},
{
day: {$sum: [{$mod: [{$sum: ["$_id.day", "$$input"]}, 32]}, 1]},
month: {
$cond: [
{$eq: [{$mod: [{$sum: ["$_id.month", 1]}, 13]}, 0]},
1,
{$sum: ["$_id.month", 1]},
]
},
year: {
$cond: [
{$eq: [{$mod: [{$sum: ["$_id.month", 1]}, 13]}, 0]},
{$sum: ["$_id.year", 1]},
"$_id.year"
]
}
},
{
day: {$mod: [{$sum: ["$_id.day", "$$input"]}, 32]},
month: "$_id.month",
year: "$_id.year"
}
]
},
{
$cond: [
{
$eq: ["$_id.month", 2]
},
{
$cond: [
{
$and: [
{$lt: [{$mod: [{$sum: ["$_id.day", "$$input"]}, 29]}, 7]},
{$gt: ["$_id.day", 20]}
]
},
{
day: {$sum: [{$mod: [{$sum: ["$_id.day", "$$input"]}, 29]}, 1]},
month: 3,
year: "$_id.year",
},
{
day: {$mod: [{$sum: ["$_id.day", "$$input"]}, 29]},
month: "$_id.month",
year: "$_id.year"
}
]
},
{
$cond: [
{
$and: [
{$lt: [{$mod: [{$sum: ["$_id.day", "$$input"]}, 31]}, 7]},
{$gt: ["$_id.day", 20]}
]
},
{
day: {$sum: [{$mod: [{$sum: ["$_id.day", "$$input"]}, 31]}, 1]},
month: {$sum: ["$_id.month", 1]},
year: "$_id.year",
},
{
day: {$mod: [{$sum: ["$_id.day", "$$input"]}, 31]},
month: "$_id,month",
year: "$_id.year"
}
]
}
]
}
]
}
}
}
}
},
{
$project: {
_id: 0,
day: {$concat: [{$toString: "$_id.day"}, "/", {$toString: "$_id.month"}, "/", {$toString: "$_id.year"}]},
newUsers: {
$size: {
$reduce: {
input: "$totalUsers",
initialValue: [],
in: {$setUnion: ["$$value", "$$this"]}
}
}
},
DateTimes: {
$map: {
input: "$R1_to_R7",
as: "next_day",
in: {
$cond: [
{
$gt: [
{
$size: {
$filter: {
input: "$DateTimes",
as: "dateTime",
cond: {
$eq: [{
day: "$$dateTime.day",
month: "$$dateTime.month",
year: "$$dateTime.year"
}, "$$next_day"]
}
}
}
},
0
]
},
{
$arrayElemAt: [
{
$filter: {
input: "$DateTimes",
as: "dateTime",
cond: {
$eq: [{
day: "$$dateTime.day",
month: "$$dateTime.month",
year: "$$dateTime.year"
}, "$$next_day"]
}
}
},
0
]
},
{
$mergeObjects: ["$$next_day", {users: 0}]
}
]
}
}
}
}
},
{
$project: {
day: 1,
newUsers: 1,
DateTimes: {
$map: {
input: "$DateTimes",
as: "datetime",
in: {
$mergeObjects: [
"$$datetime",
{percentage: {$multiply: [100, {$divide: ["$$datetime.users", "$newUsers"]}]}}
]
}
}
}
}
}
])
除此之外还有 2 件事需要考虑:
- 第一个
$addFields
阶段用于 "add" 接下来的 7 天。 Mongo 在这种情况下很难做到这一点。如果您可以在代码中完成它会更好,因为存在很多冗余。 - 如您所见,我必须更改接下来 7 天的
day
、month
和year
值,以防注册时间比方说 31 日。但我没有考虑间隔年。如果你想保持这种方式并使其稳定,你应该添加间隔年检查($mod
应该可以正常工作,因为它每 4 年一次。)到2
月(二月)的条件).