Автоматический прирост Mongoose
Согласно этой статье mongodb, можно автоматически увеличивать поле, и я хотел бы использовать способ сбора счетчиков.
Проблема с этим примером заключается в том, что у меня нет тысяч людей, которые вводят данные в базу данных с помощью консоли mongo. Вместо этого я пытаюсь использовать мангуст.
Итак, моя схема выглядит примерно так:
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 для обновления этой страницы и получения добавочного номера.
Нет схемы для счетчиков, и я бы хотел, чтобы это оставалось таким, потому что это не является сущностью, используемой приложением. Он должен использоваться только в схемах (ах) для полей автоматического увеличения.
Ответы
Ответ 1
Вот пример того, как вы можете реализовать поле auto-increment в 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();
});
});
Ответ 2
Вы можете использовать пакет 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
один раз.
Ответ 3
Самый проголосовавший ответ не работает. Это исправление:
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;
});
});
Параметры параметры дают результат обновления и создают новый документ, если он не существует.
Вы можете проверить здесь официальный документ.
И если вам нужен отсортированный индекс, проверьте этот doc
Ответ 4
Я знаю, что у этого уже есть много ответов, но я хотел бы поделиться своим решением, которое является кратким ИМО и легко понятным:
// 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
.
Ответ 5
Таким образом, объединяя несколько ответов, я использовал это:
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);
Ответ 6
Я не хотел использовать какой-либо плагин (дополнительная зависимость, инициализация соединения 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;
Надеюсь, это поможет вам.
(Если это не так, пожалуйста, скажите мне. У меня не было проблем с этим, но я не эксперт)
Ответ 7
Другой способ - использовать внешние пакеты, предоставленные mongoose. (Легко понять)
плагин последовательности мангуста
Ответ 8
Кажется, что ответы увеличивают последовательность, даже если в документе уже есть поле _id (sort, whatever). Это будет иметь место, если вы сохраните ', чтобы обновить существующий документ. Нет?
Если я прав, вам нужно вызвать next(), если this._id! == 0
Документы mongoose не очень понятны. Если он делает запрос типа обновления внутренне, тогда pre ('save' не может быть вызван.
РАЗЪЯСНЕНИЯ
Кажется, что метод "save" pre действительно вызван обновлениями.
Я не думаю, что вы хотите без лишних усилий увеличить свою последовательность. Это требует запроса и отбрасывает порядковый номер.
Ответ 9
Я использую вместе @cluny85 и @edtech. Но я не заканчиваю заканчивать эти проблемы.
counterModel.findByIdAndUpdate({_id: 'aid'}, {$inc: { seq: 1} }, function(error,counter){
Но в функции "pre ('save...) затем отклик счетчика обновлений заканчивается после сохранения документ, поэтому я не обновляю счетчик для документа.
Пожалуйста, проверьте еще раз все ответы. Спасибо.
Сожалею. Я не могу добавить комментарий. Потому что я новичок.
Ответ 10
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();
}
});
Ответ 11
Вот предложение.
Создайте отдельную коллекцию, содержащую максимальное значение для коллекции моделей
const autoIncrementSchema = new Schema({
name: String,
seq: { type: Number, default: 0 }
});
const AutoIncrement = mongoose.model('AutoIncrement', autoIncrementSchema);
Теперь для каждой необходимой схемы добавьте 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
, никакие два обновления не будут возвращать одно и то же значение seq
. Таким образом, каждая ваша вставка будет получать последовательный результат независимо от количества одновременных вставок. Также это может быть расширено до более сложной логики автоинкремента, и последовательность автоинкремента не ограничивается типом числа
Это не проверенный код. Тестируйте перед использованием, пока я не сделаю плагин для mongoose
.
Обновление Я обнаружил, что этот плагин реализовал связанный подход.
Ответ 12
Я объединил все (субъективно и объективно) хорошие части ответов и придумал этот код:
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 does not 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,
},
<...>
});