设计Google日历等日历系统

时间:2008-08-15 19:10:16

标签: algorithm calendar

我必须创建类似于Google日历的内容,因此我创建了一个包含用户所有事件的事件表。

困难的部分是处理重新发生的事件,事件表中的行有一个event_type字段,告诉你它是什么类型的事件,因为事件只能是一个日期,或者是一个重新发生的事件每隔x天。

主要的设计挑战是处理重新发生的事件。

当用户使用月份视图查看日历时,如何显示给定月份的所有事件?查询会很棘手,所以我认为创建另一个表并为每个事件创建一个行会更容易,包括重新发生的事件。

你们有什么想法?

16 个答案:

答案 0 :(得分:5)

试图存储每个事件的每个实例似乎真的有问题,而且,不可能。如果有人创建了“每个星期四,永远”发生的事件,那么您显然无法存储所有未来事件。

可以尝试按需生成未来事件,并仅在必要时填充未来事件以显示它们或发送有关它们的通知。但是,如果您打算构建“按需”生成代码,为什么不一直使用它?而不是从事件表中拉出,然后必须使用按需事件生成来填充尚未添加到表中的任何新事件,而是仅使用按需事件生成。最终结果是一样的。使用此方案,您只需存储开始和结束日期以及事件频率。

我没有看到任何可以避免按需事件生成的方法,因此我无法在事件表中看到该实用程序。如果你想要它来缓存,那么我认为你采取了错误的方法。首先,它是一个糟糕的缓存,因为无论如何你无法避免按需事件生成。其次,无论如何你应该在更高的层次上缓存。如果要缓存,则缓存生成的页面,而不是事件。

至于提高您的轮询效率,如果您每15分钟轮询一次,而您的数据库和/或服务器无法处理负载,那么您已经注定失败了。如果用户无法处理太多的事情,那么你的数据库就无法处理用户,很多更频繁的轮询而不会出汗。

答案 1 :(得分:4)

我正在解决这个问题,并且我已经完全间隔iCalendarrfc 2445)直到阅读这个帖子,所以我不知道这会有多好或不会与。无论如何,到目前为止我提出的设计看起来像这样:

  • 您不可能存储定期事件的所有实例,至少在它们发生之前不存储,所以我只有一个表将事件的第一个实例存储为实际日期,可选的到期和可空的repeat_unit和repeat_increment字段来描述重复。对于单个实例,repition字段为null,否则单位将为'day','week','month','year',而increment只是添加到下一次出现的开始日期的单位的倍数。
  • 如果您需要与模型中的其他实体建立关系,那么仅存储过去的事件似乎是有利的,即使这样,也不必在每种情况下都有一个明确的“事件实例”表。如果其他实体已经有日期/时间“实例”数据,则事件的外键(或多对多连接表)很可能就足够了。
  • 要做“更改此实例”/“更改所有未来实例”,我计划只复制事件并使过时的事件过期。因此,要更改单个实例,您将在最后一次出现时使旧的实例到期,为新的,唯一的出现创建一个副本,并且不进行任何重复,并在下一次出现时将原始文件的另一个副本重复到未来。更改所有未来的实例是类似的,您只需使原始文件到期,并使用更改和回复详细信息制作新副本。

到目前为止,我在设计中遇到的两个问题是:

  1. 这使得MWF类型的事件很难代表。这是可能的,但强制用户创建三个单独的事件,每个事件在M,W,F上单独重复,并且他们想要进行的任何更改也必须分别在每个事件上完成。这些事件在我的应用程序中并不是特别有用,但它确实给模型留下了瑕疵,使其不像我想要的那样普遍。
  2. 通过复制事件进行更改,您可以打破它们之间的关联,这在某些情况下可能很有用(或者,它可能偶尔会出现问题。)事件表理论上可以包含“copied_from”id字段到追踪事件的起源,但我还没有充分考虑过这样的事情有多么有用。首先,父/子层次关系是从SQL查询的痛苦,因此需要相当大的好处来超过查询该数据的成本。我想你可以改用nested-set
  3. 最后我认为可能使用直接SQL计算给定时间跨度的事件,但我还没有弄清楚确切的细节,我认为查询通常最终太麻烦而不值得。但是,为了参数,您可以使用以下表达式计算事件的给定月份和年份之间的月份差异:

    (:month + (:year * 12)) - (MONTH(occursOn) + (YEAR(occursOn) * 12))
    

    在最后一个示例的基础上,您可以使用MOD来确定月份差异是否是正确的倍数:

    MOD(:month + (:year * 12)) - (MONTH(occursOn) + (YEAR(occursOn) * 12), repeatIncrement) = 0
    

    无论如何这不是完美的(它不会忽略过期事件,不会考虑事件的开始/结束时间等),所以它只是作为一个激励性的例子。一般来说,虽然我认为大多数查询最终会变得太复杂。您可能最好不要查询在给定范围内发生的事件,或者在范围之前不会过期,并且在代码而不是SQL中计算实例本身。如果确实希望数据库进行处理,那么存储过程可能会让您的生活更轻松。

