I have a system that
I wish to produce history of changes that happened to the documents in a collection at a particular timestamp
我有一个系统,我希望生成在特定时间戳对集合中的文档进行更改的历史
So let's say we have a document in a collection
因此,假设我们在集合中有一个文档
[
{
"property1" : "A",
"property2" : "B",
"createdAt" : "2023-05-18T06:36:00.000+00:00"
}
]
which gets changed to:
它将更改为:
[
{
"property1" : "A1",
"property2" : "B",
"createdAt" : "2023-05-18T07:36:00.000+00:00"
}
]
then for createdAt value "2023-05-18T07:36:00.000+00:00"
I wish to produce output like
然后,对于createdAt值“2023-05-18T07:36:00.000+00:00”,我希望生成如下输出
[
{
"propertyChanged" : "property1",
"oldValue": "A"
"newValue": "A1"
}
]
Still trying to figure the aggregate pipeline operators best for this
我仍在努力计算最适合这一点的聚合管道运算符
更多回答
Will the same document get updated or a new one will be inserted into the db?
是更新相同的文档还是将新的文档插入数据库?
Similar to @CharchitKapoor - how will you be getting the old document in order to compare with the new document? Will it be stored in another collection or are you doing a find
then comparing that result with a proposed update?
类似于@ChArchitKapoor--为了与新文档进行比较,您将如何获取旧文档?它是否会存储在另一个集合中,或者您正在执行查找,然后将结果与建议的更新进行比较?
Hi @CharchitKapoor the new document would be inserted
Hi@ChArchitKapoor将插入新文档
Hi @jQueeny , The new document would be in the same collection . I wish to find what changed in 2 consecutive documents when sorted by createdat field.
Hi@jQueeny,新文档将位于同一集合中。我希望找出在按创建日期字段排序时,连续两个文档中发生了什么变化。
优秀答案推荐
How about something along these lines:
不如这样说吧:
Use $match
to filter the documents based on the timestamp
db.collection.aggregate([
{
$match: {
createdAt: "2023-05-18T07:36:00.000+00:00" }
},
//2. Use lookup to perform a self-join on the collection to find the previous version of the document based on the createdAt field.
{
$lookup: {
from: "collection",
let: { createdAt: "$createdAt" },
pipeline: [
{
$match: {
$expr: {
$lt: ["$$createdAt", "$createdAt"]
}
}
},
{ $sort: { createdAt: -1 } },
{ $limit: 1 }
],
as: "previousVersion"
}
},
//3. Use unwind to destructure the arrays from the last operation
{
$unwind: "$previousVersion"
},
//4. Finally you can use project to compare the fields
{
$project: {
propertyChanged: {
$setDifference: [
{ $objectToArray: { $arrayElemAt: ["$previousVersion", 0] } },
{ $objectToArray: { $arrayElemAt: ["$$ROOT", 0] } }
]
}
}
},
{
$unwind: "$propertyChanged"
},
{
$project: {
propertyChanged: "$propertyChanged.k",
oldValue: {
$arrayElemAt: [
{ $objectToArray: { $ifNull: ["$previousVersion", {}] } },
{ $indexOfArray: [ { $objectToArray: { $arrayElemAt: ["$previousVersion", 0] } }, "$propertyChanged.k" ] }
]
},
newValue: {
$arrayElemAt: [
{ $objectToArray: { $ifNull: ["$$ROOT", {}] } },
{ $indexOfArray: [ { $objectToArray: { $arrayElemAt: ["$$ROOT", 0] } }, "$propertyChanged.k" ] }
]
}
}]);
更多回答
Hi @ChidiEkuma , Thanks for replying I tried this aggregate pipeline and this works fine till the last stage where we are projecting to get the output . In the last stage i'm getting following error $arrayElemAt's first argument must be an array, but is object
嗨@ChidiEkuma,感谢您的回复我尝试了这个聚合管道,这工作正常,直到最后一个阶段,我们预计得到的输出.在最后一个阶段,我得到以下错误$arrayElemAt的第一个参数必须是数组,但对象
@LakhanSINGH I've updated the solution Let me know if it resolves your errors
@LakhanSINGH我已经更新了解决方案,请告诉我它是否解决了您的错误
Hi @ChidiEkuma, so just before step number 4 where we are unwinding $previousVersion it turns previousVersion into an object now inthe first $project stage in step 4 , we cannot use it as an array in the following line { $objectToArray: { $arrayElemAt: ["$previousVersion", 0] } }, I'm getting the same error now at this line error: $arrayElemAt's first argument must be an array, but is object
Hi@chadiEkuma,就在我们展开$previousVersion的第4步之前,它将previousVersion转换为对象,现在处于第4步的第一个$project阶段,我们不能在以下行中将其用作数组:{$objectToArray:{$arrayElemAt:[“$previousVersion”,0]}},现在我在此行收到相同的错误:$arrayElemAt的第一个参数必须是一个数组,但它是Object
@LakhanSINGH, I updated the original code but forgot to tag you.
@LakhanSINGH,我更新了原始代码,但忘了给你加标签。
我是一名优秀的程序员,十分优秀!