Skip to content

Модель данных

dlexeyn edited this page Nov 8, 2024 · 13 revisions

Модель данных для wiki

Реляционная модель

PostgreSQL

sql

Коллекции и сущности

n − максимально необходимый размер строки для текста. Оптимальное n = 500 символов.

Пользователь (User)

Назначение: Хранит информацию о пользователях системы, их идентификацию и роли.

Атрибуты:

  • id (PK) — уникальный идентификатор пользователя, тип: INT (4 байта)

  • surname — фамилия пользователя, тип: VARCHAR (до n+2 байт (n символов + служебная информация))

  • name — имя пользователя, тип: VARCHAR (до n+2 байт (n символов + служебная информация))

  • lastname — отчество пользователя, тип: VARCHAR (до n+2 байт (n символов + служебная информация))

  • username — уникальный логин для входа, тип: VARCHAR (до n+2 байт (n символов + служебная информация))

  • password — зашифрованный пароль пользователя, тип: VARCHAR (до n+2 байт (n символов + служебная информация))

  • role — роль пользователя в системе (Администратор, Прораб, Рабочий, Заказчик), тип: ENUM (1 байт (считаем 4 возможных значения))

  • created_at — Время создания записи пользователя. Это поле автоматически фиксирует дату и время, когда учетная запись пользователя была создана в системе, тип TIMESTAMP (8 байт)

  • updated_at — Время последнего обновления записи пользователя. Это поле автоматически обновляется при каждом изменении данных пользователя, тип TIMESTAMP (8 байт)

Общий размер: приблизительно 5n+31 байт на пользователя

Связи:

  • Один пользователь может участвовать в нескольких проектах.

  • Один пользователь может быть отправителем или получателем нескольких сообщений.

Проект (Project)

Назначение: Хранит информацию о проектах по организации ремонта, включая этапы и задачи.

Атрибуты:

  • id (PK) — уникальный идентификатор проекта, тип: INT (4 байта)

  • name — название проекта, тип: VARCHAR (до n+2 байт (n символов + служебная информация))

  • description — описание проекта, тип: TEXT (до n+2 байт (n символов + служебная информация))

  • start_date — дата начала проекта, тип: DATE (3 байта)

  • end_date — дата окончания проекта, тип: DATE (3 байта)

  • status — статус проекта (в работе, завершён), тип: ENUM (1 байт (считаем 4 возможных значения))

  • contact_id (FK) — идентификатор контактов (ссылка на контакты), тип: INT (4 байта)

  • created_at — Время создания записи проекта. Это поле автоматически фиксирует дату и время, когда учетная запись проекта была создана в системе, тип TIMESTAMP (8 байт)

  • updated_at — Время последнего обновления записи проекта. Это поле автоматически обновляется при каждом изменении данных проекта, тип TIMESTAMP (8 байт)

Общий размер: приблизительно 2n+41 байт на пользователя

Связи:

  • Один проект может включать несколько этапов.

  • Проект содержит этапы, риски и закупки.

Этап (Stage)

Назначение: Хранит информацию об этапах проекта.

Атрибуты:

  • id (PK) — уникальный идентификатор этапа, тип: INT (4 байта)

  • name — название этапа, тип: VARCHAR (до n+2 байт (n символов + служебная информация))

  • start_date — дата начала этапа, тип: DATE (3 байта)

  • end_date — дата окончания этапа, тип: DATE (3 байта)

  • project_id (FK) — идентификатор проекта, к которому относится этап, тип: INT (4 байта)

  • created_at — Время создания записи этапа. Это поле автоматически фиксирует дату и время, когда учетная запись этапа была создана в системе, тип TIMESTAMP (8 байт)

  • updated_at — Время последнего обновления записи этапа. Это поле автоматически обновляется при каждом изменении данных этапа, тип TIMESTAMP (8 байт)

  • Общий размер: приблизительно n+32 байт на этап.

Связи:

  • Этап включает несколько задач.

  • Этап связан с одним проектом.

Задача (Task)

Назначение: Хранит информацию о задачах, связанных с этапами проекта.

Атрибуты:

  • id (PK) — уникальный идентификатор задачи, тип: INT (4 байта)

  • name — название задачи, тип: VARCHAR (до n+2 байт (n символов + служебная информация))

  • description — описание задачи, тип: TEXT (до n+2 байт (n символов + служебная информация))

  • start_date — дата начала задачи, тип: DATE (3 байта)

  • end_date — дата окончания задачи, тип: DATE (3 байта)

  • status — статус задачи (запланировано, в работе, завершено), тип: ENUM (1 байт)

  • stage_id (FK) — идентификатор этапа, к которому относится задача, тип: INT (4 байта)

  • contact_id (FK) — идентификатор рабочего, ответственного за задачу, тип: INT (4 байта)

  • created_at — Время создания записи задачи. Это поле автоматически фиксирует дату и время, когда учетная запись задачи была создана в системе, тип TIMESTAMP (8 байт)

  • updated_at — Время последнего обновления записи задачи. Это поле автоматически обновляется при каждом изменении данных задачи, тип TIMESTAMP (8 байт)

Общий размер: приблизительно 2n+39 байт на задачу.

Связи:

  • Задача может быть связана с несколькими рабочими через связь многие ко многим.

  • Задача может быть связана с этапом через связь один ко многим.

Сообщение (Message)

Назначение: Хранит информацию о сообщениях между пользователями.

Атрибуты:

  • id (PK) — уникальный идентификатор сообщения, тип: INT (4 байта)

  • text — текст сообщения, тип: TEXT (до n+2 байт (n символов + служебная информация))

  • sent_date — дата отправки сообщения, тип: DATETIME (8 байт)

  • sender_id (FK) — идентификатор отправителя (ссылка на пользователя), тип: INT (4 байта)

  • receiver_id (FK) — идентификатор получателя (ссылка на пользователя), тип: INT (4 байта)

  • status — статус сообщения (прочитано или нет), тип: ENUM (1 байт)

  • created_at — Время создания записи сообщения. Это поле автоматически фиксирует дату и время, когда учетная запись сообщения была создана в системе, тип TIMESTAMP (8 байт)

  • updated_at — Время последнего обновления записи сообщения. Это поле автоматически обновляется при каждом изменении данных сообщения, тип TIMESTAMP (8 байт)

Общий размер: приблизительно n+39 байт на сообщение.

Связи:

-Одно сообщение может быть отправлено одним пользователем и направлено другому пользователю.

Закупка (Procurement)

Назначение: Хранит информацию о закупках материалов для проектов.

Атрибуты:

  • id (PK) — уникальный идентификатор закупки, тип: INT (4 байта)

  • item_name — наименование закупаемого товара, тип: VARCHAR (до n+2 байт (n символов + служебная информация))

  • quantity — количество товара, тип: INT (4 байта)

  • price — цена товара, тип: DECIMAL (8 байт)

  • created_date — дата создания закупки, тип: TIMESTAMP (8 байт)

  • project_id (FK) — идентификатор проекта, связанного с закупкой, тип: INT (4 байта)

  • created_at — Время создания записи закупки. Это поле автоматически фиксирует дату и время, когда учетная запись закупки была создана в системе, тип TIMESTAMP (8 байт)

  • updated_at — Время последнего обновления записи закупки. Это поле автоматически обновляется при каждом изменении данных закупки, тип TIMESTAMP (8 байт)

  • created_by (FK) — идентификатор пользователя, который создал закупку, тип: INT (4 байта)

  • delivery_date — дата поставки, тип: DATE (3 байта)

Общий размер: приблизительно n+46 байт на сообщение.

Связи:

  • Одна закупка привязана к одному проекту.

Риск (Risk)

Назначение: Хранит информацию о рисках, связанных с проектами.

Атрибуты:

  • id (PK) — уникальный идентификатор риска, тип: INT (4 байта)

  • name — название риска, тип: VARCHAR (до n+2 байт (n символов + служебная информация))

  • description — описание риска, тип: TEXT (до n+2 байт (n символов + служебная информация))

  • project_id (FK) — идентификатор проекта, к которому относится риск, тип: INT (4 байта)

  • created_at — Время создания записи риски. Это поле автоматически фиксирует дату и время, когда учетная запись риска была создана в системе, тип TIMESTAMP (8 байт)

  • updated_at — Время последнего обновления записи риска. Это поле автоматически обновляется при каждом изменении данных риска, тип TIMESTAMP (8 байта)

