Javascript 猫鼬自动增量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28357965/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:40:14  来源:igfitidea点击:

Mongoose auto increment

javascriptmongodbmongooseauto-increment

提问by HMR

According to this mongodb articleit is possible to auto increment a field and I would like the use the counters collection way.

根据this mongodb article,可以自动增加字段,我希望使用计数器收集方式。

The problem with that example is that I don't have thousands of people typing the data in the database using the mongo console. Instead I am trying to use mongoose.

该示例的问题在于我没有成千上万的人使用 mongo 控制台在数据库中输入数据。相反,我正在尝试使用猫鼬。

So my schema looks something like this:

所以我的架构看起来像这样:

var entitySchema = mongoose.Schema({
  testvalue:{type:String,default:function getNextSequence() {
        console.log('what is this:',mongoose);//this is mongoose
        var ret = db.counters.findAndModify({
                 query: { _id:'entityId' },
                 update: { $inc: { seq: 1 } },
                 new: true
               }
        );
        return ret.seq;
      }
    }
});

I have created the counters collection in the same database and added a page with the _id of 'entityId'. From here I am not sure how to use mongoose to update that page and get the incrementing number.

我在同一个数据库中创建了 counters 集合,并添加了一个 _id 为“entityId”的页面。从这里我不确定如何使用猫鼬来更新该页面并获得递增的数字。

There is no schema for counters and I would like it to stay that way because this is not really an entity used by the application. It should only be used in the schema(s) to auto increment fields.

没有计数器模式,我希望它保持这种状态,因为这实际上并不是应用程序使用的实体。它应该只用于模式中以自动增加字段。

回答by edtech

Here is an example how you can implement auto-increment field in Mongoose:

以下是如何在 Mongoose 中实现自动递增字段的示例:

var CounterSchema = Schema({
    _id: {type: String, required: true},
    seq: { type: Number, default: 0 }
});
var counter = mongoose.model('counter', CounterSchema);

var entitySchema = mongoose.Schema({
    testvalue: {type: String}
});

entitySchema.pre('save', function(next) {
    var doc = this;
    counter.findByIdAndUpdate({_id: 'entityId'}, {$inc: { seq: 1} }, function(error, counter)   {
        if(error)
            return next(error);
        doc.testvalue = counter.seq;
        next();
    });
});

回答by moorara

You can use mongoose-auto-incrementpackage as follows:

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

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

/* connect to your database here */

/* define your CounterSchema here */

autoIncrement.initialize(mongoose.connection);
CounterSchema.plugin(autoIncrement.plugin, 'Counter');
var Counter = mongoose.model('Counter', CounterSchema);

You only need to initialize the autoIncrementonce.

您只需要初始化autoIncrement一次。

回答by cluny85

The most voted answer doesn't work. This is the fix:

投票最多的答案不起作用。这是修复:

var CounterSchema = new mongoose.Schema({
    _id: {type: String, required: true},
    seq: { type: Number, default: 0 }
});
var counter = mongoose.model('counter', CounterSchema);

var entitySchema = mongoose.Schema({
    sort: {type: String}
});

entitySchema.pre('save', function(next) {
    var doc = this;
    counter.findByIdAndUpdateAsync({_id: 'entityId'}, {$inc: { seq: 1} }, {new: true, upsert: true}).then(function(count) {
        console.log("...count: "+JSON.stringify(count));
        doc.sort = count.seq;
        next();
    })
    .catch(function(error) {
        console.error("counter error-> : "+error);
        throw error;
    });
});

The optionsparameters gives you the result of the update and it creates a new document if it doesn't exist. You can check herethe official doc.

选项的参数为您提供了更新的结果,如果它不存在,它会创建一个新文档。你可以在这里查看官方文档。

And if you need a sorted index check this doc

如果您需要排序索引,请查看此文档

回答by Akash Agarwal

So combining multiple answers, this is what I ended up using:

所以结合多个答案,这就是我最终使用的:

counterModel.js

counterModel.js

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

const counterSchema = new Schema(
  {
  _id: {type: String, required: true},
  seq: { type: Number, default: 0 }
  }
);

counterSchema.index({ _id: 1, seq: 1 }, { unique: true })

