Страницы

Поиск по вопросам

вторник, 28 мая 2019 г.

Выбор БД для проекта с сложной структурой данных

Начал делать проект на Django. Имеются следующие данные в JSON (привожу часть структуры, остальное повторяется):
[{"name": "Hydrogen", "atomic_number": 1, "symbol": "H", "thermal": {"absolute_boiling_point": { "value": 234, "unit": "K"}, "heat_of_vaporization": { "value": {"p": 4, "M": 0.452, "n": 10}, "unit": "kJ/mol"}}}]
Для наглядности:

Видно, что структура довольно сложная и многократно вложенная.
Элемент обладает свойствами без группы (типа, Name, symbol, atomic_number, которых около десяти и они являются общими для всех элементов) и группами свойств (групп имеется несколько десятков, в каждую из которых входит также несколько десятков свойств).
Как видно значение свойств может быть представлено строкой, десятичным число и инженерной записью (для которой используется три числа - мантисса, основание, степень)
Возникла проблема - я не могу описать эту структуру через реляционную БД проекта (postgresql), не могу установить связи.
Подойдет мне реляционная БД? Или следует обратить внимание на noSQL решения? Правильные я выбрал инструменты для реализации проекта?
Создал модели для элемента
class Element(models.Model): class Meta: verbose_name = 'Element' verbose_name_plural = 'Elements' name = models.CharField(verbose_name="Name", max_length=255, blank=True, null=True) symbol = models.CharField(verbose_name="Symbol", max_length=255, blank=True, null=True)
Различных форм записи
class ScientificNotation(models.Model): class Meta: verbose_name = 'Scientific Notation' significand = models.FloatField(verbose_name="Significand", blank=True, null=True) base = models.IntegerField(verbose_name="Base", blank=True, null=True) exponent = models.IntegerField(verbose_name="Exponent", blank=True, null=True) unit = models.CharField(verbose_name="Unit", max_length=255, blank=True, null=True)
class DecimalNotation(models.Model): class Meta: verbose_name = 'Decimal Notation' value = models.FloatField(verbose_name="Value", blank=True, null=True) unit = models.CharField(verbose_name="Unit", max_length=255, blank=True, null=True)
Группы свойств. С этой моделью и возникает проблема.
class ThermalProperties(models.Model): class Meta: verbose_name = 'Thermal properties' verbose_name_plural = 'Thermal properties' element = OneToOneField(Element) melting_point = ForeignKey(ScientificNotation) boiling_point = ForeignKey(ScientificNotation) heat_of_vaporization = ForeignKey(DecimalNotation)
Я не могу ссылаться на одну и ту же модель (в конкретном примере ScientificNotation)
ERRORS: table.ThermalProperties.boiling_point: (fields.E304) Reverse accessor for 'ThermalProperties.boiling_point' clashes with reverse accessor for 'ThermalProperties.melting_point'. HINT: Add or change a related_name argument to the definition for 'ThermalProperties.boiling_point' or 'ThermalProperties.melting_point'.
И продублирую вопрос здесь, если кто-то дочитал. Подойдет мне реляционная БД? Правильные я выбрал инструменты для реализации проекта?


Ответ

Хороший вопрос, я даже рад пожертвовать час на написание ответа :)
Как бы не громко звучал заголовок, но эта структура больше простая, чем сложная.
Реляционная структура
Почти любую структуру можно описать в реляционной базе данных, хоть она и будет достаточно жетской
Прошу заметить, что в реляционных базах данных тоже есть патерны проектирования, и давайте попробуем сначала реализовать один из правильных патернов, называющихся Class Table Inheritance. Он гарантирует размещение разных элементов в разных таблицах, в которых они должны находится:
element - структура для элементов
name atomic_number symbol element_thermal - структура для параметров
atomic_number type unit element_thermal_mol - структура для параметров mol/kg
atomic_number p M n element_thermal_value - структура для обычных параметров
atomic_number value
Здесь в зависимости от unit'а требуется выбирать таблицу, которую нужно будет делать JOIN. Мы здесь используем подход разделения данных на таблицы, который полностью соответствует реляционной структуре и ее правилам, но с каждым новым типом и данными Вам придется добавлять новую таблицу и усложнять сохранения и бизнес логику.
Ок. Попробуем подход Entity-Attributes-Values (EAV), его часто называют анти-паттерном проектирования из-за отсутствия контроля типа, контроля данных, размещения данных в одной таблице и т.д и вообще это выворачивание реляционной структуры наизнанку, но сам подход на небольших данных достаточно хорошо себя проявляет.
element - структура для элементов
name atomic_number symbol element_thermal - структура для параметров
atomic_number type unit element_thermal_values - структура для всех параметров
atomic_number parameter_type value
По сути, здесь все свои значения параметров вы храните в element_thermal_values записывая в parameter_type название параметра (INT, P, M, N), после чего делайте JOIN этой таблицы и получаете все параметры и обрабатываете их, запрос на получение данных всегда один, но естественно контроль должен быть обеспечен на уровне кода.
На маленьком проекте не будет разницы в том, что вы выберете, второй вариант просто проще, первый правильнее.
Да по сути, можно вообще не создавать отдельную таблицу, а просто запихнуть данные в value в виде json, просто с ними нельзя будет работать и как-то выполнять запросы, поэтому этот подход не всегда правильный, хотя базы типа PostgreSQL позволяют производить какие-то операции на уровне JSON.
NoSQL (Неструктурированные базы)
Хотите использовать noSQL, используйте. Никто Вас в этом не ограничивает и ваша структура, в некоторой степени являющейся динамичной, будет подходить для MongoDB, когда вы будете использовать все прелести документ-ориентированного подхода. Сам ваш элемент выглядит как документ.
Если у данных нет связей или их очень мало с другими данными, то в целом NoSQL (MongoDB) отличный выбор для вашей задачи и сможет упростить в некоторой степени работу с данными.
В целом размер кода будет примерно одинаковый, т.к. обработчики на value при выводе или процессинге все равно придется писать.
Вывод
Я рекомендую Вам попробовать несколько подходов, начните с нового MongoDB, потом попробуйте грамотное наследование в реляционных таблицах, сравните плюсы и минусы тех или иных реализаций и посмотрите что подходит для вас, опыта меньше не станет.
MongoDB заинтересует Вас тем, что это база данных по сути в которой данные хранятся в виде доступном приложению (JSON) и изменяемой структурой данных, которая тоже имеет плюсы и минусы, из плюсов конечно еще масштабируемость.
Старый подход на SQL отлично решает вопросы построения систем, где нужна целостность данных, соблюдение ACID, тяжелые запросы на объединение с многими таблицами.
В двух подходах есть минусы и плюсы, в своем случае, если бы я писал систему которая содержала периодическую таблицу и мне хотелось бы пощупать новые технологии, я бы взял MongoDB и познал бы Map-Reduce и т.д.

Мы часто используем Mongo для данных, чью структуру тяжело предусмотреть, например логирование в виде истории действий пользователя с различными типами параметров, конечно было бы хорошо запихнуть это в MySQL, но желания создавать отдельную таблицу под определенные данные просто нет, а использование подхода EAV на большом объеме данных просто уничтожает всю производительность, в MongoDB же есть хорошая масштабируемость и изменяемая структура данных, поэтому плюсы перевешивают минусы.

Комментариев нет:

Отправить комментарий