2015-10-21 96 views
2

我已经阅读了大约20篇有关建模周期性事件数据的选项的帖子,并最终在实现中解决了这个问题,但在最终的细节中仍然需要一些帮助。使用Javascript和PLV8计算Postgresql中的周期性事件

首先,这里的就是我试图完成一个描述和要求:

  • 我使用的是Postgres 9.4
  • 我收留了它在Amazon RDS,只允许有限预审分机的数量。

显然,我的应用程序的很大一部分是基于调度。具体来说,我的系统的这个组件允许企业的客户安排家庭服务的约会。这些约会可以是单次发生或重复发生。虽然这些约会是典型的,因为他们有一个开始/结束时间等,每个约会也有相关的数据。这些数据可能包括入住时间,退房时间,笔记等等。

对于存储重复约会,我的选项是要生成每个实例直到预定的时间点,然后生成新的无论何时运行这些实例,或者计算那些即时返回的实例。我最终选择了后者,因为我认为从长远来看,实施将变得更简洁,更容易维护。

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

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

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

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

  • 预约ID(FK于母公司)
  • 入住时间
  • 退房时间
  • 等。

只有当Appointment实际上由服务提供商启动时才会创建AppointmentData实例。

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

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

如果有什么不清楚的地方,请告诉我。

在此先感谢!

回答

1

对于那些正在寻找做同样的事情,这是我想出来的:

首先,使用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$; 

的RESU以上是包含单个事件的预约记录的结果集,还可以为重复(仅为日期)即时生成完整的预约记录。