假设我们有两个这样的模型:
User:
_ _id
- name
- email
Company:
- _id
_ name
_ slug
现在让我们说我需要将用户连接到公司。用户可以分配一个公司。为此,我可以在用户模型中添加一个名为companyID
的新字段。但我不是把_id
场发送到前端。所有来到API的请求都只有slug
。我有两种方法可以做到这一点:
1)添加slug
以联系公司:如果我这样做,我可以从请求发送的slug并直接查询公司。
2)添加公司的_id
:如果我这样做,我需要首先使用slug查询公司,然后使用返回的_id
查询所需的数据。
我可以知道哪种方式最好吗?使用关系记录的_id
时是否有任何额外的好处?
同意第二种方法。在决定使用哪个字段作为连接键时,需要考虑几个问题(所有数据库都是如此,而不仅仅是Mongo):
由于这些原因,几乎总是建议使用仅用作关键字段的关键字段,而不存储实际信息。很多人使用社会安全号码,驾驶执照等等作为关键字段被烧毁,或者因为可能存在重复(例如,如果人们使用假号码,或者如果他们没有伪造号码,则可以复制SSN) ,或数字可以改变(例如驾驶执照)。
此外,通过这样做,您可以格式化关键字段以优化唯一生成和索引的速度。例如,如果您使用SSN,则需要针对数据库的其余部分检查SSN以确保它是唯一的。如果你有数百万条记录,这需要时间。类似于slugs,它是需要对索引进行散列和检查的文本字段。 OTOH,mongoDB本质上使用UUID作为密钥,这意味着它不必检查唯一性(该算法保证了唯一性的高统计可能性)。
最重要的是,如果你可以帮助它,有很好的理由不使用“真实”字段作为你的密钥。幸运的是,mongoDB已经为您提供了一个很好的关键字段,它满足上述所有条件,即_id字段。因此,你应该使用它。即使slug不是一个“真实”字段,你生成它的方式与_id字段完全相同,为什么还要麻烦?为什么记录必须有2个唯一标识符?
在您的情况下,第二个问题是您不会将公司的_id字段公开给用户。直观地说,似乎这应该是一个有价值的信息,不应该无缘无故地发出。但事实是,它本身没有信息价值,因为如上所述,密钥应该没有实际信息。实现安全性的地方在查询中,确保执行查询的用户有权访问她要求的记录/特定字段。隐藏密钥是一种典型的安全隐患,实际上并不能提高安全性。
隐藏主键的唯一时间是,如果您使用的是经过深思熟虑的密钥,其中包含有用的信息。例如,某人可以使用每张发票增加1的发票ID来确定您每天获得的订单数量。也可以很容易地猜到自动增量ID(如果我的发票是#5,我可以窥探发票#6吗?)。幸运的是,Mongo使用的是UUID,所以实际上没有信息漏掉(除了可能对其加密算法的定时攻击?如果你担心这一点,你需要比这篇文章更深入的安全考虑因素:-)。
从另一个角度来看:如果一个slug可靠地指向一个特定的公司和用户,那么它比仅仅使用_id更安全?
也就是说,在某些情况下,暴露二级密钥(如slu)是有帮助的,其中没有一个与安全性有关。例如,如果将来您需要迁移数据库平台并需要重新生成密钥,因为新平台无法使用旧平台;或者如果用户将手动输入标识符,那么给他们提供像slug更容易记住的内容会很有帮助。但即使在这些情况下,您也可以使用slug作为用户使用的方便标识符,但在您的数据库中,您仍应使用公司ID进行实际连接(如您的选项#2)。看看有关向用户公开_ids的优缺点的讨论:https://softwareengineering.stackexchange.com/questions/218306/why-not-expose-a-primary-key
因此,我的建议是继续向用户提供公司ID(如果你想要一个人类可读的格式,例如URL,那么就可以使用slug,尽管可以在URL中使用mongo _ids)。他们可以将其发回给您以获取用户,并且您可以(在适当的权限检查之后)进行加入并发回用户数据。如果您不想公开公司ID,那么我建议您选择#2,这基本上是相同的,除了您添加额外的查询以首先获得公司ID。恕我直言,这是浪费周期,没有真正改善安全性,但如果有其他考虑,那么它仍然是可以接受的。而且这两个选项都比使用slug作为主键更好。
第二种方法是最好的,即添加公司的_id。
使用_id是查询任何类型信息的最佳实践方法,甚至可以使用_id解决复杂查询,因为它是Mongodb创建的唯一ObjectId。填充是使用来自其他集合的文档自动替换文档中的指定路径的过程。我们可以填充单个文档,多个文档,普通对象,多个普通对象或从查询返回的所有对象。