我正在使用 django、backbone.js、tastypie 和 mongodb 开发一个 web 应用程序。为了使tastypie 和django 适应mongodb,我使用django-mongodb-engine 和tastypie-nonrel。这个应用程序有一个模型 Project,它有一个任务列表。所以它看起来像这样:

   class Project(models.Model):
       user = models.ForeignKey(User)
       tasks = ListField(EmbeddedModelField('Task'), null=True, blank=True)

   class Task(models.Model):
       title = models.CharField(max_length=200)

感谢tastypie-nonrel,通过在/api/v1/project/:id:/tasks/的GET 请求以简单的方式获取项目的任务列表

现在我想用一个注释列表来扩展这个 Task 模型:
   class Task(models.Model):
       title = models.CharField(max_length=200)
       comments = ListField(EmbeddedModelField('Comment'), null=True, blank=True)

   class Comment(models.Model):
       text = models.CharField(max_length=1000)
       owner = models.ForeignKey(User)

此实现的问题在于,tastypie-nonrel 不支持其他嵌套,因此无法简单地将评论发布到/api/v1/project/:id:/task/:id:/comments/

另一种方法是向/api/v1/project/:id:/task/发出一个任务的 PUT 请求,但是如果两个用户决定同时向同一个任务添加评论,这会产生问题,如最后一个 PUT 将覆盖前一个。

最后一个选项(除了改变tastypie-nonrel)是不要在任务中嵌入Comment而只保留ForeignKey,这样请求就会转到/api/v1/Comment/。我的问题是这是否会破坏使用 MongoDB 的好处(因为它需要交叉查询)?有没有更好的方法来做到这一点?

我对堆栈的任何技术几乎没有经验,所以可能是我没有很好地关注问题。欢迎任何建议。

最佳答案

看起来你嵌套太多了。也就是说,您可以为tastypie 创建自定义方法/URL 映射,然后运行您自己的逻辑,而不是依赖“自动魔术”tastypie。如果您担心评论覆盖问题,则无论如何都需要交易。然后,您的代码应该足够健壮以处理失败事务的行为,例如重试。如果您经常锁定具有许多作者的大对象,这肯定会大大限制您的写入,但是,这也指向设计问题。

您可以稍微缓解这种情况的一种方法是写入中间源,例如任务队列或 redis,然后根据需要转储到注释中。这仅取决于您的解决方案的可靠性/耐用性。任务队列至少会处理失败事务的重试;使用 redis,您可以使用 pub/sub 做一些事情。

关于 MongoDB 的 IMO 设计,您应该考虑一些事项。

  • 避免创建过大的单体对象。虽然这是 Mongo 的一个好处,但这取决于您的使用情况。例如,如果您总是将项目作为顶级对象返回,那么随着任务和评论的增长,仅网络流量就会扼杀性能。

    想象一个非常人为的例子,其中项目特定于
    数据是10k,单独每个任务5k,单独每个评论2k,如果你有一个项目有5个任务,每个任务10个评论,你说的是10k + 5*5k + 10*2k。对于一个有很多评论的非常活跃的项目,这将是通过网络发送的繁重。您可以执行切片/投影查询来解决这个问题,但有一些限制和影响。
  • 上面的推论,根据您的用例构建您的对象。如果您不需要将事物重新组合在一起,它们可以位于不同的集合中。仅仅因为您“认为”需要将它们重新组合在一起,并不意味着在实现方面需要在同一个 fetch 中检索它们(尽管这通常是理想的)。

    即使您确实需要一个用例/屏幕中的所有内容,在某些设计中可能的另一种解决方案是使用 AJAX 并行加载内容,甚至在页面加载后通过 JavaScript 延迟加载。例如,您可以在顶部加载任务信息,然后进行异步调用以单独加载评论,类似于 Disqus 或 Livefyre 在其他站点中作为集成工作的方式。这可以在某种程度上帮助解决您的嵌套问题,因为您可以摆脱任务/项目级别,只需在每个评论/记录上存储一些 ID,以便能够在集合之间进行查询。
  • 请记住,您可能不想一次检索所有评论,如果您有很多评论,您可能会遇到一个文档大小的限制。在最近版本的 Mongo 中,这些天的大小更大,但是无论如何,拥有大量数据的单个记录通常没有意义,回到上面的第一项。

  • 我的建议是:
  • 如果您担心丢失评论,请使用事务。
  • 如果您担心由于 #1 导致的竞争写入和丢失内容,请添加一个任务队列/redis/一些持久的东西。如果没有,请忽略它。如果你失去一个评论是世界末日吗?
  • 考虑将特别的评论重组到一个单独的集合中以缓解您的美味问题。如果需要,可以延迟加载或并行加载。
  • 10-07 14:44