const counterModel = mongoose.model('counter', counterSchema);

const autoIncrementModelID = function (modelName, doc, next) {
  counterModel.findByIdAndUpdate(        // ** Method call begins **
    modelName,                           // The ID to find for in counters model
    { $inc: { seq: 1 } },                // The update
    { new: true, upsert: true },         // The options
    function(error, counter) {           // The callback
      if(error) return next(error);

      doc.id = counter.seq;
      next();
    }
  );                                     // ** Method call ends **
}

module.exports = autoIncrementModelID;

myModel.js

我的模型.js

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

const autoIncrementModelID = require('./counterModel');

const myModel = new Schema({
  id: { type: Number, unique: true, min: 1 },
  createdAt: { type: Date, default: Date.now },
  updatedAt: { type: Date },
  someOtherField: { type: String }
});

myModel.pre('save', function (next) {
  if (!this.isNew) {
    next();
    return;
  }

  autoIncrementModelID('activities', this, next);
});

module.exports = mongoose.model('myModel', myModel);

回答by Simon

Attention!

As hammerbotand dan-dascalescupointed out this does not workif you remove documents.

If you insert 3 documents with id 1, 2and 3- you remove 2and insert another a new one it'll get 3as id which is already used!

注意力!

正如hammerbotdan-dascalescu指出的那样,如果您删除文档,这将不起作用

如果您插入 3 个带有 id 的文档12并且3- 您删除2并插入另一个新文档,它将获得3已使用的 id !

In case you don't ever remove documents, here you go:

如果您永远不会删除文档,请执行以下操作:

I know this has already a lot of answers, but I would share my solution which is IMO short and easy understandable:

我知道这已经有很多答案,但我会分享我的解决方案,它是 IMO 简短易懂的:

// Use pre middleware
entitySchema.pre('save', function (next) {

    // Only increment when the document is new
    if (this.isNew) {
        entityModel.count().then(res => {
            this._id = res; // Increment count
            next();
        });
    } else {
        next();
    }
});

Make sure that entitySchema._idhas type:Number. Mongoose version: 5.0.1.

确保entitySchema._id具有type:Number. 猫鼬版本:5.0.1.

回答by Alberto Rubio

I didn't wan to use any plugin (an extra dependencie, initializing the mongodb connection apart from the one I use in the server.js, etc...) so I did an extra module, I can use it at any schema and even, I'm considering when you remove a document from the DB.

我不想使用任何插件(一个额外的依赖,除了我在 server.js 中使用的那个之外初始化 mongodb 连接,等等...)所以我做了一个额外的模块,我可以在任何模式下使用它,并且甚至,我正在考虑何时从数据库中删除文档。

module.exports = async function(model, data, next) {
    // Only applies to new documents, so updating with model.save() method won't update id
    // We search for the biggest id into the documents (will search in the model, not whole db
    // We limit the search to one result, in descendant order.
    if(data.isNew) {
        let total = await model.find().sort({id: -1}).limit(1);
        data.id = total.length === 0 ? 1 : Number(total[0].id) + 1;
        next();
    };
};

And how to use it:

以及如何使用它:

const autoincremental = require('../modules/auto-incremental');

Work.pre('save', function(next) {
    autoincremental(model, this, next);
    // Arguments:
    // model: The model const here below
    // this: The schema, the body of the document you wan to save
    // next: next fn to continue
});

const model = mongoose.model('Work', Work);
module.exports = model;

Hope it helps you.

希望对你有帮助。

