L'indice Mongoose Unique non funziona!


95

Sto cercando di consentire a MongoDB di rilevare un valore duplicato in base al suo indice. Penso che questo sia possibile in MongoDB, ma attraverso il wrapper Mongoose le cose sembrano essere rotte. Quindi per qualcosa di simile:

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

Posso salvare 2 utenti con la stessa email. Dannazione.

Lo stesso problema è stato espresso qui: https://github.com/LearnBoost/mongoose/issues/56 , ma quel thread è vecchio e non porta da nessuna parte.

Per ora, sto effettuando manualmente una chiamata al database per trovare l'utente. Quella chiamata non è costosa poiché "email" è indicizzata. Ma sarebbe comunque bello lasciarlo gestire in modo nativo.

Qualcuno ha una soluzione a questo?


1
Cattive notizie, è ancora un problema con Mongod v2.4.3, Mangusta v3.6.20
Damphat

Unique sembra funzionare su uno dei miei host, ma non riesce a far rispettare gli utenti unici utilizzando esattamente lo stesso codice nodo / mangusta su un host diverso. L'host che funziona correttamente esegue il singolo mongod 3.4.10, quello che non esegue il set di repliche con mongod 3.2.17.Su entrambi gli host, sto creando una raccolta da zero, quindi i duplicati esistenti non sono un problema. Ho provato la maggior parte delle soluzioni su questa pagina e quella che ha funzionato è stata mongoose-unique-validator di @Isaac Pak.
Maksym

Risposte:


95

Oops! Devi solo riavviare mongo.


4
Sto riscontrando lo stesso problema, ma non riesco a scoprire come riavviare mongo su OSX. Quando interrompo il processo, verrà generato automaticamente di nuovo e l'indice univoco non funziona ancora ... qualche idea?
ragulka

7
cosa intendi con "oops devi solo riavviare mongo" ?! stai dicendo che è impossibile aggiungere un indice senza portare giù il database?
andrewrk

7
Questo non è vero. Non è necessario riavviare mongo per aggiungere un indice.
JohnnyHK

1
per la cosa unica .. HO DOVUTO riavviare mongo .. e ha funzionato .. Grazie!
Muhammad Ahsan Ayaz

2
Ho provato a riavviare. Anche reindicizzazione. Ho dovuto eliminare il database per risolvere questo problema. Indovinare il problema è che i duplicati esistevano già prima di aggiungere l'indice, quindi in un db di produzione avrei bisogno di rimuovere i duplicati e quindi riavviare?
isimmons

40

Oops! Devi solo riavviare mongo.

E anche reindicizzare, con:

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

Durante i test, poiché non ho dati importanti, puoi anche fare:

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

16
db.dropDatabase () cancella il tuo database!
Isaac Pak

28

Mi sono imbattuto nello stesso problema: ho aggiunto il vincolo univoco per il emailcampo al nostro UserSchemadopo aver già aggiunto utenti al db, ed ero ancora in grado di salvare gli utenti con e-mail duplicate. Ho risolto questo problema procedendo come segue:

1) Rimuovi tutti i documenti dalla raccolta degli utenti.

2) Dalla shell mongo, esegui il comando: db.users.createIndex({email: 1}, {unique: true})

Per quanto riguarda il passaggio 1, nota che dai documenti di Mongo:

MongoDB non può creare un indice univoco sui campi indice specificati se la raccolta contiene già dati che violerebbero il vincolo univoco per l'indice.

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


11

Questo comportamento si verifica anche se hai lasciato alcuni duplicati in Mongo. Mongoose proverà a crearli in Mongo all'avvio dell'applicazione.

Per evitare ciò, puoi gestire questo errore in questo modo:

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

10

Ok, sono stato in grado di risolvere questo problema dal mongoshell aggiungendo l'indice sul campo e impostando la proprietà univoca:

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

Shell dovrebbe rispondere in questo modo:

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

Ora per testare rapidamente dal mongoshell:

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

Dovrebbe fornire: WriteResult ({"nInserted": 1})

