You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeORM中MongoDB的findOneAndUpdate(upsert:true)未生成时间字段的解决方法

问题

在启用自动生成createdAt和updatedAt字段的TypeORM MongoDB仓库中,使用findOneAndUpdate并设置upsert:true时,这两个字段始终未被插入或更新。请问这种情况是否正常?该如何解决?需求是用最少的数据库请求实现「查询到则更新,未查询到则创建」的逻辑。

代码示例

import { ObjectID } from 'mongodb';
import { container, instanceCachingFactory } from 'tsyringe';
import {
  Column,
  Entity,
  EntityRepository,
  getCustomRepository,
  MongoRepository,
} from 'typeorm';
import { CreateDateColumn, ObjectIdColumn, UpdateDateColumn } from 'typeorm';

export abstract class TypeOrmAbstractEntity {
  @ObjectIdColumn() _id: ObjectID;
  @CreateDateColumn() createdAt: Date;
  @UpdateDateColumn() updatedAt: Date;

  get id() {
    return this._id.toHexString();
  }
}

@Entity('subscriptions')
export class Subscription extends TypeOrmAbstractEntity {
  @Column({ nullable: false }) subscriptionID: string;
  @Column({ nullable: false }) customerID: string;
}

@EntityRepository(Subscription)
export class SubscriptionRepository extends MongoRepository<Subscription> {}
container.register<SubscriptionRepository>(SubscriptionRepository, {
  useFactory: instanceCachingFactory(() =>
    getCustomRepository(SubscriptionRepository)
  ),
});

// ...

const stripeSubscriptionRepository = container.resolve(SubscriptionRepository);

subscriptionRepository.findOneAndUpdate(
  { customerID },
  {
    $set: {
      subscriptionID: subscription.id,
      customerID: customerID,
    },
  },
  { upsert: true }
);
解答

这种情况是正常的。TypeORM的CreateDateColumn和UpdateDateColumn自动填充逻辑,仅在调用TypeORM封装的实体操作方法(如save()、insert())时才会触发。而findOneAndUpdate是直接调用MongoDB原生操作,TypeORM不会为这类原生方法自动注入时间字段的填充逻辑。

解决方案

1. 手动在更新管道中维护时间字段(最少请求)

通过MongoDB的$set和$setOnInsert操作符,分别处理更新和插入场景下的时间字段:

subscriptionRepository.findOneAndUpdate(
  { customerID },
  {
    $set: {
      subscriptionID: subscription.id,
      customerID: customerID,
      updatedAt: new Date() // 更新时设置当前时间
    },
    $setOnInsert: { // 仅在插入新文档时生效
      createdAt: new Date()
    }
  },
  { upsert: true }
);
  • $setOnInsert仅在upsert触发插入操作时执行,保证createdAt只在创建时赋值
  • $set中的updatedAt会在更新和插入时都生效,确保新创建的文档也有正确的updatedAt值

2. 使用TypeORM的save()方法(更符合ORM设计)

如果可以接受一次额外的查询请求,save()方法会自动触发时间字段填充,同时内部处理「存在则更新,不存在则插入」的逻辑:

let subscriptionEntity = await subscriptionRepository.findOne({ customerID });

if (!subscriptionEntity) {
  subscriptionEntity = subscriptionRepository.create({
    customerID,
    subscriptionID: subscription.id
  });
} else {
  subscriptionEntity.subscriptionID = subscription.id;
}

await subscriptionRepository.save(subscriptionEntity);

这种方式代码更简洁,符合TypeORM的抽象设计,也更易维护。

内容的提问来源于stack exchange,提问作者A.D.V. - F

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 14:25:46