Как организовать структуру хранения атрибутов?
Как в реляционной базе данных организовать структуру хранения атрибутов объектов для множества различных видов товаров и услуг: смартфоны, отели, фильмы и прочее? Атрибуты объектов невозможно вынести в отдельные столбцы, так как их слишком много.
Первый вариант - использовать noSQL возможности postgreSQL, то есть хранить значения атрибутов в виде json. Но возникает неприемлемая проблема с индексацией. GIN индексы работают на точное вхождение. Например:
SELECT
*
FROM
products
WHERE
attributes @> '{"length": 10}'
но не работают на отбор по диапазону
SELECT
*
FROM
products
WHERE
(attributes->>'length')::int4 > 10
В документации сказано, что для поиска по диапазону в jsonb поле, нужно создать Btree индекс для конкретного ключа JSON. Этот вариант не подходит, так как атрибутов очень много
Второй вариант, использовать паттерн EAV (объект - атрибут - значение). Здесь возникают сложности с различными типами данных и со сложносоставными условиями. Например, для 2,5 млн записей следующий запрос будет достаточно тяжелым
SELECT
product_id
FROM
products_attributes
WHERE
attribute_id = 348612462852833281 AND value BETWEEN 1 AND 10
INTERSECT
SELECT
product_id
FROM
products_attributes
WHERE
attribute_id = 348612464425861121 AND value BETWEEN 10 AND 20
INTERSECT
SELECT
product_id
FROM
products_attributes
WHERE
attribute_id = 372655158259580929 AND value BETWEEN 20 AND 30
По отдельности каждый из трех запросов выполнится быстро благодаря Btree индексам. Но необходимость поиска пересечения по всем условиям сильно замедляет запрос.
Третий вариант - использовать noSql базу для хранения атрибутов. Но основные данные хранятся в реляционной базе и потребуется как-то передавать результат запроса из noSQL базы в запрос к реляционной базе. Передавать сотни тысяч идентификаторов объектов не лучшая идея.