Skip to content

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

Vladimir Chernukha edited this page Oct 31, 2024 · 86 revisions

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

MongoDB

Коллекции

users

{
  "_id": ObjectId(), //id пользователя - 12 байт
  "photo": "string", // URL фотографии пользователя - в среднем 60 байт
  "login": "string", // Логин пользователя - в среднем 10 байт
  "email": "string", // Почта пользователя - в среднем 30 байт
  "role": "string", // Роль (админ, просто юзер) пользователя - 5 байт
  "password": "string", // Пароль пользователя - 256 байт
  "registred_at": ISODate(), // Дата регистрации - 8 байт
  "updated_at": ISODate(), // Дата обновления профиля - 8 байт
  "rates_count": integer, // Количество оценок - 4 байта
  "rates":[ // Оценки пользователя - в среднем 10 оценок ~ 610 байт
    {
      "_id": ObjectId(), // id оценки - 12 байт
      "anime_id": ObjectId(), // id аниме - 12 байт
      "anime_name": "string", // название аниме - в среднем 25 байт
      "date": ISODate(), // дата оценки - 8 байт
      "rate": "integer", // оценка - 4 байта
    }
    ],
  "account_logs":[ // Изменения аккаунта - в среднем 5 изменений ~ 300 байт
     {
      "_id": ObjectId(), // id изменения - 12 байт
      "date": ISODate(), // дата изменения - 8 байт
      "type": "string", // тип изменения (создание, изменение ника...) - в среднем 10 байт
      "description": "string",    // описание - в среднем 30 байт
     }
    ],
   "reviews_count": integer, // число отзывов пользователя - 4 байта
}

anime

{
  "_id": ObjectId(), // id аниме - 12 байт        
  "cover": "string", // url фото обложки - в среднем 60 байт       
  "name":"string", // название - в среднем 25 байт
  "type": "string",   // тип (сериал, фильм...)   - в среднем 7 байт     
  "episodes":"string", // количество эпизодов  - 2 байта
  "status": "string",     // статус (вышло, выходит..) - в среднем 6 байт    
  "year":ISODate(), // год выхода - 8 байт
  "genre": "[string]",   // жанр - в среднем 10 байт       
  "age_rating": "string", // возрастной рейтинг - в среднем 3 байта
  "other_names": [string],   //  другие названия - в среднем по 2 названия, в среднем ~ 50 байт      
  "studio":"string", // студия - в среднем 15 байт
  "rating":"double", // рейтинг - 8 байт
  "rates_count": "integer" // количество оценок - 4 байта
  "description":"string", // описание - в среднем 1000 байт
  "reviews":[ // отзывы - в среднем 10 отзывов ~ 9040 байт
    {
      "_id": ObjectId(),       //  id отзыва - 12 байт 
      "user_id": ObjectId(),  // id пользователя - 12 байт
      "photo_mini": "string",   // url маленького фото пользователя - в среднем 60 байт       
      "date": ISODate(),         //  дата написания - 8 байт
      "rate": "integer",        //  оценка - 4 байта
      "text": "string"         // текст отзыва - в среднем 800 байт
      "reccomendation": "string",  //  положительный/отрицательный/нейтральный отзыв - в среднем 8 байт
        }
    ]
   "negative_rev_count": "integer" // кол-во отрицательных отзывов
   "positive_rev_count": "integer"// кол-во положительных отзывов
   "neutral_rev_count": "integer"// кол-во нейтральных отзывов
}

review

 {
      "_id": ObjectId(), // id отзыва - 12 байт        
      "user_id": ObjectId(),  // id пользователя - 12 байт
      "anime_id": ObjectId(), // id аниме - 12 байт
      "anime_name": "string",  // название аниме - в среднем 25 байт
      "anime_cover_mini":"string", // url маленькой обложки аниме - в среднем 60 байт      
      "date": ISODate(),  // дата написания - 8 байт        
      "rate": "integer", //  оценка - 8 байт        
      "text": "string", //  текст отзыва - в среднем 800 байт     
      "reccomendation": "string",  //  положительный/отрицательный/нейтральный отзыв - в среднем 8 байт 
}

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

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

  • users - 1307 байт

Общий объем для всех пользователей: u*1307 байт, где u - количество пользователей

  • anime - 10262 байт

Общий объем для всех пользователей: a*10262 байт, где a - количество аниме

  • review - 945 байт

Общий объем для всех пользователей: r*945байт, где r - количество отзывов

Если выражать через количество пользователей, то получится следующая формула:

V(u) = (1307 + 945 * 10) * u + (904 * 10 * u + 1222)= 19797 * u + 1222

где u - количество пользователей, принимается в расчёт что каждый оставляет 10 отзывов

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

Избыточные данные:

  • Отзывы в anime- можно хранить id отзывов

