PostgreSQL 模式结构

PostgreSQL schema structuring

这个问题可能太宽泛了,但我正在构建一个博客应用程序 (Express/React/Postgres),而且我是后端领域的新手。

我有 usersposts 的 table,如果我想跟踪用户反应(点赞、微笑、大笑等),您可以在帖子中这样做吗table 或创建一个新的 reactions table,fkey 引用 users.id 和 posts.id?

我问是因为我做了后者,然后意识到帖子可以有多个用户和反应,所以我不确定这样做的正确/最佳方法,即使使用 JOIN。

如果是 JSON,这就是我的想法,但我不知道如何将其转换为 SQL tables 和 knex 查询。

[
  {
    "posts": [
      {
        "post_id": "1234567890ABCDEF",
        "content": "foo bar",
        "created_at": "01/18/2020",
        "reactions": {
          "users": [
            {
              "user_id": "D237NH23DGU6",
              "reaction": "thumbsup"
            },
            {
              "user_id": "DVSY8N8VSDYN6",
              "reaction": "angry"
            },
            {
              "user_id": "3D2J892D3J8933",
              "reaction": "laughing"
            },
            {
              "user_id": "23987DNHIUO8DD",
              "reaction": "thumbsup"
            }
          ]
        }
      },
      {
        "post_id": "99FJ48FJ4F8JHSH",
        "content":  "bar foo",
        "created_at":  "01/18/2020",
        "reactions": {
          "users": [
            {
              "user_id": "DSDF879344FH97",
              "reaction": "sad"
            },
            {
              "user_id": "8S7DF62HF87H34F",
              "reaction": "peace"
            },
            {
              "user_id": "37HHF783H4K47834",
              "reaction": "laughing"
            }
          ]
        }
      },
      {
        "post_id": "1234567890ABCDEF",
        "content":  "foo foo bar bar",
        "created_at":  "01/18/2020",
        "reactions": {
          "users": [
            {
              "user_id": "SDFWEF934949J4",
              "reaction": "thumbsup"
            },
            {
              "user_id": "R9TVU8TRVU89",
              "reaction": "angry"
            },
            {
              "user_id": "IJFG0F8GJ9",
              "reaction": "laughing"
            },
            {
              "user_id": "3F4V3NHVF43N8",
              "reaction": "thumbsup"
            },
            {
              "user_id": "VXDRTHVHM6D7",
              "reaction": "angry"
            },
            {
              "user_id": "A9SFDHSFDH78",
              "reaction": "laughing"
            },
            {
              "user_id": "DFSM8H87M9N",
              "reaction": "thumbsup"
            }
          ]
        }
      }
    ]
  }
]

是的,反应 table 指的是 post 用户,他们的反应是正确的。

knex.schema.createTable('post_reactions', function(t) {
  t.integer('user').notNullable();
  t.foreign('user').references('id').inTable('users');
  t.integer('post').notNullable();
  t.foreign('post').references('id').inTable('posts');
  t.string('reaction').notNullable();

  t.unique(['user', 'post', 'reaction']);
}

独特之处在于,每个 post 用户只能做出一次特定反应。

获取对 post 3 的反应不需要连接。

knex('post_reactions')
  .select('user', 'reaction')
  .where('post', 3)

但也许您需要用户名和 post 的标题。

knex('post_reactions')
  .join('users', 'users.id', '=', 'post_reactions.user')
  .join('posts', 'posts.id', '=', 'post_reactions.post')
  .select('posts.title', 'users.name', 'reaction')
  .where('post', 3)

我做到了 post_reactions 既是因为它们是对 post 的反应(为对其他事物的反应留有余地),也是因为您可能想为所有人做出 table可能的反应并加入其中。

knex.schema.createTable('reactions', function(t) {
  t.increments();
  t.string('keyword').notNullable();
  t.unique('keyword');
}

knex.schema.createTable('post_reactions', function(t) {
  t.integer('user').notNullable();
  t.foreign('user').references('id').inTable('users');
  t.integer('post').notNullable();
  t.foreign('post').references('id').inTable('posts');
  t.integer('reaction').notNullable();
  t.foreign('reaction').references('id').inTable('reactions');

  t.unique(['user', 'post', 'reaction']);
}

如果除了 post 的标题和用户名之外还需要反应关键字,现在我们需要一个额外的连接。

knex('post_reactions')
  .join('users', 'users.id', '=', 'post_reactions.user')
  .join('posts', 'posts.id', '=', 'post_reactions.post')
  .join('reactions', 'reactions.id', '=', 'post_reactions.reaction')
  .select('posts.title', 'users.name', 'reaction.keyword')
  .where('post', 3)

这样做的好处是可以确保 post_reactions 中的每个条目都是真实的反应。错别字是不可能的。这也意味着关键字或其他关于反应的东西可以改变,但反应仍然有效。

请注意,我避免将反应图像本身存储在数据库中。您 可以 这样做,但通常最好将资产存储在磁盘上并引用文件。这允许直接链接到资产,减少数据库的负载,并让人们无需接触数据库即可使用资产。