(If this Is wrong, please, tell me. I've been having no issues with this, but, not an expert)

(如果这是错误的,请告诉我。我对此没有任何问题,但是,不是专家)

回答by PALLAMOLLA SAI

This problem is sufficiently complicatedand there are enough pitfallsthat it's best to rely on a tested mongoose plugin.

这个问题已经足够复杂,并且有足够多的陷阱,最好依赖经过测试的猫鼬插件。

Out of the plethora of "autoincrement" plugins at http://plugins.mongoosejs.io/, the best maintained and documented (and not a fork) is mongoose sequence.

http://plugins.mongoosejs.io/上的大量“自动增量”插件中,维护和记录最好的(而不是分叉)是mongoose sequence

回答by Tigran

I've combined all the (subjectively and objectively) good parts of the answers, and came up with this code:

我结合了答案的所有(主观和客观)好的部分,并提出了以下代码:

const counterSchema = new mongoose.Schema({
    _id: {
        type: String,
        required: true,
    },
    seq: {
        type: Number,
        default: 0,
    },
});

// Add a static "increment" method to the Model
// It will recieve the collection name for which to increment and return the counter value
counterSchema.static('increment', async function(counterName) {
    const count = await this.findByIdAndUpdate(
        counterName,
        {$inc: {seq: 1}},
        // new: return the new value
        // upsert: create document if it doesn't exist
        {new: true, upsert: true}
    );
    return count.seq;
});

const CounterModel = mongoose.model('Counter', counterSchema);


entitySchema.pre('save', async function() {
    // Don't increment if this is NOT a newly created document
    if(!this.isNew) return;

    const testvalue = await CounterModel.increment('entity');
    this.testvalue = testvalue;
});

One of the benefits of this approach is that all the counter related logic is separate. You can store it in a separate file and use it for multiple models importing the CounterModel.

这种方法的好处之一是所有与计数器相关的逻辑都是独立的。您可以将其存储在单独的文件中,并将其用于导入CounterModel.

If you are going to increment the _idfield, you should add its definition in your schema:

如果要增加该_id字段,则应在架构中添加其定义:

const entitySchema = new mongoose.Schema({
    _id: {
        type: Number,
        alias: 'id',
        required: true,
    },
    <...>
});

回答by MASh

Here is a proposal.

这是一个建议。

Create a separate collection to holds the max value for a model collection

创建一个单独的集合来保存模型集合的最大值

const autoIncrementSchema = new Schema({
    name: String,
    seq: { type: Number, default: 0 }
});

const AutoIncrement = mongoose.model('AutoIncrement', autoIncrementSchema);

Now for each needed schema, add a pre-save hook.

现在,对于每个需要的架构,添加一个pre-save hook.

For example, let the collection name is Test

例如,让集合名称为 Test

schema.pre('save', function preSave(next) {
    const doc = this;
    if (doc.isNew) {
         const nextSeq = AutoIncrement.findOneAndUpdate(
             { name: 'Test' }, 
             { $inc: { seq: 1 } }, 
             { new: true, upsert: true }
         );

         nextSeq
             .then(nextValue => doc[autoIncrementableField] = nextValue)
             .then(next);
    }
    else next();
 }

As findOneAndUpdateis an atomicoperation, no two updates will return same seqvalue. Thus each of your insertion will get an incremental seq regardless of number of concurrent insertions. Also this can be extended to more complex auto incremental logic and the auto increment sequence is not limited to Numbertype

作为findOneAndUpdate一个atomic操作,没有两个更新会返回相同的seq值。因此,无论并发插入的数量如何,您的每个插入都将获得增量 seq 。这也可以扩展到更复杂的自动增量逻辑,并且自动增量序列不限于Number类型

This is not a tested code. Test before you use until I make a plugin for mongoose.

这不是经过测试的代码。在您使用之前进行测试,直到我为mongoose.

UpdateI found that thisplugin implemented related approach.

更新我发现这个插件实现了相关的方法。

回答by mschwartz

The answers seem to increment the sequence even if the document already has an _id field (sort, whatever). This would be the case if you 'save' to update an existing document. No?

即使文档已经有一个 _id 字段(排序,无论如何),答案似乎会增加序列。如果您“保存”以更新现有文档,就会出现这种情况。不?

If I'm right, you'd want to call next() if this._id !== 0

如果我是对的,如果 this._id !== 0,你会想调用 next()

The mongoose docs aren't super clear about this. If it is doing an update type query internally, then pre('save' may not be called.

猫鼬文档对此并不十分清楚。如果它在内部执行更新类型查询,则可能不会调用 pre('save'。

CLARIFICATION

澄清

It appears the 'save' pre method is indeed called on updates.

看来确实在更新时调用了“保存”预方法。

I don't think you want to increment your sequence needlessly. It costs you a query and wastes the sequence number.

我认为您不想不必要地增加序列。它会花费您一次查询并浪费序列号。