ajouter les champs created_at et updated_at aux schémas de mangouste

166

Existe-t-il un moyen d'ajouter des champs created_at et updated_at à un schéma mangouste, sans avoir à les passer à chaque fois que new MyModel () est appelé?

Le champ created_at serait une date et uniquement ajouté lors de la création d'un document. Le champ updated_at serait mis à jour avec une nouvelle date chaque fois que save () est appelé sur un document.

J'ai essayé cela dans mon schéma, mais le champ n'apparaît que si je l'ajoute expressément:

var ItemSchema = new Schema({
    name    : { type: String, required: true, trim: true }
  , created_at    : { type: Date, required: true, default: Date.now }
});
chovy
la source
J'ai fait exactement ce que vous avez fait et cela a fonctionné. Utilisation de Mongoose 4.8.4. Peut-être quelque chose de nouveau?
martinedwards
1
c'était il y a un certain temps.
chovy
Ouais, j'ai pensé qu'il valait la peine de noter que ce qui précède fonctionne maintenant.
martinedwards

Réponses:

132

À partir de Mongoose 4.0, vous pouvez désormais définir une option d'horodatage sur le schéma pour que Mongoose gère cela pour vous:

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

Vous pouvez changer le nom des champs utilisés comme ceci:

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

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

Contes binaires
la source
2
D'accord, c'est la meilleure option dans Mongoose 4.0.
Tadd Giles
258

MISE À JOUR: (5 ans plus tard)

Remarque: Si vous décidez d'utiliser Kappa Architecture ( Event Sourcing + CQRS ), vous n'avez pas du tout besoin de mettre à jour la date. Étant donné que vos données sont un journal des événements immuable et ajouté uniquement, vous n'avez besoin que de la date de création de l'événement. Similaire à l' architecture Lambda , décrite ci-dessous. L'état de votre application est alors une projection du journal des événements (données dérivées). Si vous recevez un événement ultérieur concernant une entité existante, vous utiliserez la date de création de cet événement comme date de mise à jour pour votre entité. Il s'agit d'une pratique couramment utilisée (et souvent mal comprise) dans les systèmes de micro-services.

MISE À JOUR: (4 ans plus tard)

Si vous utilisez ObjectIdcomme _idchamp (ce qui est généralement le cas), tout ce que vous avez à faire est de:

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

Vérifiez ma réponse originale ci-dessous pour savoir comment obtenir l'horodatage créé à partir du _idchamp. Si vous devez utiliser des identifiants provenant d'un système externe, vérifiez la réponse de Roman Rhrn Nesterov.

MISE À JOUR: (2,5 ans plus tard)

Vous pouvez maintenant utiliser l' option #timestamps avec la version mangouste> = 4.0.

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

Si vous définissez des horodatages, des assignations de mangouste createdAtet des updatedAtchamps à votre schéma, le type attribué est Date.

Vous pouvez également spécifier les noms des fichiers d'horodatage:

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