Формула чистого объёма:

V_clean (u) = (1307 + 945 * 10)*u + (1222 + 12 * 10) = 10757 * u + 1342

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

V(u)/V_clean (u) = 1.84

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

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

  • Рост количества аниме: Прямопропорционально увеличивается общий объем данных аниме

  • Рост количества отзывов: Прямопропорционально увеличивается общий объем данных отзывов и увеличивается по объёму сущность аниме, для которого соответственно был оставлен отзыв

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

Коллекция users

{
  "_id": "233ca3fb4a4fgc7fa9f5ea21", 
  "photo": "https://azumangaphotos.com/osaka.jpg", 
  "login": "Osaka1998", 
  "email": "Osaka2003@mail.com", 
  "role": "admin", 
  "password": "admin", 
  "registred_at": "2024-09-18T15:10:50", 
  "updated_at": "2024-09-18T15:10:50", 
  "rates_count": 3, 
  "rates":[ 
    {
      "_id":"233ca3fb4a4fgc7fa9f5ea22", 
      "anime_id": "233ca3fb4a4fgc7fa9f5ea23", 
      "anime_name": "Адзуманга", 
      "date": "2024-09-18T15:10:50", 
      "rate": 10,
    }
    ],
  "account_logs":[
     {
      "_id": "233ca3fb4a4fgc7fa9f5ea25",  
      "date": "2024-09-18T15:10:50", 
      "type": "rename", 
      "description": "Логин изменён на Osaka1998",   
     }
    ],
   "reviews_count": 3,
}

Коллекция anime

{
  "_id": "233ca3fb4a4fgc7fa9f5ea23",       
  "cover": "https://anime.com/azumanga.jpg",      
  "name":"Адзуманга", 
  "type": "TV сериал",      
  "episodes":"26",
  "status": "Вышло",     
  "year":"2003-09-18T00:00:00", 
  "genre": ["Комедия"],         
  "age_rating": "PG-13", 
  "other_names": ["Azumanga Daioh: The Animation", "あずまんが大王 THE ANIMATION"],       
  "studio":"JC staff", 
  "rating": 8.03, 
  "rates_count": 16000, 
  "description":"Chiyo Mihama begins her high school career as one of the strangest students in her freshman class—a tiny, 10-year-old academic prodigy with a fondness for plush dolls and homemade cooking. But her homeroom teacher, Yukari Tanizaki, is the kind of person who would hijack a student's bike to avoid being late, so "strange" is a relative word. There certainly isn't a shortage of peculiar girls in Yukari-sensei's homeroom class. Accompanying Chiyo are students like Tomo Takino, an energetic tomboy with more enthusiasm than brains; Koyomi Mizuhara, Tomo's best friend whose temper has a fuse shorter than Chiyo; and Sakaki, a tall, athletic beauty whose intimidating looks hide a gentle personality and a painful obsession with cats. In addition, transfer student Ayumu Kasuga, a girl with her head stuck in the clouds, fits right in with the rest of the girls—and she has a few interesting theories about Chiyo's pigtails! Together, this lovable group of girls experience the ups and downs of school life, their many adventures filled with constant laughter, surreal absurdity, and occasionally even touching commentary on the bittersweet, temporal nature of high school.", 
  "reviews":[ 
    {
      "_id": "233ca3fb4a4fgc7fa9f5ea27",      
      "user_id": "233ca3fb4a4fgc7fa9f5ea21", 
      "photo_mini": "https://azumangaphotos.com/osaka_mini.jpg",         
      "date": "2023-09-18T10:00:00",        
      "rate": 10,       
      "text": "It seems to me that people who have seen Azumanga Daioh are divided into two groups. The first group consists of people who enjoyed it, and thought it was the best thing since sliced bread. The other consists of those that thought it was boring and nonsensical. I find myself right in the middle. It wasn’t so bad story wise – usually I’m all for “anything goes” anime. The problem I had with it was that it wasn’t as funny as I thought it would be. "        
      "reccomendation": "positive", 
        }
    ]
"negative_rev_count": 0,
"positive_rev_count": 1,
"neutral_rev_count": 0
}

Коллекция review

 {
      "_id":  "233ca3fb4a4fgc7fa9f5ea27",        
      "user_id": "233ca3fb4a4fgc7fa9f5ea21",  
      "anime_id":  "233ca3fb4a4fgc7fa9f5ea24", 
      "anime_name": "Адзуманга",  
      "anime_cover_mini":"https://anime.com/azumanga_mini.jpg",      
      "date": "2023-09-18T10:00:00",       
      "rate": 10,
      "text": "It seems to me that people who have seen Azumanga Daioh are divided into two groups. The first group consists of people who enjoyed it, and thought it was the best thing since sliced bread. The other consists of those that thought it was boring and nonsensical. I find myself right in the middle. It wasn’t so bad story wise – usually I’m all for “anything goes” anime. The problem I had with it was that it wasn’t as funny as I thought it would be. ",   
      "reccomendation": "positive",   
}

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