Ma quando si ripete di nuovo:

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: { : \"martyna@martycud.com\" }"
    }
})

10

Ho fatto qualcosa di simile:

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

Ho aggiunto la Foo.createIndexes()riga bc stavo ricevendo il seguente avviso di deprecazione durante l'esecuzione del codice:

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

Non sono sicuro che Foo.createIndexes()sia asincrono, ma AFAIK le cose sembrano funzionare bene


L'unica risposta alla domanda che affronta il problema piuttosto che ruota attorno ad esso.
Saksham Gupta

Questa risposta mi ha aiutato. Tutto quello che mi mancava era index: truecreare il mio indice univoco.
elcid


5

Mongoose è un po 'sciolto quando impone indici univoci dal livello dell'applicazione; pertanto, è preferibile applicare i tuoi indici univoci dal database stesso usando il mongo cli o dire esplicitamente a mongoose che sei serio riguardo uniqueall'indice scrivendo la seguente riga di codice subito dopo il tuo UserSchema:

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

Questo imporrà l'indice univoco su entrambi i campi usernamee emailnel tuo Schema utente. Saluti.


5
Un po 'tardi per la festa, ma a che serve un ORM che è "un po' sciolto quando si applicano indici unici"
Imre_G

A che serve sminuire i commenti che non aiutano in alcun modo. Questa soluzione è solida e pronta per la produzione.
Isaac Pak

4

Mongoose non riuscirà silenziosamente ad aggiungere un indice univoco quando:

  1. La raccolta ha già un indice con lo stesso nome
  2. La raccolta contiene già documenti con duplicati del campo indicizzato

Nel primo caso, elenca gli indici con db.collection.getIndexes()e rilascia il vecchio indice con db.collection.dropIndex("index_name"). Quando riavvii l'applicazione Mongoose, dovrebbe aggiungere correttamente il nuovo index.

Nel secondo caso è necessario rimuovere i duplicati prima di riavviare l'applicazione Mongoose.


3

validatore-unico-mangusta

Come utilizzare questo plugin:

1) installazione di npm --save mongoose-unique-validator

2) nel tuo schema segui questa guida:

// 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) metodi di mangusta

Quando usi metodi come findOneAndUpdatete, dovrai passare questo oggetto di configurazione:

{ runValidators: true, context: 'query' }

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

4) opzioni aggiuntive

  1. case insensitive

    usa l'opzione uniqueCaseInsensitive nel tuo schema

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

  2. messaggi di errore personalizzati

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

Ora puoi aggiungere / eliminare la proprietà univoca ai tuoi schemi senza preoccuparti di riavviare mongo, eliminare database o creare indici.

Avvertenze (dai documenti):

Poiché ci affidiamo a operazioni asincrone per verificare se un documento esiste nel database, è possibile che due query vengano eseguite contemporaneamente, entrambe recuperino 0 e quindi vengano inserite entrambe in MongoDB.

Al di fuori del blocco automatico della raccolta o della forzatura di una singola connessione, non esiste una soluzione reale.

Per la maggior parte dei nostri utenti questo non sarà un problema, ma è un caso limite di cui essere a conoscenza.


2

Risposta più recente: non è necessario riavviare mongodb, se la raccolta ha già gli stessi indici dei nomi, mangusta non ricrea nuovamente i tuoi indici, quindi, prima elimina gli indici esistenti della raccolta e ora, quando esegui mangusta, creerà un nuovo indice , il processo sopra ha risolto il mio problema.


1

Puoi anche risolvere questo problema rilasciando l'indice;

supponiamo che tu voglia rimuovere l'indice univoco dalla raccolta userse dal campo username, digita questo:

db.users.dropIndex('username_1');


1

Se la tabella / raccolta è vuota, crea un indice univoco per il campo:

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

Se la tabella / raccolta non è vuota, rilascia la raccolta e crea l'indice:

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

Ora riavvia mongoDB.


1