Общий размер: приблизительно 2n+ 28 байт на риск

Связи:

  • Один проект может иметь несколько рисков.

Контакт (Contact)

Назначение: Хранит информацию о пользователях и проектах, в которых они участвуют.

Атрибуты:

  • id (PK) — уникальный идентификатор контакта, тип: INT (4 байта)

  • user_id (FK) — идентификатор пользователя, тип: INT (4 байта)

  • project_id (FK) — идентификатор проекта, тип: INT (4 байта)

  • created_at — Время создания записи контакта. Это поле автоматически фиксирует дату и время, когда учетная запись контакта была создана в системе, тип TIMESTAMP (8 байт)

  • updated_at — Время последнего обновления записи контакта. Это поле автоматически обновляется при каждом изменении данных контакта, тип TIMESTAMP (8 байт)

Общий размер: приблизительно 28 байт на контакт

Связи:

  • Один контакт связывает пользователя с проектом.

Работник_Задача (User_Task)

Назначение: Хранит информацию о пользователях и проектах, в которых они участвуют.

Атрибуты:

  • id (PK) — уникальный идентификатор контакта, тип: INT (4 байта)

  • user_id (FK) — идентификатор пользователя, тип: INT (4 байта)

  • task_id (FK) — идентификатор задачи, тип: INT (4 байта)

  • created_at — Время создания записи таблицы. Это поле автоматически фиксирует дату и время, когда учетная запись в таблицу была создана в системе, тип TIMESTAMP (8 байт)

  • updated_at — Время последнего обновления записи в таблицу. Это поле автоматически обновляется при каждом изменении данных таблицы, тип TIMESTAMP (8 байт)

Общий размер: приблизительно 28 байт на таблицу Работник_Задача

Связи:

  • Много задач связаны с многими работниками.

Оценка удельного объема информации

Средний размер одного документа коллекции:

  • User: 5n+3 + 24 (оверхед строки) = 2527 байт

  • Project: 2n+41 +24 (оверхед строки) = 1065 байт

  • Stage: n+32 = 532 байт

  • Task: 2n+39 + 24 (оверхед строки) = 1063 байт

  • Message: n+39+ 24 (оверхед строки) = 563 байт

  • Procurement: n+46 = 546 байт

  • Risk: 2n+ 28 + 24 (оверхед строки) = 1052 байт

  • Contact: 28 байт

Предположительно пользователь будет создавать:

  • заказчиков в среднем u

  • в среднем прорабов u

  • в среднем работников 8*u

  • в среднем 1*u проектов

  • в среднем 3 этапа на проект

  • в среднем 5 задач на этап

  • в среднем 105u рассылок + 630u сообщений между пользователями

  • в среднем 100 закупок на проект

  • в среднем 8 рабочих + 1 прораб+ 1 заказчик на проект

  • в среднем 5 рисков на проект

При количестве заказчиков равным u:

V(u)=(252710 +735563+1065+3532+106315+100546+51052+10*28) ∗u= 516221∗u

Избыточность данных

  • В таблице User: избыточные данные id, created_at, updated_at (20 байт)

  • В таблице Project: избыточные данные id, created_at, updated_at, project_id (24 байт)

  • В таблице Stage: избыточные данные id, created_at, updated_at, project_id (24 байт)

  • В таблице Task: избыточные данные id, created_at, updated_at, stage_id, worker_id (28 байт)

  • В таблице Message: избыточные данные id, created_at, updated_at, receiver_id, sender_id (28 байт)

  • В таблице Procurement: избыточные данные id, created_at, updated_at, project_id (24 байт)

  • В таблице Risk: избыточные данные id, created_at, updated_at, project_id (24 байт)

  • В таблице Contact: избыточные данные вся таблица.

При количестве заказчиков равным u:

Vclean(u)=(250710 +1041+3508+103515+735535+100522+51028) ∗u=493625∗u

Тогда рассчитаем избыточность как отношение объема модели к "чистому" объему:

V(u)/Vclean(u)=516221∗u/493625∗u≈1,0457

Направление роста модели

При создании модели Project будут создаваться модели Stage, Task, Procurement, Risk, Contact

При добавлении других моделей (User, Message) ничего создаваться дополнительно не будет

Примеры данных

Таблица User

id surname name lastname username password role created_at updated_at
1 Иванов Иван Иванович ivanov_ivan e5d3be45bfeabc Администратор 2024-10-10 09:00:00 2024-10-10 09:00:00
2 Смирнова Анна Сергеевна smirnova_anna 9aaf7cc6dce123 Заказчик 2024-10-12 10:20:00 2024-10-12 10:20:00
3 Петров Петр Петрович petrov_petr c23b5d7f4fa2d7 Прораб 2024-10-13 11:30:00 2024-10-13 11:30:00
4 Сидоров Сергей Викторович sidorov_sergey f12e9873df129a Рабочий 2024-10-14 12:15:00 2024-10-14 12:15:00

Таблица Project

id name description start_date end_date status customer_id foreman_id created_at updated_at
1 Ремонт офиса Проект по ремонту офисного здания 2024-11-01 2024-12-20 в работе 2 3 2024-10-12 10:25:00 2024-10-12 10:25:00
2 Строительство склада Строительство складского помещения 2024-11-15 2025-01-30 в работе 2 3 2024-10-13 11:35:00 2024-10-13 11:35:00

Таблица Task

id name description start_date end_date status stage_id worker_id created_at updated_at
1 Установка розеток Монтаж электророзеток в офисе 2024-11-16 2024-11-17 запланировано 2 4 2024-10-14 12:20:00 2024-10-14 12:20:00
2 Укладка плитки Укладка плитки в санузлах офиса 2024-11-18 2024-11-20 запланировано 2 4 2024-10-14 12:25:00 2024-10-14 12:25:00

Таблица Stage

id name start_date end_date project_id created_at updated_at
1 Дизайн интерьера 2024-11-01 2024-11-15 1 2024-10-12 10:30:00 2024-10-12 10:30:00
2 Электромонтажные работы 2024-11-16 2024-11-30 1 2024-10-13 11:40:00 2024-10-13 11:40:00

Таблица Message

id text sent_date sender_id receiver_id status created_at updated_at
1 Здравствуйте, когда начнется проект? 2024-10-12 10:30:00 2 3 прочитано 2024-10-12 10:30:00 2024-10-12 10:30:00
2 Утверждаю смету 2024-10-13 11:35:00 2 3 не прочитано 2024

Таблица Procurement

id item_name quantity price created_date project_id created_at updated_at
1 Электрические кабели 1000 50000 2024-10-15 14:00:00 1 2024-10-15 14:00:00 2024-10-15 14:00:00
2 Плитка керамическая 500 30000 2024-10-16 10:15:00 1 2024-10-16 10:15:00 2024-10-16 10:15:00

Таблица Contact

id user_id project_id created_at updated_at
1 2 1 2024-10-12 10:50:00 2024-10-12 10:50:00
2 3 1 2024-10-12 10:51:00 2024-10-12 10:51:00

Таблица Item

id item_name quantity price created_date project_id created_at updated_at
1 Электрические кабели 1000 50000 2024-10-15 14:00:00 1 2024-10-15 14:00:00 2024-10-15 14:00:00
2 Плитка керамическая 500 30000 2024-10-16 10:15:00 1 2024-10-16 10:15:00 2024-10-16 10:15:00

Таблица Risk

id name description project_id created_at updated_at
1 Задержка поставки материалов Возможная задержка поставок 1 2024-10-12 10:40:00 2024-10-12 10:40:00
2 Отсутствие рабочей силы Нехватка работников для укладки плитки 1 2024-10-13 11:45:00 2024-10-13 11:45:00

Примеры запросов

1. Регистрация пользователя

Запрос на регистрацию пользователя:

INSERT INTO users (username, password_hash, email, role_id)
VALUES (:username, :password_hash, :email, :role_id);

Запрос на получение роли пользователя:

SELECT role_id FROM roles WHERE role_name = :role_name;

Запрос на создание логина и пароля:

UPDATE users SET password_hash = :password_hash WHERE email = :email;

2. Написание сообщения

Запрос на поиск пользователя по фамилии, имени или должности:

SELECT * FROM users WHERE last_name = :last_name OR first_name = :first_name OR position = :position;