Авторизация

db.user.find({email: "Osaka2003@mail.com", password: "admin"})

Регистрация

db.users.insertOne({
    login: "Osaka1998", 
    email: "Osaka2003@mail.com",
    role: "user",
    password: "123",
    registred_at: new Date(),
    rates_count: 0,
    reviews_count: 0,
    account_logs:[
     {
      "_id": "233ca3fb4a4fgc7fa9f5ea25",  
      "date": new Date(), 
      "type": "create", 
      "description": "Аккаунт создан",   
     }
    ]
})

Изменение логина

db.user.updateOne({_id: ObjectId('671aacc86cf54cffac86b01e')},
{
   $set: {
       login:"newOsaka"
   },
   $currentDate: { updated_at: true, "updated_at": true },
   $push: {
       account_logs:{
            _id: ObjectId(),
            "date": new Date(), 
            "type": "rename", 
            "description": "Логин изменён на newOsaka",  
       }
   },
  
}
)

Поиск аниме с фильтрами

db.anime.find({genre: "Комедия", name: "Адзуманга", type: "TV сериал"}).limit(9)

Аниме с самыми мощными (по количеству) рецензиями

db.anime.aggregate([{$project: { count: { $size:"$reviews" }}}
,  { $sort: { count: -1 } }
])

Аниме с самыми слабыми (по количеству) рецензиями

db.anime.aggregate([{$project: { count: { $size:"$reviews" }}}
,  { $sort: { count: 1 } }
])

Выставление оценки

 db.anime.aggregate([
   {
     $match: {_id: "233ca3fb4a4fgc7fa9f5ea23"} 
   },
{
$set: {rates_count: {$add: ["$rates_count", 1]}}
},
   {
     $set: {
       rating: {$divide: [ { $add:  [ { $multiply: ["$rating", "$rates_count"] }, 10] } ,"$rates_count" ]}
     }
   },

   {
     $merge: {
       into: "anime",
       whenMatched: "merge"
    }
   }
])
db.user.updateOne({_id: "233ca3fb4a4fgc7fa9f5ea21"}, {$push : {rates: { _id: ObjectId(), anime_id: "233ca3fb4a4fgc7fa9f5ea23",anime_name: "Anime",date: new Date(), rate: 7,} }, $inc: {rates_count: 1} })

Изменение оценки

 db.anime.aggregate([
   {
     $match: {_id: "233ca3fb4a4fgc7fa9f5ea23"} 
   },
   

   {
     $set: {
       rating: {$divide: [ { $subtract: [{$add:  [ { $multiply: ["$rating", "$rates_count"] }, 10]}, 7] } ,"$rates_count" ]}
     }
   },

   {
     $merge: {
       into: "anime",
       whenMatched: "merge"
    }
   }
])
db.user.updateOne({_id: "233ca3fb4a4fgc7fa9f5ea21", "rates._id": "233ca3fb4a4fgc7fa9f5ea29"}, {$set : {rates.$.rate: 7, rates.$.date: new Date()} } })

Добавление отзыва

db.review.insertOne( {    
      user_id: "233ca3fb4a4fgc7fa9f5ea21",  
      anime_id:  "233ca3fb4a4fgc7fa9f5ea23", 
      anime_name: "Anime",  
      anime_cover_mini:"https://anime.com/anime_mini.jpg",      
      date: new Date(),       
      rate: 7,
      text: "good"
	reccomendation: "positive"
})
db.anime.updateOne({_id: "233ca3fb4a4fgc7fa9f5ea23"}, 
{$push : {reviews: { _id: ObjectId(), user_id: "233ca3fb4a4fgc7fa9f5ea21",   
      photo_mini:"https://anime.com/me_mini.jpg",      
      date: new Date(),       
      rate: 7,
      text: "good",
	reccomendation: "positive"} },$inc: { positive_rev_count: 1}  }) 
db.user.updateOne({_id:"233ca3fb4a4fgc7fa9f5ea21"},{$inc: {reviews_count: 1}} )

Редактирование отзыва

db.anime.updateOne({_id: "233ca3fb4a4fgc7fa9f5ea23", "reviews._id": "233ca3fb4a4fgc7fa9f5ea27"}, {$set : {reviews.$.text: "norm" } ,$currentDate: { updated_at: true, date: true } }) 
db.review.updateOne({_id: "233ca3fb4a4fgc7fa9f5ea27"}, {$set : {text: "norm" } ,$currentDate: { updated_at: true, date: true } })

Удаление отзыва

