2013-01-22 96 views
8

我的應用有幾個用戶,每個用戶都有文檔。每個文檔需要有一個序列號,可能看起來像這樣:2013-1,2013-2(年份和序列號),或者可能只是一個簡單的數字:1,2,3 ...在Mongo/Mongoose中自動增加文檔號

當前,我在創建Mongoose docuemnt時從用戶設置中分配序列號。根據該序列號和用戶設置的數字格式,我生成最終的文檔編號。

我意識到,當同時創建2個文檔時,他們將得到完全相同的編號,因爲我在保存文檔後立即增加設置中的序號。但是我在創建(而不是保存)文檔時分配了序列號,因此兩個文檔的序列號將完全相同。

我顯然需要一種方法來處理這個序列號自動遞增挽救的那一刻...

我怎麼能保證這個號碼是唯一的,並自動遞增/生成的?

+0

[docs](hwttp://www.wiredprairie.us/blog/index.php/archives/1524)有兩個很好的例子。您可以使用計數器示例併爲用戶標識添加一個輔助鍵,這樣每個用戶都是唯一的。 – WiredPrairie

+0

給圖書館一個鏡頭:[mongodb-autoincrement](https://www.npmjs.com/package/mongodb-autoincrement) –

回答

11

@emre和@WiredPraire向我指出了正確的方向,但我想提供一個完整的貓鼬兼容的回答我的問題。我結束了以下解決方案:

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(); 
    }); 
}); 

請注意,這種方法是沒有辦法要求在架構中的數路徑,沒有一點爲好,因爲它會自動添加。

+1

+1!爲了詳細說明爲什麼這會起作用,您需要一個阻塞操作(例如寫入),以便按順序執行異步'save()'調用。由於查找和計數都是非阻塞(讀命令),所以使用查找或計算您希望遞增的集合的「簡單」解決方案失敗,因此您不能依賴它們以(可能)多個異步save()'調用。 –

+3

感謝您發佈代碼示例,這看起來像我一直在尋找的解決方案。如果這對任何人都有用,我也對它進行了調整,以便只在調用保存創建而不是更新時生成ID:if(doc.isNew){Settings.findByIdAndUpdate ...} else {next(); } –

+0

你確定有東西沒有丟失?我得到這個錯誤「對象#沒有方法'findByIdAndUpdate'」我想我必須先建立一個模型,不是嗎? –

2

此代碼取自MongoDB手冊,它實際上描述了使_id字段自動遞增。但是,它可以應用於任何領域。你想要的是在插入文檔之後檢查插入的值是否存在於數據庫中。如果已經插入,則重新遞增該值,然後嘗試再次插入。通過這種方式,您可以檢測dublicate值並重新增加它們。

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; 
} 

在此示例中,f是您希望自動增量的文檔中的字段。要做到這一點,你需要使你的字段UNIQUE可以通過索引完成。

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

嗯,這可能會工作,但任何想法我將如何實施與貓鼬?另外,我需要確保文檔編號在一個用戶的上下文中是唯一的。可以有2個用戶,都只能有一個文檔,編號爲1.因此,唯一索引應該考慮數字和用戶_id。 – ragulka

3

可以實現通過:

  1. 創建序列發生器,這僅僅是保持最後數目的計數器另一個文檔。
  2. 使用貓鼬中間件來更新自動遞增期望的字段。

這是一個工作和測試的例子與待辦事項應用程序。

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); 

您可以在節點控制檯,如下

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

隨着每一個新創建的對象,你會看到越來越優先測試它。乾杯!

2

您可以使用mongoose-auto-increment包如下:

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); 

你只需要初始化autoIncrement一次。

+0

這將是很好,如果你可以看看https://stackoverflow.com/questions/45357813/issue-with-mongoose-auto-increment-plugin-in-nested-models –