O índice exclusivo do Mongoose não está funcionando!


98

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

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

Posso salvar 2 usuários com o mesmo e-mail. Droga.

O mesmo problema foi expresso aqui: https://github.com/LearnBoost/mongoose/issues/56 , mas esse tópico é antigo e não leva a lugar nenhum.

Por enquanto, estou fazendo uma chamada manual para o banco de dados para encontrar o usuário. Essa chamada não é cara, pois o "email" está indexado. Mas ainda seria bom deixar isso ser tratado nativamente.

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


1
Más notícias, ainda é problema com mongod v2.4.3, mongoose v3.6.20
damphat

Único parece funcionar em um dos meus hosts, mas não consegue impor os únicos usando exatamente o mesmo código de nó / mangusto em um host diferente. O host que funciona corretamente executa o único mongod 3.4.10, o que não - executa o conjunto de réplicas com o mongod 3.2.17. Em ambos os hosts, estou criando uma coleção do zero, portanto, os dups existentes não são um problema. Tentei a maioria das soluções nesta página e a que funcionou foi o validador exclusivo do mangusto da @Isaac Pak.
Maksym

Respostas:


94

Ops! Você apenas tem que reiniciar o mongo.


4
Estou tendo o mesmo problema, mas não consigo descobrir como reiniciar o mongo no OSX. Quando eu mato o processo, ele vai aparecer novamente automaticamente e o índice exclusivo ainda não está funcionando ... alguma ideia?
ragulka

7
o que quer dizer "opa, você só precisa reiniciar o mongo" ?! você está dizendo que é impossível adicionar um índice sem derrubar o banco de dados?
andrewrk

7
Isso não é verdade. Você não precisa reiniciar o mongo para adicionar um índice.
JohnnyHK

1
para algo único .. Tive de reiniciar o mongo .. e funcionou .. Obrigado!
Muhammad Ahsan Ayaz

2
Tentei reiniciar. Também reindexando. Tive que descartar o banco de dados para resolver isso. Acho que o problema é que já existiam duplicatas antes de adicionar o índice, portanto, em um banco de dados de produção, eu precisaria remover as duplicatas e reiniciar?
isimmons de

40

Ops! Você apenas tem que reiniciar o mongo.

E reindexar também, com:

mongo <db-name>
> db.<collection-name>.reIndex()

No teste, como não tenho dados importantes, você também pode fazer:

mongo <db-name>
> db.dropDatabase()

16
db.dropDatabase () limpa seu banco de dados!
Isaac Pak,

28

Eu encontrei o mesmo problema: adicionei a restrição exclusiva para o emailcampo ao nosso UserSchemadepois de já ter adicionado usuários ao banco de dados e ainda era capaz de salvar usuários com e-mails enganosos. 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 à etapa 1, observe que nos documentos do Mongo:

O MongoDB não pode criar um índice exclusivo no (s) campo (s) de índice especificado (s) se a coleção já contiver dados que violariam a restrição exclusiva do índice.

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


11

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

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

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

10

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

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

A Shell deve responder desta forma:

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

Agora, para testar rapidamente a partir do mongoshell:

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

Deve fornecer: WriteResult ({"nInserted": 1})

Mas ao repetir novamente:

db.<collectionName>.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\" }"
    }
})

10

Eu fiz algo assim:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const FooSchema = new Schema({
   name: { type: String, required: true, index: true, unique: true }
});

const Foo = mongoose.model('Foo', FooSchema);

Foo.createIndexes();

module.exports = Foo

Eu adicionei a Foo.createIndexes()linha bc. Estava recebendo o seguinte aviso de suspensão de uso quando o código estava sendo executado:

(node:21553) DeprecationWarning: collection.ensureIndex is deprecated. Use createIndexes instead.

Não tenho certeza se Foo.createIndexes()é assíncrono, mas as coisas AFAIK parecem estar funcionando bem


A única resposta à pergunta que aborda o problema, em vez de girar em torno dele.
Saksham Gupta

Essa resposta me ajudou. Tudo que faltava era index: truepara que meu índice exclusivo fosse criado.
sábado


6

O Mongoose é um pouco frouxo ao impor índices exclusivos no nível do aplicativo; portanto, é preferível impor seus índices exclusivos do próprio banco de dados usando o mongo cli ou dizer explicitamente ao mongoose que você uniqueleva o índice a sério escrevendo a seguinte linha de código logo após o UserSchema:

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

Isso reforçará o índice exclusivo em ambos os campos usernamee emailem seu UserSchema. Felicidades.


6
Um pouco tarde para a festa, mas de que adianta um ORM que é "um pouco frouxo ao impor índices únicos"
Imre_G

Que bom são comentários depreciativos que não ajudam de forma alguma. Esta solução é sólida e pronta para a produção.
Isaac Pak

4

O Mongoose falhará silenciosamente ao adicionar um índice exclusivo quando:

  1. A coleção já possui um índice com o mesmo nome
  2. A coleção já contém documentos com duplicatas do campo indexado

No primeiro caso, liste os índices com db.collection.getIndexes()e elimine o índice antigo com db.collection.dropIndex("index_name"). Quando você reinicia o aplicativo Mongoose, ele deve adicionar corretamente o novo índice.

No segundo caso, você precisa remover as duplicatas antes de reiniciar o aplicativo Mongoose.


