【问题标题】:Calculating Recurring Events in Postgresql using Javascript and PLV8使用 Javascript 和 PLV8 在 Postgresql 中计算重复事件
【发布时间】:2016-01-19 21:34:35
【问题描述】:

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

首先,这里描述一下我想要完成的任务和要求:

  • 我使用的是 Postgres 9.4
  • 我将它托管在 Amazon RDS 上,它只允许有限数量的预先审查的扩展。

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

对于存储定期约会,我的选择是生成每个实例直到预定时间点,然后每当这些实例运行时在数据库中生成新实例,或者即时计算要返回的这些实例。我最终选择了后者,因为我认为从长远来看,这种实现更简洁且更易于维护。

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

我们的想法是创建一个表Appointment,其中包含与约会的实际日期、时间和重复(如果适用)相关的数据。它至少包含以下字段:

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

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

  • 约会 ID(父代的 FK)
  • 注意事项
  • 入住时间
  • 退房时间

AppointmentData 实例只会在Appointment 实际由服务提供者启动时创建。

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

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

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

提前致谢!

【问题讨论】:

    标签: javascript sql postgresql


    【解决方案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$;
    

    上述结果是一个结果集,其中包含两个单次出现的约会记录,但也为重复生成动态的完整约会记录(只是日期)。

    【讨论】:

    • 你们有任何可用的性能基准测试吗?
    • @AbdulVajid 我没有任何官方信息,但我可以告诉你正在远离它,因为它在处理许多记录时很慢。我们实际上正在转向使用性能更高的 RRule.js 在客户端呈现事件。
    • 你好@jdixon04,我知道这是一篇旧文章,但是,你能告诉我们你为什么将渲染事件移动到客户端以及你使用了什么方法吗?
    猜你喜欢
    • 1970-01-01
    • 2016-12-14
    • 1970-01-01
    • 1970-01-01
    • 2012-09-17
    • 1970-01-01
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    相关资源
    最近更新 更多