#pragma once ///////////////////////////////////////////////////////////////////// // DbCore.h - Implements NoSql database prototype // // ver 1.2 // // Jim Fawcett, CSE687 - Object Oriented Design, Spring 2018 // ///////////////////////////////////////////////////////////////////// /* * Package Operations: * ------------------- * This package provides two classes: * - DbCore implements core NoSql database operations, but does not * provide editing, querying, or persisting. Those are left as * exercises for students. * - DbElement provides the value part of our key-value database. * It contains fields for name, description, date, child collection * and a payload field of the template type. * The package also provides functions for displaying: * - set of all database keys * - database elements * - all records in the database * Required Files: * --------------- * DbCore.h, DbCore.cpp * DateTime.h, DateTime.cpp * Utilities.h, Utilities.cpp * * Maintenance History: * -------------------- * ver 1.3 : 17 Feb 2018 * - added containsChildKey, addChildKey, and removeChildKey to DbElement
* - added addRecord, removeRecord, and parents to DbCore
* ver 1.2 : 10 Feb 2018
* - moved definition of Key, Keys, and Children to NoSqlDb namespace scope
* ver 1.1 : 19 Jan 2018
* - added code to throw exception in index operators if input key is not in database
* ver 1.0 : 10 Jan 2018
* - first release
*/
#include ::containsChildKey(const Key& key)
{
Keys::iterator start = children_.begin();
Keys::iterator end = children_.end();
return std::find(start, end, key) != end;
}
//----< add key to children collection >-----------------------------
template ::addChildKey(const Key& key)
{
if (!containsChildKey(key))
{
children_.push_back(key);
return true;
}
else
{
return false;
}
}
//----< remove child key from children collection >------------------
template ::removeChildKey(const Key& key)
{
size_t keyCount = children_.size();
std::vector makeElement(const std::string& name, const std::string& descrip = "")
{
DbElement elem;
elem.name(name);
elem.descrip(descrip);
return elem;
}
/////////////////////////////////////////////////////////////////////
// DbCore class
// - provides core NoSql db operations
// - does not provide editing, querying, or persistance operations
template & operator[](const Key& key);
DbElement operator[](const Key& key) const;
typename iterator begin() { return dbStore_.begin(); }
typename iterator end() { return dbStore_.end(); }
// methods to get and set the private database hash-map storage
DbStore& dbStore() { return dbStore_; }
DbStore dbStore() const { return dbStore_; }
void dbStore(const DbStore& dbStore) { dbStore_ = dbStore; }
bool addRecord(const Key& key, const DbElement & elem);
bool removeRecord(const Key& key);
Parents parents(const Key& key);
private:
DbStore dbStore_;
bool doThrow_ = false;
};
/////////////////////////////////////////////////////////////////////
// DbCore methods
//----< show file name >---------------------------------------------
template ::identify(std::ostream& out)
{
out << "\n \"" << __FILE__ << "\"";
}
//----< does db contain this key? >----------------------------------
template ::contains(const Key& key)
{
iterator iter = dbStore_.find(key);
return iter != dbStore_.end();
}
//----< returns current key set for db >-----------------------------
template ::keys()
{
Keys dbKeys;
DbStore& dbs = dbStore();
size_t size = dbs.size();
dbKeys.reserve(size);
for (auto item : dbs)
{
dbKeys.push_back(item.first);
}
return dbKeys;
}
//----< return number of db elements >-------------------------------
template ::size()
{
return dbStore_.size();
}
//----< extracts value from db with key >----------------------------
/*
* - indexes non-const db objects
* - In order to create a key-value pair in the database like this:
* db[newKey] = newDbElement
* we need to index with the new key and assign a default element.
* That will be replaced by newDbElement when we return from operator[]
* - However, if we want to index without creating new elements, we need
* to throw an exception if the key does not exist in the database.
* - The behavior we get is determined by doThrow_. If false we create
* a new element, if true, we throw. Creating new elements is the default
* behavior.
*/
template & DbCore ::operator[](const Key& key)
{
if (!contains(key))
{
if (doThrow_)
throw(std::exception("key does not exist in db"));
else
return (dbStore_[key] = DbElement ());
}
return dbStore_[key];
}
//----< extracts value from db with key >----------------------------
/*
* - indexes const db objects
*/
template DbCore ::operator[](const Key& key) const
{
if (!contains(key))
{
throw(std::exception("key does not exist in db"));
}
return dbStore_[key];
}
//----< adds database record if key does not exist >-----------------
template ::addRecord(const Key& key, const DbElement & elem)
{
if (contains(key))
return false;
dbStore_[key] = elem;
return true;
}
//----< removes database record if key exists >----------------------
template ::removeRecord(const Key& key)
{
size_t numErased = dbStore_.erase(key);
Parents parents = this->parents(key);
for (auto dbKey : parents)
{
dbStore_[dbKey].removeChildKey(key);
}
return numErased > 0;
}
//----< find all parents of record index by key >--------------------
template ::parents(const Key& key)
{
Parents parents;
for (auto item : dbStore_)
{
if (item.second.containsChildKey(key))
parents.push_back(item.first);
}
return parents;
}
/////////////////////////////////////////////////////////////////////
// display functions
//----< display database key set >-----------------------------------
template & db, std::ostream& out = std::cout)
{
out << "\n ";
for (auto key : db.keys())
{
out << key << " ";
}
}
//----< show record header items >-----------------------------------
/*
* - will use definitions (Definitions.h):
* showKey = true
* doNotShowKey = false
* for first argument
*/
inline void showHeader(bool showKey = true, std::ostream& out = std::cout)
{
out << "\n ";
if(showKey)
out << std::setw(15) << std::left << "Key";
out << std::setw(15) << std::left << "Name";
out << std::setw(26) << std::left << "DateTime";
out << std::setw(20) << std::left << "Description";
out << std::setw(20) << std::left << "Payload";
out << "\n ";
if(showKey)
out << std::setw(15) << std::left << "-------------";
out << std::setw(15) << std::left << "-------------";
out << std::setw(26) << std::left << "------------------------";
out << std::setw(20) << std::left << "------------------";
out << std::setw(20) << std::left << "------------------";
}
//----< display DbElements >-----------------------------------------
template & el, std::ostream& out = std::cout)
{
out << "\n ";
out << std::setw(15) << std::left << el.name().substr(0, 13);
out << std::setw(26) << std::left << std::string(el.dateTime());
out << std::setw(20) << std::left << el.descrip().substr(0, 18);
out << std::setw(20) << std::left << std::string(el.payLoad()).substr(0, 18);
Children children = el.children();
if (children.size() > 0)
{
out << "\n child keys: ";
for (auto key : children)
{
out << " " << key;
}
}
}
//----< display Db records >-----------------------------------------
template & el, std::ostream& out = std::cout)
{
out << "\n ";
out << std::setw(15) << std::left << key.substr(0, 13);
out << std::setw(15) << std::left << el.name().substr(0, 13);
out << std::setw(26) << std::left << std::string(el.dateTime());
out << std::setw(20) << std::left << el.descrip().substr(0, 18);
out << std::setw(20) << std::left << std::string(el.payLoad()).substr(0, 18);
Children children = el.children();
if (children.size() > 0)
{
out << "\n child keys: ";
for (auto key : children)
{
out << " " << key;
}
}
}
//----< display all records in database >----------------------------
template & db, std::ostream& out = std::cout)
{
showHeader(true, out);
typename DbCore ::DbStore dbs = db.dbStore();
for (auto item : dbs)
{
showRecord(item.first, item.second, out);
}
}
}