db.anime.deleteOne({_id:"233ca3fb4a4fgc7fa9f5ea23", "rewirws._id": "671aba5d6cf54cffac86b024"})
db.anime.updateOne({_id:"233ca3fb4a4fgc7fa9f5ea23"}, {$inc: {positive_rev_count: -1}})
db.review.deleteOne({_id: "671aba5d6cf54cffac86b024"})

Просмотр отзывов пользователя

db.review.find({user_id: "233ca3fb4a4fgc7fa9f5ea21"}).limit(2)

Просмотр пользователей по дате регистрации

db.user.find().sort({registred_date: 1}).limit(5)

Просмотр пользователей по увеличению количества отзывов

db.user.find().sort({reviews_count: 1}).limit(5)

Просмотр пользователей по уменьшению количества отзывов

db.user.find().sort({reviews_count: -1}).limit(5)

Просмотр истории пользователя

db.user.aggregate([
        {
                $match: {
                    _id: "233ca3fb4a4fgc7fa9f5ea21",
                }
            },
            {
                $lookup: {
                    from: "review",
                    localField: "_id",
                    foreignField: "user_id",
                    as: "reviews"
                }
            },

            {
                $project: {
                    actions: {$concatArrays: ["$rates", "$account_logs", "$reviews"]}
                }
            },
            { $unwind: "$actions" },
            {$sort : {
                "actions.date": -1}}
        
        ])

Аниме, у которых самые полярные рецензии

db.anime.aggregate([
  {
    $project: {
      positive_rev_count: 1,
      negative_rev_count: 1,
      difference: { $abs: { $subtract: ["$positive_rev_count", "$negative_rev_count"] } }
    }
  },
  {
    $sort: { difference: 1 } 
  }
]);

Подсчет и заполнение вычисляемого поля rates_count (user)

db.user.aggregate([
   {
     $match: {_id: "233ca3fb4a4fgc7fa9f5ea21"} 
   },
   
    { $set: {rates_count:{ $size:"$rates" } } }
   ,

   {
     $merge: {
       into: "user",
       whenMatched: "merge"
    }
   }
])

Подсчет и заполнение вычисляемого поля reviews_count (user)

db.user.aggregate([
  {
          $match: {
              _id: "233ca3fb4a4fgc7fa9f5ea21",
          }
      },
      {
          $lookup: {
              from: "review",
              localField: "_id",
              foreignField: "user_id",
              as: "reviews"
          }
      },
      

      { $set: {reviews_count:{ $size:"$reviews" } } },
      {
        $project: {
         "reviews_count": "$reviews_count",       
       }   
     },
      {
        $merge: {
          into: "user",
          whenMatched: "merge"
       }
      }
  ])

Подсчет и заполнение вычисляемого поля rates_count (anime)

db.anime.aggregate([
  {
          $match: {
              _id: "233ca3fb4a4fgc7fa9f5ea23",
          }
      },
      {
          $lookup: {
              from: "user",
              localField: "_id",
              foreignField: "rates.anime_id",
              as: "rates"
          }
      },
      {
        $project: {
         "rates": "$rates.rates", 
       }   
     },
     { $unwind: "$rates" },
     { $unwind: "$rates" },
     {
      $match: {
          "rates.anime_id": "233ca3fb4a4fgc7fa9f5ea23",
      }
    },
    {
      $group:
        {
          _id: "233ca3fb4a4fgc7fa9f5ea23",
          r_count: {$sum: 1}
        }
    },
    { $set: {rates_count: "$r_count" } },
      {
        $project: {
         "rates_count": "$rates_count",       
       }   
     },
      {
        $merge: {
          into: "anime",
          whenMatched: "merge"
       }
      }
  ])

Подсчет и заполнение вычисляемого поля rating (anime)

db.anime.aggregate([
  {
          $match: {
              _id: "233ca3fb4a4fgc7fa9f5ea23",
          }
      },
      {
          $lookup: {
              from: "user",
              localField: "_id",
              foreignField: "rates.anime_id",
              as: "rates"
          }
      },
      {
        $project: {
         "rates": "$rates.rates", 
       }   
     },
     { $unwind: "$rates" },
     { $unwind: "$rates" },
     {
      $match: {
          "rates.anime_id": "233ca3fb4a4fgc7fa9f5ea23",
      }
    },
    {
      $group:
        {
          _id: "233ca3fb4a4fgc7fa9f5ea23",
          sumrate: { $sum: "$rates.rate" },
          count: {$sum: 1}
        }
    },
    { $set: {rating: {$divide: ["$sumrate", "$count"] } } },
      {
        $project: {
         "rating": "$rating",       
       }   
     },
      {
        $merge: {
          into: "anime",
          whenMatched: "merge"
       }
      }
  ])

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

Графическое представление модели

Коллекции

Anime

Назначение: хранение информации об аниме.

