I am writing a program in Node.js using MongoDB and I want to update a database when certain files are modified. Inside the database I store the mtime of each database entry and I want to compare this to the mtime of the file to see if the database entry is out of date. Here is my code:
function updateArticles(articles) {
const dir = "articles";
const files = fs.readdirSync(dir);
files.forEach(
(file) => {
const fpath = path.join(dir, file);
const fmtime = fs.statSync(fpath).mtimeMs;
articles.updateOne(
//refresh content and mtime if the text file has been modified
{
_id: file,
mtime: {$lte: fmtime}
},
{
$set: {content: htmlifySync(fpath), mtime: Date.now(), title: titleSync(fpath)},
//btime is only set once
$setOnInsert: {btime: Date.now()}
},
{upsert: true}
)
});
}
When I set {upsert: true}
I get a duplicate key error. When I set {upsert: false}
new entries are not added. It works if I remove the second condition in my query but then it is running htmlifySync
and titleSync
for every entry which is expensive and unneccessary.
I think the issue is that mongoDB is trying to insert a new entry which meets both query conditions which means it tries to insert a new entry with the same id. I want mtime: {$lte: fmtime}
to be a filter for the update but I don't want it to be made true by the update.
To clarify:
The update operation will try to insert a new document if there are no matches. Since you will have no match if the file has not been updated, it tries to insert a new one, triggering the error.
Possible solutions:
articles.updateOne(
{
_id: file
},
[{
$set: {
content: htmlifySync(fpath),
mtime: {$cond:[{$lt:["$mtime",fmtime]},Date.now(),"$mtime"]}
title: titleSync(fpath),
btime: {$cond:[{$eq:[{$type:"$btime"},"missing"]},Date.now(),"$btime"]}
}
}],
{upsert: true}
)
This uses $cond
to only set btime
if it doesn't already exist, and only set mtime
if the stored value is less than fmtime
, and takes advantage of the fact that MongoDB will not actually process a write if the update sets the same values that already exist.