Запрос на отправку сообщения:

INSERT INTO messages (chat_id, sender_id, content, timestamp) 
VALUES (:chat_id, :sender_id, :content, NOW());

Запрос получение сообщений от пользователя:

INSERT INTO messages (chat_id, sender_id, content, timestamp) 
VALUES (:chat_id, :sender_id, :content, NOW());

3. Просмотр графика работы

Запрос на получение задач рабочего:

SELECT * FROM tasks WHERE assigned_worker_id = :worker_id 
AND (status = 'active' OR status = 'in_progress');

Запрос на фильтрацию задач:

SELECT * FROM tasks WHERE project_id = :project_id AND status = :status AND task_date BETWEEN :start_date AND :end_date;

4. Создание проекта

Запрос на создание проекта:

INSERT INTO projects (name, description, client_id, start_date, end_date, status) 
VALUES (:name, :description, :client_id, :start_date, :end_date, 'pending');

Запрос на назначение прораба на проект:

UPDATE projects SET foreman_id = :foreman_id WHERE project_id = :project_id;

5. Просмотр и правка проекта

Запрос на просмотр информации о проекте:

SELECT * FROM projects WHERE project_id = :project_id;

Запрос на редактирование проекта:

UPDATE projects SET description = :description, start_date = :start_date, end_date = :end_date WHERE project_id = :project_id;

6. Оформление закупки

Запрос на добавление материалов в закупки:

INSERT INTO purchases (project_id, item_name, quantity, unit_price)
VALUES (:project_id, :item_name, :quantity, :unit_price);

Запрос на редактирование закупки:

UPDATE purchases SET item_name = :item_name, quantity = :quantity, unit_price = :unit_price WHERE purchase_id = :purchase_id;

7. Создание рисков

Запрос на добавление рисков проекта:

INSERT INTO risks (project_id, risk_name, description)
VALUES (:project_id, :risk_name, :description);

Запрос на редактирование риска:

UPDATE risks SET risk_name = :risk_name, description = :description WHERE risk_id = :risk_id;

8. Создание контактов

Запрос на добавление контакта в проект:

INSERT INTO project_contacts (project_id, user_id) 
VALUES (:project_id, :user_id);

Запрос на удаление контакта:

DELETE FROM project_contacts WHERE project_id = :project_id AND user_id = :user_id;

9. Создание и редактирование этапов

Запрос на добавление этапа проекта:

INSERT INTO stages (project_id, stage_name, start_date, end_date) 
VALUES (:project_id, :stage_name, :start_date, :end_date);

Запрос на редактирование этапа:

UPDATE stages SET stage_name = :stage_name, start_date = :start_date, end_date = :end_date WHERE stage_id = :stage_id;

10. Создание и редактирование задач

Запрос на создание задачи:

INSERT INTO tasks (stage_id, task_name, start_date, end_date, worker_id, status) 
VALUES (:stage_id, :task_name, :start_date, :end_date, :worker_id, 'pending');

Запрос на редактирование задачи:

UPDATE tasks SET task_name = :task_name, start_date = :start_date, end_date = :end_date, status = :status WHERE task_id = :task_id;

11. Массовый Импорт/Экспорт графика

Запрос на экспорт данных проекта для анализа:

COPY projects TO '/path/projects.csv' WITH (FORMAT csv, HEADER);

Запрос на импорт данных в проект:

COPY projects FROM '/path/projects.csv'  WITH (FORMAT csv, HEADER);

12. Получение статистики

Получение статистики по рискам

SELECT *
FROM risks
WHERE project_id IN ('60f1b8d4e138234068a9099a', '60f1b8d4e138234068a9099b')
  AND created_date BETWEEN '2023-01-01' AND '2023-12-31';

Получение статистики по закупкам

SELECT *
FROM procurements
WHERE project_id IN ('60f1b8d4e138234068a9099a', '60f1b8d4e138234068a9099b')
AND created_date BETWEEN '2023-01-01' AND '2023-12-31';

13. Получение всех сообщений всех пользователей, на которые не ответили в чате

SELECT
    m.id,
    m.text,
    m.sent_date,
    m.sender_id,
    m.receiver_id,
    m.status,
    m.created_at,
    m.updated_at
FROM
    messages m
WHERE
    m.status = 'непрочитано';

14. Получение суммы закупок по каждой стадии проекта

SELECT
    s.id AS stage_id,
    s.name AS stage_name,
    SUM(p.price * p.quantity) AS total_procurement_amount
FROM
    procurements p
JOIN
    projects pr ON pr.id = p.project_id
JOIN
    stages s ON s.project_id = pr.id
GROUP BY
    s.id, s.name
ORDER BY
    s.id;

Нереляционная модель

MongoDB

nosql

Картинка

Коллекции и сущности

1. Пользователь (User)

{
  "_id": "ObjectID",                               // Уникальный идентификатор
  "surname": "String",                   // Фамилия пользователя
  "name": "String",                      // Имя пользователя
  "father_name": "String",               // Отчество пользователя
  "username": "String",                  // Логин пользователя
  "password": "String",                  // Хеш пароля
  "role": "String",                      // Роль пользователя (можно также использовать ENUM-подобную логику с набором ролей)
  "created_at": "ISODate",               // Дата создания
  "updated_at": "ISODate",               // Дата последнего обновления
}

2. Проект (Project)

{
  "_id": "ObjectId",                     // Уникальный идентификатор проекта
  "name": "String",                      // Название проекта
  "description": "String",               // Описание проекта
  "start_date": "ISODate",               // Дата начала проекта
  "end_date": "ISODate",                 // Дата окончания проекта
  "status": "String",                    // Статус проекта (в работе/завершён)
  "created_at": "ISODate",               // Дата создания
  "updated_at": "ISODate",               // Дата последнего обновления
  
  "contacts": {                          // Контакты, связанные с проектом
    "contact_id_1": {
      "user_id": "ObjectId",             // Идентификатор пользователя
      "created_at": "ISODate",           // Дата создания контакта
      "updated_at": "ISODate"            // Дата обновления контакта
    },
    "contact_id_2": {
      "user_id": "ObjectId",
      "created_at": "ISODate",
      "updated_at": "ISODate"
    }
  },

  "stages": {                            // Этапы проекта
    "stage_id_1": {
      "name": "String",                  // Название этапа
      "start_date": "ISODate",           // Дата начала этапа
      "end_date": "ISODate",             // Дата окончания этапа
      "created_date": "ISODate",         // Дата создания этапа
      "updated_at": "ISODate",           // Дата обновления этапа
      "tasks": {                         // Задачи этапа
        "task_id_1": {
          "name": "String",              // Название задачи
          "description": "String",       // Описание задачи
          "start_date": "ISODate",       // Дата начала задачи
          "end_date": "ISODate",         // Дата окончания задачи
          "status": "String",            // Статус задачи
          "created_at": "ISODate",
          "updated_at": "ISODate",
          "workers": {
            "worker_id_1": {             // Рабочий, назначенный на задачу
              "user_id": "ObjectId",     // Идентификатор пользователя
              "name": "String"           // Имя рабочего
            }
          }
        }
      }
    }
  },

  "risks": {                             // Риски, связанные с проектом
    "risk_id_1": {
      "name": "String",                  // Название риска
      "description": "String",           // Описание риска
      "created_at": "ISODate",           // Дата создания риска
      "updated_at": "ISODate"            // Дата обновления риска
    },
    "risk_id_2": {
      "name": "String",
      "description": "String",
      "created_at": "ISODate",
      "updated_at": "ISODate"
    }
  },

  "procurements": {                      // Закупки, связанные с проектом
    "procurement_id_1": {
      "item_name": "String",             // Наименование товара
      "quantity": "Number",              // Количество товара
      "price": "Number",                 // Цена товара
      "created_by": "ObjectId",          // Идентификатор пользователя, создавшего закупку
      "created_date": "ISODate",         // Дата создания закупки
      "updated_at": "ISODate",           // Дата обновления закупки
      "delivery_date": "ISODate"         // Дата поставки товара
    },
    "procurement_id_2": {
      "item_name": "String",
      "quantity": "Number",
      "price": "Number",
      "created_by": "ObjectId",
      "created_date": "ISODate",
      "updated_at": "ISODate",
      "delivery_date": "ISODate"
    }
  }
}

