Reputation: 19
I'm using mongoose and I need to update documents based on one condition, more specifically by comparing and seeing if a new document I just fetched has a more recent date on the lastUpdated field. Here's the test documents I'm working with right now:
//This are the Matches currently on the DB
const testMatchesOnDB = [
{ id: 1, team: "teamOneV1", lastUpdated: moment().subtract(10, "days") },
{
id: 2,
team: "TeamTwoV1",
lastUpdated: moment("2022-08-29T14:18:59+01:00"),
},
{ id: 3, team: "TeamThreeV1", lastUpdated: moment().subtract(2, "days") },
];
//This are the new Matches I just fetched
const newMatchesFromAPI = [
{
id: 2,
team: "TeamTwoV2",
lastUpdated: moment("2022-08-29T14:18:59+01:00"),
},
{ id: 3, team: "TeamThreeV2", lastUpdated: moment().subtract(1, "days") },
{ id: 4, team: "TeamFourV2", lastUpdated: moment() }, //This one is here just to test the upsert (already working)
];
The goal here is to fetch a new batch of matches, and then comparing them to the matches I already have on the DB. Firstly I find the matches by their id on the database, and then, when I find matches I already have on the DB, update them if they have an older date on lastUpdated field compared to the ones I just fetched. I can't put both the id and lastUpdate conditions on the first query, since I'm using upsert, and that would result in duplicate matches.
Here's my best attempt at doing that (using BulkWrite) :
await Test.bulkWrite(
newMatchesFromAPI.map((match) => ({
updateOne: {
filter: {
id: match.id,
},
update: match,
upsert: true,
arrayFilters: [{ lastUpdated: { $lt: moment(match.lastUpdated) } }],
},
}))
);
This gives me the following error:
MongoBulkWriteError: The array filter for identifier 'lastUpdated' was not used in the update { $setOnInsert: { id: 2 } }
This is what my DB should look like after the operation runs successfully:
const desiredDB = [
{ id: 1, team: "teamOneV1", lastUpdated: moment().subtract(10, "days") }, //not found on the query stayed the same
{
id: 2,
team: "TeamTwoV1",
lastUpdated: moment("2022-08-29T14:18:59+01:00"), //stayed the same because the date is equal
},
{ id: 3, team: "TeamThreeV2", lastUpdated: moment().subtract(1, "days") }, //This one updated because more recent
{ id: 4, team: "TeamFourV2", lastUpdated: moment() }, // upserted
];
What's the correct way of achieving this?
Thank you in advance
Upvotes: 0
Views: 121
Reputation: 19
In case someone is stuck on the same problem, I managed to find myself a workaround. It's not pretty but it works:
const updateExistingMatchesOperation = newMatchesFromAPI.map((match) => ({
updateOne: {
filter: {
id: match.id,
lastUpdated: { $lt: moment(match.lastUpdated) },
},
update: match,
upsert: false,
},
}));
const insertNewMatchesOperation = newMatchesFromAPI.map((match) => ({
updateOne: {
filter: {
id: match.id,
},
update: { $setOnInsert: match },
upsert: true,
},
}));
Test.bulkWrite([
...updateExistingMatchesOperation,
...insertNewMatchesOperation,
]);
This results in double the operations we would have if there was a single function solution. What I did there was I separated the two operations I wanted. First I define only the operations where it fits both the id and the lastUpdated date being older. And update those documents only, with no upsert (to avoid duplication). Secondly what I want is to just insert the matches that don't exist on the db already so I find by ID and set upsert to true, and then I $setOnInsert so I don't change anything on the documents that are already in the db. To finish, I join both arrays into one and call it all with bulkWrite().
I know this solution is ugly, but I decided to leave it here in case nothing better comes along.
Upvotes: 1