adicione os campos created_at e updated_at aos esquemas de mangusto

166

Existe uma maneira de adicionar campos created_at e updated_at a um esquema de mangusto, sem precisar passá-los sempre que novo MyModel () for chamado?

O campo created_at seria uma data e somente será adicionado quando um documento for criado. O campo updated_at será atualizado com nova data sempre que save () for chamado em um documento.

Eu tentei isso no meu esquema, mas o campo não aparece, a menos que eu o inclua exccitly:

var ItemSchema = new Schema({
    name    : { type: String, required: true, trim: true }
  , created_at    : { type: Date, required: true, default: Date.now }
});
chovy
fonte
Eu fiz exatamente o que você fez e funcionou. Usando o Mongoose 4.8.4. Pode ser uma coisa nova?
martinedwards
1
isso foi de algum tempo atrás.
Chovy
Sim, pensei que valha a pena notar que o acima funciona agora.
martinedwards 29/11

Respostas:

132

No Mongoose 4.0, agora você pode definir uma opção de registro de data e hora no esquema para que o Mongoose lide com isso para você:

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

Você pode alterar o nome dos campos usados ​​da seguinte maneira:

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

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

Binarytales
fonte
2
Concordado, esta é a melhor opção no Mongoose 4.0.
Tadd Giles
258

ATUALIZAÇÃO: (5 anos depois)

Nota: Se você decidir usar a Arquitetura Kappa ( Event Sourcing + CQRS ), não precisará da data atualizada. Como seus dados são um log de eventos imutável e somente anexável, você só precisa da data de criação do evento. Semelhante à arquitetura Lambda , descrita abaixo. Então o estado do seu aplicativo é uma projeção do log de eventos (dados derivados). Se você receber um evento subsequente sobre a entidade existente, usará a data de criação desse evento como data atualizada para sua entidade. Essa é uma prática comumente usada (e geralmente mal compreendida) em sistemas de microsserviços.

ATUALIZAÇÃO: (4 anos depois)

Se você usa ObjectIdcomo seu _idcampo (que geralmente é o caso), tudo que você precisa fazer é:

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

Verifique minha resposta original abaixo sobre como obter o carimbo de data e hora criado no _idcampo. Se você precisar usar IDs de um sistema externo, verifique a resposta de Roman Rhrn Nesterov.

ATUALIZAÇÃO: (2,5 anos depois)

Agora você pode usar a opção #timestamps com a versão mangusto> = 4.0.

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

Se definir carimbos de data e hora, mangusto createdAte updatedAtcampos para seu esquema, o tipo designado é Date.

Você também pode especificar os nomes dos arquivos de data e hora:

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

