mongodb - MongoDB 事件驱动数据库设计
问题描述
目标
- 零冲突系统:将其设为只写系统将使我们免于冲突。人们正在离线和在线创建和更新文档,并且能够弄清楚什么更新胜过什么是重要的。
- 深度历史参考:我想知道任何时候,那个文件是什么样子的。最重要的是,我需要对每个项目如何随时间变化进行深入的历史分析。
我在考虑以下架构:
参考文件
_id: "u12345",
type: "user",
createdAt: 1584450565 //UNIX TIMESTAMP
{
_id: "<random>"
type: "user-name-revision" //{type}-{key}-Revision
referenceId: "u12345"
value: "John Doe Boy"
updatedAt: 1584450565
}
{
_id: "<random>"
type: "user-name-revision"
referenceId: "u12345"
value: "John Doe"
updatedAt: 1584450566 // 1 second higher than the above
}
{
_id: "<random>"
type: "user-email-revision"
referenceId: "u12345"
value: "john@gmail.com"
updatedAt: 1584450565
}
如果你想获得用户,你会:
referenceId
使用u12345获取所有文档。- 仅获取每种类型的最新信息
- 然后像这样组合并输出用户:
_id: "u12345",
type: "user",
createdAt: 1584450565,
name: "John Doe"
email: "john@gmail.com"
updatedAt: 1584450566 // highest timestamp
我看到的唯一问题是,如果我想对所有用户进行排序,name
比如说 - 如果我有 1000 个用户,我看不到这样做的干净方式。
我想知道是否有人对我可以使用的模式有任何建议。我正在使用 MongoDB,所以我可以使用它。
解决方案
您可以尝试以下聚合。
从 type 字段中投影 key 字段,按 updatedAt 和 group 排序以选择最新值并保留引用和 updatedAt。
对所有文档进行分组并合并不同的键值并保持更新的At和后处理以格式化文档。
查找以提取用户值,然后使用 replaceRoot 将主文档与查找文档合并。
按名称对文档进行排序。
db.collectionname.aggregate([
{"$addFields":{"key":{"$arrayElemAt":[{"$split":["$type","-"]},1]}}},
{"$sort":{"updatedAt":-1}},
{"$group":{
"_id":{"referenceId":"$referenceId","key:"$key"},
"value":{"$first":"$$ROOT"},
"referenceId":{"$first":"$referenceId"},
"updatedAt":{"$first":"$updatedAt"}
}},
{"$sort":{"updatedAt":-1}},
{"$group":{
"_id":"$_id.referenceId",
"data":{
"$mergeObjects":{"$arrayToObject":[[["$_id.key","$value"]]]}
},
"updatedAt":{"$first":"$updatedAt"}
}},
{"$addFields":{
"data.referenceId":"$referenceId",
"data.updatedAt":"$updatedAt"
}},
{"$project":{"data":1}},
{"$lookup":{
"from":"othercollectionname",
"localField":"data.referenceId",
"foreignField":"_id",
"as":"reference"
}},
{"$replaceRoot":{
"newRoot":{
"$mergeObjects":[{"$arrayElemAt":["$reference",0]},"$data"]}
}},
{"$project":{"_id":0}},
{"$sort":{"name":1}}
])
替代方法:
通过所有转换,您的查询会慢一些。你可以做一些调整。
输入
{
_id: "<random>"
type: "user",
key: "name"
referenceId: "u12345"
value: "John Doe Boy"
updatedAt: 1584450565
}
询问
db.collectionname.aggregate([
{"$sort":{"updatedAt":-1}},
{"$group":{
"_id":{"referenceId":"$referenceId","key":"$key"},
"top":{"$first":"$$ROOT"}
}},
{"$sort":{"top.updatedAt":-1}},
{"$group":{
"_id":"$_id.referenceId",
"max":{"$max":{"$cond":[{"$eq":["$key", "name"]},"$top.value",null]}},
"key-values":{"$push":{"k":"$_id.key","v":"$top.value"}},
"updatedAt":{"$first":"$top.updatedAt"}
}},
{"$lookup":{
"from":"othercollectionname",
"localField":"_id",
"foreignField":"_id",
"as":"reference"
}},
{"$project":{"_id":0}},
{"$sort":{"max":1}}
])
我们可以进一步细化我们的模式以删除一些其他阶段。我们确保在数组末尾添加最新值。就像是
输入
{
_id: "<random>"
type: "user",
key: "name"
referenceId: "u12345"
updates:[
{"value": "John Doe Boy", updatedAt: 1584450565},
{"value": "John Doe", updatedAt: 1584450566}
]
}
询问
db.collectionname.aggregate([
{"$addFields":{"latest":{"$arrayElemAt":["$updates",-1]}}},
{"$group":{
"_id":"$referenceId",
"max":{"$max":{"$cond":[{"$eq":["$key", "name"]},"$latest.value",null]}},
"updatedAt":{"$first":"$updatedAt"}
"key-values":{"$push":{"k":"$key","v":"$latest.value"}},
"updatedAt":{"$first":"$latest.updatedAt"}
}},
{"$lookup":{
"from":"othercollectionname",
"localField":"_id",
"foreignField":"_id",
"as":"reference"
}},
{"$project":{"_id":0}},
{"$sort":{"max":1}}
])
推荐阅读
- java - 用于将数字编码为字母代码的 Android 应用
- sql - SQL ORDER BY 命令不适用于 GROUP BY
- c - Xcode:类型'struct dirent'在不同的翻译单元中有不兼容的定义
- javascript - 使用 express.js 将数据发送到 Vue.js 前端
- node.js - TypeError:无法读取未定义的属性“listOrderJson”
- java - JavaFX 将数据从表格视图中获取到文本字段中
- node.js - NodeJS request.get body 没有将值存储在变量上
- kotlin - 来自 Github 的带有 Kotlin 教程的 Spark 不起作用
- android - Android - 如何从动态链接中提取数据
- apache-kafka - Apache Kafka 删除内容