3. Сообщение (Message)

{
  "_id": "ObjectId",                     // Уникальный идентификатор сообщения
  "chatId": "ObjectId",                  // Идентификатор чата, к которому относится сообщение
  "sender": "ObjectId",                   // Идентификатор отправителя
  "receiver": "ObjectId",                  // Идентификатор получателя
  "content": "string",                   // Содержание сообщения
  "status": "string",                    // Статус сообщения (например, "unread", "read")
  "timestamp": "ISODate"                // Временная метка сообщения
}


4. Этап (Stage)

{
  "_id": "ObjectId",                     // Уникальный идентификатор этапа
  "name": "String",                      // Название этапа
  "start_date": "ISODate",               // Дата начала этапа
  "end_date": "ISODate",                 // Дата окончания этапа
  "created_date": "ISODate",             // Дата создания этапа 
  "updated_at": "ISODate",               // Дата последнего обновления
  "tasks": {                             // Список задач этапа
    "task_id_1": {
      "name": "String",                  // Название задачи
      "description": "String",           // Описание задачи
      "start_date": "ISODate",           // Дата начала задачи
      "end_date": "ISODate",             // Дата окончания задачи
      "status": "String",                // Статус задачи (запланировано/в работе/завершено)
      "created_at": "ISODate",           // Дата создания задачи
      "updated_at": "ISODate",           // Дата обновления задачи
      "workers": {                       // Список рабочих, ответственных за задачу
        "worker_id_1": {
          "user_id": "ObjectId",         // Идентификатор рабочего
          "name": "String"               // Имя рабочего
        }
      }
    }
  }
}

5. Закупка (Procurement)

{
  "_id": "ObjectId",                     // Уникальный идентификатор закупки
  "item_name": "String",                 // Наименование товара
  "quantity": "Number",                  // Количество товара
  "price": "Number",                     // Цена товара
  "created_by": "ObjectId",              // Идентификатор пользователя, создавшего закупку
  "created_date": "ISODate",             // Дата создания закупки
  "updated_at": "ISODate",               // Дата последнего обновления
  "delivery_date": "ISODate",            // Дата поставки товара
  "project_id": "ObjectId"               // Идентификатор связанного проекта
}

6. Риск (Risk)

{
  "_id": "ObjectId",                     // Уникальный идентификатор риска
  "name": "String",                      // Название риска
  "description": "String",               // Описание риска
  "created_at": "ISODate",                  // Дата создания
  "updated_at": "ISODate"                   // Дата обновления
}

7. Контакт (Contact)

{
  "_id": "ObjectId",                     // Уникальный идентификатор контакта
  "user_id": "ObjectId",                 // Идентификатор пользователя
  "project_id": "ObjectId",              // Идентификатор проекта
  "created_at": "ISODate",                  // Дата создания контакта
  "updated_at": "ISODate"                   // Дата обновления
}

8. Задачи (Task)

{
  "tasks": {                            // Словарь задач в рамках этапа
    "task_id_1": {                      // Идентификатор задачи
      "name": "String",                 // Название задачи
      "description": "String",          // Описание задачи
      "start_date": "ISODate",          // Дата начала задачи
      "end_date": "ISODate",            // Дата окончания задачи
      "status": "String",               // Статус задачи (запланировано/в работе/завершено)
      "created_at": "ISODate",          // Дата создания задачи
      "updated_at": "ISODate",          // Дата обновления задачи
      "workers": {                      // Список рабочих, ответственных за задачу
        "worker_id_1": {                // Идентификатор рабочего
          "user_id": "ObjectId",        // Идентификатор пользователя
          "name": "String"              // Имя рабочего
        },
        "worker_id_2": {
          "user_id": "ObjectId",
          "name": "String"
        }
      }
    },
    "task_id_2": {
      "name": "String",
      "description": "String",
      "start_date": "ISODate",
      "end_date": "ISODate",
      "status": "String",
      "created_at": "ISODate",
      "updated_at": "ISODate",
      "workers": {
        "worker_id_1": {
          "user_id": "ObjectId",
          "name": "String"
        }
      }
    }
  }
}

9. Чат (Chat)

{
  "_id": "ObjectId",                     // Уникальный идентификатор чата
  "participants": {                      // Словарь участников чата
    "user1_id": {                        // Идентификатор пользователя
      "name": "string",                   // Имя пользователя
      "lastSeen": "ISODate"               // Временная метка последнего просмотра
    },
    "user2_id": {                        // Идентификатор пользователя
      "name": "string",                   // Имя пользователя
      "lastSeen": "ISODate"               // Временная метка последнего просмотра
    }
  },
  "lastMessage": {                       // Последнее сообщение в чате
    "content": "string",                 // Содержание сообщения
    "sender": "string",                  // Идентификатор отправителя
    "status": "string",                  // Статус сообщения (например, "unread", "read")
    "timestamp": "ISODate"               // Временная метка сообщения
  },
  "createdAt": "ISODate",                // Временная метка создания чата
  "updatedAt": "ISODate"                 // Временная метка последнего обновления чата
}


Оценка удельного объема информации

Средний размер одного документа коллекции:

Предположительно пользователь будет создавать:

  • заказчиков в среднем u

  • в среднем прорабов u

  • в среднем работников 8*u

  • в среднем 1*u проектов

  • в среднем 3 этапа на проект

  • в среднем 5 задач на этап

  • в среднем 105u рассылок + 630u сообщений между пользователями

  • в среднем 100 закупок на проект

  • в среднем 8 рабочих + 1 прораб+ 1 заказчик на проект

  • в среднем 5 рисков на проект

User:

{
  "_id": "ObjectId": 12 байт,
  "surname": "String": 500 байт,
  "name": "String": 500 байт,
  "father_name": "String": 500 байт,
  "username": "String": 500 байт,
  "password": "String": 500 байт,
  "role": "String": 50 байт,
  "created_at": "ISODate": 8 байт,
  "updated_at": "ISODate": 8 байт
}
  • Общий размер: приблизительно 2578 байт.

Project:


{
  "_id": {
    "type": "ObjectId",
    "size_bytes": 12
  },
  "name": {
    "type": "String",
    "size_bytes": 500
  },
  "description": {
    "type": "String",
    "size_bytes": 1000
  },
  "start_date": {
    "type": "ISODate",
    "size_bytes": 8
  },
  "end_date": {
    "type": "ISODate",
    "size_bytes": 8
  },
  "status": {
    "type": "String",
    "size_bytes": 50
  },
  "created_at": {
    "type": "ISODate",
    "size_bytes": 8
  },
  "updated_at": {
    "type": "ISODate",
    "size_bytes": 8
  },
  "contacts": {
    "contact_id_1": {
      "user_id": {
        "type": "ObjectId",
        "size_bytes": 12
      },
      "created_at": {
        "type": "ISODate",
        "size_bytes": 8
      },
      "updated_at": {
        "type": "ISODate",
        "size_bytes": 8
      }
    },
    "contact_id_2": {
      "user_id": {
        "type": "ObjectId",
        "size_bytes": 12
      },
      "created_at": {
        "type": "ISODate",
        "size_bytes": 8
      },
      "updated_at": {
        "type": "ISODate",
        "size_bytes": 8
      }
    },
    "total_size_bytes": 56
  },
  "stages": {
    "stage_id_1": {
      "name": {
        "type": "String",
        "size_bytes": 500
      },
      "start_date": {
        "type": "ISODate",
        "size_bytes": 8
      },
      "end_date": {
        "type": "ISODate",
        "size_bytes": 8
      },
      "created_date": {
        "type": "ISODate",
        "size_bytes": 8
      },
      "updated_at": {
        "type": "ISODate",
        "size_bytes": 8
      },
      "tasks": {
        "task_id_1": {
          "name": {
            "type": "String",
            "size_bytes": 500
          },
          "description": {
            "type": "String",
            "size_bytes": 1000
          },
          "start_date": {
            "type": "ISODate",
            "size_bytes": 8
          },
          "end_date": {
            "type": "ISODate",
            "size_bytes": 8
          },
          "status": {
            "type": "String",
            "size_bytes": 50
          },
          "created_at": {
            "type": "ISODate",
            "size_bytes": 8
          },
          "updated_at": {
            "type": "ISODate",
            "size_bytes": 8
          },
          "workers": {
            "worker_id_1": {
              "user_id": {
                "type": "ObjectId",
                "size_bytes": 12
              },
              "name": {
                "type": "String",
                "size_bytes": 50
              }
            },
            "total_size_bytes": 62
          },
          "total_size_bytes": 1644
        },
        "total_size_bytes": 1644
      },
      "total_size_bytes": 536
    },
    "total_size_bytes": 536
  },
  "risks": {
    "risk_id_1": {
      "name": {
        "type": "String",
        "size_bytes": 500
      },
      "description": {
        "type": "String",
        "size_bytes": 1000
      },
      "created_at": {
        "type": "ISODate",
        "size_bytes": 8
      },
      "updated_at": {
        "type": "ISODate",
        "size_bytes": 8
      }
    },
    "total_size_bytes": 1516
  },
  "procurements": {
    "procurement_id_1": {
      "item_name": {
        "type": "String",
        "size_bytes": 500
      },
      "quantity": {
        "type": "Number",
        "size_bytes": 8
      },
      "price": {
        "type": "Number",
        "size_bytes": 8
      },
      "created_by": {
        "type": "ObjectId",
        "size_bytes": 12
      },
      "created_date": {
        "type": "ISODate",
        "size_bytes": 8
      },
      "updated_at": {
        "type": "ISODate",
        "size_bytes": 8
      },
      "delivery_date": {
        "type": "ISODate",
        "size_bytes": 8
      }
    },
    "total_size_bytes": 540
  },
  "total_document_size_bytes":
}

  • Общий размер: приблизительно 12114 байт