3

validador único de mangusto

Como usar este plugin:

1) npm install --save mongoose-unique-validator

2) em 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) métodos de mangusto

Ao usar métodos como findOneAndUpdatevocê, você precisará passar este objeto 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. não diferencia maiúsculas de minúsculas

    use a opção uniqueCaseInsensitive em 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, descartar bancos de dados ou criar índices.

Advertências (dos documentos):

Como contamos com operações assíncronas para verificar se um documento existe no banco de dados, é possível que duas consultas sejam executadas ao mesmo tempo, ambas obtêm 0 de volta e, em seguida, são inseridas no MongoDB.

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

Para a maioria de nossos usuários, isso não será um problema, mas é um caso extremo a ser considerado.


2

Resposta mais recente: não há necessidade de reiniciar o mongodb, se a coleção já tiver índices com o mesmo nome, o mongoose não recriará seus índices novamente, portanto, remova os índices existentes da coleção primeiro, e agora, quando você executar o mongoose, ele criará um novo índice , o processo acima resolveu meu problema.


1

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

vamos supor que você deseja remover o índice exclusivo da coleção userse do campo username, digite o seguinte:

db.users.dropIndex('username_1');


1

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

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

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

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

Agora reinicie o mongoDB.


1

verifique se autoIndex no esquema é verdadeiro, ele pode ser definido como falso (verdadeiro padrão) quando você usa as opções mongoose.connect


1

Eu enfrentei o mesmo problema por um tempo e fiz muitas pesquisas e a solução para mim foi a createIndexes()função.

Eu desejo que isso ajude.

então o código será assim.

User = new Schema ({
   email: {type: String, unique: true}
});
User.createIndexes();

0

Se você estiver usando a opção autoIndex: falseno método de conexão desta forma:

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

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


0

Você pode definir seu esquema como

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

Mas talvez isso não funcione quando já existe um documento e depois disso, você alterou o esquema do usuário. Você pode criar um índice por e-mail para esta coleção de usuário se não quiser descartar a coleção. Usando este comando, você pode criar um índice no e-mail.

db.User.createIndex({email:1},{unique: true})

Ou você pode simplesmente descartar a coleção e adicionar o usuário novamente.
Para descartar a coleção, você pode inserir isto:

db.User.drop()

0

Quando eu encontrei esse problema, tentei derrubar o banco de dados, reiniciando o servidor (nodemon) várias vezes e nenhum dos truques funcionou. Eu encontrei a seguinte solução, por meio do Robo 3T:

  1. No Robo 3T, clique duas vezes no banco de dados para abrir as coleções
  2. Abra as coleções para revelar a coleção em questão. Certifique-se de que suas coleções estão vazias, para começar
  3. Clique com o botão direito na pasta Índices. Por padrão, você verá o _id_como o padrão. Agora, escolha Adicionar Índice
  4. Escolha um nome, digamos, emailpara o campo de e-mail, por exemplo
  5. Forneça chaves como JSON. Por exemplo

    {
       "email": 1
    }
    
  6. Clique na caixa de seleção Única

  7. Salve 

Isso garantirá que nenhum e-mail duplicado seja salvo no MongoDB.


qual é o significado de 1 aqui no objeto {"email": 1}?
siluveru kiran kumar

0

Certifique-se de que sua coleção não tenha redundante do campo no qual você acabou de colocar o índice exclusivo.

Em seguida, reinicie o aplicativo (mangusto). Ele apenas adiciona silenciosamente o índice falha.


0

Remover todos os documentos da coleção:

db.users.remove({})

E um reinício, como outros mencionaram, funcionou para mim


0

Se o seu MongoDB estiver funcionando como um serviço (uma maneira fácil de encontrar isso é se você não precisar se conectar ao banco de dados sem iniciar o arquivo mongod.exe via terminal), então, depois de fazer as alterações, você pode precisar reiniciar o serviço e / ou elimine totalmente seu banco de dados.

É muito estranho porque para alguns usuários simplesmente largar uma única coleção funcionou. Alguns deles apenas precisaram descartar o banco de dados. Mas isso não funcionou para mim. Abandonei o banco de dados e reiniciei o serviço MongoDB Server.

Para reiniciar um serviço de pesquisa de serviços na barra de pesquisa do Windows e encontrar o serviço MongoDB, clique duas vezes para abrir, pare e inicie o serviço novamente.

Se os outros métodos não funcionaram para você, acredito que isso funcionará.


0

No meu caso, o mangusto estava desatualizado. Eu verifiquei executando o npm desatualizado no CMD. e atualizado 'mangusto'.

Por favor, diga se isso funcionou para você também.


Qual versão você estava usando?
Baboo_

0

Quando você conecta seu banco de dados com o aplicativo, adicione esta opção: "audoIndex: true" por exemplo em meu código eu fiz isso:

const options = {
// your options go here
...
// this code is the solution
audoIndex: true
}
mongoose.connect(DB_URI, options);

Eu também abandonei a coleção com a qual tenho problemas e a recriei para ter certeza de que funcionaria. Encontrei essa solução em: https://dev.to/emmysteven/solved-mongoose-unique-index-not-working-45d5 Também tentei soluções como "reiniciar MongoDB", mas não funcionou para mim.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.