¡El índice único de Mongoose no funciona!

95

Estoy tratando de permitir que MongoDB detecte un valor duplicado según su índice. Creo que esto es posible en MongoDB, pero a través de la envoltura de Mongoose las cosas parecen estar rotas. Entonces, para algo como esto:

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

Puedo guardar 2 usuarios con el mismo correo electrónico. Maldito.

El mismo problema se ha expresado aquí: https://github.com/LearnBoost/mongoose/issues/56 , pero ese hilo es antiguo y no conduce a ninguna parte.

Por ahora, estoy haciendo una llamada manualmente a la base de datos para encontrar al usuario. Esa llamada no es cara ya que el "correo electrónico" está indexado. Pero aún sería bueno dejar que se manejara de forma nativa.

¿Alguien tiene una solución para esto?

foobar
fuente
1
Malas noticias, todavía es un problema con mongod v2.4.3, mongoose v3.6.20
damphat
Unique parece funcionar en uno de mis hosts, pero no logra hacer cumplir los únicos utilizando exactamente el mismo código de nodo / mangosta en un host diferente. El host que funciona correctamente ejecuta un solo mongod 3.4.10, el que no lo hace, ejecuta el conjunto de réplicas con mongod 3.2.17.En ambos hosts, estoy creando una colección desde cero, por lo que los dups existentes no son un problema. Probé la mayoría de las soluciones en esta página y la que funcionó fue el validador único de mangosta de @Isaac Pak.
Maksym

Respuestas:

95

¡Ups! Solo tienes que reiniciar mongo.

foobar
fuente
4
Tengo el mismo problema, pero no puedo averiguar cómo reiniciar mongo en OSX. Cuando finalice el proceso, se volverá a generar automáticamente y el índice único aún no funciona ... ¿alguna idea?
ragulka
7
¿A qué te refieres con "oops, solo tienes que reiniciar Mongo"? ¿Está diciendo que es imposible agregar un índice sin eliminar la base de datos?
andrewrk
7
Eso no es cierto. No es necesario reiniciar mongo para agregar un índice.
JohnnyHK
1
por lo único .. TENÍA que reiniciar mongo .. y funcionó .. ¡Gracias!
Muhammad Ahsan Ayaz
2
Intenté reiniciar. También reindexando. Tuve que eliminar la base de datos para resolver esto. Supongo que el problema es que ya existían duplicados antes de agregar el índice, por lo que en una base de datos de producción tendría que eliminar los duplicados y luego reiniciar.
isimmons
40

¡Ups! Solo tienes que reiniciar mongo.

Y vuelva a indexar también, con:

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

En las pruebas, como no tengo datos importantes, también puede hacer:

mongo <db-name>
> db.dropDatabase()
jpillora
fuente
16
¡db.dropDatabase () borra su base de datos!
Isaac Pak
28

Me encontré con el mismo problema: agregué la restricción única para el emailcampo a nuestro UserSchemadespués de haber agregado usuarios a la base de datos, y aún pude guardar usuarios con correos electrónicos engañados. Resolví esto haciendo lo siguiente:

1) Elimine todos los documentos de la colección de usuarios.

2) Desde el shell mongo, ejecute el comando: db.users.createIndex({email: 1}, {unique: true})

Con respecto al paso 1, tenga en cuenta que de los documentos de Mongo:

MongoDB no puede crear un índice único en los campos de índice especificados si la colección ya contiene datos que violarían la restricción única del índice.

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

Cepa de Neil
fuente
11

Este comportamiento ocurre también si ha dejado algunos duplicados en Mongo. Mongoose intentará crearlos en Mongo cuando se inicie la aplicación.

Para evitar esto, puede manejar este error de esta manera:

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

Ok, pude resolver esto desde mongoshell agregando el índice en el campo y estableciendo la propiedad única:

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

Shell debería responder de esta manera:

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

Ahora para probar rápidamente desde el mongoshell:

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

Debería dar: WriteResult ({"nInserted": 1})

Pero al repetir de nuevo:

db.<collectionName>.insert(doc)

Daré:

WriteResult({
    "nInserted" : 0,
    "writeError" : {
        "code" : 11000,
        "errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate key error index: fuelConsumption.users.$email_1  dup key: { : \"[email protected]\" }"
    }
})
woj.sierak
fuente
10

