Índice exclusivo do Mongoose não funciona!

Estou tentando deixar o MongoDB detectar um valor duplicado com base em seu índice. Eu acho que isso é possível no MongoDB, mas através do wrapper Mongoose as coisas parecem estar quebradas. Então, para algo assim:

User = new Schema ({ email: {type: String, index: {unique: true, dropDups: true}} }) 

Eu posso salvar 2 usuários com o mesmo email. Maldito.

O mesmo problema foi expresso aqui: https://github.com/LearnBoost/mongoose/issues/56 , mas esse segmento é antigo e leva a lugar nenhum.

Por enquanto, estou fazendo manualmente uma chamada para o database para encontrar o usuário. Essa chamada não é cara, já que “email” é indexado. Mas ainda seria bom deixar isso ser tratado nativamente.

Alguém tem uma solução para isso?

Opa! Você só tem que reiniciar o mongo.

Opa! Você só tem que reiniciar o mongo.

E re-indexar também!

Nos testes, acabei de fazer um:

 mongo  > db.dropDatabase() 

No entanto, se você tiver dados importantes, provavelmente precisará apenas:

 mongo  > db..reIndex() 

Eu me deparei com o mesmo problema: adicionei a restrição exclusiva para o campo de email ao nosso UserSchema depois de já ter adicionado usuários ao database, e ainda consegui salvar usuários com emails do tipo dupe. Eu resolvi isso fazendo o seguinte:

1) Remova todos os documentos da coleção de usuários.

2) No shell mongo, execute o comando: db.users.createIndex({email: 1}, {unique: true})

Em relação ao passo 1, note que a partir dos documentos do Mongo:

O MongoDB não pode criar um índice exclusivo nos campos de índice especificados se a coleção já contiver dados que violariam a restrição exclusiva do índice.

https://docs.mongodb.com/manual/core/index-unique/

Esse comportamento também acontece se você tiver deixado algumas duplicatas no Mongo. O Mongoose tentará criá-los no Mongo quando seu aplicativo for iniciado.

Para evitar isso, você pode lidar com esse erro desta maneira:

 yourModel.on('index', function(err) { if (err?) { console.error err } ); 

Ok, eu consegui resolver isso do mongoshell adicionando o índice no campo e definindo a propriedade exclusiva:

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

A Shell deve responder desta maneira:

 { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } 

Agora, para testar rapidamente a partir do mongoshell:

 var doc = {fieldName: 'abc'}; db..insert(doc) 

Deve dar: WriteResult ({“nInserted”: 1})

Mas quando repetir novamente:

 db..insert(doc) 

Darei:

 WriteResult({ "nInserted" : 0, "writeError" : { "code" : 11000, "errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate key error index: fuelConsumption.users.$email_1 dup key: { : \"martyna@martycud.com\" }" } }) 

De acordo com a documentação: https://docs.mongodb.com/v2.6/tutorial/modify-an-index/

Para modificar um índice existente, você precisa descartar e recriar o índice.

NÃO REINICIE O MONGO!

1 – largar a colecção

 db.users.drop() 

2 – reindexar a mesa

 db.users.ensureIndex({email: 1, type: 1}, {unique: true}) 

Mangusto é um pouco solto ao impor índices exclusivos do nível do aplicativo; portanto, é preferível impor seus índices exclusivos a partir do próprio database usando o mongo cli ou explicitamente dizer ao mongoose que você está falando sério sobre o índice unique , escrevendo a seguinte linha de código logo após o seu UserSchema:

UserSchema.index({ username: 1, email: 1 }, { unique: true});

Isso aplicará o índice exclusivo nos campos username e email em seu UserSchema. Felicidades.

Você também pode resolver esse problema descartando o índice;

vamos supor que você queira remover o índice exclusivo dos users da coleção e o username campo, digite:

db.users.dropIndex('username_1');

Se a tabela / coleção estiver vazia, crie um índice exclusivo para o campo:

 db..createIndex({'field':1}, {unique: true}) 

Se a tabela / coleção não estiver vazia, elimine a coleção e crie o índice:

 db..drop() db..createIndex({'field':1}, {unique: true}) 

Agora reinicie o mongoDB.

Resposta mais recente: não há necessidade de reiniciar o mongodb, se o colleciton já tiver os mesmos índices de nome, o mongoose não recriará seus índices novamente, portanto, elimine os índices existentes do colleciton em primeiro lugar e agora, ao executar o mongoose, ele criará novo índice , acima do processo resolveu o meu problema.

mongoose-unique-validator

Como usar este plugin:

1) npm install –save mongoose-unique-validator

2) no seu esquema, siga este guia:

 // declare this at the top var mongoose = require('mongoose'); var uniqueValidator = require('mongoose-unique-validator'); // exampleSchema = mongoose.Schema({}) etc... exampleSchema.plugin(uniqueValidator); // module.exports = mongoose.model(...) etc.... 

3) methods de mangusto

Ao usar methods como findOneAndUpdate você precisará passar este object de configuração:

{ runValidators: true, context: 'query' }

 ie. User.findOneAndUpdate( { email: 'old-email@example.com' }, { email: 'new-email@example.com' }, { runValidators: true, context: 'query' }, function(err) { // ... } 

4) opções adicionais

  1. insensível a maiúsculas

    use a opção uniqueCaseInsensitive no seu esquema

    ie. email: { type: String, index: true, unique: true, required: true, uniqueCaseInsensitive: true }

  2. mensagens de erro personalizadas

    ie. exampleSchema.plugin(uniqueValidator, { message: 'Error, expected {PATH} to be unique.' });

Agora você pode adicionar / excluir a propriedade exclusiva de seus esquemas sem se preocupar em reiniciar o mongo, eliminar bancos de dados ou criar índices.

Advertências (dos documentos):

Como dependemos de operações assíncronas para verificar se existe um documento no database, é possível que duas consultas sejam executadas ao mesmo tempo, ambas recebem 0 de volta e, em seguida, ambas inserem no MongoDB.

Fora de bloquear automaticamente a coleção ou forçar uma única conexão, não há solução real.

Para a maioria dos nossos usuários, isso não será um problema, mas é um bom exemplo.

Se você estiver usando a opção autoIndex: false no método de conexão como este:

mongoose.connect(CONNECTION_STRING, { autoIndex: false });

Tente remover isso. Se isso não funcionar, tente reiniciar o mongodb como muitos sugeridos neste tópico.