MongoDB дублировать документы даже после добавления уникального ключа

Я создал коллекцию и добавил такой уникальный ключ

db.user_services.createIndex({"uid":1 , "sid": 1},{unique:true,dropDups: true})

Коллекция выглядит примерно так
«user_services»

{
"_id" : ObjectId("55068b35f791c7f81000002d"),
"uid" : 15,
"sid" : 1,
"rate" : 5
},
{

"_id" : ObjectId("55068b35f791c7f81000002f"),
"uid" : 15,
"sid" : 1,
"rate" : 4
}

Проблема:

Использую драйвер php для вставки документов с одинаковыми Уид и Сид и это вставляется.

Что я хочу

  1. В Mongo Shell: Добавьте уникальный ключ в uid и sid без дубликатов документов с одинаковыми uid и sid.
  2. На стороне PHP: иметь что-то вроде mysql «вставить (значение) в частоту обновления дубликата ключа = скорость + 1». То есть всякий раз, когда я пытаюсь вставить документ, он должен быть вставлен, если нет, он должен обновить поле скорости документа

14

Решение

Поздравляю, вы, кажется, нашли ошибку. Это происходит только с MongoDB 3.0.0 в моем тестировании или, по крайней мере, отсутствует в MongoDB 2.6.6. Ошибка теперь записана в SERVER-17599

НОТА:
На самом деле не «проблема», а подтвержденная «намеренно». Отбросил опцию для версии 3.0.0. Все еще перечислены в документация хоть.

Проблема в том, что индекс не создается, и возникают ошибки при попытке создать его в коллекции с существующими дубликатами в полях «составного ключа». На вышесказанном создание индекса должно привести к этому в оболочке:

{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"errmsg" : "exception: E11000 duplicate key error dup key: { : 15.0, : 1.0 }",
"code" : 11000,
"ok" : 0
}

Если дубликатов нет, вы можете создать индекс так, как вы сейчас его пытаетесь, и он будет создан.

Чтобы обойти это, сначала удалите дубликаты с помощью процедуры, подобной этой:

db.events.aggregate([
{ "$group": {
"_id": { "uid": "$uid", "sid": "$sid" },
"dups": { "$push": "$_id" },
"count": { "$sum": 1 }
}},
{ "$match": { "count": { "$gt": 1 } }}
]).forEach(function(doc) {
doc.dups.shift();
db.events.remove({ "_id": {"$in": doc.dups }});
});

db.events.createIndex({"uid":1 , "sid": 1},{unique:true})

Тогда дальнейшие вставки, содержащие повторяющиеся данные, не будут вставлены, и соответствующая ошибка будет записана.

Последнее замечание: «dropDups» — это не очень элегантное решение для удаления дублирующихся данных. Вы действительно хотите что-то с большим контролем, как показано выше.

Для второй части, а не использовать .insert() использовать .update() метод. Имеет «Upsert» вариант

$collection->update(
array( "uid" => 1, "sid" => 1 ),
array( '$set' => $someData ),
array( 'upsert' => true )
);

Таким образом, «найденные» документы «изменены», а не найденные документы «вставлены». Также см $setOnInsert для способа создания определенных данных только когда документ фактически вставлен, а не когда изменен.


Для вашей конкретной попытки правильный синтаксис .update() это три аргумента. «запрос», «обновление» и «параметры»:

$collection->update(
array( "uid" => 1, "sid" => 1 ),
array(
'$set' => array( "field" => "this" ),
'$inc' => array( "counter" => 1 ),
'$setOnInsert' => array( "newField" => "another" )
),
array( "upsert" => true )
);

Ни одной из операций обновления не разрешен «доступ к тому же пути», который используется в другой операции обновления в этом разделе «обновления» документа.

32

Другие решения

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

Удалить дубликаты по ключу для mongo> 3.0 просто. Просто запустите этот запрос, заменив yourDuplicateKey и предполагая _id ваш первичный ключ (убедитесь, что вы mongodump на всякий случай):

db.yourCollection.aggregate([
{ "$group": {
"_id": { "yourDuplicateKey": "$yourDuplicateKey" },
"dups": { "$push": "$_id" },
"count": { "$sum": 1 }
}},
{ "$match": { "count": { "$gt": 1 } }}
]).forEach(function(doc) {
doc.dups.shift();
db.yourCollection.remove({ "_id": {"$in": doc.dups }});
});
12