Task:

{
  "_id": "ObjectId": 12 байт,
  "name": "String": 500 байт,
  "description": "String": 1000 байт,
  "start_date": "ISODate": 8 байт,
  "end_date": "ISODate": 8 байт,
  "status": "String": 50 байт,
  "created_at": "ISODate": 8 байт,
  "updated_at": "ISODate": 8 байт,
  "workers": [{ // Предположим 3 работника
    "user_id": "ObjectId": 12 байт,
    "name": "String": 500 байт
  }] * 3 = (12 * 3 + 500 * 3) = 1536 байт
}
  • Общий размер: приблизительно 3130 байт

Procurement:

{
  "_id": "ObjectId": 12 байт,
  "item_name": "String": 500 байт,
  "quantity": "Number": 8 байт,
  "price": "Number": 8 байт,
  "created_date": "ISODate": 8 байт,
  "updated_at": "ISODate": 8 байт,
  "project_id": "ObjectId": 12 байт
}
  • Общий размер: приблизительно 556 байт

Чат:

{
  "_id": "12 байт",
  "participants": {
    "user1_id": {
      "name": "50 байт",
      "lastSeen": "8 байт"
    },
    "user2_id": {
      "name": "50 байт",
      "lastSeen": "8 байт"
    }
  },
  "lastMessage": {
    "content": "200 байт",
    "sender": "50 байт",
    "status": "20 байт",
    "timestamp": "8 байт"
  },
  "createdAt": "8 байт",
  "updatedAt": "8 байт",
}

  • Общий размер: приблизительно 422 байт

При количестве заказчиков равным u:

V(u)=(2578×10 + 422×8+ 12114+735×570+15×3130+556×100)×u= 562770×u

Избыточность данных

Предположительно пользователь будет создавать:

  • заказчиков в среднем u

  • в среднем прорабов u

  • в среднем работников 8*u

  • в среднем 1*u проектов

  • в среднем 3 этапа на проект

  • в среднем 5 задач на этап

  • в среднем 105×u рассылок + 630×u сообщений между пользователями

  • в среднем 100 закупок на проект

  • в среднем 8 рабочих + 1 прораб+ 1 заказчик на проект

  • в среднем 5 рисков на проект

User:

{
  "surname": "String": 500 байт,
  "name": "String": 500 байт,
  "father_name": "String": 500 байт,
  "username": "String": 500 байт,
  "password": "String": 500 байт,
  "role": "String": 50 байт,
}
  • Общий размер: приблизительно 2550 байт.

Project:


  "name": {
    "type": "String",
    "size_bytes": 500
  },
  "description": {
    "type": "String",
    "size_bytes": 1000
  },
  "start_date": {
    "type": "ISODate",
    "size_bytes": 8
  },
  "end_date": {
    "type": "ISODate",
    "size_bytes": 8
  },
  "status": {
    "type": "String",
    "size_bytes": 50
  },
  "stages": {
    "stage_id_1": {
      "name": {
        "type": "String",
        "size_bytes": 500
      },
      "start_date": {
        "type": "ISODate",
        "size_bytes": 8
      },
      "end_date": {
        "type": "ISODate",
        "size_bytes": 8
      },
      "tasks": {
        "task_id_1": {
          "name": {
            "type": "String",
            "size_bytes": 500
          },
          "description": {
            "type": "String",
            "size_bytes": 1000
          },
          "start_date": {
            "type": "ISODate",
            "size_bytes": 8
          },
          "end_date": {
            "type": "ISODate",
            "size_bytes": 8
          },
          "status": {
            "type": "String",
            "size_bytes": 50
          },
          "workers": {
            "worker_id_1": {
              "name": {
                "type": "String",
                "size_bytes": 50
              }
            },
            "total_size_bytes": 50
          },
          "total_size_bytes": 1564
        },
      },
      "total_size_bytes": 516
    },
  },
  "risks": {
    "risk_id_1": {
      "name": {
        "type": "String",
        "size_bytes": 500
      },
      "description": {
        "type": "String",
        "size_bytes": 1000
      },
    },
    "total_size_bytes": 1500
  },
  "procurements": {
    "procurement_id_1": {
      "item_name": {
        "type": "String",
        "size_bytes": 500
      },
      "quantity": {
        "type": "Number",
        "size_bytes": 8
      },
      "price": {
        "type": "Number",
        "size_bytes": 8
      },
      "created_by": {
        "type": "ObjectId",
        "size_bytes": 12
      },
      "created_date": {
        "type": "ISODate",
        "size_bytes": 8
      },
    },
    "total_size_bytes": 536
  },
  "total_document_size_bytes":


  • Общий размер: приблизительно 10344 байт

Message:

{
  "text": "String": 500 байт,
  "sent_date": "ISODate": 8 байт,
  "sender_id": "ObjectId": 12 байт,
  "receiver_id": "ObjectId": 12 байт,
  "status": "String": 10 байт
}
  • Общий размер: приблизительно 542 байт

Task:

{
  "name": "String": 500 байт,
  "description": "String": 1000 байт,
  "start_date": "ISODate": 8 байт,
  "end_date": "ISODate": 8 байт,
  "status": "String": 50 байт,
  "workers": [{ // Предположим 3 работника
    "name": "String": 500 байт
  }] * 3 = (500 * 3) = 1500 байт
}
  • Общий размер: приблизительно 3066 байт

Procurement:

{
  "item_name": "String": 500 байт,
  "quantity": "Number": 8 байт,
  "price": "Number": 8 байт,
}
  • Общий размер: приблизительно 516 байт

Чат:

{
  "participants": {
    "user1_id": {
      "name": "50 байт",
      "lastSeen": "8 байт"
    },
    "user2_id": {
      "name": "50 байт",
      "lastSeen": "8 байт"
    }
  },
  "lastMessage": {
    "content": "200 байт",
    "sender": "50 байт",
    "status": "20 байт",
    "timestamp": "8 байт"
  },
}

  • Общий размер: приблизительно 394 байт

Vclean(u)=(2550×10 + 394×8 + 10344+735×542+15×3066+516×100)×u =534956×u

Тогда рассчитаем избыточность как отношение объема модели к "чистому" объему:

V(u)/Vclean(u)=562770∗u/534956 ∗u≈1,052

Направление роста модели

Модель представленных данных демонстрирует несколько направлений роста, каждое из которых может влиять на общую архитектуру и производительность системы. Вот ключевые направления:

Рост количества пользователей (User)

  • С увеличением числа пользователей растет объем данных, что требует эффективного управления идентификаторами, паролями и ролями.
  • Каждое новое добавление пользователя увеличивает объем дублированных данных, например, при хранении информации о проектах и сообщениях, связанных с пользователями.

Рост числа проектов (Project)

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

