Agregar campos created_at y updated_at a esquemas de mangosta

166

¿Hay alguna manera de agregar campos created_at y updated_at a un esquema de mangosta, sin tener que pasarlos cada vez que se llama a MyModel ()?

El campo created_at sería una fecha y solo se agregará cuando se cree un documento. El campo updated_at se actualizará con una nueva fecha cada vez que se llame a save () en un documento.

He intentado esto en mi esquema, pero el campo no aparece a menos que lo agregue explícitamente:

var ItemSchema = new Schema({
    name    : { type: String, required: true, trim: true }
  , created_at    : { type: Date, required: true, default: Date.now }
});
chovy
fuente
Hice exactamente lo que hiciste y funcionó. Usando Mangosta 4.8.4. ¿Podría ser algo nuevo?
Martinedwards
1
Esto fue desde hace bastante tiempo.
chovy
Sí, pensé que valía la pena señalar que lo anterior ahora funciona.
Martinedwards

Respuestas:

132

A partir de Mongoose 4.0 ahora puede establecer una opción de marcas de tiempo en el Esquema para que Mongoose maneje esto por usted:

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

Puede cambiar el nombre de los campos utilizados así:

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

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

Cuentos binarios
fuente
2
De acuerdo, esta es la mejor opción en Mongoose 4.0.
Tadd Giles
258

ACTUALIZACIÓN: (5 años después)

Nota: Si decide usar Kappa Architecture ( Event Sourcing + CQRS ), entonces no necesita una fecha actualizada. Dado que sus datos son un registro de eventos inmutable y de solo agregado, solo necesita la fecha de creación del evento. Similar a la arquitectura Lambda , que se describe a continuación. Entonces, el estado de su aplicación es una proyección del registro de eventos (datos derivados). Si recibe un evento posterior sobre una entidad existente, utilizará la fecha de creación de ese evento como fecha actualizada para su entidad. Esta es una práctica comúnmente utilizada (y comúnmente incomprendida) en sistemas de microservicios.

ACTUALIZACIÓN: (4 años después)

Si lo usa ObjectIdcomo su _idcampo (que suele ser el caso), entonces todo lo que necesita hacer es:

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

Verifique mi respuesta original a continuación sobre cómo obtener la marca de tiempo creada desde el _idcampo. Si necesita usar ID de un sistema externo, verifique la respuesta de Roman Rhrn Nesterov.

ACTUALIZAR: (2.5 años después)

Ahora puede usar la opción # timestamps con la versión de mangosta> = 4.0.

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

Si establece marcas de tiempo, asignaciones de mangosta createdAty updatedAtcampos en su esquema, el tipo asignado esDate .

También puede especificar los nombres de los archivos de marca de tiempo:

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

Nota: Si está trabajando en una aplicación grande con datos críticos, debería reconsiderar la actualización de sus documentos. Le aconsejaría que trabaje con datos inmutables, solo anexados ( arquitectura lambda ). Lo que esto significa es que solo se permiten inserciones. ¡Las actualizaciones y eliminaciones no deberían permitirse! Si desea "eliminar" un registro, puede insertar fácilmente una nueva versión del documento con algunos timestamp/ version archivados y luego establecer un campo indefinido o falso ').deleted campo en true. Del mismo modo, si desea actualizar un documento, cree uno nuevo con los campos apropiados actualizados y el resto de los campos copiados. Luego, para consultar este documento, obtendrá el que tenga la marca de tiempo más nueva o la versión más alta que sea no "borrado" (eldeleted

La inmutabilidad de los datos garantiza que sus datos sean depurables: puede rastrear el historial de cada documento. También puede retroceder a la versión anterior de un documento si algo sale mal. Si utiliza una arquitectura de este tipo, ObjectId.getTimestamp()es todo lo que necesita y no depende de Mongoose.


RESPUESTA ORIGINAL:

Si está utilizando ObjectId como su campo de identidad, no necesita el created_atcampo. Los ObjectIds tienen un método llamado getTimestamp().

ObjectId ("507c7f79bcf86cd7994f6c0e"). GetTimestamp ()

Esto devolverá el siguiente resultado:

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

Más información aquí ¿Cómo extraigo la fecha de creación de un Object ID de Mongo?

