38

我即将构建我的 node.js / express / mongoose / passport应用程序,并且我正在考虑为用户和帐户进行正确的架构设计。

会有用户从 twitter 和 facebook 以及本地帐户登录。在稍后阶段,我希望用户将 twitter 和 facebook 与我的应用程序(甚至更多外部帐户)连接起来。

对于这种情况,我想不出一个好的解决方案。以下是我正在考虑的选项:

1.拥有档案模型和账户模型。Profile 文档代表唯一的用户,而帐户提供用户名和密码(内部帐户)或来自 auth-provider(外部帐户)的身份验证数据。配置文件必须至少有一个嵌套的帐户文档。

var ExtAccountSchema = new Schema({
    type: String, // eg. twitter, facebook, native
    uid: String
});

var IntAccountSchema = new Schema({
    username: String,
    password: String
});

var ProfileSchema = new Schema({
    firstname: String,
    lastname: String,
    email: String,
    accounts: [Account] // Pushing all the accounts in there
});

我不喜欢它的是由不同的帐户数据导致的不太一致的帐户文档,以及当我的用户登录时我很难找到正确的帐户(在嵌套文档中搜索 uid 和帐户类型-.-)

2.将所有数据放在一个模型中

var ProfileSchema = new Schema({
    firstname: String,
    lastname: String,
    email: String,        
    twitter-uid: String,
    facebook-uid: String
    password: String
});

好吧,这很丑-.-找到正确的帐户数据可能更容易/更快,但维护起来并不好。

有更好的解决方案吗?有最佳实践吗?

4

2 回答 2

61

1) 在 MongoDB 中构建数据可以采用三种策略:

  • a) 嵌入文档数组
  • b) 嵌入引用数组
  • c) 展开到父文档

策略 (a) 是您描述的第一个策略,其中 Profile 文档包含一组 Account 子文档。

策略 (b) 与策略 (a) 类似,但您将使用对其他文档(通常在 Account 集合中)的引用数组,而不是嵌入实际文档。

策略 (c) 是您将其描述为“在单个模型中拥有所有数据”的策略。

2) 通常认为最佳实践是使用一组嵌入式文档,尤其是在其中的信息可能不同的情况下。如果它会让您的生活更轻松,您可以使用密钥来区分帐户的类型,如下所示:

  { 
    firstname: 'Fred',
    lastname: 'Rogers',
    email: 'fred.rogers@example.com',

    accounts: [
             { kind: 'facebook',
               uid: 'fred.rogers'
             },
             { kind: 'internal',
               username: 'frogers',
               password: '5d41402abc4b2a76b9719d911017c592'
             },
             { kind: 'twitter',
               uid: 'fredr'
             }
          ]
    }

3) MongoDB 允许您搜索嵌入的文档。因此,您将编写以下查询(JavaScript 语法):

 db.profile.find( 
        { email: 'fred.rogers@example.com', 'accounts.kind': 'facebook' }
        );

使用适当的索引,此查询将非常快。

于 2012-06-21T22:10:44.063 回答
3

我希望它可以帮助像你一样有类似需求的人。

MongoDB 中的模式设计和数据建模

  • SQL 具有固定/严格模式,而 NoSQL 具有动态/灵活模式,即不强制执行文档结构

  • MongoDB有两种数据模型:

    • 嵌入式数据建模:
      • 具有单一文档结构,被称为非规范化模型
      • 支持文档级原子操作。
      • 易于执行 CRUD
      • 用于 70% 的情况,并且对于读取操作具有高性能
      • 大小很容易达到其阈值 16 MB 并且容易出现高冗余
      • 推荐在一对一和一对多关系中
    • 参考或链接数据建模
      • 模仿 SQL 数据库的规范化表以减少数据重复和冗余。
      • reference 或 _id 用于引用另一个文档,类似于使用主键和外键在 SQL 中连接表。
      • 用于 30% 的案例
      • 在多对多关系中推荐

数据建模的观点

  • 概念数据建模:关于功能和服务的大图,其中还包括
    • ER 数据建模:数据库设计的图形化方法。
    • 架构设计
  • 逻辑数据建模:概念数据建模将使用编程语言、表格等转换为逻辑数据建模(程序)。(服务器代码)
  • 物理数据建模:将逻辑 DM 付诸实践,实际数据由用户插入。(数据库)

数据模型的类型

  • 平面、星形、分层、关系、对象关系

文档之间的模型关系

  • 通常,您应该构建您的模式,以便您的应用程序在一次读取操作中接收所有所需的信息。

使用嵌入式文档建模一对一关系

  - In referenced or normalized data model, If one document is frequetly refering some data in another document, It would create better data model to embed both documents into one.
  - If a single document seems to be large, it is better split your data into referential model, the most frequently-accessed portion of the data should go in the collection that the application loads first
  ```json
  // one person and one address
  {
     _id: "joe",
     name: "Joe Bookreader",
     address: {
              street: "123 Fake Street",
              city: "Faketon",
              state: "MA",
              zip: "12345"
              }
  }
  ```

使用嵌入式文档建模一对多关系

  • 在为其设计模型方面,它也基于与一对一相同的概念。
// one person and his multiple address
{
   "_id": "joe",
   "name": "Joe Bookreader",
   "addresses": [
               {
                  "street": "123 Fake Street",
                  "city": "Faketon",
                  "state": "MA",
                  "zip": "12345"
               },
               {
                  "street": "1 Some Other Street",
                  "city": "Boston",
                  "state": "MA",
                  "zip": "12345"
               }
            ]
}

使用参考文档建模一对多关系

  • 在某些情况下,最好使用参考模型来获得更好的性能,如下所示。
  • 为避免出版商数据重复,请使用参考资料并将出版商信息保存在与图书收藏不同的收藏中。
   {
   _id: 'some string'
   name: "O'Reilly Media",
   founded: 1980,
   location: "CA",

   books: [123456789, 234567890, ...]

   }

   {
      _id: 123456789,
      title: "MongoDB: The Definitive Guide",
      author: [ "Kristina Chodorow", "Mike Dirolf" ],
      published_date: ISODate("2010-09-24"),
      pages: 216,
      language: "English"
   }

   {
      _id: 234567890,
      title: "50 Tips and Tricks for MongoDB Developer",
      author: "Kristina Chodorow",
      published_date: ISODate("2011-05-06"),
      pages: 68,
      language: "English"
   }
  • 如果将来书籍数组有可能变得庞大,最好将出版商引用存储在书籍文档中。
  • 观察架构中的智能变化,_id信息(即出版商 ID)如何在 BOOK COLLECTION 中被称为publisher_id.
   {
   _id: "oreilly",
   name: "O'Reilly Media",
   founded: 1980,
   location: "CA"
   }

   {
      _id: 123456789,
      title: "MongoDB: The Definitive Guide",
      author: [ "Kristina Chodorow", "Mike Dirolf" ],
      published_date: ISODate("2010-09-24"),
      pages: 216,
      language: "English",

      publisher_id: "oreilly"

   }

   {
      _id: 234567890,
      title: "50 Tips and Tricks for MongoDB Developer",
      author: "Kristina Chodorow",
      published_date: ISODate("2011-05-06"),
      pages: 68,
      language: "English",

      publisher_id: "oreilly"

   }
于 2020-07-20T08:38:42.307 回答