Расширение функционала этапов (Stage) и задач (Task)

  • С увеличением числа проектов и этапов возникает необходимость в более сложной структуре для управления задачами.
  • Увеличение числа задач может привести к необходимости более детальной оценки статусов и сроков выполнения, что также увеличивает объем данных.

Увеличение количества сообщений (Message)

  • По мере роста количества пользователей и проектов возрастает и объем сообщений между ними. Это может привести к увеличению нагрузки на систему и потребовать оптимизации хранения и обработки данных.

Расширение структуры рисков (Risk) и закупок (Procurement)

  • С увеличением числа проектов и их сложностью увеличивается необходимость в управлении рисками и закупками. Это требует создания более детализированных записей и статусов для рисков и закупок, что увеличивает общий объем данных.

Рост связей между сущностями

  • Увеличение числа связей между пользователями, проектами, этапами, задачами, рисками и закупками требует эффективного управления этими связями.
  • С увеличением связей увеличивается сложность запросов и операций в базе данных, что может повлиять на производительность.

Экспоненциальный рост избыточности данных

  • Каждое новое добавление пользователя, проекта или задачи может привести к экспоненциальному увеличению избыточности данных, особенно если данные дублируются в разных коллекциях.
  • Это создает необходимость в реализации методов нормализации данных и стратегий управления дублирующимися записями, чтобы минимизировать избыточность.

Примеры данных

1. Пользователь (User)

{
  "_id": ObjectId("60c72b2f9c3d4d1234567890"),
  "surname": "Иванов",
  "name": "Иван",
  "father_name": "Иванович",
  "username": "ivanov_i",
  "password": "$2a$10$CwF5W.x5Bz4L.D1BZj/R7e.OW2iWO5NOhp7M7N5QAn3ceWDhSEzy6", // Пример хеша пароля
  "role": "Администратор",
  "created_at": ISODate("2023-01-01T00:00:00Z"),
  "updated_at": ISODate("2023-01-15T00:00:00Z")
}

2. Проект (Project)

{
  "_id": ObjectId("64e3b8c1f2b3c9d4e5f6a7b8"),
  "name": "Проект разработки ПО",
  "description": "Разработка нового программного обеспечения для клиента",
  "start_date": ISODate("2023-10-01T00:00:00Z"),
  "end_date": ISODate("2023-12-31T00:00:00Z"),
  "status": "в работе",
  "created_at": ISODate("2023-09-25T00:00:00Z"),
  "updated_at": ISODate("2023-10-05T00:00:00Z"),

  "contacts": {
    "contact_id_1": {
      "user_id": ObjectId("64e3b8c1f2b3c9d4e5f6a7b9"),
      "created_at": ISODate("2023-09-25T00:00:00Z"),
      "updated_at": ISODate("2023-10-05T00:00:00Z")
    },
    "contact_id_2": {
      "user_id": ObjectId("64e3b8c1f2b3c9d4e5f6a7ba"),
      "created_at": ISODate("2023-09-25T00:00:00Z"),
      "updated_at": ISODate("2023-10-05T00:00:00Z")
    }
  },

  "stages": {
    "stage_id_1": {
      "name": "Этап разработки",
      "start_date": ISODate("2023-10-01T00:00:00Z"),
      "end_date": ISODate("2023-10-31T00:00:00Z"),
      "created_date": ISODate("2023-09-25T00:00:00Z"),
      "updated_at": ISODate("2023-10-05T00:00:00Z"),
      "tasks": {
        "task_id_1": {
          "name": "Разработка нового модуля",
          "description": "Разработка и тестирование нового модуля для приложения",
          "start_date": ISODate("2023-10-01T00:00:00Z"),
          "end_date": ISODate("2023-10-15T00:00:00Z"),
          "status": "в работе",
          "created_at": ISODate("2023-09-25T00:00:00Z"),
          "updated_at": ISODate("2023-10-05T00:00:00Z"),
          "workers": {
            "worker_id_1": {
              "user_id": ObjectId("64e3b8c1f2b3c9d4e5f6a7b9"),
              "name": "Иван Иванов"
            }
          }
        }
      }
    }
  },

  "risks": {
    "risk_id_1": {
      "name": "Риск задержки поставки",
      "description": "Возможная задержка поставки оборудования",
      "created_at": ISODate("2023-09-25T00:00:00Z"),
      "updated_at": ISODate("2023-10-05T00:00:00Z")
    },
    "risk_id_2": {
      "name": "Риск нехватки ресурсов",
      "description": "Возможная нехватка человеческих ресурсов",
      "created_at": ISODate("2023-09-25T00:00:00Z"),
      "updated_at": ISODate("2023-10-05T00:00:00Z")
    }
  },

  "procurements": {
    "procurement_id_1": {
      "item_name": "Ноутбук",
      "quantity": 10,
      "price": 50000,
      "created_by": ObjectId("64e3b8c1f2b3c9d4e5f6a7b9"),
      "created_date": ISODate("2023-10-01T00:00:00Z"),
      "updated_at": ISODate("2023-10-05T00:00:00Z"),
      "delivery_date": ISODate("2023-10-15T00:00:00Z")
    },
    "procurement_id_2": {
      "item_name": "Монитор",
      "quantity": 5,
      "price": 20000,
      "created_by": ObjectId("64e3b8c1f2b3c9d4e5f6a7ba"),
      "created_date": ISODate("2023-10-01T00:00:00Z"),
      "updated_at": ISODate("2023-10-05T00:00:00Z"),
      "delivery_date": ISODate("2023-10-20T00:00:00Z")
    }
  }
}


3. Этап (Stage)

{
  "_id": ObjectId("64e3b8c1f2b3c9d4e5f6a7b8"),
  "name": "Этап разработки",
  "start_date": ISODate("2023-10-01T00:00:00Z"),
  "end_date": ISODate("2023-10-31T00:00:00Z"),
  "created_date": ISODate("2023-09-25T00:00:00Z"),
  "updated_at": ISODate("2023-10-05T00:00:00Z"),
  "tasks": {
    "task_id_1": {
      "name": "Разработка нового модуля",
      "description": "Разработка и тестирование нового модуля для приложения",
      "start_date": ISODate("2023-10-01T00:00:00Z"),
      "end_date": ISODate("2023-10-15T00:00:00Z"),
      "status": "в работе",
      "created_at": ISODate("2023-09-25T00:00:00Z"),
      "updated_at": ISODate("2023-10-05T00:00:00Z"),
      "workers": {
        "worker_id_1": {
          "user_id": ObjectId("64e3b8c1f2b3c9d4e5f6a7b9"),
          "name": "Иван Иванов"
        },
        "worker_id_2": {
          "user_id": ObjectId("64e3b8c1f2b3c9d4e5f6a7ba"),
          "name": "Петр Петров"
        }
      }
    },
    "task_id_2": {
      "name": "Обновление документации",
      "description": "Обновление и дополнение документации для проекта",
      "start_date": ISODate("2023-10-10T00:00:00Z"),
      "end_date": ISODate("2023-10-20T00:00:00Z"),
      "status": "запланировано",
      "created_at": ISODate("2023-10-01T00:00:00Z"),
      "updated_at": ISODate("2023-10-01T00:00:00Z"),
      "workers": {
        "worker_id_1": {
          "user_id": ObjectId("64e3b8c1f2b3c9d4e5f6a7b9"),
          "name": "Иван Иванов"
        }
      }
    }
  }
}

4. Задача (Task)

{
  "tasks": [
    {
      "_id": ObjectId("60c72b2f9c3d4d1234567890"),
      "name": "Разработка модуля аутентификации",
      "description": "Создание и тестирование модуля аутентификации для веб-приложения.",
      "start_date": ISODate("2023-10-01T00:00:00Z"),
      "end_date": ISODate("2023-10-15T00:00:00Z"),
      "status": "в работе",
      "created_at": ISODate("2023-09-25T00:00:00Z"),
      "updated_at": ISODate("2023-10-05T00:00:00Z"),
      "workers": [
        {
          "user_id": ObjectId("60c72b2f9c3d4d1234567891"),
          "name": "Иван Иванов"
        },
        {
          "user_id": ObjectId("60c72b2f9c3d4d1234567892"),
          "name": "Петр Петров"
        }
      ]
    },
    {
      "_id": ObjectId("60c72b2f9c3d4d1234567893"),
      "name": "Тестирование интерфейса",
      "description": "Проведение тестирования пользовательского интерфейса на различных устройствах.",
      "start_date": ISODate("2023-10-16T00:00:00Z"),
      "end_date": ISODate("2023-10-25T00:00:00Z"),
      "status": "запланировано",
      "created_at": ISODate("2023-09-28T00:00:00Z"),
      "updated_at": ISODate("2023-10-01T00:00:00Z"),
      "workers": [
        {
          "user_id": ObjectId("60c72b2f9c3d4d1234567894"),
          "name": "Анна Сидорова"
        },
        {
          "user_id": ObjectId("60c72b2f9c3d4d1234567895"),
          "name": "Сергей Кузнецов"
        }
      ]
    }
  ]
}

