#pragma once /////////////////////////////////////////////////////////////////////// // Query.h - Returns information from db metadata and payload // // ver 1.1 // // Jim Fawcett, CSE687 - Object Oriented Design, Spring 2018 // /////////////////////////////////////////////////////////////////////// /* * Package Operations: * ------------------- * This package provides classes: * - Conditions holds the conditions that define the data returned from a query * - Query provides methods: select, from, keys, and show * * Required Files: * --------------- * Query.h, Query.cpp * PayLoad.h, PayLoad.cpp - application defined package * DbCore.h, DbCore.cpp * * Maintenance History: * -------------------- * ver 1.1 : 11 Mar 2018 * - discovered while grading midterms that Query

::select(Conditions

&) and * template Query

::select(CallObj) were iterating over all * keys in db, not just the keys in the query. * This was a performance issue, not a correctness issue, now fixed. * - fixed potential race condition in * template Query

::asyncSelect(CallObj) * Ver 1.0 : 10 Feb 2018 * - first release * */ #include "../DbCore/Definitions.h" #include "../DbCore/DbCore.h" #include #include #include #include #include namespace NoSqlDb { //using Key = std::string; //using Keys = std::vector; ///////////////////////////////////////////////////////////////////// // Conditions class // - defines data returned by query template class Conditions { public: using RegExp = const std::string&; Conditions() = default; void value(DbElement

& elem) { pDbElem_ = &elem; } // set pointer to element used during query bool match(); void name(RegExp regExp) { nameRegExp_ = regExp; } bool matchName(); void description(RegExp regExp) { descriptionRegExp_ = regExp; } bool matchDescription(); void lowerBound(const DateTime& lower) { lowerBound_ = lower; } void upperBound(const DateTime& upper = DateTime.now()) { upperBound_ = upper; } bool matchDateTimeInterval(); void children(Keys keys) { keys_ = keys; } bool matchChildren(); /////////////////////////////////////////////////////////////// // Not currently using the two functions, below. // Included, as I may decide to use them later. // template // void payLoadCallableObject(CallObj callObj) { callObj_ = callObj; } // template // bool matchPayLoad() // { // return callObj_(pDbElem_->payLoad()); // } /////////////////////////////////////////////////////////////// private: // DbElement

is referenced through pointer, not C++ reference type, // so the element can be changed at any time. DbElement

* pDbElem_ = nullptr; std::string nameRegExp_ = ""; std::string descriptionRegExp_ = ""; DateTime lowerBound_ = DateTime().now(); DateTime upperBound_ = DateTime().now(); Keys keys_; /////////////////////////////////////////////////////////////// // Not currently used, but may later. // template // static CallObj callObj_; }; /*----< test element metadata for specified conditions >-----------*/ template bool Conditions

::match() { try { return matchName() && matchDescription() && matchChildren() && matchDateTimeInterval(); } catch (std::exception& ex) { std::cout << "\n exception thrown:\n " << ex.what(); } return false; } /*----< test element metadata for name match >---------------------*/ template bool Conditions

::matchName() { if (nameRegExp_ == "") return true; std::regex re(nameRegExp_); return std::regex_search(pDbElem_->name(), re); } /*----< test metadata for description match >----------------------*/ template bool Conditions

::matchDescription() { if (descriptionRegExp_ == "") return true; std::regex re(descriptionRegExp_); std::string desc = pDbElem_->descrip(); return std::regex_search(desc, re); } /*----< test metadata for time interval match >--------------------*/ template bool Conditions

::matchDateTimeInterval() { if (lowerBound_ == upperBound_) return true; DateTime elemDateTime = pDbElem_->dateTime(); bool aboveLower = lowerBound_ < elemDateTime; bool belowUpper = upperBound_ > elemDateTime; return aboveLower && belowUpper; } /*----< test metadata for children matches >-----------------------*/ template bool Conditions

::matchChildren() { if (keys_.size() == 0) return true; Keys::iterator start = pDbElem_->children().begin(); Keys::iterator end = pDbElem_->children().end(); for (Key key : keys_) { if (std::find(start, end, key) == end) return false; } return true; } ///////////////////////////////////////////////////////////////////// // Query class template class Query { public: Query() {} Query(DbCore

