【问题标题】:How to enforce foreign keys in NoSql databases (MongoDB)?如何在 NoSql 数据库(MongoDB)中强制执行外键?
【发布时间】:2018-02-06 15:00:54
【问题描述】:

假设我有一个文档集合,例如:

{ "_id" : 0 , "owner":0 "name":"Doc1"},{ "_id" : 1 , "owner":1, "name":"Doc1"}, etc

另一方面,所有者被表示为一个单独的集合:

{ "_id" : 0 , "username":"John"}, { "_id" : 1 , "username":"Sam"}

如何确保在我插入文档时它以正确的方式引用用户。在老式 RDBMS 中,这可以使用外键轻松完成。

我知道我可以从我的业务代码中检查插入的正确性,但是如果攻击者篡改了我对服务器的请求并将 "owner" : 100 设置为 100 并且 Mongo 不会返回任何异常。

我想知道在真实应用程序中应该如何处理这种情况。

提前谢谢你!

【问题讨论】:

标签: mongodb nosql


【解决方案1】:

MongoDB 没有外键(您可能已经注意到了)。因此,基本上答案是:“不要让用户篡改请求。只让应用程序插入符合您的参照完整性规则的数据。”

MongoDB 在很多方面都很棒...但是如果您发现需要外键,那么它可能不是解决问题的正确方法。

【讨论】:

    【解决方案2】:

    为了回答您的具体问题 - 虽然 MongoDB 鼓励在客户端处理外键关系,但它们也提供了“数据库引用”的想法 - 请参阅 this help page

    也就是说,我不建议使用 DBRef。要么让您的客户端代码管理关联,要么(更好)从一开始就将文档链接在一起。您可能需要考虑将所有者的“文档”嵌入到所有者对象本身中。组装您的文档以匹配您的使用模式,MongoDB 将会大放异彩。

    【讨论】:

      【解决方案3】:

      这是一对一的关系。最好将一个文档嵌入到另一个文档中,而不是维护单独的集合。查看here 了解如何在 mongodb 中对其进行建模及其优势。

      尽管在文档中没有明确提到,嵌入给你的效果与外键约束相同。只是想把这个想法说清楚。当你有两个这样的集合时:

      C1:

      { "_id" : 0 , "owner":0 "name":"Doc1"},{ "_id" : 1 , "owner":1, "name":"Doc1"}, etc
      

      C2:

      { "_id" : 0 , "username":"John"}, { "_id" : 1 , "username":"Sam"}
      

      如果您要在 C2._id 上声明外键约束以引用 C1._id(假设 MongoDB 允许),这意味着您不能将文档插入到 C2 中,其中 C2._id 不存在C1。将此与嵌入文档进行比较:

      {
          "_id" : 0 , 
          "owner" : 0,
          "name" : "Doc1",
          "owner_details" : {
              "username" : "John"
          }
      }
      

      现在 owner_details 字段表示来自 C2 集合的数据,其余字段表示来自 C1 的数据。您不能将 owner_details 字段添加到不存在的文档中。你基本上达到了同样的效果。

      【讨论】:

      • 让我们澄清一下。 C1 是一个文档表,每个文档都有一个所有者。 C2 是所有者表。它们之间的关系是多对一的,C1(多)到C2(一)。
      • @StanislavKarakhanov,我认为这是一对一的关系!这个博客为我在 MongoDB 中建模一对多关系提供了很好的服务mongodb.com/blog/post/…
      【解决方案4】:

      如果有人真的想在项目/WebApp 中强制执行外键。那么你应该使用 MixSQL 方法,即 SQL + NoSQL

      我希望没有那么多引用的大容量数据可以存储在 NoSQL 数据库存储中。 Like :Hotels 或 Places 类型的数据。

      但如果有一些严肃的事情,如 OAuth 模块表、TokenStore 和 UserDetails 和 UserRole(映射表)等......那么你可以使用 SQL。

      【讨论】:

        【解决方案5】:

        我还建议如果用户名是唯一的,则将它们用作 _id。您将保存在索引上。在存储的文档中,在创建文档时将应用程序中'owner'的值设置为'username'的值,不要让任何其他代码更新它。

        如果需要更改所有者,则提供适当的 API 并实施业务规则。

        不需要外键。

        【讨论】:

        • 除了,如果你这样做,如果用户想改变他的用户名,你必须删除现有的记录,然后用改变的用户名插入一个新的记录。
        猜你喜欢
        • 2011-02-06
        • 2012-02-23
        • 1970-01-01
        • 2011-03-02
        相关资源
        最近更新 更多