5. Сообщение (Message)

{
    "chatId": "64ff1234a1b2c3d4e5f67890",
    "sender": "user1_id",
    "receiver": "user2_id",
    "content": "Привет, как дела?",
    "status": "unread",
    "timestamp": "2024-11-08T12:31:00Z"
}


6. Закупка (Procurement)

{
  "_id": "ObjectId",                     // Уникальный идентификатор закупки
  "item_name": "String",                 // Наименование товара
  "quantity": "Number",                  // Количество товара
  "price": "Number",                     // Цена товара
  "created_by": "ObjectId",              // Идентификатор пользователя, создавшего закупку
  "created_date": "ISODate",             // Дата создания закупки
  "updated_at": "ISODate",               // Дата последнего обновления
  "delivery_date": "ISODate",            // Дата поставки товара
  "project_id": "ObjectId"               // Идентификатор связанного проекта
}


7. Риск (Risk)

{
  "_id": ObjectId("60c72b2f9c3d4d1234567890"),
  "name": "Задержка поставок",
  "description": "Возможная задержка поставок оборудования, что может привести к задержке проекта.",
  "created_at": ISODate("2023-01-01T00:00:00Z"),
  "updated_at": ISODate("2023-01-15T00:00:00Z")
}

8. Контакт (Contact)

{
  "_id": ObjectId("60c72b2f9c3d4d1234567890"),
  "user_id": ObjectId("60c72b2f9c3d4d1234567891"),
  "project_id": ObjectId("60c72b2f9c3d4d1234567892"),
  "created_at": ISODate("2023-01-01T00:00:00Z"),
  "updated_at": ISODate("2023-01-15T00:00:00Z")
}

9. Чат (Chat)

{
  "_id": "ObjectId('64ff1234a1b2c3d4e5f67890')",  
  "participants": {                             
    "user1_id": {                              
      "name": "Иван Иванов",                   
      "lastSeen": ISODate("2024-11-08T12:00:00Z")       
    },
    "user2_id": {                              
      "name": "Мария Петрова",                 
      "lastSeen": ISODate("2024-11-08T12:05:00Z")       
    }
  },
  "lastMessage": {                             
    "content": "Последнее сообщение",          
    "sender": "user1_id",                      
    "status": "unread",                        
    "timestamp": ISODate("2024-11-08T12:30:00Z")       
  },
  "createdAt": ISODate("2024-11-08T10:00:00Z"),       
  "updatedAt": ISODate("2024-11-08T12:30:00Z")        
}



Примеры запросов

Авторизация пользователя

MONGO_DETAILS = "mongodb://localhost:27017"
client = AsyncIOMotorClient(MONGO_DETAILS)
database = client['repair-db']

# ...

async def authenticate_user(username: str, password: str):
    user_collection = database.get_collection("users")
    user = await user_collection.find_one({"username": username})

    if user and verify_password(password, user["password"]):
        return user
    else:
        return None

Добавление пользователя

MONGO_DETAILS = "mongodb://localhost:27017"
client = AsyncIOMotorClient(MONGO_DETAILS)
database = client['repair-db']

# ...

async def create_user(user: User):
    user_collection = database.get_collection("users")
    user.password = hash_password(user.password)
    user.created_at = datetime.utcnow()
    user.updated_at = datetime.utcnow()
    result = await user_collection.insert_one(user.dict(by_alias=True))
    return result.inserted_id

Создание проекта

MONGO_DETAILS = "mongodb://localhost:27017"
client = AsyncIOMotorClient(MONGO_DETAILS)
database = client['repair-db']

# ...
class Project(BaseModel):
    _id: ObjectId = Field(default_factory=ObjectId, alias="_id")
    name: str
    description: str
    start_date: datetime
    end_date: datetime
    status: str
    created_at: datetime
    updated_at: datetime
    contacts: Dict[str, dict] = {}
    stages: Dict[str, dict] = {}
    risks: Dict[str, dict] = {}
    procurements: Dict[str, dict] = {}

# ...

async def create_project(project: Project):
    project_collection = database.get_collection("projects")
    project.created_at = datetime.utcnow()
    project.updated_at = datetime.utcnow()
    result = await project_collection.insert_one(project.dict(by_alias=True))
    return result.inserted_id

Создание задачи

MONGO_DETAILS = "mongodb://localhost:27017"
client = AsyncIOMotorClient(MONGO_DETAILS)
database = client['repair-db']

# ...

class Task(BaseModel):
    _id: ObjectId = Field(default_factory=ObjectId, alias="_id")
    name: str
    description: str
    start_date: datetime
    end_date: datetime
    status: str
    created_at: datetime
    updated_at: datetime
    workers: Dict[str, Worker] = {}

# ...

async def create_task(task: Task):
    task_collection = database.get_collection("tasks")
    task.created_at = datetime.utcnow()
    task.updated_at = datetime.utcnow()
    result = await task_collection.insert_one(task.dict(by_alias=True))
    return result.inserted_id

Добавление пользователя в задачу

MONGO_DETAILS = "mongodb://localhost:27017"
client = AsyncIOMotorClient(MONGO_DETAILS)
database = client['repair-db']

# ...
async def add_worker_to_task(task_id: str, worker: Worker):
    task_collection = database.get_collection("tasks")
    result = await task_collection.update_one(
        {"_id": ObjectId(task_id)},
        {"$set": {f"workers.{str(worker.user_id)}": worker.dict(by_alias=True)}}
    )
    return result.modified_count

Создание чата и сообщения

MONGO_DETAILS = "mongodb://localhost:27017"
client = AsyncIOMotorClient(MONGO_DETAILS)
database = client['repair-db']

# ...

class LastMessage(BaseModel):
    content: str
    sender: str
    status: str
    timestamp: datetime

class Participant(BaseModel):
    name: str                                                    
    lastSeen: datetime

class Chat(BaseModel):
    _id: ObjectId = Field(default_factory=ObjectId, alias="_id")
    participants: Dict[ObjectId, Participant]
    lastMessage: LastMessage
    createdAt: datetime
    updatedAt: datetime

# ...

class Message(BaseModel):
    _id: ObjectId = Field(default_factory=ObjectId, alias="_id")
    chatId: ObjectId
    sender: str
    receiver: str
    content: str
    status: str
    timestamp: datetime

# ...

async def create_chat(chat: Chat):
    chat_collection = database.get_collection("chats")
    chat.createdAt = datetime.utcnow()
    chat.updatedAt = datetime.utcnow()
    result = await chat_collection.insert_one(chat.dict(by_alias=True))
    return result.inserted_id

async def create_message(message: Message):
    message_collection = database.get_collection("messages")
    result = await message_collection.insert_one(message.dict(by_alias=True))
    return result.inserted_id

Обновление чата (появление нового сообщения)

MONGO_DETAILS = "mongodb://localhost:27017"
client = AsyncIOMotorClient(MONGO_DETAILS)
database = client['repair-db']

# ...

async def update_last_message(chat_id: str, message: Message):
    chat_collection = database.get_collection("chats")
    last_message = LastMessage(
        content=message.content,
        sender=message.sender,
        status=message.status,
        timestamp=message.timestamp
    )
    result = await chat_collection.update_one(
        {"_id": ObjectId(chat_id)},
        {"$set": {"lastMessage": last_message.dict(by_alias=True), "updatedAt": datetime.utcnow()}}
    )
    return result


async def create_message_route(message_create: Message):
    message = Message(**message_create.dict())
    message_id = await create_message(message)
    await update_last_message(message.chatId, message)
    return {"_id": message_id, **message.dict()}

Поиск чатов

MONGO_DETAILS = "mongodb://localhost:27017"
client = AsyncIOMotorClient(MONGO_DETAILS)
database = client['repair-db']