Nota: Se você estiver trabalhando em um grande aplicativo com dados críticos, reconsidere a atualização de seus documentos. Eu recomendaria que você trabalhasse com dados imutáveis ​​e somente anexáveis ( arquitetura lambda ). O que isso significa é que você só permite inserções. Atualizações e exclusões não devem ser permitidas! Se você quiser "excluir" um registro, poderá inserir facilmente uma nova versão do documento com alguns timestamp/ version arquivados e, em seguida, defina um deletedcampo como true. Da mesma forma, se você deseja atualizar um documento - você cria um novo com os campos apropriados atualizados e o restante dos campos copiados. Então, para consultar este documento, você obtém o que possui o carimbo de data / hora mais recente ou a versão mais alta que é não "excluído" (odeleted campo indefinido ou falso`).

A imutabilidade dos dados garante que seus dados sejam depuráveis ​​- você pode rastrear o histórico de todos os documentos. Você também pode reverter para a versão anterior de um documento se algo der errado. Se você optar por uma arquitetura desse tipo, ObjectId.getTimestamp()é tudo o que precisa e não depende do Mongoose.


RESPOSTA ORIGINAL:

Se você estiver usando o ObjectId como seu campo de identidade, não precisará do created_atcampo. Os ObjectIds têm um método chamado getTimestamp().

ObjectId ("507c7f79bcf86cd7994f6c0e"). GetTimestamp ()

Isso retornará a seguinte saída:

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

Mais informações aqui Como extrair a data criada de um Mongo ObjectID

Para adicionar updated_atarquivos, você precisa usar o seguinte:

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
fonte
3
Como você faz isso no Mongoose / node.js?
Zebra Propulsion Lab
6
Vale a pena saber, mesmo que não seja a solução que é usada, muito interessante!
involuntário
Mas se eu quiser passar o create at date para a view, precisaria definir uma variável especial para isso ou passar o id certo?
3
Agradeço a resposta inicial, mas defender o uso da arquitetura lambda também parece uma ótima maneira de resolver 5x níveis de meta problemas e possivelmente nunca fornecer, em vez de criar o aplicativo CRUD e mantê-lo simples, a menos que a complexidade dessa abordagem seja realmente justificada. Lembre-se de que você está postando sobre o MongoDB, que 'mal' é dimensionado para começar, e você está defendendo um novo registro para cada gravação que acabe com o Mongo rapidamente em qualquer escala significativa. Mova esta para a seção Cassandra ...
John Culviner
1
Na verdade, mudei meu design de back-end para realizar exclusões dinâmicas no lugar de exclusões reais, graças às informações fornecidas nesta resposta. Ele fornece um conhecimento mais profundo que é realmente útil.
Kenna
133

Isto é o que acabei fazendo:

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
fonte
8
1. Armazene o horário atual em um var local e atribua-o ao invés de cada vez que chamar new Date (), isso garantirá que, à primeira passagem, created_at e updated_at tenham o mesmo valor exato. 2. new Date => new Date ()
Shay Erlichmen 27/10
13
Gostaria apenas de salientar que, se você usar o ObjectId, poderá obter o created_at a partir daí ... não precisará de um campo separado. ConfiragetTimestamp()
Xerri
6
Outra opção para o created_at poderia ser alterar o modelo para -> created_at: {type: Date, default: Date.now},
OscarVGG
1
@ajbraus Crie um plug
wlingke
2
também utilizar Date.now(), sempre que possível, em vez de new Datesua mais rápido, pois é um método estático
karlkurzer
107

Use a timestampsopção interna para o seu esquema.

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

Isto irá adicionar automaticamente createdAte updatedAtcampos ao esquema.

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

mcompeau
fonte
1
Requer versão em mangusto> = 4.0
Jensen
2
Acho que os documentos não são claros - isso adiciona os campos, mas também garante que eles sejam atualizados a cada atualização?
Ludwik
geralmente, createdAt é sempre armazenado apenas uma vez ... quando o objeto é criado. updatedAté atualizado em cada novo save (quando o obj é alterado)
codyc4321
1
Eu recebi este "2016-12-07T11: 46: 46.066Z" .. Poderia explicar o formato do carimbo de hora, como alterar o fuso horário? é levar o fuso horário do servidor mongoDB ??
Mahesh K
@mcompeau, obrigado pela resposta! Eu sei que é um tiro no escuro, mas você se lembra por acaso se os campos criados dessa maneira podem ser usados ​​como índice?
manidos
29

Se usar update()oufindOneAndUpdate()

com {upsert: true}opção

você pode usar $setOnInsert

var update = {
  updatedAt: new Date(),
  $setOnInsert: {
    createdAt: new Date()
  }
};
Roman Rhrn Nesterov
fonte
1
isso funciona na maioria das bibliotecas mongo. mudando a aceita resposta
Chovy
1
Se você estiver usando o _idcampo padrão do Mogo , não precisará de um createdAtcampo. Verifique minha resposta abaixo para mais detalhes.
Pavel Nikolov
1
... verifique minha RESPOSTA ORIGINAL .
Pavel Nikolov
25

Adicione timestampsao seu Schemagosto assim createdAte updatedAtgerará automaticamente para você

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

insira a descrição da imagem aqui
Além disso, você pode mudar createdAt -> created_atpor

timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }
Phan Van Linh
fonte
2
Agora que o Mongoose faz todo esse trabalho para você, concordo que esta é a melhor solução agora.
Vince Bowdren
Sim, concorde com isso @VinceBowdren
Mr.spShuvo
8

Foi assim que consegui ter criado e atualizado.

Dentro do meu esquema, adicionei o criado e atualizado da seguinte forma:

   / **
     * Esquema do artigo
     * /
    var ArticleSchema = novo esquema ({
        criada: {
            tipo: Data,
            padrão: Date.now
        }
        Atualizada: {
            tipo: Data,
            padrão: Date.now
        }
        title: {
            tipo: String,
            padrão: '',
            trim: true,
            obrigatório: 'O título não pode ficar em branco'
        }
        conteúdo: {
            tipo: String,
            padrão: '',
            trim: true
        }
        do utilizador: {
            tipo: Schema.ObjectId,
            ref: 'Usuário'
        }
    });

Em seguida, no meu método de atualização de artigo dentro do controlador de artigo, adicionei:

/ **
     * Atualize um artigo
     * /
    imports.update = função (req, res) {
        var artigo = req.article;

        article = _.extend (article, req.body);
        article.set ("atualizado", Date.now ());

        article.save (function (err) {
            if (err) {
                return res.status (400) .send ({
                    mensagem: errorHandler.getErrorMessage (err)
                });
            } outro {
                res.json (artigo);
            }
        });
    };

As seções em negrito são as partes de interesse.

rOOb85
fonte
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
fonte
Como você adiciona índice no campo createdAt?
Valor_ 17/07
4

Você pode usar o timestampplug - in de mongoose-trooppara adicionar esse comportamento a qualquer esquema.

JohnnyHK
fonte
3

Você pode usar este plugin com muita facilidade. Dos 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)

E também defina o nome dos campos, se desejar:

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

podemos conseguir isso usando o plugin de esquema também.

No helpers/schemaPlugin.jsarquivo

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

e no models/ItemSchema.jsarquivo:

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
fonte
2

No esquema do modelo, basta adicionar um registro de data e hora do atributo e atribuir um valor verdadeiro a ele, conforme mostrado: -

var ItemSchema = new Schema({
   name :  { type: String, required: true, trim: true },
},{timestamps : true}
);
Pavneet Kaur
fonte
O que isso fará?
chovy 18/12/19
O atributo Timestamp adicionará os campos created_at e updated_at em cada documento. O campo Created_at indica a hora da criação do documento e o campo updated_at indica a hora da atualização, se houver, a hora da criação do documento. O valor de ambos os campos está no formato de hora ISO. Espero que isso esclareça suas dúvidas Chovy.
Pavneet Kaur 23/12/19
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 }
});

Agora você pode usar isso, para que não haja necessidade de incluir essa opção em todas as tabelas

Raghu
fonte
1

Minha versão do mangusto é 4.10.2

Parece que apenas o gancho findOneAndUpdateé trabalho

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

Use uma função para retornar o valor padrão 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
fonte
não há necessidade de envolver Date.now()em uma função apenas fazer:...default: Date.now()
karlkurzer
1
Eu o envolvo em uma função para poder zombar de '.now ()' nos testes. Caso contrário, ele será executado apenas uma vez durante a inicialização e o valor não poderá ser alterado facilmente.
precisa saber é o seguinte
1
Note que default: Date.now()isso estaria errado. Se alguma coisa é default: Date.now. Caso contrário, todos os seus documentos terão o mesmo carimbo do tempo: O tempo em que a aplicação iniciada;)
Max Truxa
Eu gosto da sua default: Date.nowestratégia. muito mais limpo.
precisa saber é o seguinte
0

Na verdade, eu faço isso nas costas

Se tudo correr bem com a atualização:

 // 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
fonte
0

Use machinepack-datetime para formatar o 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
});

O pacote de máquinas é ótimo com API clara, diferente do mundo Javascript expresso ou geral.

Piyush Patel
fonte
-2

Você pode usar middleware e virtuais . Aqui está um exemplo para o seu updated_atcampo:

ItemSchema.virtual('name').set(function (name) {
  this.updated_at = Date.now;
  return name;
});
zemirco
fonte
Quando isso realmente seria definido? e vai persistir? Então, daqui a três dias, ele ainda teria uma data de três dias atrás?
chovy 01/10/12
o virtual será chamado sempre que você alterar a propriedade especificada, neste caso name. E sim, deve ser persistente.
Zemirco 01/10/12
isso funcionaria para todos os campos no objeto Item? I "m não tem certeza esta solução faz o que eu quero
Chovy