M changelog.md => changelog.md +1 -0
@@ 12,6 12,7 @@
* `[audio]` Added support for headset microphone.
* `[filesystem]` Added support for standard file IO library.
* [`[messages]`] Added fetching text messages at phone startup.
+* `[service/db]` Added support for ListView in FileIndexer DB.
## Changed
M module-services/service-db/agents/file_indexer/FileIndexerAgent.cpp => module-services/service-db/agents/file_indexer/FileIndexerAgent.cpp +159 -22
@@ 17,7 17,7 @@ FileIndexerAgent::FileIndexerAgent(sys::Service *parentService) : DatabaseAgent(
void FileIndexerAgent::initDb()
{
- LOG_INFO("[ServiceDB][File Indexer] Initializing");
+ LOG_INFO("[ServiceDB][File Indexer] Initialized");
database->execute(getDbInitString().c_str());
}
@@ 49,6 49,11 @@ void FileIndexerAgent::registerMessages()
std::bind(&FileIndexerAgent::handleGetAllProperties, this, _1));
parentService->connect(FileIndexer::Messages::SetPropertiesMessage(),
std::bind(&FileIndexerAgent::handleSetProperties, this, _1));
+
+ parentService->connect(FileIndexer::Messages::RegisterOnFileChange(),
+ std::bind(&FileIndexerAgent::handleRegisterOnFileChange, this, _1));
+ parentService->connect(FileIndexer::Messages::UnregisterOnFileChange(),
+ std::bind(&FileIndexerAgent::handleUnregisterOnFileChange, this, _1));
}
auto FileIndexerAgent::getDbInitString() -> const std::string
@@ 61,7 66,6 @@ auto FileIndexerAgent::getDbInitString() -> const std::string
auto FileIndexerAgent::getDbFilePath() -> const std::string
{
-
return USER_PATH("file_indexer.db");
}
auto FileIndexerAgent::getAgentName() -> const std::string
@@ 69,10 73,55 @@ auto FileIndexerAgent::getAgentName() -> const std::string
return std::string("fileIndexerAgent");
}
+auto FileIndexerAgent::dbRegisterFileChange(std::string dir, std::string service) -> bool
+{
+ auto retQuery = database->query(FileIndexer::Statements::getNotification, dir.c_str(), service.c_str());
+ if (nullptr == retQuery || FileIndexer::ZERO_ROWS_FOUND == retQuery->getRowCount()) {
+ // notification does not exist in db, insert a new one
+ return database->execute(FileIndexer::Statements::setNotification, dir.c_str(), service.c_str());
+ }
+ // service already registered for the dir
+ return false;
+}
+
+auto FileIndexerAgent::dbUnregisterFileChange(std::string dir, std::string service) -> bool
+{
+ return database->execute(FileIndexer::Statements::clearNotificationdRow, dir.c_str(), service.c_str());
+}
+
+auto FileIndexerAgent::handleRegisterOnFileChange(sys::Message *req) -> sys::MessagePointer
+{
+ if (auto msg = dynamic_cast<FileIndexer::Messages::RegisterOnFileChange *>(req)) {
+ if (dbRegisterFileChange(msg->directory, msg->sender)) {
+ auto it = fileChangeRecipents.find(msg->directory);
+ if (fileChangeRecipents.end() == it) {
+ fileChangeRecipents[msg->directory] = {msg->sender};
+ }
+ else {
+ it->second.insert(msg->sender);
+ }
+ }
+ }
+ return std::make_shared<sys::ResponseMessage>();
+}
+
+auto FileIndexerAgent::handleUnregisterOnFileChange(sys::Message *req) -> sys::MessagePointer
+{
+ if (auto msg = dynamic_cast<FileIndexer::Messages::UnregisterOnFileChange *>(req)) {
+ if (dbUnregisterFileChange(msg->directory, msg->sender)) {
+ auto it = fileChangeRecipents.find(msg->directory);
+ if (fileChangeRecipents.end() != it) {
+ it->second.erase(msg->sender);
+ }
+ }
+ }
+ return std::make_shared<sys::ResponseMessage>();
+}
+
auto FileIndexerAgent::dbGetFilesCount() -> unsigned int
{
auto retQuery = database->query(FileIndexer::Statements::getFilesCount);
- if (nullptr == retQuery || 1 != retQuery->getRowCount()) {
+ if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
return 0;
}
return (*retQuery)[0].getInt32();
@@ 81,20 130,41 @@ auto FileIndexerAgent::dbGetFilesCount() -> unsigned int
auto FileIndexerAgent::dbListDir(std::unique_ptr<FileIndexer::ListDirData> listDir)
-> std::unique_ptr<FileIndexer::ListDirData>
{
-
auto retQuery = database->query(FileIndexer::Statements::getFilesByDir, listDir->directory.c_str());
- if (nullptr == retQuery) {
+ if (nullptr == retQuery || FileIndexer::ZERO_ROWS_FOUND == retQuery->getRowCount()) {
listDir->count = 0;
+ listDir->fileList.clear();
return listDir;
}
+ listDir->count = retQuery->getRowCount();
+ unsigned int index = 0;
- listDir->count = retQuery->getRowCount();
- if (retQuery->getRowCount() > 0) {
- listDir->fileList.clear();
+ // returns all files in directory
+ if (listDir->list_limit == 0) {
do {
listDir->fileList.push_back(FileIndexer::FileRecord(retQuery.get()));
} while (retQuery->nextRow());
+ return listDir;
+ }
+
+ // returns files from directory in chunks (offset,limit)
+ // validate offset against count
+ if (listDir->list_offset > listDir->count) {
+ listDir->count = 0;
+ return listDir;
}
+ // Navigate to the proper row specified by offset
+ if (listDir->list_offset > 0) {
+ do {
+ index++;
+ } while (retQuery->nextRow() && (index < listDir->list_offset));
+ }
+ // Add records to the file list starting form list_offset and limited by list_limit
+ do {
+ listDir->fileList.push_back(FileIndexer::FileRecord(retQuery.get()));
+ index++;
+ } while (retQuery->nextRow() && (index < listDir->list_offset + listDir->list_limit));
+
return listDir;
}
@@ 112,13 182,13 @@ auto FileIndexerAgent::dbGetRecord(std::unique_ptr<FileIndexer::FileRecord> reco
if (false == record->path.empty()) {
retQuery = database->query(FileIndexer::Statements::getFileInfoByPath, record->path.c_str());
- if (nullptr == retQuery || 1 != retQuery->getRowCount()) {
+ if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
return FileIndexer::FileRecord{};
}
}
else if (record->file_id != FileIndexer::FILE_ID_NOT_EXISTS) {
retQuery = database->query(FileIndexer::Statements::getFileInfoById, record->file_id);
- if (nullptr == retQuery || 1 != retQuery->getRowCount()) {
+ if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
return FileIndexer::FileRecord{};
}
}
@@ 130,14 200,30 @@ auto FileIndexerAgent::dbGetRecord(std::unique_ptr<FileIndexer::FileRecord> reco
auto FileIndexerAgent::dbSetRecord(std::unique_ptr<FileIndexer::FileRecord> record) -> bool
{
- return database->execute(FileIndexer::Statements::insertFileInfo,
- record->file_id,
+
+ auto retQuery = database->query(FileIndexer::Statements::getFileInfoByPath, record->path.c_str());
+ FileIndexer::FileRecord retRecord(retQuery.get());
+
+ if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
+ // file do not exist in db, insert a new file record
+ return database->execute(FileIndexer::Statements::insertFileInfo,
+ // retRecord.file_id,
+ record->path.c_str(),
+ record->size,
+ record->mime_type,
+ record->mtime,
+ record->directory.c_str(),
+ record->file_type);
+ }
+ // update existing file record
+ return database->execute(FileIndexer::Statements::updateFileInfo,
record->path.c_str(),
record->size,
record->mime_type,
record->mtime,
record->directory.c_str(),
- record->file_type);
+ record->file_type,
+ retRecord.file_id);
}
auto FileIndexerAgent::dbUpdateRecord(std::unique_ptr<FileIndexer::FileRecord> record) -> bool
@@ 177,6 263,11 @@ auto FileIndexerAgent::handleSetRecord(sys::Message *req) -> sys::MessagePointer
sys::Bus::SendUnicast(std::move(updateMsg), msg->sender, parentService);
dbSetRecord(std::make_unique<FileIndexer::FileRecord>(record));
}
+
+ for (auto recipient : fileChangeRecipents[record.directory]) {
+ auto notifeMsg = std::make_shared<FileIndexer::Messages::FileChanged>(record.directory);
+ sys::Bus::SendUnicast(std::move(notifeMsg), recipient, parentService);
+ }
}
return std::make_shared<sys::ResponseMessage>();
}
@@ 189,7 280,7 @@ auto FileIndexerAgent::dbGetProperty(std::unique_ptr<FileIndexer::FileMetadata>
std::unique_ptr<QueryResult> retQuery = nullptr;
retQuery = database->query(FileIndexer::Statements::getPropertyValue, property.c_str(), metaData->path.c_str());
- if (nullptr == retQuery || 1 != retQuery->getRowCount()) {
+ if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
return *metaData;
}
FileIndexer::FileMetadata retMetaData;
@@ 221,21 312,62 @@ auto FileIndexerAgent::dbGetAllProperties(std::unique_ptr<FileIndexer::FileMetad
auto FileIndexerAgent::dbSetProperty(std::unique_ptr<FileIndexer::FileMetadata> metaData) -> bool
{
+ std::unique_ptr<QueryResult> retQuery = nullptr;
+ unsigned int fileId;
+
+ if (false == metaData->path.empty()) {
+ retQuery = database->query(FileIndexer::Statements::getFileInfoByPath, metaData->path.c_str());
+ if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
+ return false;
+ }
+ fileId = (*retQuery)[0].getInt32();
+ }
+ else if (metaData->file_id != FileIndexer::FILE_ID_NOT_EXISTS) {
+ retQuery = database->query(FileIndexer::Statements::getFileInfoById, metaData->file_id);
+ if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
+ return false;
+ }
+ fileId = metaData->file_id;
+ }
+ else {
+ // there is no way to identify the file (id or path)
+ return false;
+ }
+
auto itr = metaData->properties.begin();
auto property = itr->first;
auto value = itr->second;
- return database->execute(
- FileIndexer::Statements::insertPropertyValue, metaData->file_id, property.c_str(), value.c_str());
+ return database->execute(FileIndexer::Statements::insertPropertyValue, fileId, property.c_str(), value.c_str());
}
auto FileIndexerAgent::dbSetProperties(std::unique_ptr<FileIndexer::FileMetadata> metaData) -> bool
{
- bool statusCode = true;
+ bool statusCode = true;
+ std::unique_ptr<QueryResult> retQuery = nullptr;
+ unsigned int fileId;
+
+ if (false == metaData->path.empty()) {
+ retQuery = database->query(FileIndexer::Statements::getFileInfoByPath, metaData->path.c_str());
+ if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
+ return false;
+ }
+ fileId = (*retQuery)[0].getInt32();
+ }
+ else if (metaData->file_id != FileIndexer::FILE_ID_NOT_EXISTS) {
+ retQuery = database->query(FileIndexer::Statements::getFileInfoById, metaData->file_id);
+ if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
+ return false;
+ }
+ fileId = metaData->file_id;
+ }
+ else {
+ // there is no way to identify the file (id or path)
+ return false;
+ }
+
for (auto propVal : metaData->properties) {
- statusCode = database->execute(FileIndexer::Statements::insertPropertyValue,
- metaData->file_id,
- propVal.first.c_str(),
- propVal.second.c_str());
+ statusCode = database->execute(
+ FileIndexer::Statements::insertPropertyValue, fileId, propVal.first.c_str(), propVal.second.c_str());
if (!statusCode)
return statusCode;
}
@@ 312,7 444,12 @@ auto FileIndexerAgent::handleSetProperties(sys::Message *req) -> sys::MessagePoi
auto updateMsg = std::make_shared<FileIndexer::Messages::PropertyChangedMessage>(
std::move(metaDataPtr), std::make_unique<FileIndexer::FileMetadata>(msg->dbMetaData));
sys::Bus::SendUnicast(std::move(updateMsg), msg->sender, parentService);
- dbSetProperty(std::make_unique<FileIndexer::FileMetadata>(metaData));
+ dbSetProperties(std::make_unique<FileIndexer::FileMetadata>(metaData));
+
+ for (auto recipient : fileChangeRecipents[metaData.directory]) {
+ auto notifyMsg = std::make_shared<FileIndexer::Messages::FileChanged>(metaData.directory);
+ sys::Bus::SendUnicast(std::move(notifyMsg), recipient, parentService);
+ }
}
return std::make_shared<sys::ResponseMessage>();
}
M module-services/service-db/agents/file_indexer/FileIndexerAgent.hpp => module-services/service-db/agents/file_indexer/FileIndexerAgent.hpp +6 -0
@@ 19,6 19,12 @@ class FileIndexerAgent : public DatabaseAgent
void dbTest();
private:
+ using MapOfRecipentsToBeNotified = std::map<std::string, std::set<std::string>>;
+ MapOfRecipentsToBeNotified fileChangeRecipents;
+ auto handleRegisterOnFileChange(sys::Message *req) -> sys::MessagePointer;
+ auto handleUnregisterOnFileChange(sys::Message *req) -> sys::MessagePointer;
+ auto dbRegisterFileChange(std::string dir, std::string service) -> bool;
+ auto dbUnregisterFileChange(std::string dir, std::string service) -> bool;
auto dbListDir(std::unique_ptr<FileIndexer::ListDirData> listDir) -> std::unique_ptr<FileIndexer::ListDirData>;
auto dbGetProperty(std::unique_ptr<FileIndexer::FileMetadata> metaData) -> FileIndexer::FileMetadata;
auto dbSetProperty(std::unique_ptr<FileIndexer::FileMetadata> metaData) -> bool;
M module-services/service-db/agents/file_indexer/FileIndexer_queries.hpp => module-services/service-db/agents/file_indexer/FileIndexer_queries.hpp +28 -2
@@ 47,8 47,8 @@ namespace FileIndexer::Statements
)sql";
constexpr auto insertFileInfo = R"sql(
- INSERT OR REPLACE INTO file_tab (file_id, path, size, mime_type, mtime, directory, file_type) VALUES
- ( '%lu', '%q', '%lu', '%lu' , '%lu', '%q', '%lu') ;
+ INSERT OR REPLACE INTO file_tab (path, size, mime_type, mtime, directory, file_type) VALUES
+ ('%q', '%lu', '%lu' , '%lu', '%q', '%lu') ;
)sql";
constexpr auto updateFileInfo = R"sql(
@@ 85,4 85,30 @@ namespace FileIndexer::Statements
WHERE file_id= '%lu' AND property = '%q' ;
)sql";
+ constexpr auto setNotification = R"sql(
+ INSERT OR REPLACE INTO notifications_tab (path, service) VALUES
+ ( '%q' , '%q' ) ;
+ )sql";
+
+ constexpr auto updateNotification = R"sql(
+ UPDATE notifications_tab SET path = '%q', service = '%q'
+ WHERE id = '%lu';
+ )sql";
+
+ constexpr auto clearNotificationdRow = R"sql(
+ DELETE FROM notifications_tab
+ WHERE path = '%q' AND service = '%q';
+ )sql";
+
+ constexpr auto getAllNotifications = R"sql(
+ SELECT id, path, service
+ FROM notifications_tab AS NT;
+ )sql";
+
+ constexpr auto getNotification = R"sql(
+ SELECT id, path, service
+ FROM notifications_tab AS NT
+ WHERE path= '%q' AND service = '%q';
+ )sql";
+
} // namespace FileIndexer::Statements
M module-services/service-db/agents/file_indexer/file_indexer.sql => module-services/service-db/agents/file_indexer/file_indexer.sql +15 -5
@@ 34,24 34,34 @@ CREATE TABLE IF NOT EXISTS notifications_tab (
path TEXT NOT NULL,
service TEXT,
CONSTRAINT notification_unique
- UNIQUE(path, service)
+ UNIQUE(path, service)
);
-- ----------- insert default values ----------------------
INSERT OR REPLACE INTO file_tab (file_id, path, size, mime_type, mtime, directory, file_type) VALUES
(1, 'mp3/track1.mp3', 456666, 1, 1603929600, 'mp3',12297),
- (2, 'mp3/track2.mp3', 345354 ,1, 1603929604, 'mp3',12297);
+ (2, 'mp3/track2.mp3', 345354 ,1, 1603929604, 'mp3',12297),
+ (3, 'mp3/track3.mp3', 34534 ,1, 1603929604, 'mp3',12297),
+ (4, 'mp3/track4.mp3', 345354 ,1, 1603929604, 'mp3',12297);
INSERT OR REPLACE INTO metadata_tab (file_id, property, value) VALUES
(1,'artist','Sting'),
(1,'genre','Rock'),
(1,'album','Album1'),
- (2,'artist','Nick Levis'),
- (2,'genre','Soul'),
- (2,'album','Album2');
+ (2,'artist','Madonna'),
+ (2,'genre','Rock'),
+ (2,'album','Album2'),
+ (3,'artist','Lady Gaga'),
+ (3,'genre','Rock'),
+ (3,'album','Album3'),
+ (4,'artist','Nick Levis'),
+ (4,'genre','Soul'),
+ (4,'album','Album4');
+INSERT OR REPLACE INTO notifications_tab (id, path, service) VALUES
+ (1,'mp3','ServiceDB');
COMMIT;
M module-services/service-db/service-db/FileIndexerMessages.hpp => module-services/service-db/service-db/FileIndexerMessages.hpp +54 -4
@@ 15,21 15,24 @@ namespace FileIndexer
{
constexpr unsigned int FILE_ID_NOT_EXISTS = 0;
constexpr unsigned int FILE_RECORD_COLUMN_COUNT = 7;
+ constexpr unsigned int ONE_ROW_FOUND = 1;
+ constexpr unsigned int ZERO_ROWS_FOUND = 0;
// refers to file_tab
struct FileRecord
{
unsigned int file_id = FILE_ID_NOT_EXISTS;
- std::string path = "";
+ std::string path;
unsigned int size;
unsigned int mime_type;
unsigned int mtime;
- std::string directory = "";
+ std::string directory;
unsigned int file_type;
[[nodiscard]] auto RecordEqual(FileRecord fr) const -> bool
{
- bool records_equal = (fr.path == path) && (fr.mime_type == mime_type) && (fr.size == size);
+ bool records_equal = (fr.path == path) && (fr.mime_type == mime_type) && (fr.size == size) &&
+ (fr.mtime == mtime) && (fr.file_type == file_type) && (fr.directory == directory);
return records_equal;
}
@@ 46,6 49,22 @@ namespace FileIndexer
file_type = (*query)[6].getInt32();
}
}
+ explicit FileRecord(unsigned int fileId,
+ std::string fpath,
+ unsigned int fsize,
+ unsigned int fmime_type,
+ unsigned int fmtime,
+ std::string fdirectory,
+ unsigned int ftype)
+ {
+ file_id = fileId;
+ path = fpath;
+ size = fsize;
+ mime_type = fmime_type;
+ mtime = fmtime;
+ directory = fdirectory;
+ file_type = ftype;
+ }
};
// refers to metadata_tab
@@ 59,7 78,6 @@ namespace FileIndexer
std::vector<FileRecord> fileList;
unsigned int list_limit;
unsigned int list_offset;
- unsigned int list_size;
unsigned int count;
};
@@ 72,6 90,34 @@ namespace FileIndexer
~FileIndexerMessage() override = default;
};
+ class RegisterOnFileChange : public FileIndexerMessage
+ {
+ public:
+ RegisterOnFileChange() = default;
+ explicit RegisterOnFileChange(std::string dir) : directory(std::move(dir))
+ {}
+ std::string directory;
+ };
+
+ class UnregisterOnFileChange : public FileIndexerMessage
+ {
+ public:
+ UnregisterOnFileChange() = default;
+ explicit UnregisterOnFileChange(std::string dir) : directory(std::move(dir))
+ {}
+ std::string directory;
+ };
+
+ class FileChanged : public FileIndexerMessage
+ {
+ public:
+ FileChanged() = default;
+ explicit FileChanged(std::string dir) : directory(dir)
+ {}
+
+ std::string directory;
+ };
+
class GetListDirMessage : public FileIndexerMessage
{
public:
@@ 94,6 140,10 @@ namespace FileIndexer
{
return listDir->count;
}
+ auto getResults() -> std::vector<FileRecord>
+ {
+ return listDir->fileList;
+ }
};
class RecordMessage : public FileIndexerMessage