Должен ли тег быть собственным ресурсом или вложенным свойством?
Я на перекрестке, решив, должны ли теги быть их собственным ресурсом или вложенным свойством заметки. Этот вопрос немного затрагивает дизайн 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, так как легче справиться с кодом, но может и не быть правильным вариантом.
Мне очень интересно услышать некоторые мысли о разных подходах, особенно потому, что я не могу найти похожие вопросы по поводу этой темы.
Обновление
Спасибо за ответы. Одна из самых важных вещей для меня - это определение того, почему использование одного над другим выгодно. Я бы хотел, чтобы в ответ был включен список pro/con.
Ответы
Ответ 1
TL;DR
Учитывая ваши требования, IMO вы должны хранить tags
в качестве ресурсов, и ваш API должен вернуть notes
с тегами в качестве встроенных свойств.
Дизайн базы данных
Держите notes
и tags
в виде отдельных коллекций (или таблиц). Поскольку у вас много заметок и много тегов, и учитывая тот факт, что основные функции зависят от поиска/автозаполнения на этих 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
для обработки дубликатов путем замены tagx
, tagy
на аналогичные tags
.
Дизайн API
1. Извлечение notes
для user
:
GET /users/{userid}/notes
Вставить tags
в объект notes
, когда вы обрабатываете этот маршрут на сервере. Объект notes
ваш API-адрес должен выглядеть примерно так:
{
'id': 101,
'title': 'Note title',
'body': 'Some note',
'tags': ['batman'] // replacing the tag1 by its name from tag collection
}
2. Получение tags
для user
:
GET /users/{userid}/tags
Если это не требуется, вы можете пропустить отправку свойства notes
, которое содержит id
для вашего notes
.
3. Удаление tags
для notes
:
DELETE /users/{userid}/{noteid}/{tag}
4. Добавление tags
для notes
:
PUT /users/{userid}/{noteid}/{tag}
Устраняя проблемы производительности, выборка tags
для user
должна быть быстрой, потому что у вас есть отдельная коллекция для нее. Кроме того, обработка дубликатов будет проще, потому что вы можете просто добавить аналогичный tags
(через id
или name
) в массив related
. Надеюсь, это было полезно.
Почему бы не сохранить теги как вложенные свойства
-
Дизайн не такой масштабируемый, как в предыдущем случае. Если tags
- это вложенное свойство, а tag
нужно отредактировать или добавить некоторую информацию, тогда для всех notes
потребуются изменения, поскольку несколько notes
могут содержать один и тот же tag
. Принимая во внимание, что сохранение tags
в качестве ресурсов, тот же notes
будет сопоставлен с их ids
, и в коллекции tags
/table > будет потребоваться одно изменение.
-
Обработка дубликата tags
может быть не такой простой, как сохранение их в качестве отдельных ресурсов.
-
При поиске tags
вам нужно будет искать все tags
, встроенные в каждый note
. Это добавляет накладные расходы.
Единственное преимущество использования tags
как вложенного свойства IMO - это упростить добавление или удаление tags
для определенного note
.
Ответ 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
. Конечно, мы также используем логику для фильтрации корня слов, но, как вы можете видеть из приведенного выше примера, мы также используем дублирующее значение со специальными Roots, такими как "Superhero" и "Suppahero", которые для нас одинаковы.
И вы можете подумать, что это много информации для "autosuggest" или "autocomplete", но мы никогда не сталкивались с проблемами с производительностью (в случае, если разумная поддержка поддержки на стороне). И вся информация важна для каждого использования, и Note
в этом случае тоже.
Ответ 3
Сохранение тегов в качестве вложенного свойства имеет смысл, если вы хотите иметь все данные в одной строке. Позвольте мне привести вам пример.
В счет-фактуре вы добавляете элементы,
Название, описание, цена, количество, налог,...
налог в этом случае может быть: НДС 20%, поэтому вы рассчитываете счет-фактуру с 20%, но однодневный налог изменяется до 22%, а все счета-фактуры, которые сохраняются в БД, будут на 2% больше. В этом случае вы добавляете новый столбец, и вы сохраняете его как необработанное число 20, и когда вы читаете этот счет-фактуру из db, вы получаете все данные из одной строки, а не вычисляете их из разных таблиц или переменных.
То же самое происходит с тегами. Если вы каким-то образом захотите объединить дубликаты, его легко сделать с помощью идентификаторов, а не строк.
Также есть некоторые другие факторы, которые вы могли бы рассмотреть.
в социальной сети пользователь может иметь теги, которые называются навыками, интересами, спортом и т.д. Нет никакого реального способа отличить теги от (https://github.com/mbleigh/acts-as-taggable-on)
Итак, если вы создаете теги, которые вы отметите многие вещи, вы должны использовать id