标签应该是它自己的资源还是嵌套的 属性?

Should a tag be it's own resource or a nested property?

我正处于决定标签应该是它们自己的资源还是注释的嵌套 属性 的十字路口。这个问题涉及到 RESTful 设计和数据库存储。

上下文: 我有笔记资源。用户可以有很多笔记。每个笔记可以有很多标签。

职能目标: 我需要创建路线来执行以下操作:
1)获取所有用户标签。类似于:GET /users/:id/tags
2) 删除与笔记关联的标签。
3) 为特定的笔记添加标签。

Data/Performance 目标
1)获取用户标签应该很快。这是为了 "autosuggest"/"autocomplete".
2)防止重复(尽可能)。我希望标签尽可能被复用,目的是能够通过标签查询数据。例如,当标签 "superhero" 已经存在时,我想缓解用户键入标签(例如 "superheroes")的情况。

也就是说,在我看来,有两种在笔记资源上存储标签的方法:

1) 标记为嵌套 属性。例如:

type: 'notes',
attributes: {
  id: '123456789',
  body: '...',
  tags: ['batman', 'superhero'] 
}

2) 标签作为自己的资源。例如:

type: 'notes',
data: {
  id: '123456789',
  body: '...',
  tags: [1,2,3] // <= Tag IDs instead of strings
}

上述任何一种方法都可行,但我正在寻找一种可实现可伸缩性和数据一致性的解决方案(想象一百万张纸条和一千万个标签)。在这一点上,我倾向于选项 #1,因为它更容易处理代码,但不一定是正确的选项。

我非常想听听关于不同方法的一些想法,尤其是因为我找不到关于此主题的类似问题。

更新 谢谢你的回答。对我来说最重要的事情之一是确定为什么使用一个比另一个更有优势。我希望答案包含一些 pro/con 列表。

可能有点复杂。所以我可以分享我在 Tag 工作中的经验(在我们的例子中,它是 VoIP 应用程序的主要功能)。

在任何情况下,所有 Tags 都将作为唯一对象,其中包含很多信息。如您所知,转移会更复杂,但您需要此信息,例如以下。当然,Json 这是最快的解决方案。

type: 'notes',
data: {
  id: '123456789',
  body: '...',
  tags: [UUID1,UUID2,UUID3] 
}

例如,您需要多少信息。当您想要更改标签的颜色或大小时,基于标签率、基于数字使用的颜色、链接(不相同)、重复等。

type: 'tag',
data: {
  uuid: '234-se-324',
  body: 'superhero',
  linked: [UUID3, UUID4]
  rate: 4.6,
  usage: 4323
  duplicate: [superheros, suppahero]
}

如您所见,我们甚至使用了重复项。只是为了保存每个 Tag 的唯一性。当然,我们也包含过滤词根的逻辑,但正如您从上面的示例中看到的,我们还使用具有特殊根的重复值,例如 "Superhero" 和 "Suppahero",这对我们来说是相同的。

您可能会认为,这是关于 "autosuggest" 或 "autocomplete" 的大量信息,但我们从未遇到过性能问题(以防万一,如果服务器端支持健全)。所有信息对于每次使用都很重要,Note 在这种情况下也很重要。

tl;dr

考虑到您的要求,IMO 您应该将 tags 作为资源存储,而您的 API 应该 return 带有标签的 notes 作为嵌入属性。


数据库设计

notestags 保留为单独的集合(或表)。由于您有很多注释和许多标签,并且考虑到核心功能依赖于 searching/autocomplete 这些 tags 的事实,这将提高搜索 notes 特定 tags 时的性能].一个非常基本的设计看起来像:

笔记

{
    'id': 101,    // noteid
    'title': 'Note title',
    'body': 'Some note',
    'tags': ['tag1', 'tag2', ...]
}

标签

{
    'id': 'tag1',    // tagid
    'name': 'batman',
    'description': 'the dark knight',
    'related': ['tagx', 'tagy', ...],
    'notes': [101, 103, ...]
}

您可以使用 related 属性 将 tagxtagy 替换为类似的 tags

来处理重复项

API设计

1.为 user 获取 notes:

GET /users/{userid}/notes

在后端处理此路由时,将 tags 嵌入到 notes 对象中。您的 API 发送的 notes 对象应该如下所示:

{
    'id': 101,
    'title': 'Note title',
    'body': 'Some note',
    'tags': ['batman']    // replacing the tag1 by its name from tag collection
}

2。为 user 获取 tags

GET /users/{userid}/tags

如果不需要,您可以跳过发送 notes 属性,其中包含您的 notes.

id

3。为 notes 删除 tags:

DELETE /users/{userid}/{noteid}/{tag}

4.为 notes 添加 tags:

PUT /users/{userid}/{noteid}/{tag}

解决性能问题,为 user 获取 tags 应该很快,因为您有一个单独的集合。此外,处理重复项会更简单,因为您可以简单地将类似的 tags(通过 idname)添加到 related 数组中。希望这对您有所帮助。


为什么不保持标签嵌套 属性

  • 该设计的可扩展性不如前一个案例。如果 tags 嵌套 属性 并且必须编辑 tag 或必须添加某些信息,则需要更改所有 notes,因为多个 notes 可以包含相同的 tag。然而,将 tags 保留为资源,相同的 notes 将与其 ids 映射,并且需要在 tags collection/table 中进行一次更改。

  • 处理重复 tags 可能不像将它们作为单独的资源那样简单。

  • 搜索 tags 时,您需要搜索每个 note 中嵌入的所有 tags。这会增加开销。


使用 tags 作为嵌套 属性 IMO 的唯一优势是它可以更轻松地为特定 note 添加或删除 tags

如果您希望所有数据都在同一行,则将标签保存为嵌套 属性 很有意义。我举个例子。

您在发票上添加项目,

标题、描述、价格、数量、税...

这种情况下的税收可能是:增值税 20%,因此您将发票计算为 20%,但有一天税收会变为 22%,并且保存在数据库中的所有发票都将增加 2%。在这种情况下,您添加新列并将其保存为原始数字 20,当您从 db 读取该发票时,您从一行中获取所有数据,而不是从不同的表或变量中计算它。

标签也一样。如果您想以某种方式合并重复项,使用 ID 而不是字符串很容易做到。

还有一些您可能会考虑的其他因素。

in a social network, a user might have tags that are called skills, interests, sports, and more. There is no real way to differentiate between tags from (https://github.com/mbleigh/acts-as-taggable-on)

因此,如果您要制作标签,您将标记很多东西,您必须使用 id