Поля:

  • id: INT(4)

  • name: VARCHAR(N + 1)

  • cover: VARCHAR(N + 1)

  • id_type: INT(4)

  • episodes: INT(4)

  • date: DATETIME(8)

  • id_genre: INT(4)

  • id_age_rating: INT(4)

  • id_other_names: INT(4)

  • id_studio: INT(4)

  • rating: DOUBLE(8)

  • rates_count: INT(4)

  • description: VARCHAR(N + 1)

  • id_reviews: INT(4)

Genres

Назначение: хранение информации о жанрах аниме.

Поля:

  • id: INT(4)

  • name: VARCHAR(N + 1)

Types_Anime:

Назначение: хранение информации о типах аниме.

Поля:

  • id: INT(4)

  • name: VARCHAR(N + 1)

Age_Ratings

Назначение: хранение информации о возрастных рейтингах аниме.

Поля:

  • id: INT(4)

  • name: VARCHAR(N + 1)

Other_Names

Назначение: хранение информации о других названиях аниме.

Поля:

  • id: INT(4)

  • id_other_names: INT(4)

  • name: VARCHAR(N + 1)

Studios

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

Поля:

  • id: INT(4)

  • name: VARCHAR(N + 1)

Status

Назначение коллекции: хранение информации о статусе, в котором сейчас находится данное аниме.

Поля:

  • id: INT(4)

  • name: VARCHAR(N + 1)

Users

Назначение: хранение информации о зарегистрированных пользователях приложения

Поля:

  • id: INT(4)

  • photo: VARCHAR(N + 1)

  • login: VARCHAR(N + 1)

  • password: VARCHAR(N + 1)

  • email: VARCHAR(N + 1)

  • id_role: INT(4)

  • registred_at: DATETIME(8)

  • updated_at: DATETIME(8)

Account_Logs

Назначение: хранение логов действий пользователя со своим профилем в приложении.

Поля:

  • id: INT(4)

  • date: DATETIME(8)

  • id_type: INT(4)

  • description: VARCHAR(N + 1)

  • id_user: INT(4)

Types_Update

Назначение: хранение типов действий пользователя со своим профилем.

Поля:

  • id: INT(4)

  • name: VARCHAR(N + 1)

Reviews

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

Поля:

  • id: INT(4)

  • id_user: INT(4)

  • id_anime: INT(4)

  • date: DATETIME(8)

  • rate: DOUBLE(8)

  • text: VARCHAR(N + 1)

  • id_reccomendation: INT(4)

Reccomendations

Назначение: хранение отзывов на аниме, оставленных пользователями.

Поля:

  • id: INT(4)

  • reccomendation: VARCHAR(N + 1)

Оценка объема информации, хранимой в построенной модели.

Оценка размера одной записи в каждой коллекции

Пусть N = 100 символам, где каждый символ занимает 1 байт памяти, тогда каждая запись в коллекциях будет занимать следующие объемы места:

  • Anime: 4 * 9 + 8 * 2 + (N + 1) * 3 = 355 (байт)

  • Genres: 4 + (N + 1) = 105 (байт)

  • Types_Anime: 4 + (N + 1) = 105 (байт)

  • Age_Ratings: 4 + (N + 1) = 105 (байт)

  • Other_Names: 4 + (N + 1) = 105 (байт)

  • Studios: 4 * 2 + (N + 1) = 109 (байт)

  • Users: 4 * 2 + 8 * 2 + (N + 1) * 4 = 428 (байт)

  • Accounts_Logs: 4 * 2 + 8 + (N + 1) = 117 (байт)

  • Types_Update: 4 + (N + 1) = 105 (байт)

  • Reviews: 4 * 4 + 8 * 2 + (N + 1) = 133 (байт)

  • Reccomendations: 4 + (N + 1) = 105 (байт)

Оценка размера заполненных коллекций

Предположительно коллекции:
Genres, Types_Anime, Age_Ratings, Other_Names, Studios, Types_Update будут иметь около 20 записей;
коллекция Anime будет иметь около 10000 записей;
коллекция Accounts_Logs будет иметь 1000 записей на каждого пользователя;
Коллекции Reviews и Reccomendations будут иметь по 10 записей на каждого пользователя.

При количестве пользователей U имеем следующий объем занятого места:
V(U) = 10000 * 355 + 20 * 105 + 20 * 105 + 20 * 105 + 10 * 105 + 10 * 109 + U * 428 + 1000 * U * 117 + 20 * 105 + 10 * 10000 * U * 133 + 10 * 10000 * U * 105.
После приведения подобных слагаемых получим следующее выражение:
V(U) = 23917428 * U + 3560540 (байт)
Выразим полученный результат в Мбайт:
V(U) = 22.8 * U + 3.4 (Мбайт)

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

Оценка избыточности модели

