MongoDB: вставить запись, если она не существует, игнорировать, если она существует


У меня есть серия записей MongoDB, таких как:

{"name":"Bob", "gpa":4} {"name":"Sarah", "gpa":3}

Я столкнусь с различными дополнительными записями, которые могут относиться или не относиться к одним и тем же людям. Я хочу принять их, если они новые люди, и не обновлять их, если они люди, которых мы видели раньше. Поэтому, если я получаю {"name":"Jim", "gpa":2}, я хочу принять это как есть. Если я получаю {"name":"Sarah", "gpa":4} (другое значение GPA), я хочу просто игнорировать его. Это не похоже на логику ни update с" upsert", установленным в yes, ни findAndModify с "upsert."

Https://stackoverflow.com/a/3260290/514757 предлагает способ (уникальный индекс в первом поле, вставить запись, немедленно обновить второе поле), но ему уже несколько лет, и я задаюсь вопросом, нет ли лучшего способа сделать это за один шаг.

Правка:

Принятый ответ кажется отличным, но он не работает для меня! Сначала я создал индекс (это с драйвером Java):
nameIndex.put("name", 1);
nameIndex.put("unique", true);
queue.ensureIndex(nameIndex);

Я вижу из командной строки, что индекс существует и уникален. Затем вставляется элемент:

DBObject person = new BasicDBObject();
person.put("name", "Bob");
person.put("score", 200000);
queue.insert(person);

Позже у элемента с наибольшей оценкой ее оценка уменьшается до нуля:

BasicDBObject reset = new BasicDBObject();
reset.put("$set", new BasicDBObject("score", 0));
DBObject dbObj = queue.findAndModify(null, new BasicDBObject("score", -1), reset);

Все это работает именно так, как задумано! Но, позже, то же самое имя может быть найдено снова, с новым счетом. Когда этот последний бит кода запускается, новый элемент создается с другой оценкой, что именно то, что я не хочу:

BasicDBObject queueable = new BasicDBObject();
queueable.put("name", "Could be Bob again, could be someone new");
queueable.put("score", 1234);
queue.insert(queueable);

Если я ищу Боба, я нахожу:

{ "_id" : ObjectId("4f5e865ad6d09315326ea0f0"), "score" : 0, "name" : "Bob" }
{ "_id" : ObjectId("4f5e8691d6d09315326ea190"), "name" : "Bob", "score" : 886 }

Была создана вторая запись, с более высокой снова счет. Имеет ли значение порядок полей? Это не похоже на то, что должно быть, и я не знаю, как это контролировать.

2 7

2 ответа:

Ваш вызов ensureIndex () не совсем корректен. Первый аргумент функции ensureIndex () содержит ключи, которые должны быть проиндексированы; параметры индекса (например, уникальность) могут быть переданы в качестве второго аргумента. Увидеть водитель документацию.

Сейчас вы пытаетесь создать неуникальный индекс для двух полей: "name"и " unique".

Попробуйте создать свой индекс следующим образом:

DBObject nameIndex = new BasicDBObject();
nameIndex.put("name",1);

DBObject nameIndexOptions = new BasicDBObject();
nameIndexOptions.put("unique", true);

queue.ensureIndex(nameIndex, nameIndexOptions);

Лучший способ сделать это-установить уникальный индекс в поле name, используя:

db.foo.ensureIndex({name:1}, {unique:true});

Это приведет к тому, что Sarah не будет обновляться при вызове insert, потому что он найдет другую запись, где "Sarah" уже установлен для поля name.