Para agregar updated_atarchivado, debe usar esto:

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();
});
Pavel Nikolov
fuente
3
¿Cómo se hace eso en Mongoose / node.js?
Laboratorio de propulsión de cebra
66
Vale la pena saberlo, incluso si no es la solución que se usa, ¡muy interesante!
Inconsciente el
Pero si quiero pasar la fecha de creación a la vista, necesitaría establecer una variable especial para esto, o pasar la identificación, ¿verdad?
3
Agradezco la respuesta inicial, pero abogar por el uso de la arquitectura lambda también parece una excelente manera de resolver 5x niveles de metaproblemas y posiblemente nunca entregue en lugar de construir la aplicación CRUD y mantenerla simple a menos que la complejidad de este enfoque esté realmente justificada. Recuerde que está publicando sobre MongoDB que 'apenas' se escala para comenzar y está abogando por un nuevo registro para cada escritura que rápidamente matará a Mongo a cualquier escala significativa. Mueva esto a la sección Cassandra ...
John Culviner
1
De hecho, cambié mi diseño de back-end para realizar eliminaciones suaves en lugar de eliminaciones reales, gracias a la información proporcionada en esta respuesta. Proporciona un conocimiento más profundo que es realmente útil.
Kenna
133

Esto es lo que terminé haciendo:

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();
});
chovy
fuente
8
1. Almacene la hora actual en una variable local y asígnela en lugar de llamar cada vez a Date (), esto se asegurará de que, al primer paso, created_at y updated_at tengan el mismo valor de excedencia. 2. nueva Fecha => nueva Fecha ()
Shay Erlichmen
13
Solo quisiera señalar que si usa ObjectId, puede obtener created_at desde allí ... no necesita un campo separado. Echa un vistazogetTimestamp()
Xerri
66
Otra opción para created_at podría ser cambiar el modelo a -> created_at: {type: Date, default: Date.now},
OscarVGG
1
@ajbraus Hacer un complemento de esquema
wlingke
2
También utilizar Date.now()siempre que sea posible en lugar de new Datesu más rápido, ya que es un método estático
karlkurzer
107

Use la timestampsopción integrada para su esquema.

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

Esto agregará automáticamente createdAty updatedAtcampos a su esquema.

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

mcompeau
fuente
1
Requiere versión de mangosta> = 4.0
Jensen
2
Encuentro que los documentos no están claros: esto agrega los campos, pero ¿también se asegura de que se actualicen en cada actualización?
Ludwik el
en general, createdAt siempre se almacena solo una vez ... cuando se crea el objeto. updatedAtse actualiza en cada nuevo guardado (cuando se cambia el obj)
codyc4321
1
Obtuve este "2016-12-07T11: 46: 46.066Z" .. ¿Podría explicar el formato de la marca de tiempo, cómo cambiar la zona horaria? es tomar zona horaria del servidor mongoDB?
Mahesh K
@mcompeau, ¡Gracias por la respuesta! Sé que es una posibilidad remota, pero ¿recuerdas por casualidad si los campos creados de esta manera se pueden usar como índice?
manidos
29

Si usa update()ofindOneAndUpdate()

con {upsert: true}opción

puedes usar $setOnInsert

var update = {
  updatedAt: new Date(),
  $setOnInsert: {
    createdAt: new Date()
  }
};
Roman Rhrn Nesterov
fuente
1
esto funciona en la mayoría de las bibliotecas mongo. cambiando a respuesta aceptada
chovy
1
Si está utilizando el _idcampo predeterminado de Mogo , entonces no necesita un createdAtcampo. Consulte mi respuesta a continuación para obtener más detalles.
Pavel Nikolov
1
... revisa mi RESPUESTA ORIGINAL .
Pavel Nikolov
25

Agregue timestampsa su Schemagusto así createdAty updatedAtgenerará automáticamente para usted

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

ingrese la descripción de la imagen aquí
También puedes cambiar createdAt -> created_atpor

timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }
Phan Van Linh
fuente
2
Ahora que Mongoose hace todo este trabajo por usted, estoy de acuerdo en que esta es la mejor solución ahora.
Vince Bowdren
Sí, de acuerdo en eso @VinceBowdren
Mr.spShuvo
8

Así es como logré haber creado y actualizado.

