【问题标题】:Many to many design for NoSql (RavenDB)NoSql (RavenDB) 的多对多设计
【发布时间】:2013-11-29 20:35:47
【问题描述】:

我一直在对 Nosql(尤其是 RavenDB)进行一些研究,但我仍然不确定解决以下问题的最佳方法:

我有两个简单的对象,“用户”和“事件”。一个用户可以输入许多事件,一个事件可以由许多用户输入——标准的多对多关系。我正在努力摆脱关系数据库的思维模式!

以下是我想针对数据库运行的查询/操作:

  • 获取用户未进入的所有事件
  • 获取用户输入的所有事件
  • 非常频繁地更新所有事件(剩余空间等属性)(从各种外部数据源轮询的数据)。
  • 在事件过期时移除事件

所以根据我的阅读,我有几个选择:

  1. 创建一个链接用户和事件的新对象。例如,存储 userId、eventId 的“预订”对象。

    这对我来说似乎最合乎逻辑,但感觉更像是“关系数据库”方法?

  2. 对用户对象中的事件数据进行非规范化处理,因此用户上有一个 eventId 列表。

    看起来很明智,但这不会使双向查询变得困难吗?

  3. 不要为此使用 RavenDB,而是使用关系数据库。
根据上面的详细信息,您建议处理此问题的最佳方法是什么?

非常感谢,

丹C

【问题讨论】:

    标签: nosql ravendb


    【解决方案1】:

    实际上,RavenDB 非常适合这种情况。要正确地做到这一点,请问自己模型中的主要实体是什么?其中每一个都将是 RavenDB 中的一种文档类型。

    所以在您的场景中,您将拥有EventUser。然后,Event 可以有一个User ID 列表,然后您可以轻松地对其进行索引和查询。有更多的方法可以做到这一点,实际上我 discussed this in my blog some time in the past 可能会提出一些进一步的考虑。

    唯一重要的一点可能是用于回答诸如“用户尚未输入的所有事件”之类的查询的索引,但这也很容易做到:

    public class Events_Registrations : AbstractIndexCreationTask<Event>
    {
            public Events_Registrations()
            {
                    Map = events => from e in events
                                  select new { EventId = e.Id, UserId = e.Registrations.SelectMany(x => x.UserId) });
    
            }
    }
    

    一旦你有了这样的索引,你就可以像下面这样查询来获取指定用户没有注册的所有事件:

    var events = RavenSession.Advanced.LuceneQuery<Event, Events_Registrations>()
                                    .Where("EventId:*")
                                    .AndAlso()
                                    .Not
                                    .WhereEquals("UserId", userId).ToList();
    

    然后处理过期事件等非常容易。绝对不要对 User 对象中的事件数据进行非规范化处理,这会让你的生活陷入地狱。

    但有一些数据丢失 - 例如每个事件允许多少注册。如果有太多,您可能希望将其从实际的 Event 对象中分离出来,或者恢复为您提到的 Booking 对象。我在我的《RavenDB in Action》一书中详细讨论了这一点(我知道这是无耻的插件,但在这里实际讨论的时间太长了)。

    【讨论】:

    • 感谢您的回复-仍然有点困惑!如果我要查询“用户未输入的所有事件”——我计划在主页上列出,那么最常见的查询将是——使用地图是否会由于索引没有及时更新而导致混淆?例如用户注册了一个事件,但是由于时间流逝更新该索引,主页仍然将其列为“未输入”事件?这就是为什么我认为存储在用户对象中输入的事件 ID 是明智的,因为它会更新得更快?为什么不将事件存储在用户身上,而将事件存储在用户身上?
    • 您提到的延迟可以忽略不计,可以通过在注册事件后等待非陈旧结果来轻松避免,但我不确定即使到那时我也会遇到这个麻烦。如果这是一个如此频繁使用的查询,您可能希望它缓存到某种程度。对我来说,注册是一件大事;例如,它使事件及其所有注册过期变得更加容易。
    【解决方案2】:

    不知道确切的要求和预期用途会使设计建议有点机会。也就是说,我会说选择选项 2。这是最合乎逻辑的结构。

    然后创建索引以支持您需要的其他类型的视图。您没有在问题中指定它,但您可能希望查看哪些用户输入了单个事件。这是通过索引完成的。

    “删除事件”。你真的要删除它们吗?我认为您希望能够列出所有“活动”事件。正确的?如果是这样,这也可以通过索引轻松完成。

    我想说的是,在设计您的实体并使用索引来支持您的查询时,请使用“聚合根”的思维方式。另请注意,RavenDB 索引与 RDBMS 中的索引不同。您可以在某种程度上将 RavenDB 索引视为 RDBMS“视图”。

    还知道有不同类型的 RavenDB 索引。这些类型在客户端编程接口中不是很清楚区分(我发现这是Raven的一个大缺陷,它仍然让我感到困惑)。在工作室中浏览索引是了解索引中数据的好方法。

    【讨论】:

    • 感谢您的反馈 - 我会阅读聚合根。关于删除事件 - 我确实想删除它们,因为一旦它们发生,我的网站就不再需要存储该信息。我不妨摆脱并保持数据库精简。
    猜你喜欢
    • 1970-01-01
    • 2011-12-29
    • 2015-12-27
    • 2015-07-04
    • 2019-02-24
    • 1970-01-01
    • 2012-12-26
    • 2014-09-25
    • 2018-12-07
    相关资源
    最近更新 更多