MongoDB Duplicate Documents даже после добавления уникального ключа

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

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-драйвер для вставки документов с такими же uid и sid, и он встает.

Что я хочу

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

Поздравляем, вы обнаружили ошибку. Это происходит только с 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() правильный синтаксис .update() – это три аргумента. «запрос», «обновление» и «параметры»:

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

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

Я чувствую, что текущий самый популярный ответ слишком локальный и подробный для такой элементарной операции 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 }}); });