Database design

I've decided to make my own "database" tailored for things I need to do.

Signed-off-by: Bryan Schumaker <bjschuma@gmail.com>
This commit is contained in:
Bryan Schumaker 2013-06-27 16:20:38 -04:00 committed by Anna Schumaker
parent 80e00dd914
commit 2ea1ea8d26
1 changed files with 144 additions and 53 deletions

View File

@ -17,92 +17,183 @@ Files:
design.txt
ocarina/gui/
ocarina/include/
database.h
database.hpp
group.h
library.h
playlist.h
ocarina/lib/
database.cpp
group.cpp
library.cpp
playlist.cpp
ocarina/tests/
$HOME/.ocarina{-debug}/
library/global.db
library/{0-X}
album.db
artist.db
genre.db
library.db
track.db
Database: (ocarina.db)
<Research FTS tables and inverted indexes for filtering>
On-disk files:
I use the disk to store data between sessions, this could include
library state and user preferences. In theory, file formats do not
change often so updating between file formats should be possible.
Supporting all previous file formats can create a lot of clutter in
the code, so I will ONLY support updating from the previous file-format
version. Ocarina 5.x used two different numbers to represent the
current file format (library = 2 and playlist = 3). I want to unify
this into a single number shared across all files for simplicity, and
then create a class to read and write data on disk.
Database: (lib/database.cpp)
Ocarina 5.x created a different save file format for each type of
data that needed to be stored (preferences, library paths, playlists).
I intend to unify everything into a generic file format that can be
accessed through a generic database interface. The database code will
be in charge of printing the "valid" bit for each DatabaseEntry so that
child classes do not need to call into the parent class. If valid ==
true, the DatabaseEntry will be streamed out followed by a newline. If
valid == false the database will print the next entry in the vector.
Modules should inherit from the DatabasEntry class and implement their
own read() and write() functions. The "valid" field will be stored
before these functions are called, and the entry will be skipped if
valid is set to false.
The Database class is a templated class, so code could potentially
get messy. Normal class declarations can still exist in the file
include/database.h and member functions can be written in the file
include/database.hpp, which will be included by database.h. Any
function not relying on a template can be written in lib/database.cpp.
Structures and constants:
#define FILE_VERSION 4
class DatabaseEntry { /* let database modify valid flag */
private:
bool valid;
public:
virtual istream &operator>>(istream &) = 0;
virtual ostream &operator<<(ostream &) = 0;
friend class Database;
};
template <class T>
class Database {
private:
unsigned int _size; /* Number of valid rows */
string filename;
vector<T> db;
public:
Database::Database(filename);
void load();
void save();
void insert(T);
void delete(unsigned int);
const unsigned int &size();
const T &operator[](unsigned int);
};
File formats:
Database:
FILE_VERSION db.size()
INDEX db[INDEX].valid DatabaseEntry
INDEX db[INDEX].valid DatabaseEntry
...
DatabaseEntry:
<CHILD_CLASS_DATA>
API:
Database.Database(filename);
Initializes database to use ~/.ocarina{-debug}/filename
Database.load();
Reads data from file. Call after static initialization of
Ocarina to ensure idle tasks are configured.
Database.save();
Saves data to file.
Database.insert(T &);
Adds a new item to the db
Database.delete(unsigned int index);
Mark db[index] as invalid (quick deletion)
Database.size();
Returns number of valid rows in the database
Database.operator[unsigned int index]
Return a reference to db[index]
Tags index -
<another FTS table>
Tag Name -> song ids
User settings -
create table if not exists config(
name PRIMARY KEY,
value INT,
);
Library: (lib/library.cpp)
This file will manage and control access to the library.
This file will manage and control access to the library. The library
will be split into 5 database tables based on the content being stored,
and should be initialized before access attempts.
Album and artist information tables will be saved to a file in
library/global.db and can be shared across multiple library paths.
Internal:
struct Album {
unsigned int id;
class Album : public DatabaseEntry {
string name;
short year;
};
vector<Album>;
struct Artist {
unsigned int id;
class Artist : public DatabaseEntry {
string name;
};
vector<Artist>
struct Genre {
unsigned int id;
class Genre : public DatabaseEntry {
string name;
};
vector<Genre>
struct Track {
unsigned int id;
unsigned int artist_id;
unsigned int album_id;
unsigned int genre_id;
bool valid;
class Library : public DatabaseEntry {
string base_path;
bool enabled;
};
string filepath;
string title;
string length_str;
short track;
short last_year;
short last_month;
short last_day;
unsigned int play_count;
unsigned int length;
};
class Track : public DatabaseEntry {
unsigned int artist_id;
unsigned int album_id;
unsigned int genre_id;
unsigned int library_id;
class Library {
string base_path;
bool enabled;
bool valid;
vector<struct Track>
};
short track;
short last_year;
short last_month;
short last_day;
unsigned int play_count;
unsigned int length;
string title;
string length_str;
string filepath;
};
typedef struct trackid_t {
unsigned int library_id;
unsigned int track_id;
};
Database album_db;
Database artist_db;
Database genre_db;
Database library_db;
Database track_db;
File formats:
Album:
year name
Artist:
name
Genre:
name
Track:
artist_id album_id genre_id library_id track last_year last_month last_day play_count length
title
length_str
filepath
Library:
enabled base_path;
- API