Драйвер Java MongoDB: вставьте документ, если он не существует, иначе ничего не делайте

Я пытаюсь написать функцию Java, которая вставляет список слов в коллекцию. Я хочу один документ для каждого слова с уникальным полем "слово". Список слов, которые я хочу вставить, содержит много дубликатов, поэтому я хочу, чтобы моя функция вставляла документ только в том случае, если в коллекции уже нет документа с таким же значением слова. Если документ с таким же значением слова уже существует, функция не должна изменять или заменять этот документ, а продолжать вставлять следующее слово из моего списка.

Я создал индекс в поле "слово", чтобы избежать дублирования документов и поймать исключение дубликата ключа, но я не уверен, что это правильный способ решения этой проблемы.

    IndexOptions uniqueWord = new IndexOptions().unique(true);
    collection.createIndex(Indexes.ascending("word"), uniqueWord);


        try {
            File file = new File("src/words.txt");
            Scanner scanner = new Scanner(file);


            while (scanner.hasNextLine()) {
                  String word= scanner.next();

                    Document document = new Document();
                    document.put("word", word);

                    InsertManyOptions unordered= new InsertManyOptions();
                    ArrayList<Document> docs = new ArrayList<>();
                    docs.add(document);

                    try{
                    collection.insertMany(docs, unordered.ordered(false));
                    }catch(Exception e){
                        //System.out.println(e.getMessage());
                    }

1 ответ

Решение

Вы написали:

Если документ с таким же значением слова уже существует, функция не должна изменять или заменять этот документ, а продолжать вставлять следующее слово из моего списка.

Это исключает использование атомарной операции, такой как findOneAndUpdate или же findOneAndReplace с upsert: true,

Вместо этого я думаю, что ваши параметры ограничены проверкой перед записью, такой как:

if (collection.count(Filters.eq("word", "..."))) {
    // insert
} else {
    // ignore because there is already a document for this word
}

Это зависит от возможных условий гонки, если ваш писатель является многопоточным, например, когда один поток реагирует на ложный результат из collection.count() другому потоку удается написать запись для этого слова. findOneAndReplace является атомным, поэтому он не склонен к этой проблеме,

Я бы посоветовал вам использовать findOneAndReplace с FindOneAndReplaceOptions.upsert == true, это будет иметь тот же конечный результат, что и игнорирование уже написанного документа (хотя и путем замены его идентичным документом), но это, возможно, более безопасно, чем применение проверки перед записью, если существует.

Обновление отредактированного вопроса подразумевает, что вы "вставляете много", но каждый раз во время цикла вы вставляете только один документ (несмотря на использование collection.insertMany()) поэтому приведенное выше предложение остается в силе. Например:

while (scanner.hasNextLine()) {
    String word= scanner.next();

    if (collection.count(Filters.eq("word", word)) == 0L) {
        Document document = new Document();
        document.put("word", word);

        collection.insertOne(document);
    }
}
Другие вопросы по тегам