Страницы

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

воскресенье, 8 марта 2020 г.

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

#база_данных #django #nosql


Начал делать проект на 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'.


И продублирую вопрос здесь, если кто-то дочитал. Подойдет мне реляционная БД? Правильные
я выбрал инструменты для реализации проекта? 
    


Ответы

Ответ 1



Хороший вопрос, я даже рад пожертвовать час на написание ответа :) Как бы не громко звучал заголовок, но эта структура больше простая, чем сложная. Реляционная структура Почти любую структуру можно описать в реляционной базе данных, хоть она и будет достаточно жетской Прошу заметить, что в реляционных базах данных тоже есть патерны проектирования, и давайте попробуем сначала реализовать один из правильных патернов, называющихся 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 же есть хорошая масштабируемость и изменяемая структура данных, поэтому плюсы перевешивают минусы.

Ответ 2



Подойдет мне реляционная БД? Да вполне подойдёт. У каждого элемента будет сотни свойств и десятки групп, в которые эти свойства вложены. (часть принадлежит самому элементу, часть группам свойств, которые в свою очередь принадлежат элементу) Ваша задача выглядит как задача про отношения. То есть, я считаю, что реляционная БД подойдёт лучше, чем нереляционная. Правильные я выбрал инструменты для реализации проекта? Всегда есть несколько способов решить одну и ту же задачу. Как видно значение свойств может быть представлено строкой, десятичным число и инженерной записью (для которой используется три числа - мантисса, основание, степень) Можно ещё решить задачу с помощью фреймворка для обобщённых связей в django Я не могу ссылаться на одну и ту же модель (в конкретном примере ScientificNotation) Ну естесственно не можете. ForeignKey определяет отношение один-ко-многим, между таблицами(объектами), а вы зачем-то попытались определить его два раза к одной таблице. Можно было, например, сделать дополнительную таблицу. Вариантов решения вашей задачи не один. У вас есть элемент, и способы его записи. Разные типы записей можно реализовать через классы. 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 DecimalNotationElement(Element): # Наследумеся от элемента! """ Наследуясь от Element, в Джанге, автоматически создаётся отношение один_к_одному. """ 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) # Работать будет так: element = DecimalNotationElement.objects.create( name="lalala", symbol="S", value=num, unit="some unit" ) # То есть просто обращаемся к нужному полю, и уже не играет # роли что поля из разных классов. >>>element = DecimalNotationElement.objects.get(name="lalala") >>>element.name >>>'lalala' >>>element.unit >>>'some unit'

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

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