2013-01-22 20 views
8

La mia app ha diversi utenti, ogni utente ha documenti. Ogni documento deve avere un numero di sequenza, che può essere qualcosa del genere: 2013-1, 2013-2 (anno e numero di sequenza), o forse solo un semplice numero: 1, 2, 3 ...Numero di incremento automatico del documento in Mongo/Mongoose

Attualmente , Sto assegnando il numero di sequenza dalle impostazioni dell'utente quando viene creato il documento Mongoose. Sulla base del numero di sequenza e del formato numerico delle impostazioni dell'utente, sto generando il numero del documento finale.

Quello che ho capito è che quando vengono creati 2 documenti contemporaneamente, otterranno esattamente lo stesso numero, perché sto incrementando il numero di sequenza nelle impostazioni subito dopo aver salvato un documento. Ma sto assegnando il numero di sequenza quando sto creando (non salvando ancora) il documento in modo che il numero di sequenza sia esattamente lo stesso per entrambi i documenti.

Io, ovviamente, bisogno di un modo per gestire questo numero di sequenza incremento automatico al momento del salvataggio ...

Come vi posso assicurare che questo numero è unico e automaticamente incrementato/generato?

+0

I [documenti] (hhttp: //www.wiredprairie.us/blog/index.php/archives/1524) hanno due buoni esempi. È possibile utilizzare l'esempio di contatori e aggiungere una chiave secondaria per l'ID utente in modo che sia univoca per utente. – WiredPrairie

+0

Dai a questa libreria un'inquadratura: [mongodb-autoincrement] (https://www.npmjs.com/package/mongodb-autoincrement) –

risposta

11

@emre e @WiredPraire mi indicò la direzione giusta, ma ho voluto fornire una risposta compatibile con Mongoose pieno alla mia domanda. Ho finito con la seguente soluzione:

var Settings = new Schema({ 
    nextSeqNumber: { type: Number, default: 1 } 
}); 

var Document = new Schema({ 
    _userId: { type: Schema.Types.ObjectId, ref: "User" }, 
    number: { type: String } 
}); 

// Create a compound unique index over _userId and document number 
Document.index({ "_userId": 1, "number": 1 }, { unique: true }); 

// I make sure this is the last pre-save middleware (just in case) 
Document.pre('save', function(next) { 
    var doc = this; 
    // You have to know the settings_id, for me, I store it in memory: app.current.settings.id 
    Settings.findByIdAndUpdate(settings_id, { $inc: { nextSeqNumber: 1 } }, function (err, settings) { 
    if (err) next(err); 
    doc.number = settings.nextSeqNumber - 1; // substract 1 because I need the 'current' sequence number, not the next 
    next(); 
    }); 
}); 

prega di notare che con questo metodo non v'è alcun modo per richiedere il percorso numero nello schema, e non esiste un punto così, perché è aggiunto automaticamente.

+1

+1! Per approfondire il motivo per cui funziona, è necessaria un'operazione di blocco (una scrittura, ad esempio) in modo che le chiamate asincrone 'save()' seguano in ordine. La soluzione "più semplice" di utilizzare Trova o Conta la raccolta su cui si desidera incrementare fallisce, perché sia ​​Find che Count sono non bloccanti (comandi di lettura), quindi non si può fare affidamento su di essi per comportarsi in serie con (potenzialmente) più asincri 'save()' chiama. –

+3

Grazie per aver postato un esempio di codice, questa sembra la soluzione che stavo cercando anche io. Nel caso questo sia utile per chiunque, l'ho anche adattato in modo che l'ID venga generato solo quando si chiama save per creazioni e non aggiornamenti: if (doc.isNew) {Settings.findByIdAndUpdate ...} else {next(); } –

+0

sei sicuro che qualcosa non manchi? Ottengo questo errore "L'oggetto n. non ha metodo 'findByIdAndUpdate'" Penso di dover prima costruire un modello, no? –

2

Questo codice è preso dal manuale MongoDB e in realtà descrive la creazione dell'aumento automatico del campo _id. Tuttavia, può essere applicato a qualsiasi campo. Quello che vuoi è controllare se il valore inserito esiste nel database subito dopo aver inserito il documento. Se è già inserito, incrementare il valore, quindi provare a inserirlo di nuovo. In questo modo è possibile rilevare i valori pubblici e ri-incrementarli.

while (1) { 

    var cursor = targetCollection.find({}, { f: 1 }).sort({ f: -1 }).limit(1); 

    var seq = cursor.hasNext() ? cursor.next().f + 1 : 1; 

    doc.f = seq; 

    targetCollection.insert(doc); 

    var err = db.getLastErrorObj(); 

    if(err && err.code) { 
     if(err.code == 11000 /* dup key */) 
      continue; 
     else 
      print("unexpected error inserting data: " + tojson(err)); 
    } 

    break; 
} 

In questo esempio f è il campo nel documento che si desidera incrementare automaticamente. Per fare questo lavoro è necessario rendere il tuo campo UNICO che può essere fatto con gli indici.

db.myCollection.ensureIndex({ "f": 1 }, { unique: true }) 
+1

Hmm, potrebbe funzionare, ma qualche idea su come implementerei questo con Mongoose? Inoltre, ho bisogno di assicurarmi che i numeri dei documenti siano unici nel contesto di un utente. Possono esserci 2 utenti, entrambi possono avere un solo documento, con il numero 1. Quindi l'indice univoco dovrebbe considerare il numero e l'utente _id. – ragulka

3

È possibile ottenere che, attraverso:

  1. creare generatore di sequenza, che è solo un altro documento che mantiene un contatore del l'ultimo numero.
  2. Utilizzare un middleware mangimi per aggiornare l'incremento automatico del campo desiderato.

Ecco un esempio funzionante e testato con l'app todo.

var mongoose = require('mongoose'); 
mongoose.connect('mongodb://localhost/todoApp'); 

// Create a sequence 
function sequenceGenerator(name){ 
    var SequenceSchema, Sequence; 

    SequenceSchema = new mongoose.Schema({ 
    nextSeqNumber: { type: Number, default: 1 } 
    }); 

    Sequence = mongoose.model(name + 'Seq', SequenceSchema); 

    return { 
    next: function(callback){ 
     Sequence.find(function(err, data){ 
     if(err){ throw(err); } 

     if(data.length < 1){ 
      // create if doesn't exist create and return first 
      Sequence.create({}, function(err, seq){ 
      if(err) { throw(err); } 
      callback(seq.nextSeqNumber); 
      }); 
     } else { 
      // update sequence and return next 
      Sequence.findByIdAndUpdate(data[0]._id, { $inc: { nextSeqNumber: 1 } }, function(err, seq){ 
      if(err) { throw(err); } 
      callback(seq.nextSeqNumber); 
      }); 
     } 
     }); 
    } 
    }; 
} 

// sequence instance 
var sequence = sequenceGenerator('todo'); 

var TodoSchema = new mongoose.Schema({ 
    name: String, 
    completed: Boolean, 
    priority: Number, 
    note: { type: String, default: '' }, 
    updated_at: { type: Date, default: Date.now } 
}); 

TodoSchema.pre('save', function(next){ 
    var doc = this; 
    // get the next sequence 
    sequence.next(function(nextSeq){ 
    doc.priority = nextSeq; 
    next(); 
    }); 
}); 

var Todo = mongoose.model('Todo', TodoSchema); 

È possibile verificare il lavoro svolto nella console nodo come segue

function cb(err, data){ console.log(err, data); } 
Todo.create({name: 'hola'}, cb); 
Todo.find(cb); 

Con ogni oggetto appena creato la si vedrà la priorità crescente. Saluti!

2

È possibile utilizzare mongoose-auto-increment pacchetto come segue:

var mongoose  = require('mongoose'); 
var autoIncrement = require('mongoose-auto-increment'); 

/* connect to your database here */ 

/* define your DocumentSchema here */ 

autoIncrement.initialize(mongoose.connection); 
DocumentSchema.plugin(autoIncrement.plugin, 'Document'); 
var Document = mongoose.model('Document', DocumentSchema); 

Hai solo bisogno di inizializzare il autoIncrement una volta.

+0

Sarebbe bello se si potesse consultare https://stackoverflow.com/questions/45357813/issue-with-mongoose-auto-increment-plugin-in-nested-models –

Problemi correlati