добавить поля create_at и updated_at в схемы mongoose


166

Есть ли способ добавить поля create_at и updated_at в схему mongoose, не передавая их каждый раз, когда вызывается новый MyModel ()?

Поле create_at будет датой и будет добавлено только при создании документа. Поле updated_at будет обновляться с новой датой всякий раз, когда save () вызывается для документа.

Я пробовал это в моей схеме, но поле не отображается, если я не добавлю его с точностью:

var ItemSchema = new Schema({
    name    : { type: String, required: true, trim: true }
  , created_at    : { type: Date, required: true, default: Date.now }
});

Я сделал именно то, что вы сделали, и это сработало. Использование Mongoose 4.8.4. Может быть, новая вещь?
martinedwards

1
это было довольно давно.
Чови

Да, подумал, что стоит отметить, что вышесказанное сейчас работает.
martinedwards

Ответы:


132

Начиная с Mongoose 4.0, теперь вы можете установить опцию временных меток в схеме, чтобы Mongoose справился с этим за вас:

var thingSchema = new Schema({..}, { timestamps: true });

Вы можете изменить название используемых полей следующим образом:

var thingSchema = new Schema({..}, { timestamps: { createdAt: 'created_at' } });

http://mongoosejs.com/docs/guide.html#timestamps


2
Согласитесь, это лучший вариант в Mongoose 4.0.
Тэдд Джайлс

258

ОБНОВЛЕНИЕ: (5 лет спустя)

Примечание. Если вы решите использовать архитектуру Kappa ( Event Sourcing + CQRS ), вам не потребуется обновлять дату. Поскольку ваши данные являются неизменяемым журналом событий только для добавления, вам всегда нужна только дата создания события. Похоже на лямбда-архитектуру , описанную ниже. Тогда состояние вашего приложения является проекцией журнала событий (производные данные). Если вы получите последующее событие о существующей сущности, то вы будете использовать дату создания этого события в качестве обновленной даты для вашей сущности. Это широко используемая (и часто неправильно понимаемая) практика в системах микросервиса.

ОБНОВЛЕНИЕ: (4 года спустя)

Если вы используете в ObjectIdкачестве _idполя (как правило, так), все, что вам нужно сделать, это:

let document = {
  updatedAt: new Date(),
}

Проверьте мой оригинальный ответ ниже о том, как получить созданную временную метку с _idполя. Если вам нужно использовать идентификаторы из внешней системы, проверьте ответ Рхрна Нестерова.

ОБНОВЛЕНИЕ: (2,5 года спустя)

Теперь вы можете использовать опцию #timestamps с версией mongoose> = 4.0.

let ItemSchema = new Schema({
  name: { type: String, required: true, trim: true }
},
{
  timestamps: true
});

Если установлены временные метки, Мангуста назначает createdAtи updatedAtполя для вашей схемы, назначенный тип Date.

Вы также можете указать имена файлов временных меток:

timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }

