【问题标题】:Should a tag be it's own resource or a nested property?标签应该是它自己的资源还是嵌套属性?
【发布时间】:2024-11-18 03:40:01
【问题描述】:

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

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

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

数据/绩效目标
1) 获取用户标签应该很快。这是为了“自动建议”/“自动完成”。
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,因为它更容易处理代码,但不一定是正确的选择。

我很想听听关于不同方法的一些想法,特别是因为我找不到关于这个主题的类似问题。

更新 谢谢你的回答。对我来说,最重要的事情之一是确定为什么使用其中一种是有利的。我希望答案包括一些赞成/反对清单。

【问题讨论】:

  • 您是在存储在数据库中还是在从静止返回输出时谈论这个?
  • 标签除了名字和ID之外还有其他信息吗?
  • 标签只包含一个字符串值。这个问题涉及 RESTful 设计和数据库存储 - 我已经更新了我的问题以反映。

标签: rest tags api-design


【解决方案1】:

tl;dr

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


数据库设计

notestags 保留为单独的集合(或表)。由于您有许多注释和许多标签,并且考虑到核心功能依赖于对这些 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 发送包含idnotes 属性。

3.为notes 删除tags

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

4.为notes 添加tags

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

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


为什么不将标签保留为嵌套属性

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

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

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


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

【讨论】:

  • 请注意,根据您的底层数据库,您可能需要associative table
  • 我在您的回答中没有看到的一件事是选择一种方法而不是另一种方法的原因。为什么要将标签视为单独的资源?
  • 我添加了一些关于使用tags 作为属性的要点。希望现在这更有意义。
【解决方案2】:

这可能有点复杂。所以我可以分享我对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”,它们对我们来说是相同的。

您可能会认为,“自动建议”或“自动完成”的信息很多,但我们从未遇到过性能问题(以防万一,如果服务器端支持健全)。所有信息对于每次使用都很重要,Note 在这种情况下也是如此。

【讨论】:

  • 感谢您的回答。我在您的回答中没有看到的一件事是选择一种方法而不是另一种方法的原因。为什么要将标签视为单独的资源?
【解决方案3】:

如果您想将所有数据保存在同一行中,则将标签保存为嵌套属性是有意义的。举个例子吧。

在发票上添加项目,

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

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

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

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

在社交网络中,用户可能拥有称为技能、兴趣、运动等的标签。没有真正的方法来区分标签 和 (https://github.com/mbleigh/acts-as-taggable-on)

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

【讨论】:

    最近更新 更多