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

时间:2016-12-05 01:35:03

标签: rest tags api-design

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

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

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

数据/效果目标
1)获取用户标签应该很快。这是为了" autosuggest" /"自动完成"。
2)防止重复(尽可能多)。我希望尽可能多地重用标签,以便能够按标签查询数据。例如,我想减轻用户键入标签的情况,例如"超级英雄"当标签"超级英雄"已经存在。

话虽如此,我看到它的方式,有两种方法可以在记事资源上存储标签:

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,因为它更容易处理代码,但可能不一定是正确的选项。

我非常有兴趣听到有关不同方法的一些想法,特别是因为我在SO上找不到关于这个主题的类似问题。

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

3 个答案:

答案 0 :(得分:6)

<强> TL;博士

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

数据库设计

notestags保留为单独的集合(或表)。由于您有许多注释和许多标记,并且考虑到核心功能依赖于这些tags上的搜索/自动填充,这将在搜索特定notes的{​​{1}}时提高性能。一个非常基本的设计可能看起来像:

备注

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, ...] } 属性来处理重复项,方法是将relatedtagx替换为类似的tagy

API设计

<强> 1。获取tags的{​​{1}}:

notes

在后端处理此路由时,将user嵌入GET /users/{userid}/notes 对象中。 API发送的tags对象应如下所示:

notes

<强> 2。获取notes的{​​{1}}:

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

如果不是必需的话,您可以跳过发送包含tags的{​​{1}}的{​​{1}}媒体资源。

第3。删除user的{​​{1}}:

GET /users/{userid}/tags

<强> 4。为notes添加id

notes

解决性能问题,为tags提取notes应该很快,因为您有一个单独的集合。此外,处理重复项会更简单,因为您只需将类似的DELETE /users/{userid}/{noteid}/{tag} tagsnotes)添加到PUT /users/{userid}/{noteid}/{tag} 数组中即可。希望这有用。

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

  • 设计不像前一种情况那样可扩展。如果tags是嵌套属性并且必须编辑user或者必须添加某些信息,则需要对所有tags进行更改,因为多个id可以包含相同的name。鉴于将related保留为资源,相同的tags将与其tag一起映射,并且notes集合/表中将需要进行一次更改。

  • 处理重复的notes可能不像将它们作为单独的资源保存一样简单。

  • 搜索tag时,您需要搜索每个tags内嵌的所有notes。这增加了开销。

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

答案 1 :(得分: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的唯一身份。当然我们还包含过滤Words Root的逻辑,但正如您从上面的例子中可以看到的,我们也使用特殊根的重复值,例如&#34; Superhero&#34;和#34; Suppahero&#34;这对我们来说是一样的。

您可能会认为,这是&#34; autosuggest&#34;的大量信息。或者&#34;自动完成&#34;,但我们从未遇到过性能问题(如果是服务器端支持理智的话)。并且所有信息对于每种用法都很重要,在这种情况下也是Note

答案 2 :(得分:1)

如果要将所有数据放在同一行中,将标记保存为嵌套属性是有意义的。我举个例子。

在发票上添加项目

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

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

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

您还可以考虑其他一些因素。

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

之间的标签

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