Dentro de mi esquema, agregué lo creado y actualizado así:

   / **
     * Esquema del artículo
     * /
    var ArticleSchema = nuevo esquema ({
        creado: {
            tipo: Fecha,
            predeterminado: Date.now
        },
        actualizado: {
            tipo: Fecha,
            predeterminado: Date.now
        },
        título: {
            tipo: cadena,
            defecto: '',
            ajuste: verdadero,
            requerido: 'El título no puede estar en blanco'
        },
        contenido: {
            tipo: cadena,
            defecto: '',
            ajuste: verdadero
        },
        usuario: {
            tipo: Schema.ObjectId,
            ref: 'Usuario'
        }
    });

Luego, en mi método de actualización del artículo dentro del controlador del artículo, agregué:

/ **
     * Actualizar un artículo
     * /
    export.update = function (req, res) {
        artículo var = req.article;

        artículo = _.extend (artículo, req.body);
        article.set ("actualizado", Date.now ());

        article.save (function (err) {
            si (err) {
                return res.status (400) .send ({
                    mensaje: errorHandler.getErrorMessage (err)
                });
            } más {
                res.json (artículo);
            }
        });
    };

Las secciones en negrita son las partes de interés.

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

ItemSchema.set('timestamps', true); // this will add createdAt and updatedAt timestamps

Documentos: https://mongoosejs.com/docs/guide.html#timestamps

Nikolay_R
fuente
¿Cómo se agrega el índice en el campo createdAt?
Valor_
4

Puede usar el timestampcomplemento de mongoose-trooppara agregar este comportamiento a cualquier esquema.

JohnnyHK
fuente
3

Puede usar este complemento muy fácilmente. De los documentos:

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

Y también establezca el nombre de los campos si lo desea:

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

podemos lograr esto usando el complemento de esquema también.

En helpers/schemaPlugin.jsarchivo

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);
};

y en models/ItemSchema.jsarchivo:

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);
Shaishab Roy
fuente
2

En el esquema de su modelo, simplemente agregue las marcas de tiempo de un atributo y asigne un valor verdadero como se muestra:

var ItemSchema = new Schema({
   name :  { type: String, required: true, trim: true },
},{timestamps : true}
);
Pavneet Kaur
fuente
¿Qué hará esto?
chovy
El atributo de marca de tiempo agregará los campos created_at y updated_at en cada documento. El campo Created_at indica la hora de creación del documento y el campo updated_at indica la hora de actualización, en cualquier caso, la hora de creación del documento. El valor de ambos campos está en formato de hora ISO. Espero que esto aclare tus dudas Chovy.
Pavneet Kaur
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 }
});

Ahora puede usar esto, para que no sea necesario incluir esta opción en cada tabla

Raghu
fuente
1

Mi versión de mangosta es 4.10.2

Parece que solo el gancho findOneAndUpdatees trabajo

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

Use una función para devolver el valor predeterminado calculado:

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();
});
orourkedd
fuente
no hay necesidad de envolver Date.now()en una función acaba de hacer:...default: Date.now()
karlkurzer
1
Lo envuelvo en una función para poder burlarme de '.now ()' en las pruebas. De lo contrario, solo se ejecuta una vez durante la inicialización y el valor no se puede cambiar fácilmente.
orourkedd
1
Tenga en cuenta que default: Date.now()estaría mal. Si algo es así default: Date.now. De lo contrario, todos sus documentos tendrán la misma marca de tiempo: la hora en que se inició su solicitud;)
Max Truxa
Me gusta tu default: Date.nowestrategia Mucho más limpio.
orourkedd
0

Realmente hago esto en la parte de atrás

Si todo va bien con la actualización:

 // 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
      });
    });
shuk
fuente
0

Use machinepack-datetime para formatear la fecha y hora.

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
});

El paquete de máquina es excelente con una API clara a diferencia del mundo Javascript expreso o general.

Piyush Patel
fuente
-2

Puede usar middleware y virtuales . Aquí hay un ejemplo para su updated_atcampo:

ItemSchema.virtual('name').set(function (name) {
  this.updated_at = Date.now;
  return name;
});
zemirco
fuente
¿Cuándo se establecería esto realmente? y persistirá? Entonces, dentro de 3 días, ¿todavía tendría una fecha de hace 3 días?
chovy
Se llamará al virtual cada vez que cambie la propiedad dada, en este caso name. Y sí, debería ser persistente.
zemirco
¿funcionaría esto para todos los campos en el objeto Item? No estoy seguro de que esta solución haga lo que quiero
chovy