He hecho algo como esto:

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

Agregué la Foo.createIndexes()línea porque recibía la siguiente advertencia de desaprobación cuando se estaba ejecutando el código:

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

No estoy seguro de si Foo.createIndexes()es asincrónico, pero AFAIK las cosas parecen estar funcionando bien

solsticio333
fuente
La única respuesta a la pregunta que aborda el problema en lugar de girar en torno a él.
Saksham Gupta
Esta respuesta me ayudó. Todo lo que me faltaba era index: trueque se creara mi índice único.
elcid
6

Según la documentación: https://docs.mongodb.com/v2.6/tutorial/modify-an-index/

Para modificar un índice existente, debe eliminar y volver a crear el índice.

¡NO REINICIES MONGO!

1 - suelta la colección

db.users.drop()

2 - reindexar la tabla

db.users.ensureIndex({email: 1, type: 1}, {unique: true})
Damien Romito
fuente
Dice el índice de caída, no la colección
Zero0Ho
5

Mongoose es un poco flojo al aplicar índices únicos desde el nivel de la aplicación; por lo tanto, es preferible hacer cumplir sus índices únicos de la base de datos usando el mongo cli o decirle explícitamente a mangosta que se toma en serio el uniqueíndice escribiendo la siguiente línea de código justo después de su UserSchema:

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

Esto aplicará el índice único en ambos campos usernamey emailen su UserSchema. Salud.

moeabdol
fuente
5
Un poco tarde para la fiesta, pero de qué sirve un ORM que es "un poco suelto cuando se
aplican
De qué sirven menospreciar los comentarios que no ayudan en nada. Esta solución es sólida y está lista para producción.
Isaac Pak
4

Mongoose fallará silenciosamente al agregar un índice único cuando:

  1. La colección ya tiene un índice del mismo nombre
  2. La colección ya contiene documentos con duplicados del campo indexado.

En el primer caso, enumere los índices con db.collection.getIndexes()y suelte el índice antiguo con db.collection.dropIndex("index_name"). Cuando reinicie la aplicación Mongoose, debería agregar correctamente el nuevo índice.

En el segundo caso, debe eliminar los duplicados antes de reiniciar la aplicación Mongoose.

Derek Hill
fuente
3

validador-único-mangosta

Cómo utilizar este complemento:

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

2) en su esquema, siga esta guía:

// 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 mangosta

Cuando utilice métodos como findOneAndUpdate, deberá pasar este objeto de configuración:

{ runValidators: true, context: 'query' }

ie. User.findOneAndUpdate(
      { email: '[email protected]' },
      { email: '[email protected]' },
      { runValidators: true, context: 'query' },
      function(err) {
        // ...
    }

4) opciones adicionales

  1. no distingue entre mayúsculas y minúsculas

    use la opción uniqueCaseInsensitive en su esquema

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

  2. mensajes de error personalizados

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

Ahora puede agregar / eliminar la propiedad única a sus esquemas sin preocuparse por reiniciar mongo, eliminar bases de datos o crear índices.

Advertencias (de los documentos):

Debido a que confiamos en operaciones asíncronas para verificar si un documento existe en la base de datos, es posible que se ejecuten dos consultas al mismo tiempo, ambas obtienen 0 y luego ambas se insertan en MongoDB.

Fuera de bloquear automáticamente la colección o forzar una sola conexión, no existe una solución real.

Para la mayoría de nuestros usuarios, esto no será un problema, pero es un caso extremo a tener en cuenta.

Isaac Pak
fuente
2

Respuesta más reciente: no hay necesidad de reiniciar mongodb en absoluto, si colleciton ya tiene índices del mismo nombre, mongoose no volverá a crear sus índices, por lo tanto, elimine los índices existentes de colleciton primero, y ahora, cuando ejecute mongoose, creará un nuevo índice , el proceso anterior resolvió mi problema.

chen Jacky
fuente
1

También puede resolver este problema eliminando el índice;

supongamos que desea eliminar el índice único de la colección usersy el campo username, escriba esto:

db.users.dropIndex('username_1');

Ademola Adegbuyi
fuente
1

Si la tabla / colección está vacía, cree un índice único para el campo:

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