Ниже представлены коллекции, а затем их поля, являющиеся избыточными:

  • Anime: id_type, id_genre, id_age_rating, id_other_names, id_studio, id_reviews: 4 * 6 * 10000 = 240000 (байт)

  • Other_Names: id_other_names: 4 * 20 = 80 (байт)

  • User: id_role, id_type: U * 4 * 2 = 8 * U (байт)

  • Accounts_Logs: id_type: 1000 * 4 * U = 4000 * U (байт)

  • Reviews: id_user, id_anime, id_reccomendation: 2 * 4 * U * 10 = 80 * U (байт)

  • Итоговая избыточность модели: 240000 + 80 + 8 * U + 4000 * U + 80 * U = 4088 * U + 240000

Формула для вычисления избыточности модели:

Больший коэффициент зависящий от числа пользователей чем свободный член позволяет сделать допущение и пренебречь свободным членом, тогда формула избыточности имеет вид:

$$\frac{V(U)}{V_{clear}(U)}$$ = $$\frac{23917428}{4088}$$ = 1.0002

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

Линейный рост сущностей будет при росте числа объектов следующих сущностей:

  • Genres

  • Types_Anime

  • Age_Ratings

  • Other_Names

  • Studios

  • Types_Update

  • Role

При росте количества объектов сущности Anime будет рост количества объектов сущностей Reviews и Reccomendations.

При росте количества объектов сущности Users будет расти количество объектов сущностей Reviews, Reccomendations, Accounts_Logs.

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

Genre

id name
1 Авангард
2 Спорт
3 Экшен
4 Драма
5 Триллер
6 Комедия
7 Повседневность
8 Фантастика
9 Приключения
10 Тайна
11 Романтика
12 Ужасы
13 Фэнтэзи

Types_Anime

id name
1 TV
2 Special
3 Ova
4 Movie

Age_Ratings

id name
1 G
2 PG
3 PG-13
4 R-17
5 R+

Other_Names

id id_other_names name
1 1 name1
2 2 name3
3 1 name4
4 1 name3
5 3 name1

Studios

id name
1 studio_1
2 studio_2
3 studio_3
4 studio_4
5 studio_5

Status

id name
1 Закончено
2 Сейчас выходит
3 Еще не вышло

Anime

id name cover id_type episodes date id_genre id_age_rating id_other_names id_studio rating id_status description id_reviews
1 name_1 url_1 2 35 15.10.2019 3 2 1 5 5.8 3 value_1 1
2 name_2 url_2 1 21 19.06.2003 2 5 2 3 7.1 1 value_2 2
3 name_3 url_3 3 12 01.09.2024 1 4 3 4 10.0 2 value_6 1

Users

id photo login password email role registered_at updated_at
1 user1_photo.jpg user1 pass1234 user1@example.com Пользователь 2023-10-10 12:00:00 2024-01-05 09:00:00
2 user2_photo.jpg user2 pass2345 user2@example.com Администратор 2023-11-15 14:30:00 2024-02-20 10:15:00
3 user3_photo.jpg user3 pass3456 user3@example.com Пользователь 2023-12-20 16:45:00 2024-03-25 11:45:00
4 user4_photo.jpg user4 pass4567 user4@example.com Пользователь 2024-01-25 18:00:00 2024-04-30 12:30:00
5 user5_photo.jpg user5 pass5678 user5@example.com Администратор 2024-02-28 09:15:00 2024-05-05 08:45:00

Accounts_Logs

id date id_type description
1 2023-10-10 15:30:00 1 Initial event description
2 2023-11-05 09:00:00 2 Follow-up event
3 2023-12-01 17:45:00 1 Update event details
4 2024-01-20 12:10:00 3 Special event description
5 2024-02-14 14:25:00 2 Regular check

Types_Update

id name
1 Event Type A
2 Event Type B
3 Event Type C
4 Event Type D
5 Event Type E

Reviews

id id_user id_anime date rate text id_reccomendation
1 101 201 2023-10-10 15:30:00 8.5 Great storyline 3
2 102 202 2023-11-11 16:45:00 9.0 Excellent animation 2
3 103 203 2023-12-12 18:00:00 7.5 Good but slow-paced 1
4 104 204 2024-01-13 20:30:00 8.0 Strong character development 5
5 105 205 2024-02-14 12:15:00 9.5 Amazing action scenes 4

Reccomendations

id recommendation
1 Highly recommended
2 Worth watching
3 Good for anime fans
4 Suitable for all audiences
5 Only for genre enthusiasts

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

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

Пусть пользователь зарегистрирован в приложении с электронной почтой = "user@mail.ru" и паролем = "123123123".

SELECT email, password
FROM USERS
WHERE email = "user@mail.ru" AND password = "123123123";

Количество запросов: 1. Количество задействованных таблиц: 1.

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

