我如何将 Postgres DateRange 类型与 TypeOrm 一起使用

Posted

技术标签:

【中文标题】我如何将 Postgres DateRange 类型与 TypeOrm 一起使用【英文标题】:how can i use the Postgres DateRange type with TypeOrm 【发布时间】:2020-02-23 13:46:09 【问题描述】:

我尝试使用 Nest.js 框架实现基本的酒店预订系统。 我使用 TypeOrm 和 Postgres 数据库。

我有一个预订实体(酒店房间预订) 并且 Booking 应该有一个 DateRange(从入住到退房日期)。

我找不到如何将 DateRange 对象类型与 TypeOrm 一起使用。

@Entity( name: 'booking' )
export class Booking
    @PrimaryGeneratedColumn()
    id: number;

    @Column( 
        type: 'daterange', // I expect to find DateRange type here
    )
    date_range: any;

有什么想法吗?

【问题讨论】:

【参考方案1】:

daterange 类型读取为字符串,写入为字符串。您需要使用/解析postgres range type 的正确字符串表示。

为了使解析更容易,您可以在表上添加一个约束以仅允许包含范围。将此装饰器添加到类中:

@Check('booking_range_valid', 'NOT isempty(range) AND lower_inc(range) AND NOT upper_inc(range)')

您需要编写自己的转换器,将 Postgres 日期范围值转换为区间表示。如果你使用 Luxon Interval,你可以使用:

// Just a small helper to make transformers work with find operators
export const fixTransformer = (transformer: ValueTransformer) => (
  to(value: unknown) 
    if (value instanceof FindOperator) 
      return new FindOperator(value.type , transformer.to(value.value),
          value.useParameter, value.multipleParameters);
     else 
      // eslint-disable-next-line @typescript-eslint/no-unsafe-return
      return transformer.to(value);
    
  ,
  from(value: unknown) 
    // eslint-disable-next-line @typescript-eslint/no-unsafe-return
    return transformer.from(value);
  
)
import DateTime, Interval from 'luxon';

// Postgres daterange string representation. [ means that start day is included, ) that end day is not.
const pattern = /\[(\d4-\d2-\d2),\s*(\d4-\d2-\d2)\)/;
export const  dateRangeIntervalTransformer = fixTransformer(
    to(value: Interval | undefined): string | undefined 
        if (value !== undefined && !value.isValid) 
            throw new Error(`Got an invalid daterange, reason was $value.invalidReason || '?'`);
        
        if (value) 
            if (!value.start.startOf('day').equals(value.start)) 
                throw new Error('Start of daterange needs to be the start of the day.');
            
            if (!value.end.startOf('day').equals(value.end)) 
                throw new Error('End of daterange needs to be the start of the day.');
            
            return '[' + value.start.toISODate() + ',' + value.end.toISODate() + ')';
         else 
            return undefined;
        
    ,
    from(value: string | undefined): Interval | undefined 
        if (value) 
            const match = pattern.exec(value);
            if (!match) 
                throw new Error(`Date range does not match pattern $pattern.source: $value`);
            
            return Interval.fromDateTimes(
                DateTime.fromISO(match[1]).startOf('day'),
                DateTime.fromISO(match[2]).startOf('day'));
         else 
            return undefined;
        
    
);

然后在你的实体中使用它:

@Entity( name: 'booking' )
@Check('booking_range_valid', 'NOT isempty(range) AND lower_inc(range) AND NOT upper_inc(range)')
export class Booking
    @PrimaryGeneratedColumn()
    id: number;

    @Column( 
        type: 'daterange',
        nullable: false, // < If you allow null, change the converter to handle null values
        transformer: dateRangeIntervalTransformer
    )
    date_range = Interval.after(DateTime.local().startOf('day'), days: 1); // < default value

【讨论】:

以上是关于我如何将 Postgres DateRange 类型与 TypeOrm 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 jOOQ gradle 插件将 postgres 中的 bigint[] 字段转换为类字段

react-daterange-picker 并排显示上个月

Rails 中无限时间范围的问题

过滤数据的功能

错误:意外值“node_modules/ngx-mat-daterange-picker/ngx-mat-daterange-picker.d.ts 中的 NgxMatDrpModule”

Node/Express 项目:创建“加入类/模型”并将其同步到 Postgres 数据库