& db) : pDb_(&db) { keys_ = pDb_->keys(); } Query(const Query

& q); Query(Query

&& q); Query

& operator=(const Query

& q); Query

& operator=(Query

&& q); static void identify(std::ostream& out = std::cout); Query

& select(Conditions

& conds); template Query

& select(CallObj callObj); template std::future> asyncSelect(CallObj& callobj); Query

& query_or(Query

& q); Query

& from(const Keys& keys) { keys_ = keys; return *this; } void show(std::ostream& out = std::cout); Keys& keys() { return keys_; } private: DbCore

* pDb_; // non-owning pointer to db Keys keys_; }; //----< copy constructor >------------------------------------------- template Query

::Query(const Query

& q) { keys_ = q.keys_; pDb_ = q.pDb_; // non-owning so don't have to make src nullptr } //----< move constructor >------------------------------------------- template Query

::Query(Query

&& q) { keys_ = q.keys_; pDb_ = q.pDb_; // non-owning so don't have to make src nullptr } //----< copy assignment >-------------------------------------------- template Query

& Query

::operator=(const Query

& q) { if (this == &q) return *this; keys_ = q.keys_; pDb_ = q.pDb_; // non-owning so just copy return *this; } //----< move assignment >-------------------------------------------- template Query

& Query

::operator=(Query

&& q) { if (this == &q) return *this; keys_ = q.keys_; pDb_ = q.pDb_; // non-owning so don't have to make src nullptr return *this; } //----< show file name >--------------------------------------------- template void Query

::identify(std::ostream& out) { out << "\n \"" << __FILE__ << "\""; } /*----< returns keys for elements that match conds >---------------*/ template Query

& Query

::select(Conditions

& conds) { Keys newKeys; for (auto key : keys_) // searches current query key set { conds.value((*pDb_)[key]); if (conds.match()) newKeys.push_back(key); } ///////////////////////////////////////////////////// // code from ver 1.0 to show change // // for (auto item : *pDb_) // searches entire db // { // conds.value(item.second); // if (conds.match()) // newKeys.push_back(item.first); // } keys_ = newKeys; return *this; } /*----< supports application defined queries for payload >---------*/ /* * - CallObj is defined by the application to return results from * application's payload. See test stub for example uses. */ template template Query

& Query

::select(CallObj callObj) { Keys newKeys; for (auto key : keys_) // searches current keyset { if (callObj((*pDb_)[key])) newKeys.push_back(key); } /////////////////////////////////////////// // code from ver 1.0 to show change // // for (auto item : *pDb_) // searches entire db // { // if (callObj(item.second)) // newKeys.push_back(item.first); // } keys_ = newKeys; return *this; } /*----< supports application defined queries for payload >---------*/ /* * - CallObj is defined by the application to return results from * application's payload. This function selects asynchronously. */ template template std::future> Query

::asyncSelect(CallObj& callObj) { auto search = [&]() { Keys newKeys; for (Key key : keys_) { if (callObj((*pDb_)[key])) newKeys.push_back(key); } keys_ = newKeys; return *this; }; return std::async(search); //////////////////////////////////////////////////////// // Original desgin: // This code demonstrates concern about race condition. // // std::future> Query

::asyncSelect(CallObj& callObj) // { // std::future> f; // std::thread t( // [&]() { // std::promise> p; // std::future> f_thread(p.get_future()); // Keys newKeys; // f = std::move(f_thread); // race condition ?? // for (auto item : *pDb_) // { // if (callObj(item.second)) // newKeys.push_back(item.first); // } // keys_ = newKeys; // Query

q(*pDb_); // p.set_value(q.from(newKeys)); // } // ); // t.detach(); // return f; // } } /*----< form union of keys with q.keys() >-------------------------*/ template Query

& Query

::query_or(Query

& q) { Keys keys = q.keys(); for (auto key : keys) { Keys::iterator start = keys_.begin(); Keys::iterator end = keys_.end(); if(std::find(start, end, key) == end) keys_.push_back(key); } return *this; } /*----< displays query results >-----------------------------------*/ /* * - displays an element for each key in query keys */ template void Query

::show(std::ostream& out) { showHeader(showKey, out); for (auto key : keys_) { DbElement

temp = (*pDb_)[key]; showRecord

(key, temp, out); } } }