我应该如何在 MongoDB 中实现这个模式?

Posted

技术标签:

【中文标题】我应该如何在 MongoDB 中实现这个模式?【英文标题】:How should I implement this schema in MongoDB? 【发布时间】:2011-06-07 10:41:51 【问题描述】:

我正在尝试编写一个跟踪脚本,但无法弄清楚数据库应该如何工作。

mysql 中,我会创建一个类似于

的表
User:
   username_name: string

Campaign:
   title: string
   description: string
   link: string

UserCampaign:
   user_id: integer
   camp_id: integer

Click:
   os: text
   referer: text
   camp_id: integer
   user_id: integer

我需要能够:

查看每次点击的信息,例如 IP、Referer、操作系统等 查看来自 X IP、X Referer、X OS 的点击频率 将每次点击与用户和广告系列相关联

如果我按照以下方式做某事

User 
     Campaigns: [
         
           Clicks: []
         
     ]

我遇到了两个问题:

它为每个用户创建一个新的活动对象,这是一个问题,因为如果我需要更新我的活动,我需要为每个用户更新对象 我希望 Clicks 数组包含大量数据,我觉得将它作为 User 对象的一部分会使查询变得非常慢

【问题讨论】:

【参考方案1】:

好的,我认为您需要将其分解为基本的“品种”。

你有两个“实体”风格的对象:

User Campaign

你有一个“映射”样式的对象:

UserCampaign

您有一个“事务”样式的对象:

Click

第 1 步:实体

让我们从简单的开始:User & Campaign。它们确实是两个独立的对象,没有一个真正依赖于另一个的存在。两者之间也没有隐含的层次结构:用户不属于营销活动,营销活动也不属于用户。

当您拥有两个这样的***对象时,它们通常会获得自己的收藏。所以你需要一个Users 集合和一个Camapaigns 集合。

第 2 步:映射

UserCampaign 当前用于表示 N 到 M 映射。现在,一般来说,当你有一个 N 到 1 的映射时,你可以把 N 放在 1 里面。但是,对于 N 到 M 的映射,你通常必须“选择一边”。

理论上,您可以执行以下操作之一:

    在每个User 中加入Campaign IDs 列表 在每个Campaign 中加入Users IDs 列表

就个人而言,我会做#1。您可能有更多的用户进行活动,并且您可能希望将数组放在更短的地方。

第 3 步:事务性

Clicks 真的是完全不同的野兽。在对象方面,您可以考虑以下内容:Clicks“属于”UserClicks“属于”Campaign。因此,理论上,您可以将点击存储为这些对象中的任何一个的一部分。很容易认为点击属于 用户或广告系列。

但如果你真的深入挖掘,上面的简化确实是有缺陷的。在您的系统中,Clicks 确实是一个中心对象。事实上,您甚至可以说用户和广告系列实际上只是与点击“关联”。

查看您提出的问题/疑问。所有这些问题实际上都围绕着点击。 用户和广告活动不是您数据中的中心对象,点击次数才是。

此外,点击量将成为您系统中最丰富的数据。您将获得比其他任何东西更多的点击次数。

这是为此类数据设计架构时遇到的最大问题。有时,当它们不是最重要的事情时,您需要推开“父”对象。想象一下构建一个简单的电子商务系统。很明显,orders 将“属于”users,但orders 对系统来说是如此重要,以至于它将成为一个“***”对象。

总结

您可能需要三个集合:

    用户 -> 有活动列表._id 广告系列 点击次数 -> 包含 user._id、campaign._id

这应该可以满足您的所有查询需求:

查看每次点击的信息,例如 IP、Referer、操作系统等

db.clicks.find()

查看来自 X IP、X Referer、X OS 的点击频率

db.clicks.group() 或运行Map-Reduce。

将每次点击与用户和广告系列相关联

db.clicks.find(user_id : blah) 也可以将点击 ID 推送到用户和活动中(如果有意义的话)。

请注意,如果您的点击次数非常多,那么您确实必须分析您最常运行的查询。您无法为每个字段编制索引,因此您通常需要运行 Map-Reduce 来“汇总”这些查询的数据。

【讨论】:

感谢您的精彩回答!它确实帮助我理解了在设计架构时应该考虑什么 这是一个很好的解释。您应该将其移至博客:)。话虽如此,如果您需要每隔几秒钟使用上述发现读取数据,那么这在 MongoDB 中的性能如何?在这种情况下,Mongo 是最好的解决方案,还是其他数据库会更好地处理它? 视情况而定,您是阅读全部三个合集还是仅阅读其中的部分?您正在执行哪些查询? 我只想说 3 年后,我仍然不时地回到这个答案,以使这些概念在我脑海中保持新鲜。再次感谢您的精彩回答!【参考方案2】:

我在这里看到的主要问题是您正在尝试将关系数据库概念应用到面向文档的数据库中。两者之间的主要区别在于您不必担心 NOSQL 数据库中的模式或结构,而是担心集合和文档。

理解在 NOSQL 的许多实现中没有像 SQL 中那样的连接概念是非常重要/必要的。这意味着,如果您将数据分散到集合中,那么您以后需要做很多工作来粘合它。此外,通过在 SQL db 的规范化中跨集合传播数据也没有其他好处。您需要考虑哪些数据是您的文档的一部分以及它适用于哪个集合,而不必担心 NOSQL db 下的实现。因此,对于您的问题,答案可能是......并且会支持您所要求的一切......

db.trackclicks==> 集合 跟踪点击 = 操作系统:XP, 用户 : John Doe, 广告系列:title:test,desc:test,link:url, 推荐人:google.com

【讨论】:

【参考方案3】:

    如果某公司发生了一些变化,mongodb更新大量文档是没有问题的。

    是否有嵌套集合实际上取决于集合中有多少数据。 在您的情况下,如果您知道“点击”集合将包含“大量数据”,您需要创建一个单独的集合。因为对于“点击”肯定需要分页、过滤等,而用户将是“轻”集合。

所以我建议如下:

User 
     Campaigns: []


Clicks 
 user_id,
 camp_id

【讨论】:

以上是关于我应该如何在 MongoDB 中实现这个模式?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 node.js 和 mongodb 中实现 geoip

如何在 Tkinter 中实现 MVC 模式

如何在C ++中实现公式模式?

如何在 SQL 中实现聚合? (这与 GroupBy 无关)

如何在qt中实现代码完成[关闭]

如何在与MongoDB集成的Spring boot Elastic Search中实现对非结构化数据的搜索