dodaj pola created_at i updated_at do schematów mangusty

166

Czy istnieje sposób na dodanie pól created_at i updated_at do schematu mangusty bez konieczności ich przekazywania za każdym razem, gdy wywoływana jest funkcja new MyModel ()?

Pole created_at byłoby datą i jest dodawane tylko podczas tworzenia dokumentu. Pole updated_at będzie aktualizowane nową datą za każdym razem, gdy wywoływana jest metoda save () w dokumencie.

Próbowałem tego w moim schemacie, ale pole nie pojawia się, chyba że dodam je w sposób dokładny:

var ItemSchema = new Schema({
    name    : { type: String, required: true, trim: true }
  , created_at    : { type: Date, required: true, default: Date.now }
});
chovy
źródło
Zrobiłem dokładnie to, co ty i zadziałało. Korzystanie z Mongoose 4.8.4. Może to coś nowego?
martinedwards
1
to było od jakiegoś czasu.
chovy
Tak, pomyślałem, że warto zauważyć, że powyższe działa teraz.
martinedwards

Odpowiedzi:

132

Począwszy od Mongoose 4.0, możesz teraz ustawić opcję znaczników czasu w schemacie, aby Mongoose zajął się tym za Ciebie:

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

Możesz zmienić nazwy pól używanych w następujący sposób:

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

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

Binarytales
źródło
2
Zgoda, to najlepsza opcja w Mongoose 4.0.
Tadd Giles
258

UPDATE: (5 lat później)

Uwaga: jeśli zdecydujesz się na korzystanie z architektury Kappa ( źródło zdarzeń + CQRS ), nie potrzebujesz w ogóle zaktualizowanej daty. Ponieważ Twoje dane są niezmiennym dziennikiem zdarzeń, który można tylko dopisać, potrzebujesz tylko daty utworzenia zdarzenia. Podobna do opisanej poniżej architektury lambda . W takim przypadku stan aplikacji jest projekcją dziennika zdarzeń (danych pochodnych). Jeśli otrzymasz kolejne zdarzenie dotyczące istniejącej encji, użyjesz daty utworzenia tego zdarzenia jako zaktualizowanej daty swojej encji. Jest to powszechnie stosowana (i często niezrozumiana) praktyka w systemach mikrousług.

UPDATE: (4 lata później)

Jeśli używasz ObjectIdjako swojego _idpola (co zwykle ma miejsce), wszystko, co musisz zrobić, to:

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

Sprawdź moją oryginalną odpowiedź poniżej, jak uzyskać utworzony znacznik czasu z _idpola. Jeśli potrzebujesz użyć identyfikatorów z systemu zewnętrznego, sprawdź odpowiedź Romana Rhrna Nesterova.

AKTUALIZACJA: (2,5 roku później)

Możesz teraz użyć opcji #timestamps z wersją mangusty> = 4.0.

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

Jeśli ustawisz sygnatury czasowe, przypisania mongoose createdAti updatedAtpola do schematu, przypisany typ to Date.

Możesz także określić nazwy zbiorów sygnatur czasowych:

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