Примечание. Если вы работаете с большим приложением с важными данными, вам следует пересмотреть вопрос об обновлении документов. Я бы посоветовал вам работать с неизменяемыми данными только для добавления ( лямбда-архитектура ). Это означает, что вы разрешаете только вставки. Обновления и удаления не должны быть разрешены! Если вы хотите «удалить» запись, вы можете легко вставить новую версию документа с некоторыми timestamp/ version filed, а затем установить deletedполе в true. Точно так же, если вы хотите обновить документ - вы создаете новый с обновленными соответствующими полями, а остальные поля копируются поверх. Затем для запроса этого документа вы получите тот, который имеет самую новую временную метку или самую высокую версию, которая является не "удалено" (deleted поле не определено или ложно`).

Неизменность данных гарантирует, что ваши данные можно отлаживать - вы можете отслеживать историю каждого документа. Вы также можете выполнить откат к предыдущей версии документа, если что-то пойдет не так. Если вы используете такую ​​архитектуру, ObjectId.getTimestamp()это все, что вам нужно, и она не зависит от Mongoose.


ОРИГИНАЛЬНЫЙ ОТВЕТ:

Если вы используете ObjectId в качестве поля идентификации, вам не нужно created_atполе. ObjectIds имеет метод с именем getTimestamp().

ObjectId ( "507c7f79bcf86cd7994f6c0e"). GetTimestamp ()

Это вернет следующий вывод:

ISODate ( "2012-10-15T21: 26: 17Z")

Более подробная информация здесь Как мне извлечь созданную дату из Mongo ObjectID

Для того, чтобы добавить updated_atполе необходимо использовать это:

var ArticleSchema = new Schema({
  updated_at: { type: Date }
  // rest of the fields go here
});

ArticleSchema.pre('save', function(next) {
  this.updated_at = Date.now();
  next();
});

3
Как вы делаете это в Mongoose / node.js?
Лаборатория Зебры г.

6
Это стоит знать, даже если это не решение, которое привыкает, очень интересно!
невольно

Но если я хочу передать дату создания в представление, мне нужно установить специальную переменную для этого или передать идентификатор правильно?

3
Я ценю первоначальный ответ, но поддержка использования лямбда-архитектуры также звучит как отличный способ решения пятикратных уровней мета-проблем и, возможно, никогда не доставит, а не создаст приложение CRUD и сделает его простым, если сложность этого подхода не является действительно оправданной. Помните, что вы пишете о MongoDB, который «едва» масштабируется с самого начала, и вы выступаете за новую запись для каждой записи, которая быстро убьет Mongo в любом значимом масштабе. Переместите это в секцию Кассандры ...
Джон Кульвинер,

1
Я фактически изменил свой дизайн бэкэнда, чтобы выполнять мягкие удаления вместо реальных удалений, благодаря информации, предоставленной в этом ответе. Это дает более глубокие знания, которые действительно полезны.
Кенна

133

Это то, что я в итоге сделал:

var ItemSchema = new Schema({
    name    : { type: String, required: true, trim: true }
  , created_at    : { type: Date }
  , updated_at    : { type: Date }
});


ItemSchema.pre('save', function(next){
  now = new Date();
  this.updated_at = now;
  if ( !this.created_at ) {
    this.created_at = now;
  }
  next();
});

8
1. Сохраните текущее время в локальной переменной и присвойте его вместо того, чтобы каждый раз вызывать new Date (), это обеспечит, чтобы при первом проходе create_at и updated_at имели одно и то же значение исключения. 2. новая дата => новая дата ()
Шей Эрличмен

13
Хотелось бы отметить, что если вы используете ObjectId, вы можете получить оттуда create_at .... вам не нужно отдельное поле. ВыездgetTimestamp()
Xerri

6
Другим вариантом для созданного_ат может быть изменение модели на -> созданный_ат: {тип: дата, по умолчанию: Date.now},
OscarVGG

1
@ajbraus Сделайте плагин схемы
wlingke

2
также используйте, Date.now()где это возможно, вместо new Dateболее быстрого, поскольку это статический метод
karlkurzer

107

Используйте встроенную timestampsопцию для вашей схемы.

var ItemSchema = new Schema({
    name: { type: String, required: true, trim: true }
},
{
    timestamps: true
});

Это автоматически добавит createdAtиupdatedAt поля к вашей схеме.

http://mongoosejs.com/docs/guide.html#timestamps


1
Требуется версия mongoose> = 4.0
Дженсен

2
Я нахожу документы неясными - это добавляет поля, но также гарантирует, что они обновляются при каждом обновлении?
Людвик

В общем, созданный объект всегда сохраняется только один раз ... при создании объекта. updatedAtобновляется при каждом новом сохранении (при изменении
объекта

1
Я получил это "2016-12-07T11: 46: 46.066Z" .. Не могли бы вы объяснить формат отметки времени, как изменить часовой пояс ?? такое взять часовой пояс сервера mongoDB ??
Махеш К

@mcompeau, спасибо за ответ! Я знаю, что это длинный путь, но помните ли вы случайно, что поля, созданные таким образом, можно использовать в качестве индекса?
manidos

29

Если использовать update()илиfindOneAndUpdate()

с {upsert: true}опцией

ты можешь использовать $setOnInsert

var update = {
  updatedAt: new Date(),
  $setOnInsert: {
    createdAt: new Date()
  }
};

1
это работает в большинстве библиотек Монго. переход к принятому ответу
chovy

1
Если вы используете _idполе по умолчанию Mogo , то вам не нужно createdAtполе. Проверьте мой ответ ниже для более подробной информации.
Павел Николов

1
... проверь мой ОРИГИНАЛЬНЫЙ ОТВЕТ .
Павел Николов

25

Добавить timestampsк вашему Schemaкак это , то createdAtи updatedAtбудет автоматический генерировать для вас

var UserSchema = new Schema({
    email: String,
    views: { type: Number, default: 0 },
    status: Boolean
}, { timestamps: {} });

введите описание изображения здесь
Также вы можете изменить createdAt -> created_at с помощью

timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }

2
Теперь, когда Mongoose делает всю эту работу за вас, я согласен, что сейчас это лучшее решение.
Винс Боудрен

Да, согласитесь на это @VinceBowdren
Mr.spShuvo

8

Вот как я добился, создав и обновив.

Внутри моей схемы я добавил созданное и обновленное примерно так:

   / **
     * Схема статьи
     * /
    var ArticleSchema = новая схема ({
        создано: {
            тип: дата,
            по умолчанию: Date.now
        },
        обновлено: {
            тип: дата,
            по умолчанию: Date.now
        },
        заглавие: {
            Тип: Строка,
            дефолт: '',
            отделка: правда,
            обязательно: «Заголовок не может быть пустым»
        },
        содержание: {
            Тип: Строка,
            дефолт: '',
            отделка: есть
        },
        пользователь: {
            тип: Schema.ObjectId,
            ref: 'Пользователь'
        }
    });

Затем в моем методе обновления статьи внутри контроллера статьи я добавил:

/ **
     * Обновить статью
     * /
    exports.update = function (req, res) {
        var article = req.article;

        article = _.extend (article, req.body);
        article.set ("обновлено", Date.now ());

        article.save (function (err) {
            if (err) {
                вернуть res.status (400) .send ({
                    сообщение: errorHandler.getErrorMessage (err)
                });
            } еще {
                res.json (статья);
            }
        });
    };

Жирные разделы представляют интерес.



4

Вы можете использовать timestampплагин mongoose-troopдля добавления этого поведения в любую схему.


3

Вы можете использовать этот плагин очень легко. Из документов:

var timestamps = require('mongoose-timestamp');
var UserSchema = new Schema({
    username: String
});
UserSchema.plugin(timestamps);
mongoose.model('User', UserSchema);
var User = mongoose.model('User', UserSchema)

А также установите имя поля, если хотите:

mongoose.plugin(timestamps,  {
  createdAt: 'created_at', 
  updatedAt: 'updated_at'
});

2

мы можем достичь этого, используя также плагин схемы .

В helpers/schemaPlugin.jsфайле

module.exports = function(schema) {

  var updateDate = function(next){
    var self = this;
    self.updated_at = new Date();
    if ( !self.created_at ) {
      self.created_at = now;
    }
    next()
  };
  // update date for bellow 4 methods
  schema.pre('save', updateDate)
    .pre('update', updateDate)
    .pre('findOneAndUpdate', updateDate)
    .pre('findByIdAndUpdate', updateDate);
};

и в models/ItemSchema.jsфайле:

var mongoose = require('mongoose'),
  Schema   = mongoose.Schema,
  SchemaPlugin = require('../helpers/schemaPlugin');

var ItemSchema = new Schema({
  name    : { type: String, required: true, trim: true },
  created_at    : { type: Date },
  updated_at    : { type: Date }
});
ItemSchema.plugin(SchemaPlugin);
module.exports = mongoose.model('Item', ItemSchema);

2

В схеме вашей модели просто добавьте метки времени атрибута и присвойте ему значение true, как показано ниже:

var ItemSchema = new Schema({
   name :  { type: String, required: true, trim: true },
},{timestamps : true}
);

Что это будет делать?
Чови

Атрибут Timestamp добавит поля create_at и updated_at в каждый документ. Поле Created_at указывает время создания документа, а поле updated_at указывает время обновления, если оно есть, время создания документа. Значение обоих полей в формате времени ISO. Надеюсь, что это очистит ваши сомнения Чови.
Павнет Каур

1
const mongoose = require('mongoose');
const config = require('config');
const util = require('util');

const Schema = mongoose.Schema;
const BaseSchema = function(obj, options) {
  if (typeof(options) == 'undefined') {
    options = {};
  }
  if (typeof(options['timestamps']) == 'undefined') {
    options['timestamps'] = true;
  }

  Schema.apply(this, [obj, options]);
};
util.inherits(BaseSchema, Schema);

var testSchema = new BaseSchema({
  jsonObject: { type: Object }
  , stringVar : { type: String }
});

Теперь вы можете использовать это, так что нет необходимости включать эту опцию в каждую таблицу


1

Моя версия мангуста 4.10.2

Кажется только крючок findOneAndUpdateэто работа

ModelSchema.pre('findOneAndUpdate', function(next) {
  // console.log('pre findOneAndUpdate ....')
  this.update({},{ $set: { updatedAt: new Date() } });
  next()
})

0

Используйте функцию для возврата вычисленного значения по умолчанию:

var ItemSchema = new Schema({
    name: {
      type: String,
      required: true,
      trim: true
    },
    created_at: {
      type: Date,
      default: function(){
        return Date.now();
      }
    },
    updated_at: {
      type: Date,
      default: function(){
        return Date.now();
      }
    }
});

ItemSchema.pre('save', function(done) {
  this.updated_at = Date.now();
  done();
});

Не нужно оборачиваться Date.now()в функцию, просто сделайте:...default: Date.now()
karlkurzer

1
Я обертываю его в функцию, чтобы в тестах я мог использовать «.now ()». В противном случае он запускается только один раз во время инициализации, и значение не может быть легко изменено.
orourkedd

1
Обратите внимание, что default: Date.now()это будет неправильно. Во всяком случае это default: Date.now. В противном случае все ваши документы будут иметь одинаковую метку времени: время, когда ваше приложение запустилось;)
Макс Трукса

Мне нравится ваша default: Date.nowстратегия. намного чище
orourkedd

0

Я на самом деле делаю это в спину

Если все идет хорошо с обновлением:

 // All ifs passed successfully. Moving on the Model.save
    Model.lastUpdated = Date.now(); // <------ Now!
    Model.save(function (err, result) {
      if (err) {
        return res.status(500).json({
          title: 'An error occured',
          error: err
        });
      }
      res.status(200).json({
        message: 'Model Updated',
        obj: result
      });
    });

0

Используйте machinepack-datetime для форматирования даты и времени.

tutorialSchema.virtual('createdOn').get(function () {
    const DateTime = require('machinepack-datetime');
    let timeAgoString = "";
    try {
        timeAgoString = DateTime.timeFrom({
            toWhen: DateTime.parse({
                datetime: this.createdAt
            }).execSync(),
            fromWhen: new Date().getTime()
        }).execSync();
    } catch(err) {
        console.log('error getting createdon', err);
    }
    return timeAgoString; // a second ago
});

Машинный пакет великолепен с понятным API, в отличие от экспресса или общего мира Javascript.


-2

Вы можете использовать промежуточное программное обеспечение и виртуалы . Вот пример для вашего updated_atполя:

ItemSchema.virtual('name').set(function (name) {
  this.updated_at = Date.now;
  return name;
});

Когда это на самом деле будет установлено? и будет ли оно сохраняться? Таким образом, через 3 дня у него все еще будет дата 3 дня назад?
Чови

В этом случае виртуальное будет вызываться всякий раз, когда вы изменяете данное свойство name. И да, это должно быть постоянным.
Zemirco

это будет работать для всех полей объекта Item? Я не уверен, что это решение делает то, что я хочу
chovy
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.