答案 2 :(得分:4)

如前所述, 不会重新发明轮子 ,只需加强它。

Checkout VCalendar,它是开源的,有PHP,ASP和ASP.Net(C#)!

另外,您可以查看Day Pilot,其中提供了使用Asp.Net 2.0编写的日历。它们提供了一个你可以查看的精简版,如果它适用于你,你可以购买许可证。

更新(9/30/09):

除非轮子坏了!此外,如果你愿意,你可以放一个闪亮的新涂料(即:制作更好的用户界面)。但至少试图找到一些基础来建立起来,因为日历系统可能很棘手(使用重复事件),并且已经完成了数千次。

答案 3 :(得分:3)

我会说从标准开始。如果您将它用作模型,那么您将能够完成谷歌日历,Outlook,Mac(程序)的所有操作,并实现与它们的即时集成。

从那里开始,你的ajax和javascript的时间到了,因为没有大量的ajax和javascript,你不能拥有一个带有拖放和多个日历的华丽web ui。

答案 4 :(得分:2)

您应该有一个开始日期,结束日期和到期日期。单日活动将具有相同的开始日期和结束日期,并允许您进行部分日活动。至于重新发生的事件,那么开始和结束日期将是同一天,但有不同的时间,那么你有一个枚举或表格,指定重复频率(每日,每周,每月等)。

这允许您每天说“此事件每天都出现”,每周“此事件出现在每周的第二天”每周,“此事件出现在每月的第5天”,每月一次,“这个事件出现在每年的第215天“,只要日期小于失效日期,就会每年一次。

答案 5 :(得分:2)

的Darren,

这就是我实际上设计我的事件表的方式,但是在考虑它时,说我有100K用户创建了事件,这个表会受到很大的打击,特别是如果我要发送电子邮件来提醒他们的活动的人(活动也可以在一天的特定时间!),所以我可能每15分钟轮询一次这个桌子!

这就是为什么我想创建另一个扩展所有事件/重新发生事件的表,这样我就可以简单地访问该表并获得用户几个月的事件视图,而无需执行任何复杂的查询和业务逻辑,而且它也会使投票更加有效。

问题是,这个次要表应该是第二天还是第二个月?什么更有意义?由于用户可以查看的最大值是月视图,因此我倾向于使用一个表格来写出给定月份的所有事件。

(当然,我必须为用户可能对原始事件表进行的任何编辑维护此辅助表。)

ChanChan,

我实际上已经设计了相同类型的功能,但我只是指我将如何存储事件,特别是如何处理重新发生的事件。

答案 6 :(得分:2)

蛮力但仍然合理的方法是在单个事件表中为周期性事件的每个实例创建一个新行,所有这些都指向而不是系列之前的事件但是系列中的第一个事件。这简化了选择和/或删除特定系列中的所有元素的过程,因为您可以根据父ID进行选择。它还允许用户从系列中删除单个项目而不影响其余项目。

此查询将为您提供从元素3开始的系列:

SELECT * FROM events WHERE id = 3 OR parentid = 3

要获取本月的所有项目,假设您的活动表中有开始日期和结束日期,您所要做的就是:

SELECT * FROM events WHERE startdate >= '2008-08-01' AND enddate <= '2008-08-31'

以编程方式处理系列的创建/修改并不是很困难,但它实际上取决于您要提供的功能集以及您认为它将如何使用。如果你想区分系列和事件,你可以在你的事件中有一个单独的系列表和一个可以为null的series_id,这样你就可以自由地处理各个事件,同时仍然保持对你的系列的控制。

答案 7 :(得分:1)

根据过去的经验,我会为每个发生的事件创建一个新记录,然后有一个引用上一个事件的列,以便您可以跟踪系列中的所有事件。

这有两个好处:

  1. 没有复杂的例程来计算下一个事件日期
  2. 可以编辑个别事件而不影响其余部分
  3. 希望这能为你提供一些思考的食物:)

