猫鼬自动增量
Mongoose auto increment
根据 this mongodb article 可以自动增加一个字段,我想使用计数器收集方式。
该示例的问题在于,我没有成千上万的人使用 mongo 控制台在数据库中键入数据。相反,我正在尝试使用 mongoose.
所以我的架构看起来像这样:
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;
}
}
});
我在同一数据库中创建了计数器集合,并添加了一个 _id 为 'entityId' 的页面。从这里我不确定如何使用 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();
});
});
您可以按如下方式使用 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);
您只需初始化 autoIncrement
一次。
得票最多的答案无效。这是修复:
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;
});
});
options 参数为您提供更新结果,如果不存在,它会创建一个新文档。
你可以查看here官方文档
如果您需要排序索引,请检查此 doc
答案似乎增加了序列,即使文档已经有一个 _id 字段(排序,随便什么)。如果您 'save' 更新现有文档,就会出现这种情况。没有?
如果我是对的,如果 this._id !== 0
你会想调用 next()
猫鼬文档对此不是很清楚。如果它在内部做更新类型的查询,那么pre('save'可能不会被调用。
澄清
似乎确实在更新时调用了 'save' pre 方法。
我认为您不想不必要地增加序列。它会花费您一个查询并浪费序列号。
Attention!
As hammerbot and dan-dascalescu pointed out this does not work if you remove documents.
If you insert 3 documents with id 1
, 2
and 3
- you remove 2
and insert another a new one it'll get 3
as id which is already used!
如果您从未删除过文档,请执行以下操作:
我知道这已经有很多答案,但我会分享我的解决方案,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();
}
});
确保 entitySchema._id
有 type:Number
。
猫鼬版本:5.0.1
.
我一起使用@cluny85 和@edtech。
但是我没有完成这个问题。
counterModel.findByIdAndUpdate({_id: 'aid'}, {$inc: { seq: 1} }, function(error,counter){
但是在函数“pre('save...) 中,更新计数器的响应在保存文档后完成。
所以我不更新柜台文件。
请answer.Thank你们再检查一遍
对不起。我无法添加评论。因为我是新手
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) {
if (this.isNew) {
var doc = this;
counter.findByIdAndUpdate({ _id: 'entityId' }, { $inc: { seq: 1 } }, { new: true, upsert: true })
.then(function(count) {
doc.testvalue = count.seq;
next();
})
.catch(function(error) {
throw error;
});
} else {
next();
}
});
我不想使用任何插件(一个额外的依赖,初始化 mongodb 连接,除了我在 server.js 中使用的连接,等等...)所以我做了一个额外的模块,我可以在任何模式下使用它,甚至,我正在考虑何时从数据库中删除文档。
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();
};
};
以及使用方法:
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;
希望对你有帮助。
(如果这是错误的,请告诉我。我对此没有任何问题,但不是专家)
结合多个答案,这就是我最终使用的:
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
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);
这个问题足以complicated and there are enough pitfalls,最好依赖经过测试的 mongoose 插件。
在 http://plugins.mongoosejs.io/, the best maintained and documented (and not a fork) is mongoose sequence 的大量 "autoincrement" 插件中。
这是一个提案。
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);
现在为每个需要的 schema 添加一个 pre-save hook
.
例如,让集合名称为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();
}
因为 findOneAndUpdate
是一个 atomic
操作,所以没有两次更新会 return 相同的 seq
值。因此,无论并发插入的数量如何,您的每个插入都将获得一个增量序列。 也可以扩展到更复杂的自增逻辑,自增序列不限于数量类型
这不是经过测试的代码。在我为 mongoose
.
制作插件之前先测试再使用
更新 发现this插件实现了相关方法
我结合了所有(主观和客观)好的答案,得出了这个代码:
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;
});
这种方法的好处之一是所有与计数器相关的逻辑都是独立的。您可以将其存储在单独的文件中,并将其用于导入 CounterModel
.
的多个模型
如果您要增加 _id
字段,您应该在您的模式中添加它的定义:
const entitySchema = new mongoose.Schema({
_id: {
type: Number,
alias: 'id',
required: true,
},
<...>
});
我在使用 Mongoose Document 通过 put() 为 Schema 的字段赋值时遇到问题。 count
returns 对象本身,我必须访问它 属性。
我玩了@Tigran 的回答,这是我的输出:
// My goal is to auto increment the internalId field
export interface EntityDocument extends mongoose.Document {
internalId: number
}
entitySchema.pre<EntityDocument>('save', async function() {
if(!this.isNew) return;
const count = await counter.findByIdAndUpdate(
{_id: 'entityId'},
{$inc: {seq: 1}},
{new: true, upsert: true}
);
// Since count is returning an array
// I used get() to access its child
this.internalId = Number(count.get('seq'))
});
版本:mongoose@5.11.10
test.pre("save",function(next){
if(this.isNew){
this.constructor.find({}).then((result) => {
console.log(result)
this.id = result.length + 1;
next();
});
}
})
当您的模式中有唯一字段时,上述答案的 None 有效
因为数据库级别的唯一检查和增量发生在数据库级别验证之前,所以你可以像上面的解决方案一样跳过自动增量中的很多数字
只有在 post save 中才能找到 data 是否已经保存在 db 或 return error
schmea.post('save', function(error, doc, next) {
if (error.name === 'MongoError' && error.code === 11000) {
next(new Error('email must be unique'));
} else {
next(error);
}
});
这就是为什么 none 以上答案不像 dbs
中 sql 中的原子操作自动递增
根据 this mongodb article 可以自动增加一个字段,我想使用计数器收集方式。
该示例的问题在于,我没有成千上万的人使用 mongo 控制台在数据库中键入数据。相反,我正在尝试使用 mongoose.
所以我的架构看起来像这样:
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;
}
}
});
我在同一数据库中创建了计数器集合,并添加了一个 _id 为 'entityId' 的页面。从这里我不确定如何使用 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();
});
});
您可以按如下方式使用 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);
您只需初始化 autoIncrement
一次。
得票最多的答案无效。这是修复:
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;
});
});
options 参数为您提供更新结果,如果不存在,它会创建一个新文档。 你可以查看here官方文档
如果您需要排序索引,请检查此 doc
答案似乎增加了序列,即使文档已经有一个 _id 字段(排序,随便什么)。如果您 'save' 更新现有文档,就会出现这种情况。没有?
如果我是对的,如果 this._id !== 0
你会想调用 next()猫鼬文档对此不是很清楚。如果它在内部做更新类型的查询,那么pre('save'可能不会被调用。
澄清
似乎确实在更新时调用了 'save' pre 方法。
我认为您不想不必要地增加序列。它会花费您一个查询并浪费序列号。
Attention!
As hammerbot and dan-dascalescu pointed out this does not work if you remove documents.
If you insert 3 documents with id
1
,2
and3
- you remove2
and insert another a new one it'll get3
as id which is already used!
如果您从未删除过文档,请执行以下操作:
我知道这已经有很多答案,但我会分享我的解决方案,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();
}
});
确保 entitySchema._id
有 type:Number
。
猫鼬版本:5.0.1
.
我一起使用@cluny85 和@edtech。 但是我没有完成这个问题。
counterModel.findByIdAndUpdate({_id: 'aid'}, {$inc: { seq: 1} }, function(error,counter){
但是在函数“pre('save...) 中,更新计数器的响应在保存文档后完成。
所以我不更新柜台文件。
请answer.Thank你们再检查一遍
对不起。我无法添加评论。因为我是新手
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) {
if (this.isNew) {
var doc = this;
counter.findByIdAndUpdate({ _id: 'entityId' }, { $inc: { seq: 1 } }, { new: true, upsert: true })
.then(function(count) {
doc.testvalue = count.seq;
next();
})
.catch(function(error) {
throw error;
});
} else {
next();
}
});
我不想使用任何插件(一个额外的依赖,初始化 mongodb 连接,除了我在 server.js 中使用的连接,等等...)所以我做了一个额外的模块,我可以在任何模式下使用它,甚至,我正在考虑何时从数据库中删除文档。
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();
};
};
以及使用方法:
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;
希望对你有帮助。
(如果这是错误的,请告诉我。我对此没有任何问题,但不是专家)
结合多个答案,这就是我最终使用的:
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
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);
这个问题足以complicated and there are enough pitfalls,最好依赖经过测试的 mongoose 插件。
在 http://plugins.mongoosejs.io/, the best maintained and documented (and not a fork) is mongoose sequence 的大量 "autoincrement" 插件中。
这是一个提案。
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);
现在为每个需要的 schema 添加一个 pre-save hook
.
例如,让集合名称为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();
}
因为 findOneAndUpdate
是一个 atomic
操作,所以没有两次更新会 return 相同的 seq
值。因此,无论并发插入的数量如何,您的每个插入都将获得一个增量序列。 也可以扩展到更复杂的自增逻辑,自增序列不限于数量类型
这不是经过测试的代码。在我为 mongoose
.
更新 发现this插件实现了相关方法
我结合了所有(主观和客观)好的答案,得出了这个代码:
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;
});
这种方法的好处之一是所有与计数器相关的逻辑都是独立的。您可以将其存储在单独的文件中,并将其用于导入 CounterModel
.
如果您要增加 _id
字段,您应该在您的模式中添加它的定义:
const entitySchema = new mongoose.Schema({
_id: {
type: Number,
alias: 'id',
required: true,
},
<...>
});
我在使用 Mongoose Document 通过 put() 为 Schema 的字段赋值时遇到问题。 count
returns 对象本身,我必须访问它 属性。
我玩了@Tigran 的回答,这是我的输出:
// My goal is to auto increment the internalId field
export interface EntityDocument extends mongoose.Document {
internalId: number
}
entitySchema.pre<EntityDocument>('save', async function() {
if(!this.isNew) return;
const count = await counter.findByIdAndUpdate(
{_id: 'entityId'},
{$inc: {seq: 1}},
{new: true, upsert: true}
);
// Since count is returning an array
// I used get() to access its child
this.internalId = Number(count.get('seq'))
});
版本:mongoose@5.11.10
test.pre("save",function(next){
if(this.isNew){
this.constructor.find({}).then((result) => {
console.log(result)
this.id = result.length + 1;
next();
});
}
})
None 有效
因为数据库级别的唯一检查和增量发生在数据库级别验证之前,所以你可以像上面的解决方案一样跳过自动增量中的很多数字
只有在 post save 中才能找到 data 是否已经保存在 db 或 return error
schmea.post('save', function(error, doc, next) {
if (error.name === 'MongoError' && error.code === 11000) {
next(new Error('email must be unique'));
} else {
next(error);
}
});
这就是为什么 none 以上答案不像 dbs
中 sql 中的原子操作自动递增