Автоматический прирост Mongoose - программирование
Подтвердить что ты не робот

Автоматический прирост 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 для обновления этой страницы и получения добавочного номера.

Нет схемы для счетчиков, и я бы хотел, чтобы это оставалось таким, потому что это не является сущностью, используемой приложением. Он должен использоваться только в схемах (ах) для полей автоматического увеличения.

4b9b3361

Ответ 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;

Надеюсь, это поможет вам.

(Если это не так, пожалуйста, скажите мне. У меня не было проблем с этим, но я не эксперт)

Ответ 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,
    },
    <...>
});