使用Javascript和PLV8计算Postgresql中的重复事件

我已经阅读了大约20篇关于模拟重复事件数据的选项的不同帖子,并最终确定了一个实现,但仍需要在最终细节方面提供一些帮助.

首先,这里是我要完成的内容和要求的描述:

>我正在使用Postgres 9.4
>我在Amazon RDS上托管它,它只允许有限数量的预先审查扩展.

显然,我的应用程序的很大一部分是基于调度.具体来说,我系统的这个组件允许企业的客户安排家庭服务的约会.这些约会可以是单次出现,也可以是经常出现.虽然这些约会是典型的,因为它们具有开始/结束时间等,但每个约会也具有与其相关联的数据.该数据可能包括登记入住时间,退房时间,备注等.

为了存储定期约会,我的选择是生成每个实例直到预定的时间点,然后在这些实例运行时在数据库中生成新实例或者在运行中计算这些实例以便返回.我最终选择了后者,因为我认为从长远来看,实施更清洁,更容易维护.

话虽如此,使用this post的见解,我选择将我的重复模式存储为RRULE字符串,并使用PGV8和rrule.js库进行任何计算.该帖提到了物化视图的使用(阅读它们,但从未使用过),但我不确定它是否适用于我的情况,因为我必须在任何时候更改或创建重复约会时重新生成这些视图.我似乎还需要每个业务的物化视图,并且不确定这可能会影响存储/性能,因为可能有1000个业务.如果您对此有任何见解,请告诉我.

这个想法是有一个表,约会,其中包含与约会的实际日期,时间和重复(如果适用)有关的数据.它至少包含以下字段:

>开始日期
>结束日期
>重复模式(RRULE)
>例外
>服务ID

然后,第二个表,AppointmentData,将存储有关约会本身的任何元数据.例如,它可能包含以下字段:

>预约ID(FK给父母)
>笔记
>入住时间
>退房时间
>等

只有在服务提供者实际启动约会时才会创建AppointmentData实例.

通常,我只需要能够在任何给定时间检索31天或更少的约会(除了检索单个实例).也就是说,我的想法是能够将开始和结束日期传递给数据库,该数据库将查找属于该范围的所有单次约会.此外,对于包含重复发生模式的任何记录,我将使用我的PLV8函数返回该范围内的日期列表. rrule.js库有一个函数可以返回重复模式的所有日期(rule.between(new Date(2012,7,1),new Date(2012,8,1))).

这是我磕磕绊绊的地方.现在我在DB中有一个可以动态计算重复日期的函数,我有点不清楚如何将这些与单个事件“融合”并将它们作为单个结果集返回.请注意,对于每个重复实例,我还需要返回Appointment表中的所有列,例如serviceID.

如果有任何不清楚的地方,请告诉我.

提前致谢!

最佳答案 对于那些希望做类似事情的人来说,这就是我想出的:

首先,使用rrule.js从iCAL RRule字符串生成重复的函数:

CREATE OR REPLACE FUNCTION public.generate_recurrences(
  recurrence_pattern CHARACTER VARYING,
  start_date date,
  end_date date)
 RETURNS SETOF TEXT
 LANGUAGE plv8
 IMMUTABLE STRICT
AS $function$

  // parse the RRULE string
  var rule = RRule.fromString(recurrence_pattern);

  // return all occurrences between start date and end date
  var recurrences = rule.between(start_date, end_date);

  for(var i = 0; i < recurrences.length; i++) {
     plv8.return_next(new Date(recurrences[i]).toISOString());
  }

$function$

最后是抓住任意非约会约会实例的函数,并将它们与上述函数生成的重复实例融合:

CREATE OR REPLACE FUNCTION recurring_events_for(
   for_business_id INTEGER,
   range_start DATE,
   range_end   DATE
)
   RETURNS SETOF appointment
   LANGUAGE plpgsql STABLE
   AS $BODY$
DECLARE
   appointment appointment;
   recurrence  TIMESTAMPTZ;
   appointment_length INTERVAL;
BEGIN
   FOR appointment IN
       SELECT *
         FROM appointment
        WHERE business_id = for_business_id
          AND (
                  recurrence_pattern IS NOT NULL
              OR  (
                     recurrence_pattern IS NULL
                 AND scheduled_start_time BETWEEN range_start AND range_end
              )
          )
    LOOP
       IF appointment.recurrence_pattern IS NULL THEN
         RETURN NEXT appointment;
         CONTINUE;
       END IF;

       appointment_length := appointment.scheduled_end_time - appointment.scheduled_start_time;

       FOR recurrence IN
           SELECT *
             FROM generate_recurrences(
                      appointment.recurrence_pattern,
                      range_start,
                      range_end
             )
       LOOP
           EXIT WHEN recurrence::date > range_end::date;
           CONTINUE WHEN recurrence::date < range_start::date AND recurrence::date > range_end::date;
           appointment.scheduled_start_time := recurrence;
           appointment.scheduled_end_time := recurrence + appointment_length;
           RETURN NEXT appointment;
       END LOOP;
   END LOOP;
   RETURN;
END;
$BODY$;

上述结果是一个结果集,其中包含单个事件的约会记录,但也可以动态生成完整的约会记录(这些记录只是日期).

点赞