Пусть пользователь регистрируется в приложении с электронной почтой = "user2@mail.ru", логином = "login" и паролем = "Querty_0987765".

INSERT INTO USERS (email, login, password)  
VALUES ("user2@mail.ru", "login", "Querty_0987765");

Количество запросов: 1. Количество задействованных таблиц: 1.

Получение страницы с аниме

Пусть название аниме = "anime_name"

SELECT Anime.name, Anime.cover, Anime.episodes, Anime.date, Anime.rating, Anime.rates_count, Anime.description, Genres.name, Types_Anime.name, Age_Ratings.name, Other_Names.name, Studios.name, Reviews.date, Reviews.rate, Reviews,text, Reccomendations.reccomendation, User.photo, User.login FROM Anime
INNER JOIN Genres
ON Anime.id_genre = Genres.id
INNER JOIN Types_Anime
ON Anime.id_type = Types_Anime_id
INNER JOIN Age_Ratings
ON Anime.id_age_rating = Age_Ratings.id
INNER JOIN Other_Names
ON Anime.id_other_names = Other_Names.id_other_names
INNER JOIN Studios
ON Anime.id_studio = Studios.id
INNER JOIN Reviews
ON Anime.id_reviews = Reviews.id_anime
INNER JOIN Users
ON Reviews.id_user = User.id
INNER JOIN Reccomendations
ON Reviews.id_reccomendation = Reccomendations.reccomendation
WHERE name = "anime_name";

Количество запросов: 1. Количество задействованных таблиц: 9.

Профиль пользователя

Пусть пользователь зарегистрирован с логином "Osaka1998".

SELECT photo, login, registred_at, updated_at, Reviews.text, Reviews.date, Anime.name, Anime.rating
FROM Users
INNER JOIN Reviews
ON Users.id = Reviews.id_user
INNER JOIN Anime
ON Reviews.id_anime = Anime.id
WHERE login = "Osaka1998";

Количество запросов: 1. Количество задействованных таблиц: 3.

Изменение профиля пользователя

Пусть пользователь зарегистрирован с логином "Osaka1998". Произведем замену этого логина на "Makaka_228".

UPDATE Users SET login = "Makaka_228", password = "key", photo = "url1"
WHERE login = "Osaka1998";

Количество запросов: 1. Количество задействованных таблиц: 1.

История профиля пользователя

Пусть пользователь зарегистрирован с логином "Osaka1998".

SELECT login, Types_Update.name, Accounts_Logs.date, Accounts_Logs.description
FROM Users
INNER JOIN Accounts_Logs
ON Users.id = Accounts_Logs.id_user
INNER JOIN Types_Update
ON Accounts_Logs.id_type = Types_Update.id
WHERE login = "Osaka1998";

Количество запросов: 1. Количество задействованных таблиц: 3.

Все отзывы пользователя

Пусть пользователь зарегистрирован с логином "Osaka1998".

SELECT Anime.name, Reviews.date, Reviews.rate, Reviews.text
FROM Users
INNER JOIN Reviews
ON Users.id = Reviews.id_user
INNER JOIN Anime
ON Reviews.id_anime = Anime.id
WHERE login = "Osaka1998";

Количество запросов: 1. Количество задействованных таблиц: 3.

Аниме с самыми мощными (по количеству) рецензиями

SELECT Anime.name, Anime.cover, Anime.episodes, Anime.date, Anime.rating, Anime.rates_count, Anime.description, Genres.name, Types_Anime.name, Age_Ratings.name, Other_Names.name, Studios.name FROM Anime
INNER JOIN Genres
ON Anime.id_genre = Genres.id
INNER JOIN Types_Anime
ON Anime.id_type = Types_Anime_id
INNER JOIN Age_Ratings
ON Anime.id_age_rating = Age_Ratings.id
INNER JOIN Other_Names
ON Anime.id_other_names = Other_Names.id_other_names
INNER JOIN Studios
ON Anime.id_studio = Studios.id
INNER JOIN Reviews
ON Anime.id_reviews = Reviews.id_anime
GROUP BY Anime.id
ORDER BY COUNT(*) DESC
LIMIT 20;

Количество запросов: 1. Количество задействованных таблиц: 7.

Аниме с самыми слабыми (по количеству) рецензиями

SELECT Anime.name, Anime.cover, Anime.episodes, Anime.date, Anime.rating, Anime.rates_count, Anime.description, Genres.name, Types_Anime.name, Age_Ratings.name, Other_Names.name, Studios.name FROM Anime
INNER JOIN Genres
ON Anime.id_genre = Genres.id
INNER JOIN Types_Anime
ON Anime.id_type = Types_Anime_id
INNER JOIN Age_Ratings
ON Anime.id_age_rating = Age_Ratings.id
INNER JOIN Other_Names
ON Anime.id_other_names = Other_Names.id_other_names
INNER JOIN Studios
ON Anime.id_studio = Studios.id
INNER JOIN Reviews
ON Anime.id_reviews = Reviews.id_anime
GROUP BY Anime.id
ORDER BY COUNT(*) ASC
LIMIT 20;

