I have several documents of the same type that I would like to update at the same time, as well a couple of other operations that I am looking to do within a transaction using the Swift driver.
This is the code I currently have below, but I am stuck on what would be the best way to update several of the same documents? I need to $inc a field by 1 for all of them and then the tricky part is updating another field with their own specific values.
Should I loop through them one at time and use the updateOne operator? Is there a better way to do this?
return req.application.mongoClient.withSession { session in
return session.startTransaction().flatMap { _ -> EventLoopFuture<InsertManyResult?> in
req.playerRatingsCollection.insertMany(playerRatings, session: session)
}
.flatMap { _ -> EventLoopFuture<InsertManyResult?> in
req.memberPlayerRatingsCollection.insertMany(Array(insertNewMemberPlayerRatings), session: session)
}
.flatMap { _ -> EventLoopFuture<UpdateResult?> in
// several documents where I need to $inc a totalRaitngs fields by 1 and update the average ratings for each document
req.memberPlayerRatingsCollection.updateMany(filter: <#T##BSONDocument#>, update: <#T##BSONDocument#>)
}
.map { _ in
Response(status: .ok)
}
}
.hop(to: req.eventLoop)
Please let me know if anything doesn’t make sense or needs further clarity. I’ve looked into several different ways of solving the problem I am stuck on using change streams and views but I’ve found them both quite complex and would ideally like to use the above approach.
So if I understand correctly you want to avoid the need of fetching the updated documents before the update, otherwise I think doing a single document math calculations might be easier on the client side saving the calculated values directly.
If you still want to do it server side I would recommend looking into pipeline updates available from MongoDB 4.2 .
You can filter the needed documents and run few stages on each. Since I don’t know the specific of your logic and documents i would say it should look like:
[ Stage 1 - update the total ratings by inc 1,
Stage 2 - calculate the avg based on the totalRatings from previous stage and update
]
struct MemberPlayerRating: Content {
var _id: BSONObjectID?
let averageRating: Double
let totalRatings: Int
let playerID: BSONObjectID
let userID: BSONObjectID
}
So what I am unsure about is the best way to update say 5 different MemberPlayerRating documents with different new ratings in order to calculate their new average rating. I need them either to all fail or all succeed ideally.
so if I had the following data, which I would like to use to calculate the new averageRating for a MemberPlayerRating:
Even if I calculated the averageRating client side, I would still then want to update all the averageRating at the same time for the different documents. I don’t think I can achieve this with updateMany or an aggregation pipeline. is that correct? if so is there a recommended way to do this?
If you want multiple document update to succeed or fail as one you should consider using transactions
Another option is to keep updated data in one document to update it as one.
An update with multi true will update all documents in one command but as it is not atomic as a whole it cannot guarantee that all will succeed or fail everything.