PostgreSQL: Как пометить каждую таблицу в моей базе данных? Общие внешние ключи, столбец массива или другой метод

В настоящее время я разрабатываю базу данных PostgreSQL, в которой мне необходимо реализовать функциональность тегов. По сути, любой элемент из любой таблицы в моей базе данных может быть помечен с помощью тегов с ключевыми значениями, очень похожих на теги Azure.

Я рассматриваю различные подходы к реализации этой функции и нахожусь в замешательстве относительно лучшего подхода:

  1. Использование Generic Foreign Keys в Django:
    Один из подходов, который я рассматриваю, заключается в использовании функции Generic Foreign Keys в Django для создания универсальной таблицы "Tags", которая может ссылаться на любую другую таблицу в базе данных. Это обеспечивает гибкость и позволяет избежать сложной логики. Однако могут возникнуть такие недостатки, как снижение производительности или база данных не будет иметь смысла без ORM Django.
  2. Ручная реализация в одной таблице, без общих внешних ключей:
    Другой вариант - реализовать таблицу "Tags" вручную , не полагаясь на Generic Foreign Keys в Django. Я бы создал отдельную таблицу, которая может ссылаться на любую другую таблицу в базе данных, но без использования формальных внешних ключей. Это потребует либо хранения некоторой логики в базе данных, либо в бэкенде, что будет сложнее в обслуживании.
  3. Отдельная таблица перекрестных ссылок для каждой таблицы:
    В качестве альтернативы я мог бы создать отдельную таблицу (например, car_tags, house_tags, person_tags) для каждой таблицы в моей базе данных, для которой требуются теги. Такой подход обеспечит четкое разделение и потенциально лучшую производительность, но может привести к избыточности и усложнению запросов и обслуживания.
  4. Добавление столбца tags TEXT[] массива в каждую таблицу:
    Введение
  5. столбца
массива "tags" в каждой таблице, требующей тегирования, вместо того, чтобы поддерживать генерацию

Какой подход наиболее подходит для данного случая использования? Меня больше волнует поддерживаемость и масштабируемость базы данных, а не производительность. Если эти варианты не подходят, вы можете предложить какие-либо лучшие практики для реализации гибкой системы тегов в PostgreSQL.

  1. Django's GenericForeignKey are just weak, unenforced/unguarded references from db's perspective. The Tags table would have two additional columns, one identifying what type of thing it's tagging, the other one holding a unique identifier of that thing.

    It shouldn't be too hard to make sense out of that, with or without Django ORM. It's actually how PostgreSQL manages some things internally: pg_class.relkind tells you what type of thing it's listing, pg_class.oid identifies the specific thing. When you look things up in system views or information_schema, it'll filter down to the specific type of thing listed in pg_class and join to it on oid.

  2. You can easily implement this on your own a similar way Django does:

    create table tag (
        id bigint generated by default as identity primary key,
        name text);
    create table tag_anything (
        tag_id bigint references tag(id),
        other_thing_type text,
        other_thing_id text);
    create view cat_tagged as 
        select cat.*,array_agg(tag.name) as tags
        from cat 
        join tag_anything ta 
          on ta.other_thing_type='cat'
         and ta.other_thing_id=cat.id
        join tag
          on tag.id=ta.tag_id;
        group by cat.id;
    

    One problem is setting up the views and joins for those things, another that each thing can use a different, possibly multi-column primary key, which you need to map to the single generic foreign key reference. Some systems use uuid as the pk for everything, making each identifier of each thing, globally unique and uniform in type, which simplifies this.
    You also need to implement your own mechanics to handle ON UPDATE, ON DELETE behaviour as well as the link validation to avoid consistency loss.

  3. That's the normalized approach. If you think about what Django does with the GenericForeignKey and what you may try to reimplement, this is just a single table-per-thing more than that: tag_anything from the example above becomes tag_cat, loses the thing_type column and gets a proper foreign key reference, with adequate type.

    You get the on update, on delete and consistency checks for free.

  4. Adding a text[] or a jsonb everywhere sounds like a nightmare. You'd duplicate tags all over the place and weigh every tagged thing down. Querying based on those would have to hit every tagged table in the db.

Вернуться на верх