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



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

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

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

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

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

更新谢谢你的回答。对我来说,最重要的事情之一是确定为什么使用一个而不是另一个是有利的。我希望答案包括一些赞成/反对列表。

tl;博士

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


数据库设计

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

笔记

{
'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来处理重复项。


接口设计

1. 获取usernotes

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. 获取usertags

GET /users/{userid}/tags

如果不需要,则可以跳过发送包含notesidnotes属性。

3. 删除notestags

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

4. 为notes添加tags

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

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


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

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

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

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


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

这可能有点复杂。所以我可以分享我在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的唯一性.当然,我们还包含过滤单词根的逻辑,但正如您从上面的例子中看到的那样,我们还使用带有特殊根的重复值,例如"超级英雄"和"Suppahero",它们对我们来说是相同的。

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

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

在发票上添加项目,

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

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

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

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

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

因此,如果您正在制作标签,您将标记许多您必须使用 id

的东西

最新更新