Piszę program w Node.js przy użyciu MongoDB i chcę zaktualizować bazę danych, gdy niektóre pliki zostaną zmodyfikowane. W bazie danych przechowuję czas mtime każdego wpisu bazy danych i chcę go porównać z czasem mtime pliku, aby sprawdzić, czy wpis w bazie danych jest nieaktualny. Oto mój kod:
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}
)
});
}
Kiedy ustawiam {upsert: true}
, pojawia się zduplikowany błąd klucza. Kiedy ustawię {upsert: false}
nowe wpisy nie są dodawane. Działa, jeśli usunę drugi warunek z mojego zapytania, ale wtedy działa htmlifySync
i titleSync
dla każdego wpisu, co jest drogie i niepotrzebne.
Myślę, że problem polega na tym, że mongoDB próbuje wstawić nowy wpis, który spełnia oba warunki zapytania, co oznacza, że próbuje wstawić nowy wpis o tym samym identyfikatorze. Chcę, aby mtime: {$lte: fmtime}
było filtrem dla aktualizacji, ale nie chcę, aby stało się to prawdą przez aktualizację.
W celu wyjaśnienia:
- Jeśli istnieje wpis DB dla pliku:
- Jeśli plik został zmodyfikowany od czasu ostatniej aktualizacji bazy danych, chcę go zaktualizować
- Jeśli plik nie był modyfikowany od czasu ostatniej aktualizacji bazy danych, chcę go pozostawić bez zmian
- Jeśli nie ma wpisu DB dla pliku, chcę go utworzyć
2 odpowiedzi
Operacja aktualizacji spróbuje wstawić nowy dokument, jeśli nie ma dopasowań. Ponieważ nie będziesz mieć dopasowania, jeśli plik nie został zaktualizowany, próbuje wstawić nowy, wywołując błąd.
Możliwe rozwiązania:
- Złap błąd i zignoruj go
Błąd duplikatu klucza oznacza, że podany plik już istnieje w bazie danych, ale czas pliku nie jest dłuższy niż czas przechowywania, więc nie jest wymagana żadna akcja. - Użyj przypisania warunkowego w aktualizacji Jeśli używasz MongoDB 4.2, możesz użyć wyrażeń agregacji w aktualizacji.
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}
)
Używa $cond
tylko do ustawienia btime
, jeśli jeszcze nie istnieje, i tylko do ustawienia mtime
, jeśli przechowywana wartość jest mniejsza niż fmtime
, i wykorzystuje fakt że MongoDB w rzeczywistości nie przetworzy zapisu, jeśli aktualizacja ustawi te same wartości, które już istnieją.
Zdałem sobie sprawę, że naprawdę chcę uniknąć oceny htmlifySync
dokumentów, które nie wymagają aktualizacji. Myślę, że jedynym sposobem, aby to zrobić, jest najpierw findOne
, a następnie insertOne
lub updateOne
jako jedyny sposób, aby zatrzymać htmlifySync
i titleSync
ocenę za każdym razem polega na umieszczeniu ich w instrukcjach if.
articles.findOne({_id: file}, {mtime: 1}).then(
entry => {
if (!entry) {
articles.insertOne(
{
_id: file,
mtime: Date.now(),
title: titleSync(fpath),
content: htmlifySync(fpath),
btime: Date.now()
}
).then((res) => {
console.log(res.res)
})
} else if (entry.mtime < fmtime) {
console.log(file);
articles.updateOne(
{_id: file},
{
$set: {
mtime: Date.now(),
title: titleSync(fpath),
content: htmlifySync(fpath)
}
}
).then((res) => {
console.log(res.res)
})
}
}
)