# ...

async def get_chats_for_user(user_id: str):
    chat_collection = database.get_collection("chats")
    chats = await chat_collection.find({"participants.{}".format(user_id): {"$exists": True}}).to_list(None)
    return chats

Получение всех сообщений всех пользователей, на которые не ответили в чате

async def get_unread_messages():
    message_collection = database.get_collection("messages")
    messages = await message_collection.find({"status": "unread"}).to_list(None)
    return messages

Создание риска

client = MongoClient('mongodb://localhost:27017/')
db = client['repair-db']

# ...

def create_risk(name, description):
    risks_collection = db['risks']
    risk = {
        "_id": ObjectId(),
        "name": name,
        "description": description,
        "created_at": datetime.utcnow(),
        "updated_at": datetime.utcnow()
    }
    risks_collection.insert_one(risk)

create_risk("Risk 1", "Description of Risk 1")

Создание закупки

client = MongoClient('mongodb://localhost:27017/')
db = client['repair-db']

# ...

def create_procurement(item_name, quantity, price, project_id):
    procurements_collection = db['procurements']
    procurement = {
        "_id": ObjectId(),
        "item_name": item_name,
        "quantity": quantity,
        "price": price,
        "created_date": datetime.utcnow(),
        "updated_at": datetime.utcnow(),
        "project_id": project_id
    }
    procurements_collection.insert_one(procurement)

create_procurement("Item 1", 10, 100.0, ObjectId())

Получение суммарных сумм закупок по каждой стадии проекта

async def get_total_procurement_sums():
    procurement_collection = database.get_collection("procurements")
    pipeline = [
        {
            "$group": {
                "_id": None,
                "total_sum": {"$sum": {"$multiply": ["$quantity", "$price"]}}
            }
        },
        {
            "$project": {
                "_id": 0,
                "total_sum": 1
            }
        }
    ]
    result = await procurement_collection.aggregate(pipeline).to_list(None)
    return result

Получение статистики

client = MongoClient('mongodb://localhost:27017/')
db = client['repair-db']

# ...

def get_statistics(stat_type, project_ids, start_date, end_date):
    if stat_type not in ['risks', 'procurements']:
        raise ValueError("Неподдерживаемый тип статистики. Поддерживаемые типы: 'risks', 'procurements'.")

    query = {
        'project_id': {'$in': [ObjectId(pid) for pid in project_ids]},
        'created_date': {
            '$gte': start_date,
            '$lte': end_date
        }
    }

    if stat_type == 'risks':
        collection = db['risks']
    elif stat_type == 'procurements':
        collection = db['procurements']

    statistics = collection.find(query)
    return list(statistics)

roject_ids = ['60f1b8d4e138234068a9099a', '60f1b8d4e138234068a9099b']  
start_date = datetime(2023, 1, 1)
end_date = datetime(2023, 12, 31)

# Получение статистики по рискам
risk_statistics = get_statistics('risks', project_ids, start_date, end_date)

# Получение статистики по закупкам
procurement_statistics = get_statistics('procurements', project_ids, start_date, end_date)

Импорт на примере коллекции projects

import json
from pymongo import MongoClient
from datetime import datetime
from bson import ObjectId


client = MongoClient('mongodb://localhost:27017/')
db = client['repair-db']
projects_collection = db['projects']

def convert_date_strings(data):
    for item in data:
        for key in ['start_date', 'end_date', 'created_at', 'updated_at']:
            if key in item:
                try:
                    item[key] = datetime.strptime(item[key], '%Y-%m-%dT%H:%M:%SZ')
                except ValueError:
                    raise ValueError(f"Некорректный формат даты для поля {key}: {item[key]}")
        if '_id' in item:
            item['_id'] = ObjectId(item['_id'])
    return data

def validate_data(data):
    required_fields = ['_id', 'name', 'description', 'start_date', 'end_date', 'status', 'created_at', 'updated_at']
    for item in data:
        for field in required_fields:
            if field not in item:
                raise ValueError(f"Отсутствует обязательное поле {field} в элементе: {item}")
    return data


with open('data.json', 'r') as file:
    data = json.load(file)


data = validate_data(data)
data = convert_date_strings(data)
projects_collection.insert_many(data)

Экспорт на примере коллекции projects

import json
from pymongo import MongoClient
from datetime import datetime
from bson import ObjectId

client = MongoClient('mongodb://localhost:27017/')
db = client['repair-db']
projects_collection = db['projects']

def convert_datetime_to_string(data):
    for item in data:
        for key in ['start_date', 'end_date', 'created_at', 'updated_at']:
            if key in item and isinstance(item[key], datetime):
                item[key] = item[key].strftime('%Y-%m-%dT%H:%M:%SZ')
        if '_id' in item:
            item['_id'] = str(item['_id'])
    return data


data = list(projects_collection.find())
data = convert_datetime_to_string(data)


with open('exported_projects.json', 'w') as file:
    json.dump(data, file, indent=4)

Сравнение моделей

Удельный объем информации

Реляционная модель данных обычно имеет меньший удельный объем информации по сравнению с нереляционной. Это связано с тем, что в нереляционных системах данные часто дублируются для уменьшения числа обращений к базе данных, что приводит к избыточности. В реляционной модели используются разные таблицы для хранения связанных данных, что требует больше времени на объединение (JOIN) при сложных запросах, но обеспечивает более компактное хранение и минимизацию дублирования. Выбор между реляционной и нереляционной моделями зависит от требований системы, таких как скорость доступа или целостность данных.

Сравнения для моделей

  • для модели User:  2578 байт(NoSQL) | 2527 байт(SQL)

  • для модели Project: 12062 байт(NoSQL) | 11253 байт(SQL)

  • для модели Task: 3130 байт(NoSQL) | 1063 байт(SQL)

  • для модели Message: 570 байт(NoSQL) | 563 байт(SQL)

  • для модели Procurement: 556 байт(NoSQL) | 546 байт(SQL)

Запросы по отдельным юзкейсам

В нереляционной модели количество запросов для выполнения юзкейсов остаётся неизменным, независимо от количества объектов в базе данных. В реляционной модели с увеличением числа объектов количество запросов может возрастать.

Сравнения количества запросов для отдельных юзкейсов

  • Регистрация/Авторизация: 1 запрос в (SQL) vs 1 запрос (NoSQL)

  • Создание проекта: 1 запрос в (SQL) vs 1 запрос (NoSQL)

  • Создание риска: 1 запрос в (SQL) vs 1 запрос (NoSQL)

  • Поиск сообщений: 1 запрос в (SQL) vs 1 запрос (NoSQL)

  • Получение статистики: 1 запрос в (SQL) vs 1 запрос (NoSQL)

Количество задействованных коллекций

  • Регистрация/Авторизация: 1 (SQL) vs 1(NoSQL)

  • Создание проекта: 1 (SQL) vs 1 (NoSQL)

  • Создание риска: 1 (SQL) vs 1 (NoSQL)

  • Поиск сообщений: 1 (SQL) vs 1 (NoSQL)

  • Получение статистики: 1 (SQL) vs 1 (NoSQL)

Вывод

На основе проведённого анализа можно сделать следующие выводы для данного проекта:

  • Удельный объём информации: Реляционная модель данных обладает меньшим объёмом хранения, чем нереляционная. Это позволяет более эффективно использовать дисковое пространство за счёт минимизации дублирования данных.

  • Количество запросов: Обе модели демонстрируют приблизительно одинаковые результаты по количеству запросов, необходимых для выполнения основных юзкейсов, что свидетельствует о схожей производительности при обращении к данным.

  • Число используемых коллекций: Нереляционная модель позволяет уменьшить количество задействованных коллекций за счёт встроенной поддержки сложных структур данных, что может упростить некоторые аспекты разработки.

Несмотря на то, что MongoDB (нереляционная модель) может упростить архитектуру запросов и снизить нагрузку на уровне приложений, она требует более тщательного управления бизнес-логикой в самой базе данных. Это может привести к усложнению разработки и снижению гибкости при масштабировании приложения.

С учётом этих факторов реляционная модель на базе SQL выглядит предпочтительнее для данного проекта, так как она позволяет более эффективно использовать ресурсы и облегчает поддержку приложения в долгосрочной перспективе.

Clone this wiki locally