Количество запросов: 1. Количество задействованных таблиц: 7.

Аниме, у которых самые полярные рецензии (много как положительных, так и отрицательных рецензий)

SELECT Anime.name, Anime.cover, Anime.episodes, Anime.date, Anime.rating, Anime.rates_count, Anime.description, Genres.name, Types_Anime.name, Age_Ratings.name, Other_Names.name, Studios.name FROM Anime
INNER JOIN Genres
ON Anime.id_genre = Genres.id
INNER JOIN Types_Anime
ON Anime.id_type = Types_Anime_id
INNER JOIN Age_Ratings
ON Anime.id_age_rating = Age_Ratings.id
INNER JOIN Other_Names
ON Anime.id_other_names = Other_Names.id_other_names
INNER JOIN Studios
ON Anime.id_studio = Studios.id
INNER JOIN Reviews
ON Anime.id_reviews = Reviews.id_anime
GROUP BY Anime.id
HAVING AVG(Reviews.rate) >= 3 AND AVG(Reviews.rate) <= 7
ORDER BY COUNT(*) DESC
LIMIT 20;

Количество запросов: 1. Количество задействованных таблиц: 7.

Подсчет и заполнение вычисляемого поля rates_count

UPDATE Anime
SET review_count = (
    SELECT COUNT(*)
    FROM Reviews
    WHERE Reviews.id_anime = Anime.id
);

Количество запросов: 2. Количество задействованных таблиц: 2.

Подсчет и заполнение вычисляемого поля rating

UPDATE Anime
SET rating = (
    SELECT AVG(rating)
    FROM Reviews
    WHERE Reviews.id_anime = Anime.id
);

Количество запросов: 2. Количество задействованных таблиц: 2.

Подсчет и заполнение вычисляемого поля reviews_count

UPDATE Users
SET reviews_count = (
    SELECT COUNT(*)
    FROM Reviews
    WHERE Reviews.id_user = Users.id
);

Количество запросов: 2. Количество задействованных таблиц: 2.

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

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

Основываясь на расчётах объемов информации в пункте "Оценка удельного объема информации" для нереляционных и реляционных БД, можно сделать следующее сравнение.

  • Разница общих объемов нереляционной и реляционной БД соответственно:

V(U) = (19797 * U + 1210) - (23917428 * U + 3560540) = -2399761 * U - 3560330

  • Разница чистых объемов нереляционной и реляционной БД соответственно:

V_clean(U) = (10757 * U + 1330) - (4088 * U + 240000) = 6669 * U - 238670

В реляционных БД данные занимают больше объема. Однако, при расчете "чистых" объемов реляционные БД оказываются более "легкими".

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

Количество запросов для совершения юзкейсов в зависимости от числа объектов в БД и прочих параметров:

Use case Нереляционная модель Реляционная модель
Авторизация 1 1
Регистрация 1 1
Выход из аккаунта 1 1
Массовый импорт U U
Массовый экспорт 1 1
Поиск аниме 1 1
Просмотр информации об аниме 1 1
Выставление оценки произведению 1 1
Комментирование произведения 1 1
Редактирование комментария произведения 1 1
Просмотр профиля 1 1
Просмотр истории пользователя 1 1
Изменение настроек 1 1
Просмотр отзывов пользователя 1 1
Просмотр статистики 1 1
Поиск пользователя 1 1

Количество задействованных коллекций в нереляционной модели равно трем(users, anime, review). В реляционной же БД это количество будет значительно больше, поскольку каждая подколлекция выражается в дополнительную сущность (Anime, Genres, Types_Anime, Age_Ratings, Other_Names, Studios, Status, Users, Account_Logs, Types_Update, Reviews, Reccomendations).

Вывод

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

  1. Удельный объем информации: Реляционные БД демонстрируют больший общий объем, однако при рассмотрении "чистых" объемов, то есть объема информации без учета вспомогательной структуры, оказывается, что реляционные модели имеют меньший объем.

  2. Запросы по отдельным юзкейсам: Нереляционная БД использует меньшее количество коллекций, что приводит к более простой модели данных и меньшему числу необходимых запросов. Также реляционная БД требует гораздо большего количества сущностей, что может усложнить процесс запросов и увеличивает траты ресурсов.

Таким образом, в данном проекте нереляционная модель данных подходит лучше поскольку выигрывает по простоте реализации, а также обеспечивают гибкость и простоту при работе с менее структурированными данными.

Clone this wiki locally