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ć
1
AntPraxis 19 marzec 2020, 21:16

2 odpowiedzi

Najlepsza odpowiedź

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ą.

0
Joe 20 marzec 2020, 04:06

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)
            })
        }
    }
)
0
AntPraxis 20 marzec 2020, 17:36