Uwaga: jeśli pracujesz nad dużą aplikacją z krytycznymi danymi, powinieneś ponownie rozważyć aktualizację dokumentów. Radziłbym pracować z niezmiennymi danymi tylko do dołączania ( architektura lambda ). Oznacza to, że zezwalasz tylko na wstawianie. Aktualizacje i usuwanie nie powinny być dozwolone! Jeśli chcesz „usunąć” rekord, możesz łatwo wstawić nową wersję dokumentu z jakimś timestamp/ version wypełnionym, a następnie ustawić deletedpole na true. Podobnie, jeśli chcesz zaktualizować dokument - tworzysz nowy z zaktualizowanymi odpowiednimi polami i skopiowanymi pozostałymi polami, a następnie, aby odpytać ten dokument, otrzymasz dokument z najnowszym znacznikiem czasu lub najwyższą wersją, która jest nie „usunięte” (deleted pole jest niezdefiniowane lub fałszywe`).

Niezmienność danych zapewnia możliwość debugowania danych - możesz prześledzić historię każdego dokumentu. Możesz także przywrócić poprzednią wersję dokumentu, jeśli coś pójdzie nie tak. Jeśli wybierzesz taką architekturę, ObjectId.getTimestamp()to wszystko, czego potrzebujesz, i nie jest ona zależna od Mongoose.


ORYGINALNA ODPOWIEDŹ:

Jeśli używasz ObjectId jako pola tożsamości, nie potrzebujesz created_atpola. ObjectIds mają metodę o nazwie getTimestamp().

ObjectId ("507c7f79bcf86cd7994f6c0e"). GetTimestamp ()

To zwróci następujące dane wyjściowe:

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

Więcej informacji tutaj Jak wyodrębnić datę utworzenia z Mongo ObjectID

Aby dodać pole updated_at, musisz użyć tego:

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
źródło
3
Jak to robisz w Mongoose / node.js?
Zebra Propulsion Lab,
6
Warto to wiedzieć, nawet jeśli nie jest to rozwiązanie, które jest używane, bardzo interesujące!
nieświadomie
Ale jeśli chcę przekazać datę utworzenia do widoku, musiałbym ustawić specjalną zmienną do tego lub przekazać identyfikator, prawda?
3
Doceniam początkową odpowiedź, ale opowiadanie się za użyciem architektury lambda również brzmi jak świetny sposób na rozwiązanie 5-krotnych poziomów meta problemów i prawdopodobnie nigdy nie dostarcza, zamiast budować aplikację CRUD i utrzymywać ją w prostocie, chyba że złożoność tego podejścia jest naprawdę uzasadniona. Pamiętaj, że piszesz o MongoDB, który „ledwo” skaluje się na początku, i opowiadasz się za nowym rekordem dla każdego zapisu, który szybko zabije Mongo w dowolnej znaczącej skali. Przenieś to do sekcji Cassandra ...
John Culviner
1
Faktycznie zmieniłem projekt backendu, aby wykonywać miękkie usuwanie zamiast prawdziwych, dzięki informacjom podanym w tej odpowiedzi. Dostarcza głębszej wiedzy, która jest naprawdę przydatna.
Kenna
133

Oto, co ostatecznie zrobiłem:

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
źródło
8
1. Przechowuj bieżący czas w lokalnej zmiennej zmiennej i przypisuj go zamiast za każdym razem wywoływać new Date (), dzięki temu upewnisz się, że w pierwszym przejściu created_at i updated_at mają tę samą wartość excect. 2. nowa data => nowa data ()
Shay Erlichmen
13
Chciałbym tylko zaznaczyć, że jeśli używasz ObjectId, możesz pobrać stamtąd utworzony_at ... nie potrzebujesz osobnego pola. SprawdźgetTimestamp()
Xerri
6
Inną opcją dla created_at może być zmiana modelu na -> created_at: {type: Date, default: Date.now},
OscarVGG
1
@ajbraus Utwórz wtyczkę schematu
wlingke
2
w Date.now()miarę możliwości używaj również zamiast new Dateszybszego, ponieważ jest to metoda statyczna
karlkurzer
107

Użyj wbudowanej timestampsopcji dla swojego schematu.

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

To automatycznie doda createdAti updatedAtpola do schematu.

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

mcompeau
źródło
1
Wymaga wersji Mongoose> = 4.0
Jensen
2
Wydaje mi się, że dokumenty są niejasne - powoduje to dodanie pól, ale czy zapewnia również aktualizację przy każdej aktualizacji?
Ludwik
ogólnie rzecz biorąc, createdAt jest zawsze przechowywane tylko raz ... kiedy tworzony jest obiekt. updatedAtjest aktualizowany przy każdym nowym zapisie (po zmianie przedmiotu)
codyc4321
1
Mam to „2016-12-07T11: 46: 46.066Z” .. Czy mogę wyjaśnić format znacznika czasu, jak zmienić strefę czasową? czy zajmuje strefę czasową serwera mongoDB?
Mahesh K
@mcompeau, dziękuję za odpowiedź! Wiem, że to długa szansa, ale czy pamiętasz przypadkiem, czy pola utworzone w ten sposób mogą być używane jako indeks?
manidos
29

Jeśli używasz update()lubfindOneAndUpdate()

z {upsert: true}opcją

możesz użyć $setOnInsert

var update = {
  updatedAt: new Date(),
  $setOnInsert: {
    createdAt: new Date()
  }
};
Roman Rhrn Nesterov
źródło
1
działa to w większości bibliotek mongo. zmiana na zaakceptowaną odpowiedź
chovy
1
Jeśli używasz domyślnego _idpola Mogo , nie potrzebujesz createdAtpola. Sprawdź moją odpowiedź poniżej, aby uzyskać więcej informacji.
Pavel Nikolov,
1
... sprawdź moją ORYGINALNĄ ODPOWIEDŹ .
Pavel Nikolov
25

Dodaj timestampsdo swojej Schematak wtedy createdAti updatedAtbędzie automatyczne generowanie dla Ciebie

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

wprowadź opis obrazu tutaj
Ponadto można zmienić createdAt -> created_atprzez

timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }
Phan Van Linh
źródło
2
Teraz, gdy Mongoose wykonuje całą tę pracę za Ciebie, zgadzam się, że jest to teraz najlepsze rozwiązanie.
Vince Bowdren
Tak, zgadzam się na to @VinceBowdren
Mr.spShuvo
8

W ten sposób osiągnąłem, tworząc i aktualizując.

Wewnątrz mojego schematu dodałem utworzony i zaktualizowany w następujący sposób:

   / **
     * Schemat artykułu
     * /
    var ArticleSchema = new Schema ({
        Utworzony: {
            typ: Data,
            domyślnie: Date.now
        },
        zaktualizowane: {
            typ: Data,
            domyślnie: Date.now
        },
        title: {
            typ: Sznurek,
            domyślna: '',
            wykończenie: prawda,
            wymagane: „Tytuł nie może być pusty”
        },
        zadowolony: {
            typ: Sznurek,
            domyślna: '',
            wykończenie: prawda
        },
        użytkownik: {
            type: Schema.ObjectId,
            ref: 'Użytkownik'
        }
    });

Następnie w mojej metodzie aktualizacji artykułu w kontrolerze artykułu dodałem:

/ **
     * Zaktualizuj artykuł
     * /
    exports.update = function (req, res) {
        var artykuł = req.article;

        artykuł = _.extend (artykuł, req.body);
        article.set ("zaktualizowany", Date.now ());

        article.save (function (err) {
            if (err) {
                return res.status (400) .send ({
                    komunikat: errorHandler.getErrorMessage (err)
                });
            } else {
                res.json (artykuł);
            }
        });
    };

Pogrubione sekcje to części, które Cię interesują.

rOOb85
źródło
7
var ItemSchema = new Schema({
    name : { type: String, required: true, trim: true }
});

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

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

Nikolay_R
źródło
Jak dodać indeks w polu createdAt?
Valor_
4

Możesz użyć timestampwtyczki programu, mongoose-troopaby dodać to zachowanie do dowolnego schematu.

JohnnyHK
źródło
3

Możesz bardzo łatwo korzystać z tej wtyczki . Z dokumentów:

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

A jeśli chcesz, ustaw również nazwy pól:

mongoose.plugin(timestamps,  {
  createdAt: 'created_at', 
  updatedAt: 'updated_at'
});
Orr
źródło
2

możemy to osiągnąć również za pomocą wtyczki schematu .

W helpers/schemaPlugin.jspliku

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

aw models/ItemSchema.jspliku:

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
źródło
2

W schemacie modelu po prostu dodaj sygnatury czasowe atrybutu i przypisz mu wartość true , jak pokazano: -

var ItemSchema = new Schema({
   name :  { type: String, required: true, trim: true },
},{timestamps : true}
);
Pavneet Kaur
źródło
Co to zrobi?
chovy
Atrybut Timestamp doda pola created_at i updated_at w każdym dokumencie. Pole Created_at wskazuje czas utworzenia dokumentu, a pole updated_at wskazuje czas aktualizacji, jeśli w ogóle czas utworzenia dokumentu. Wartość obu pól jest w formacie czasu ISO. Mam nadzieję, że to rozwiąże twoje wątpliwości 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 }
});

Teraz możesz tego użyć, aby nie trzeba było uwzględniać tej opcji w każdej tabeli

Raghu
źródło
1

Moja wersja mangusty to 4.10.2

Wygląda na findOneAndUpdateto, że działa tylko hak

ModelSchema.pre('findOneAndUpdate', function(next) {
  // console.log('pre findOneAndUpdate ....')
  this.update({},{ $set: { updatedAt: new Date() } });
  next()
})
John Xiao
źródło
0

Użyj funkcji, aby zwrócić obliczoną wartość domyślną:

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
źródło
nie trzeba zawijać Date.now()funkcji po prostu:...default: Date.now()
karlkurzer
1
Zawijam go w funkcji, aby móc mockować '.now ()' w testach. W przeciwnym razie jest uruchamiany tylko raz podczas inicjalizacji, a wartości nie można łatwo zmienić.
orourkedd,
1
Zauważ, że default: Date.now()byłoby to złe. Jeśli już, to jest default: Date.now. W przeciwnym razie wszystkie Twoje dokumenty będą miały tę samą sygnaturę czasową: Godzina rozpoczęcia aplikacji;)
Max Truxa
Podoba mi się twoja default: Date.nowstrategia. dużo czystszy.
orourkedd
0

Właściwie robię to z tyłu

Jeśli wszystko pójdzie dobrze z aktualizacją:

 // 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
źródło
0

Sformatuj datę i godzinę za pomocą pakietu maszynowego-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
});

Pakiet maszynowy jest świetny z przejrzystym interfejsem API, w przeciwieństwie do ekspresowego lub ogólnego świata Javascript.

Piyush Patel
źródło
-2

Możesz użyć oprogramowania pośredniego i wirtualnych . Oto przykład dla twojego updated_atpola:

ItemSchema.virtual('name').set(function (name) {
  this.updated_at = Date.now;
  return name;
});
zemirco
źródło
Kiedy to faktycznie zostanie ustawione? i czy będzie trwać? Czyli za 3 dni nadal będzie miała datę sprzed 3 dni?
chovy
wirtualny zostanie wywołany za każdym razem, gdy zmienisz daną właściwość, w tym przypadku name. I tak, powinno być trwałe.
zemirco
czy to zadziała dla wszystkich pól w obiekcie Item? Nie jestem pewien, czy to rozwiązanie robi to, czego chcę
chovy