controlla che autoIndex nello schema sia vero, potrebbe essere impostato falso (valore predefinito true) quando usi le opzioni mongoose.connect


1

Ho affrontato lo stesso problema per un po 'e ho fatto molte ricerche e la soluzione per me era la createIndexes()funzione.

Spero che aiuti.

quindi il codice sarà così.

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

0

Se stai usando l'opzione autoIndex: falsenel metodo di connessione come questo:

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

Prova a rimuoverlo. Se non funziona, prova a riavviare mongodb come molti suggeriti in questo thread.


0

Puoi definire il tuo schema come

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

Ma forse questo non funzionerà quando c'è già un documento e dopo di ciò, hai cambiato lo schema dell'utente. Puoi creare un indice sull'email per questa raccolta utente se non desideri eliminare la raccolta. Usando questo comando puoi creare un indice sulla posta.

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

Oppure puoi semplicemente eliminare la raccolta e aggiungere di nuovo l'utente.
Per Dropping the collection, puoi inserire questo:

db.User.drop()

0

Quando ho riscontrato questo problema, ho provato a rilasciare il database, a riavviare il server (nodemon) molte volte e nessuno dei trucchi non ha funzionato affatto. Ho trovato il seguente lavoro in giro, tramite Robo 3T:

  1. In Robo 3T, fai doppio clic sul database per aprire le collezioni
  2. Apri le collezioni per rivelare la collezione in questione. Assicurati che le tue raccolte siano vuote, per cominciare
  3. Fare clic con il tasto destro sulla cartella degli indici. Per impostazione predefinita, vedrai _id_come predefinito. Ora scegli Aggiungi indice
  4. Scegli un nome, ad esempio emailper il campo e-mail
  5. Fornisci chiavi come JSON. Per esempio

    {
       "email": 1
    }
    
  6. Fare clic sulla casella di controllo Unica

  7. Salva

Ciò assicurerà che nessun messaggio di posta elettronica duplicato venga salvato in MongoDB.


qual è il significato di 1 qui nell'oggetto {"email": 1}?
siluveru kiran kumar

0

Assicurati che la tua raccolta non abbia ridondanza del campo su cui hai appena inserito l'indice univoco.

Quindi riavvia la tua app (mangusta). Si aggiunge solo silenziosamente l'indice non riesce.


0

Rimozione di tutti i documenti dalla raccolta:

db.users.remove({})

E un riavvio, come altri hanno detto, ha funzionato per me


0

Se MongoDB funziona come un servizio (un modo semplice per trovarlo è se non è necessario connettersi al database senza avviare il file mongod.exe tramite terminale), dopo aver eseguito le modifiche potrebbe essere necessario riavviare il servizio e / o rilascia completamente il database.

È abbastanza strano perché per alcuni utenti ha funzionato solo il rilascio di una singola raccolta. Alcuni di loro dovevano solo eliminare il database. Ma quelli non hanno funzionato per me. Ho abbandonato il database, quindi ho riavviato il servizio MongoDB Server.

Per riavviare un servizio, cerca Servizi sulla barra di ricerca di Windows, quindi trova il servizio MongoDB, fai doppio clic per aprire, quindi interrompi e riavvia il servizio.

Se gli altri metodi non hanno funzionato per te, credo che questo farà il lavoro.


0

Nel mio caso la mangusta era obsoleta. l'ho controllato eseguendo npm obsoleto su CMD. e "mangusta" aggiornata.

Per favore dimmi se ha funzionato anche per te.


Quale versione stavi usando?
Baboo_

0

Quando colleghi il tuo database con l'applicazione aggiungi questa opzione: "audoIndex: true" per esempio nel mio codice ho fatto questo:

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

Ho anche abbandonato la raccolta con cui ho problemi e l'ho ricreata per assicurarmi che funzionasse. Ho trovato questa soluzione su: https://dev.to/emmysteven/solved-mongoose-unique-index-not-working-45d5 Ho anche provato soluzioni come "riavvia MongoDB" ma non ha funzionato per me.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.