答案 8 :(得分:1)

我必须同意@ChanChan阅读关于如何存储这些东西的规范。没有简单的方法来处理重复,特别是那些有异常的方法。我已经构建并重建并重建了一个数据库来处理这个问题,并且我一直回到ical。

根据用例,创建从属表并不是一个坏主意。用于精确计算出现次数的算法。 。 。嗯,发生。 。 。确实很复杂。没有远离它的运行,但值得考虑缓存结果。

答案 9 :(得分:1)

@GateKiller

我没想过你编辑个别事件的情况。在这种情况下,您可以单独存储事件。

但是,当您这样做时,您将来会在多大程度上存储活动?你选择任意日期吗?您是否在用户第一次浏览未来几个月/几年时自动生成新事件?

此外,您如何处理用户想要编辑整个系列的情况。 “我们每周二早上10:30开会,但我们将于周三8点开始开会”

答案 10 :(得分:0)

  

undefined 写道:

     

......这张桌子会很漂亮   很难,特别是如果我要成为   发送电子邮件以提醒人们   他们的活动(活动可以是   当天的特定时间也是!),所以我可能每隔15分钟轮询这张桌子!

为通知创建一个表。仅轮询它。

更新事件(重复或其他)时更新通知表。

编辑:数据库视图可能不违反正常形式,如果这是一个问题。但是,您可能希望跟踪发送的通知以及尚未发送的通知。

答案 11 :(得分:0)

德里克公园, 我将在表中创建事件的每个实例,并且每个月都会重新生成此表(因此任何设置为“永远”重新发送的事件将使用Windows服务提前一个月重新生成,或者可能在sql server级别)。 轮询不仅每15分钟进行一次,这可能仅适用于与电子邮件通知相关的民意调查。当有人想要查看他们一个月的活动时,我将不得不对他们所有的事件进行fetech,并重新发生事件并找出要显示的事件(因为重新发生的事件可能是在6个月前创建的,但与之相关用户正在查看的一个月。)

Zack,我并不太关心拥有一个完全规范化的数据库,我正在考虑创建一个辅助表这一事实已经打破了其中一条规则。我的核心数据库表遵循“规则”,但我不介意创建辅助表/列,因为它有利于提高性能。

答案 12 :(得分:0)

我想我理解你的第二段意味着你正在考虑第二个事件表,每次事件发生时都有一行。我会避免这种情况。

重新发生的事件应该有一个开始日期和一个停止日期(对于每隔X天“永远”持续的事件可能是空的)你必须决定你想要允许的频率类型 - 每个X天,每个月的第N天,每个工作日等等。

我可能会倾向于两个表 - 一个是一次性事件,另一个是反复发生的事件。你必须分别查询和显示这两个。

如果我要解决这个问题(我会尽可能地努力避免重新发明这个轮子)我会寻找开源库,或者至少是使用Calendars的开源项目可以看看。有人推荐吗?

答案 13 :(得分:0)

Ra-Ajax Calendar starter-kit提供了处理RenderDate事件的示例,该事件可以修改具体的日期。虽然“反复发生的事件”更像是一种算法问题,但我怀疑很少有日历会对你有所帮助......

答案 14 :(得分:0)

如果有人在做Ruby,那么有一个很棒的库Runt可以做这种事情。值得一试。 http://runt.rubyforge.org/

答案 15 :(得分:0)

  

这就是我实际上设计我的事件表的方式,但是在考虑它时,说我有100K用户创建了事件,这个表会受到很大的打击,特别是如果我要发送电子邮件来提醒他们的活动的人(活动也可以在一天的特定时间!),所以我可能每15分钟轮询一次这个桌子!

数据库执行处理数据集的异常作业,所以我不会太担心。你应该做的是使用它作为主表,然后当事件过期然后将它们移动到另一个表(如存档)。

您要尝试的下一件事是尽可能少地查询数据库,因此将信息移动到缓存层(如velocity)并将数据保存到数据库中。

然后,您可以跨多个数据库对信息进行分区以进行扩展。即服务器1上存在用户1-10000个日历,服务器2上存在10001-20000等

这就是我如何扩展这样的解决方案,但我仍然认为我提出的原始解决方案是要走的路,这就是你如何扩展它成为问题。