From cd520b9360f0d0d4ab7582d2cbf2aa96060a0500 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 27 Apr 2023 16:03:55 +1000 Subject: [PATCH] Make queries a little less copy-paste --- src/database/database.cpp | 41 +++++++--------- src/database/include/database.hpp | 80 ++++++++++++++++++++++++------- src/main/app_console.cpp | 14 ++++-- 3 files changed, 89 insertions(+), 46 deletions(-) diff --git a/src/database/database.cpp b/src/database/database.cpp index 8ca72771..2cff51ce 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -1,4 +1,5 @@ #include "database.hpp" +#include #include "esp_log.h" #include "ff.h" @@ -92,34 +93,24 @@ auto Database::Populate() -> std::future { }); } -auto Database::GetSongs(std::size_t page_size) -> std::future> { - return RunOnDbTask>([&]() -> DbResult { - std::unique_ptr it( - db_->NewIterator(leveldb::ReadOptions())); - it->Seek("title:"); - std::vector results; - IterateAndParse(it.get(), page_size, - [&](const leveldb::Slice& key, const leveldb::Slice& val) { - Song s; - s.title = key.ToString(); - results.push_back(s); - }); - return DbResult(results, std::move(it)); +auto parse_song(const leveldb::Slice& key, const leveldb::Slice& val) + -> std::optional { + Song s; + s.title = key.ToString(); + return s; +} + +auto Database::GetSongs(std::size_t page_size) -> std::future> { + return RunOnDbTask>([=, this]() -> Result { + return Query("title:", page_size, &parse_song); }); } -auto Database::GetMoreSongs(std::size_t page_size, DbResult continuation) - -> std::future> { - return RunOnDbTask>([&]() -> DbResult { - std::unique_ptr it(continuation.it()); - std::vector results; - IterateAndParse(it.get(), page_size, - [&](const leveldb::Slice& key, const leveldb::Slice& val) { - Song s; - s.title = key.ToString(); - results.push_back(s); - }); - return DbResult(results, std::move(it)); +auto Database::GetMoreSongs(std::size_t page_size, Continuation c) + -> std::future> { + leveldb::Iterator* it = c.release(); + return RunOnDbTask>([=, this]() -> Result { + return Query(it, page_size, &parse_song); }); } diff --git a/src/database/include/database.hpp b/src/database/include/database.hpp index bc102ca8..61918d96 100644 --- a/src/database/include/database.hpp +++ b/src/database/include/database.hpp @@ -11,6 +11,7 @@ #include "leveldb/cache.h" #include "leveldb/db.h" #include "leveldb/iterator.h" +#include "leveldb/options.h" #include "leveldb/slice.h" #include "result.hpp" @@ -33,17 +34,33 @@ struct Song { struct SongMetadata {}; +typedef std::unique_ptr Continuation; + template -class DbResult { +class Result { public: - DbResult(const std::vector& values, std::unique_ptr it) - : values_(values), it_(std::move(it)) {} - auto values() -> std::vector { return values_; } - auto it() -> leveldb::Iterator* { return it_.release(); }; + auto values() -> std::unique_ptr> { + return std::move(values_); + } + auto continuation() -> Continuation { return std::move(c_); } + auto HasMore() -> bool { return c_->Valid(); } + + Result(std::unique_ptr> values, Continuation c) + : values_(std::move(values)), c_(std::move(c)) {} + + Result(Result&& other) + : values_(std::move(other.values_)), c_(std::move(other.c_)) {} + + Result operator=(Result&& other) { + return Result(other.values(), other.continuation()); + } + + Result(const Result&) = delete; + Result& operator=(const Result&) = delete; private: - std::vector values_; - std::unique_ptr it_; + std::unique_ptr> values_; + Continuation c_; }; class Database { @@ -58,23 +75,23 @@ class Database { auto Populate() -> std::future; - auto GetArtists(std::size_t page_size) -> std::future>; - auto GetMoreArtists(std::size_t page_size, DbResult continuation) - -> std::future>; + auto GetArtists(std::size_t page_size) -> std::future>; + auto GetMoreArtists(std::size_t page_size, Continuation c) + -> std::future>; auto GetAlbums(std::size_t page_size, std::optional artist) - -> std::future>; - auto GetMoreAlbums(std::size_t page_size, DbResult continuation) - -> std::future>; + -> std::future>; + auto GetMoreAlbums(std::size_t page_size, Continuation c) + -> std::future>; - auto GetSongs(std::size_t page_size) -> std::future>; + auto GetSongs(std::size_t page_size) -> std::future>; auto GetSongs(std::size_t page_size, std::optional artist) - -> std::future>; + -> std::future>; auto GetSongs(std::size_t page_size, std::optional artist, - std::optional album) -> std::future>; - auto GetMoreSongs(std::size_t page_size, DbResult continuation) - -> std::future>; + std::optional album) -> std::future>; + auto GetMoreSongs(std::size_t page_size, Continuation c) + -> std::future>; auto GetSongIds(std::optional artist, std::optional album) -> std::future>; @@ -89,6 +106,33 @@ class Database { std::unique_ptr cache_; Database(leveldb::DB* db, leveldb::Cache* cache); + + template + using Parser = std::function(const leveldb::Slice& key, + const leveldb::Slice& value)>; + + template + auto Query(const leveldb::Slice& prefix, + std::size_t max_results, + Parser parser) -> Result { + leveldb::Iterator* it = db_->NewIterator(leveldb::ReadOptions()); + it->Seek(prefix); + return Query(it, max_results, parser); + } + + template + auto Query(leveldb::Iterator* it, std::size_t max_results, Parser parser) + -> Result { + auto results = std::make_unique>(); + for (std::size_t i = 0; i < max_results && it->Valid(); i++) { + std::optional r = std::invoke(parser, it->key(), it->value()); + if (r) { + results->push_back(*r); + } + it->Next(); + } + return {std::move(results), std::unique_ptr(it)}; + } }; } // namespace database diff --git a/src/main/app_console.cpp b/src/main/app_console.cpp index fc2a4fe5..00bfa993 100644 --- a/src/main/app_console.cpp +++ b/src/main/app_console.cpp @@ -176,10 +176,18 @@ int CmdDbSongs(int argc, char** argv) { return 1; } - database::DbResult res = + database::Result res = sInstance->database_->GetSongs(10).get(); - for (database::Song s : res.values()) { - std::cout << s.title << std::endl; + while (true) { + std::unique_ptr> r = res.values(); + for (database::Song s : *r) { + std::cout << s.title << std::endl; + } + if (res.HasMore()) { + res = sInstance->database_->GetMoreSongs(10, res.continuation()).get(); + } else { + break; + } } return 0;