Si la tabla / colección no está vacía, suelte la colección y cree un índice:

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

Ahora reinicie mongoDB.

Rakin Afser
fuente
1

compruebe que autoIndex en el esquema sea verdadero, tal vez se establezca falso (predeterminado verdadero) cuando use las opciones de mongoose.connect

Lee Quinze
fuente
1

Enfrenté el mismo problema por un tiempo e hice muchas búsquedas y la solución para mí fue la createIndexes()función.

Deseo que eso ayude.

por lo que el código será así.

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

Si está utilizando la opción autoIndex: falseen el método de conexión de esta manera:

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

Intenta eliminar eso. Si eso no funciona, intente reiniciar mongodb como se sugiere en este hilo.

Rafael Mejía
fuente
0

Puede definir su esquema como

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

Pero quizás esto no funcione cuando ya existe un documento y después de eso, ha cambiado el esquema del Usuario. Puede crear un índice en el correo electrónico para esta colección de usuarios si no desea eliminar la colección. Con este comando puede crear un índice en el correo electrónico.

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

O simplemente puede eliminar la colección y agregar el usuario nuevamente.
Para eliminar la colección, puede ingresar esto:

db.User.drop()
Pulkit Aggarwal
fuente
0

Cuando encontré este problema, intenté eliminar la base de datos, reiniciar el servidor (nodemon) muchas veces y ninguno de los trucos funcionó en absoluto. Encontré el siguiente trabajo, a través de Robo 3T:

  1. En Robo 3T, haga doble clic en la base de datos para abrir las colecciones.
  2. Abra las colecciones para revelar la colección en cuestión. Asegúrese de que sus colecciones estén vacías, para empezar
  3. Haga clic derecho en la carpeta de índices. De forma predeterminada, verá _id_como predeterminado. Ahora, elija Agregar índice
  4. Elija un nombre, digamos emailpara el campo de correo electrónico, por ejemplo
  5. Proporcione claves como JSON. Por ejemplo

    {
       "email": 1
    }
    
  6. Haga clic en la casilla de verificación Único

  7. Salvar

Esto asegurará que no se guarden correos electrónicos duplicados en MongoDB.

Balepur
fuente
¿Cuál es el significado de 1 aquí en el objeto {"email": 1}?
siluveru kiran kumar
0

Asegúrese de que su colección no sea redundante del campo en el que acaba de colocar el índice único.

Luego, simplemente reinicie su aplicación (mangosta). Simplemente agrega silenciosamente el índice falla.

Zero0Ho
fuente
0

Eliminar todos los documentos de la colección:

db.users.remove({})

Y un reinicio, como otros mencionaron, funcionó para mí.

StefanBob
fuente
0

Si su MongoDB está funcionando como un servicio (una forma fácil de encontrar esto es si no necesita conectarse a la base de datos sin iniciar el archivo mongod.exe a través de la terminal), luego de hacer los cambios, es posible que deba reiniciar el servicio y / o elimine completamente su base de datos.

Es bastante extraño porque para algunos usuarios solo funcionó eliminar una sola colección. Algunos de ellos solo necesitaban eliminar la base de datos. Pero esos no funcionaron para mí. Dejé caer la base de datos y luego reinicié el servicio del servidor MongoDB.

Para reiniciar un servicio, busque Servicios en la barra de búsqueda de Windows y luego busque el servicio MongoDB, haga doble clic para abrir, luego detenga e inicie el servicio nuevamente.

Si los otros métodos no le funcionaron, creo que este funcionará.

mella
fuente
0

En mi caso, la mangosta estaba desactualizada. Lo comprobé ejecutando npm obsoleto en CMD. y actualizado 'mangosta'.

Por favor, diga si eso también le funcionó.

nishant srivastava
fuente
¿Qué versión estabas usando?
Baboo_
0

Cuando conecte su base de datos con la aplicación agregue esta opción: "audoIndex: true" por ejemplo en mi código hice esto:

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

También dejé caer la colección con la que tengo problemas y la recreé para asegurarme de que funcionará. Encontré esta solución en: https://dev.to/emmysteven/solved-mongoose-unique-index-not-working-45d5 También probé soluciones como "reiniciar MongoDB" pero no funcionó para mí.

Hasan
fuente