Remarque: si vous travaillez sur une grande application avec des données critiques, vous devez reconsidérer la mise à jour de vos documents. Je vous conseillerais de travailler avec des données immuables, ajoutées uniquement ( architecture lambda ). Cela signifie que vous n'autorisez que les insertions. Les mises à jour et les suppressions ne devraient pas être autorisées! Si vous souhaitez "supprimer" un enregistrement, vous pouvez facilement insérer une nouvelle version du document avec certains timestamp/ version classés, puis définir un deletedchamp sur true. De même, si vous souhaitez mettre à jour un document - vous en créez un nouveau avec les champs appropriés mis à jour et le reste des champs copié.Ensuite, pour interroger ce document, vous obtiendrez celui avec l'horodatage le plus récent ou la version la plus élevée qui est pas "supprimé" (ledeleted champ est indéfini ou faux`).

L'immuabilité des données garantit que vos données sont déboguables - vous pouvez retracer l'historique de chaque document. Vous pouvez également revenir à la version précédente d'un document en cas de problème. Si vous optez pour une telle architecture, ObjectId.getTimestamp()c'est tout ce dont vous avez besoin, et elle n'est pas dépendante de Mongoose.


RÉPONSE ORIGINALE:

Si vous utilisez ObjectId comme champ d'identité, vous n'avez pas besoin de created_atchamp. Les ObjectIds ont une méthode appelée getTimestamp().

ObjectId ("507c7f79bcf86cd7994f6c0e"). GetTimestamp ()

Cela renverra la sortie suivante:

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

Plus d'informations ici Comment extraire la date de création d'un objet Mongo ObjectID

Pour ajouter un updated_atfichier, vous devez utiliser ceci:

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
la source
3
Comment faites-vous cela dans Mongoose / node.js?
Zebra Propulsion Lab
6
Cela vaut la peine de savoir même si ce n'est pas la solution qui s'utilise, très intéressante!
sans
Mais si je veux passer la création à la date à la vue, je devrais définir une variable spéciale pour cela, ou passer l'ID à droite?
3
J'apprécie la réponse initiale, mais préconiser l'utilisation de l'architecture lambda semble également être un excellent moyen de résoudre 5x niveaux de méta-problèmes et peut-être de ne jamais livrer plutôt que de créer l'application CRUD et de la garder simple à moins que la complexité de cette approche ne soit vraiment justifiée. N'oubliez pas que vous publiez des articles sur MongoDB qui s'échelonnent à peine au départ et que vous préconisez un nouveau record pour chaque écriture qui tuera rapidement Mongo à n'importe quelle échelle significative. Déplacez-le dans la section Cassandra ...
John Culviner
1
J'ai en fait changé la conception de mon backend afin d'effectuer des suppressions logicielles à la place de suppressions réelles, grâce aux informations fournies dans cette réponse. Il fournit des connaissances plus approfondies qui sont vraiment utiles.
Kenna le
133

C'est ce que j'ai fini par faire:

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
la source
8
1. Stockez l'heure actuelle dans une variable locale et attribuez-la au lieu d'appeler à chaque fois new Date (), cela garantira qu'au premier passage created_at et updated_at aient la même valeur exacte. 2. nouvelle date => nouvelle date ()
Shay Erlichmen
13
Je voudrais juste souligner que si vous utilisez ObjectId, vous pouvez obtenir le created_at à partir de là .... vous n'avez pas besoin d'un champ séparé. DépartgetTimestamp()
Xerri
6
Une autre option pour le created_at pourrait être de changer le modèle en -> created_at: {type: Date, default: Date.now},
OscarVGG
1
@ajbraus Créer un plugin de schéma
wlingke
2
utilisez également Date.now()si possible au lieu de new Dateson plus rapide car il s'agit d'une méthode statique
karlkurzer
107

Utilisez l' timestampsoption intégrée pour votre schéma.

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

Cela ajoutera automatiquement des champs createdAtet updatedAtà votre schéma.

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

Mcompeau
la source
1
Nécessite la version mangouste> = 4.0
Jensen
2
Je trouve que les documents ne sont pas clairs - cela ajoute les champs, mais cela garantit-il également qu'ils sont mis à jour à chaque mise à jour?
Ludwik
généralement, createdAt n'est toujours stocké qu'une seule fois ... lorsque l'objet est créé. updatedAtest mis à jour à chaque nouvelle sauvegarde (lorsque l'objet est modifié)
codyc4321
1
J'ai eu ce "2016-12-07T11: 46: 46.066Z" .. Pourrait s'il vous plaît expliquer le format d'horodatage, comment changer le fuseau horaire ?? est prendre le fuseau horaire du serveur mongoDB ??
Mahesh K
@mcompeau, Merci pour la réponse! Je sais que c'est long, mais vous souvenez-vous par hasard si les champs créés de cette façon peuvent être utilisés comme index?
manidos
29

Si vous utilisez update()oufindOneAndUpdate()

avec {upsert: true}option

vous pouvez utiliser $setOnInsert

var update = {
  updatedAt: new Date(),
  $setOnInsert: {
    createdAt: new Date()
  }
};
Roman Rhrn Nesterov
la source
1
cela fonctionne dans la plupart des bibliothèques mongo.
Changement de
1
Si vous utilisez le _idchamp par défaut de Mogo , vous n'avez pas besoin d'un createdAtchamp. Vérifiez ma réponse ci-dessous pour plus de détails.
Pavel Nikolov
1
... vérifiez ma RÉPONSE ORIGINALE .
Pavel Nikolov
25

Ajoutez timestampsà votre Schemacomme ceci alors createdAtet updatedAtgénérera automatiquement pour vous

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

entrez la description de l'image ici
Vous pouvez également changer createdAt -> created_atpar

timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }
Phan Van Linh
la source
2
Maintenant que Mongoose fait tout ce travail pour vous, je suis d'accord que c'est la meilleure solution maintenant.
Vince Bowdren
Ouais, d'accord sur ça @VinceBowdren
Mr.spShuvo
8

C'est ainsi que j'ai réalisé après avoir créé et mis à jour.

Dans mon schéma, j'ai ajouté le créé et mis à jour comme suit:

   / **
     * Schéma de l'article
     * /
    var ArticleSchema = nouveau schéma ({
        établi: {
            type: Date,
            par défaut: Date.now
        },
        actualisé: {
            type: Date,
            par défaut: Date.now
        },
        Titre: {
            type: Chaîne,
            défaut: '',
            trim: vrai,
            obligatoire: "Le titre ne peut pas être vide"
        },
        contenu: {
            type: Chaîne,
            défaut: '',
            trim: vrai
        },
        utilisateur: {
            type: Schema.ObjectId,
            ref: 'Utilisateur'
        }
    });

Ensuite, dans ma méthode de mise à jour d'article dans le contrôleur d'article, j'ai ajouté:

/ **
     * Mettre à jour un article
     * /
    exports.update = fonction (req, res) {
        var article = req.article;

        article = _.extend (article, req.body);
        article.set ("mis à jour", Date.now ());

        article.save (fonction (err) {
            if (err) {
                retourne res.status (400) .send ({
                    message: errorHandler.getErrorMessage (err)
                });
            } autre {
                res.json (article);
            }
        });
    };

Les sections en gras sont les parties d'intérêt.

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

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

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

Nikolay_R
la source
Comment ajouter un index sur le champ createdAt?
Valor_
4

Vous pouvez utiliser le timestampplugin de mongoose-trooppour ajouter ce comportement à n'importe quel schéma.

JohnnyHK
la source
3

Vous pouvez utiliser ce plugin très facilement. À partir de la documentation:

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

Et définissez également le nom des champs si vous le souhaitez:

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

nous pouvons y parvenir en utilisant également le plugin de schéma .

Dans le helpers/schemaPlugin.jsdossier

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

et dans le models/ItemSchema.jsfichier:

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
la source
2

Dans votre schéma de modèle, ajoutez simplement un horodatage d' attribut et attribuez-lui la valeur true comme indiqué: -

var ItemSchema = new Schema({
   name :  { type: String, required: true, trim: true },
},{timestamps : true}
);
Pavneet Kaur
la source
Qu'est-ce que cela va faire?
chovy
L'attribut d'horodatage ajoutera les champs created_at et updated_at dans chaque document. Le champ Created_at indique l'heure de création du document et le champ updated_at indique l'heure de la mise à jour le cas échéant l'heure de création du document. La valeur des deux champs est au format d'heure ISO. J'espère que cela dissipe vos doutes 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 }
});

Vous pouvez maintenant l'utiliser, de sorte qu'il n'est pas nécessaire d'inclure cette option dans chaque table

Raghu
la source
1

Ma version mangouste est la 4.10.2

Il semble que seul le crochet findOneAndUpdatefonctionne

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

Utilisez une fonction pour renvoyer la valeur par défaut calculée:

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
la source
pas besoin d'envelopper Date.now()une fonction, faites simplement:...default: Date.now()
karlkurzer
1
Je l'enveloppe dans une fonction pour que je puisse me moquer de '.now ()' dans les tests. Sinon, il n'est exécuté qu'une seule fois lors de l'initialisation et la valeur ne peut pas être facilement modifiée.
orourkedd
1
Notez que ce default: Date.now()serait faux. Si quelque chose c'est default: Date.now. Sinon, tous vos documents auront le même horodatage: l'heure à laquelle votre application a démarré;)
Max Truxa
J'aime ta default: Date.nowstratégie. beaucoup plus propre.
orourkedd
0

Je fais ça dans le dos

Si tout se passe bien avec la mise à jour:

 // 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
la source
0

Utilisez machinepack-datetime pour formater le 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
});

Le pack machine est génial avec une API claire contrairement au monde Javascript express ou général.

Piyush Patel
la source
-2

Vous pouvez utiliser des intergiciels et des virtuels . Voici un exemple pour votre updated_atdomaine:

ItemSchema.virtual('name').set(function (name) {
  this.updated_at = Date.now;
  return name;
});
zemirco
la source
Quand est-ce que cela serait réglé? et persistera-t-il? Donc, dans 3 jours, il aurait encore une date d'il y a 3 jours?
chovy
le virtuel sera appelé chaque fois que vous modifiez la propriété donnée, dans ce cas name. Et oui, cela devrait être persistant.
zemirco
cela fonctionnerait-il pour tous les champs de l'objet Item? Je ne suis pas sûr que cette solution fasse ce que je veux
chovy