Compare commits

...

5 Commits

10 changed files with 281 additions and 42 deletions

View File

@ -41,12 +41,13 @@ int CommandAdd::handle(QStringList arguments)
{ {
QCommandLineParser parser; QCommandLineParser parser;
parser.addOptions({{{"c", "continue"}, parser.addOptions({{{"c", "continue"},
"Continue adding files, don't exit on first error. If this option is not given, looqs will " "Continue adding files, don't exit on first error. Exit code will be 0. If this option is not "
"exit asap, but it's possible that a few files will still be processed. " "given, looqs will "
"exit asap, but it's possible that a few files will still be processed."
"Set -t 1 to avoid this behavior, but processing will be slower. "}, "Set -t 1 to avoid this behavior, but processing will be slower. "},
{{"n", "no-content"}, "Only add paths to database. Do not index content"}, {{"n", "no-content"}, "Only add paths to database. Do not index content"},
{{"v", "verbose"}, "Print paths of files being processed"},
{{"f", "fill-content"}, "Index content for files previously indexed with -n"}, {{"f", "fill-content"}, "Index content for files previously indexed with -n"},
{"tags", "Comma-separated list of tags to assign"},
{{"t", "threads"}, "Number of threads to use.", "threads"}}); {{"t", "threads"}, "Number of threads to use.", "threads"}});
parser.addHelpOption(); parser.addHelpOption();
parser.addPositionalArgument("add", "Add paths to the index", parser.addPositionalArgument("add", "Add paths to the index",
@ -56,6 +57,8 @@ int CommandAdd::handle(QStringList arguments)
this->keepGoing = parser.isSet("continue"); this->keepGoing = parser.isSet("continue");
bool pathsOnly = parser.isSet("no-content"); bool pathsOnly = parser.isSet("no-content");
bool fillContent = parser.isSet("fill-content"); bool fillContent = parser.isSet("fill-content");
bool verbose = parser.isSet("verbose");
if(parser.isSet("threads")) if(parser.isSet("threads"))
{ {
QString threadsCount = parser.value("threads"); QString threadsCount = parser.value("threads");
@ -85,18 +88,43 @@ int CommandAdd::handle(QStringList arguments)
fileSaverOptions.keepGoing = keepGoing; fileSaverOptions.keepGoing = keepGoing;
fileSaverOptions.fillExistingContentless = fillContent; fileSaverOptions.fillExistingContentless = fillContent;
fileSaverOptions.metadataOnly = pathsOnly; fileSaverOptions.metadataOnly = pathsOnly;
fileSaverOptions.verbose = false; fileSaverOptions.verbose = verbose;
indexer = new Indexer(*this->dbService); indexer = new Indexer(*this->dbService);
indexer->setFileSaverOptions(fileSaverOptions); indexer->setFileSaverOptions(fileSaverOptions);
indexer->setTargetPaths(files.toVector()); indexer->setTargetPaths(files.toVector());
if(verbose)
{
indexer->setProgressReportThreshold(1);
}
connect(indexer, &Indexer::pathsCountChanged, this, connect(indexer, &Indexer::pathsCountChanged, this,
[](int pathsCount) { Logger::info() << "Found paths: " << pathsCount << Qt::endl; }); [](int pathsCount) { Logger::info() << "Found paths: " << pathsCount << Qt::endl; });
connect(indexer, &Indexer::indexProgress, this, connect(indexer, &Indexer::indexProgress, this,
[](int pathsCount, unsigned int /*added*/, unsigned int /*skipped*/, unsigned int /*failed*/, [verbose, this](int pathsCount, unsigned int /*added*/, unsigned int /*skipped*/, unsigned int /*failed*/,
unsigned int /*totalCount*/) { Logger::info() << "Processed files: " << pathsCount << Qt::endl; }); unsigned int /*totalCount*/)
{
Logger::info() << "Processed files: " << pathsCount << Qt::endl;
if(verbose)
{
IndexResult indexResult = indexer->getResult();
int newlyAdded = indexResult.results.count() - currentResult.results.count();
if(newlyAdded > 0)
{
int newOffset = indexResult.results.count() - newlyAdded;
for(int i = newOffset; i < indexResult.results.count(); i++)
{
auto result = indexResult.results.at(i);
Logger::info() << SaveFileResultToString(result.second) << result.first << Qt::endl;
}
}
this->currentResult = indexResult;
}
}
);
connect(indexer, &Indexer::finished, this, &CommandAdd::indexerFinished); connect(indexer, &Indexer::finished, this, &CommandAdd::indexerFinished);
this->autoFinish = false; this->autoFinish = false;

View File

@ -13,6 +13,8 @@ class CommandAdd : public Command
bool keepGoing = true; bool keepGoing = true;
protected: protected:
IndexResult currentResult;
public: public:
using Command::Command; using Command::Command;

View File

@ -3,14 +3,39 @@
#include "logger.h" #include "logger.h"
#include "tagmanager.h" #include "tagmanager.h"
bool CommandTag::ensureAbsolutePaths(const QVector<QString> &paths, QVector<QString> &absolutePaths)
{
for(const QString &path : paths)
{
QFileInfo info{path};
if(!info.exists())
{
Logger::error() << "Can't add tag for file " + info.absoluteFilePath() + " because it does not exist"
<< Qt::endl;
return false;
}
QString absolutePath = info.absoluteFilePath();
if(!this->dbService->fileExistsInDatabase(absolutePath))
{
Logger::error() << "Only files that have been indexed can be tagged. File not in index: " + absolutePath
<< Qt::endl;
return false;
}
absolutePaths.append(absolutePath);
}
return true;
}
int CommandTag::handle(QStringList arguments) int CommandTag::handle(QStringList arguments)
{ {
QCommandLineParser parser; QCommandLineParser parser;
parser.addPositionalArgument("add", "Adds a tag to a file", parser.addPositionalArgument("add", "Adds a tag to a file",
"add [tag] [paths...]. Adds the tag to the specified paths"); "add [tag] [paths...]. Adds the tag to the specified paths");
parser.addPositionalArgument("remove", "Removes a file associated to tag", "remove [tag] [file]"); parser.addPositionalArgument("remove", "Removes a path associated to a tag", "remove [tag] [path]");
parser.addPositionalArgument("delete", "Deletes a tag", "delete [tag]"); parser.addPositionalArgument("delete", "Deletes a tag", "delete [tag]");
parser.addPositionalArgument("list", "Lists paths associated with a tag, or all tags", "list [tag]"); parser.addPositionalArgument("list", "Lists paths associated with a tag, or all tags", "list [tag]");
parser.addPositionalArgument("show", "Lists tags associated with a path", "show [path]");
parser.addHelpOption(); parser.addHelpOption();
parser.parse(arguments); parser.parse(arguments);
@ -21,9 +46,8 @@ int CommandTag::handle(QStringList arguments)
parser.showHelp(EXIT_FAILURE); parser.showHelp(EXIT_FAILURE);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
TagManager tagManager{*this->dbService};
QString cmd = args[0]; QString cmd = args[0];
qDebug() << cmd;
if(cmd == "add") if(cmd == "add")
{ {
if(args.length() < 3) if(args.length() < 3)
@ -33,28 +57,14 @@ int CommandTag::handle(QStringList arguments)
return EXIT_FAILURE; return EXIT_FAILURE;
} }
QString tag = args[1]; QString tag = args[1];
auto paths = args.mid(2).toVector(); QVector<QString> paths = args.mid(2).toVector();
for(int i = 0; i < paths.size(); i++)
{
QFileInfo info{paths[i]};
if(!info.exists())
{
Logger::error() << "Can't add tag for file " + info.absoluteFilePath() + " because it does not exist"
<< Qt::endl;
return EXIT_FAILURE;
}
QString absolutePath = info.absoluteFilePath();
if(!this->dbService->fileExistsInDatabase(absolutePath))
{
Logger::error() << "Only files that have been indexed can be tagged. File not in index: " + absolutePath
<< Qt::endl;
return EXIT_FAILURE;
}
paths[i] = absolutePath;
}
TagManager tagManager{*this->dbService}; QVector<QString> absolutePaths;
bool result = tagManager.addPathsToTag(tag, paths); if(!ensureAbsolutePaths(paths, absolutePaths))
{
return EXIT_FAILURE;
}
bool result = tagManager.addPathsToTag(tag, absolutePaths);
if(!result) if(!result)
{ {
Logger::error() << "Failed to assign tags" << Qt::endl; Logger::error() << "Failed to assign tags" << Qt::endl;
@ -62,6 +72,82 @@ int CommandTag::handle(QStringList arguments)
} }
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
if(cmd == "list")
{
return 0; QString tag;
if(args.length() >= 2)
{
tag = args[1];
}
QVector<QString> entries;
if(tag.isEmpty())
{
entries = tagManager.getTags();
}
else
{
entries = tagManager.getPaths(tag);
}
for(const QString &entry : entries)
{
Logger::info() << entry << Qt::endl;
}
}
if(cmd == "remove")
{
if(args.length() < 3)
{
Logger::error() << "Not enough arguments provided. 'remove' requires a tag followed by at least one path"
<< Qt::endl;
return EXIT_FAILURE;
}
QString tag = args[1];
QVector<QString> paths = args.mid(2).toVector();
QVector<QString> absolutePaths;
if(!ensureAbsolutePaths(paths, absolutePaths))
{
return EXIT_FAILURE;
}
if(!tagManager.removePathsForTag(tag, absolutePaths))
{
Logger::error() << "Failed to remove path assignments" << Qt::endl;
return EXIT_FAILURE;
}
}
if(cmd == "delete")
{
if(args.length() != 2)
{
Logger::error() << "The 'delete' command requires the tag to delete" << Qt::endl;
return EXIT_FAILURE;
}
if(!tagManager.deleteTag(args[1]))
{
Logger::error() << "Failed to delete tag" << Qt::endl;
return EXIT_FAILURE;
}
}
if(cmd == "show")
{
if(args.length() != 2)
{
Logger::error() << "The 'show' command requires a path to show the assigned tags" << Qt::endl;
return EXIT_FAILURE;
}
QString path = args[1];
QVector<QString> absolutePaths;
if(!ensureAbsolutePaths({path}, absolutePaths))
{
return EXIT_FAILURE;
}
QVector<QString> tags = tagManager.getTags(absolutePaths.at(0));
for(const QString &entry : tags)
{
Logger::info() << entry << Qt::endl;
}
}
return EXIT_SUCCESS;
} }

View File

@ -4,6 +4,9 @@
class CommandTag : public Command class CommandTag : public Command
{ {
protected:
bool ensureAbsolutePaths(const QVector<QString> &paths, QVector<QString> &absolutePaths);
public: public:
using Command::Command; using Command::Command;

View File

@ -246,6 +246,8 @@ QString PreviewGeneratorPlainText::generateLineBasedPreviewText(QTextStream &in,
totalWordCountMap[it->first] = totalWordCountMap.value(it->first, 0) + it->second; totalWordCountMap[it->first] = totalWordCountMap.value(it->first, 0) + it->second;
} }
} }
if(!resultText.isEmpty())
{
if(isTruncated) if(isTruncated)
{ {
header += "(truncated) "; header += "(truncated) ";
@ -256,7 +258,9 @@ QString PreviewGeneratorPlainText::generateLineBasedPreviewText(QTextStream &in,
} }
header += "<hr>"; header += "<hr>";
return header + resultText; resultText = header + resultText;
}
return resultText;
} }
QSharedPointer<PreviewResult> PreviewGeneratorPlainText::generate(RenderConfig config, QString documentPath, QSharedPointer<PreviewResult> PreviewGeneratorPlainText::generate(RenderConfig config, QString documentPath,

View File

@ -142,6 +142,27 @@ QVector<QString> SqliteDbService::getTagsForPath(QString path)
return result; return result;
} }
QVector<QString> SqliteDbService::getPathsForTag(QString tag)
{
QVector<QString> result;
auto query = QSqlQuery(dbFactory->forCurrentThread());
query.prepare(
"SELECT file.path FROM tag INNER JOIN filetag ON tag.id = filetag.tagid INNER JOIN file ON filetag.fileid "
"= file.id WHERE tag.name = ?");
query.addBindValue(tag.toLower());
query.setForwardOnly(true);
if(!query.exec())
{
throw LooqsGeneralException("Error while trying to retrieve paths from database: " + query.lastError().text());
}
while(query.next())
{
QString path = query.value(0).toString();
result.append(path);
}
return result;
}
bool SqliteDbService::setTags(QString path, const QSet<QString> &tags) bool SqliteDbService::setTags(QString path, const QSet<QString> &tags)
{ {
QSqlDatabase db = dbFactory->forCurrentThread(); QSqlDatabase db = dbFactory->forCurrentThread();
@ -379,3 +400,68 @@ bool SqliteDbService::addTag(QString tag, const QVector<QString> &paths)
return true; return true;
} }
bool SqliteDbService::removePathsForTag(QString tag, const QVector<QString> &paths)
{
QSqlDatabase db = dbFactory->forCurrentThread();
QSqlQuery tagQuery(db);
QSqlQuery fileTagQuery(db);
tag = tag.toLower();
fileTagQuery.prepare(
"DELETE FROM filetag WHERE fileid = (SELECT id FROM file WHERE path = ?) AND tagid = (SELECT id "
"FROM tag WHERE name = ?)");
fileTagQuery.bindValue(1, tag);
for(const QString &path : paths)
{
fileTagQuery.bindValue(0, path);
if(!fileTagQuery.exec())
{
Logger::error() << "An error occured while trying to remove paths from tag assignment" << Qt::endl;
return false;
}
}
return true;
}
bool SqliteDbService::deleteTag(QString tag)
{
QSqlDatabase db = dbFactory->forCurrentThread();
if(!db.transaction())
{
Logger::error() << "Failed to open transaction while trying to delete tag " << tag << " : " << db.lastError()
<< Qt::endl;
return false;
}
tag = tag.toLower();
QSqlQuery assignmentDeleteQuery(db);
assignmentDeleteQuery.prepare("DELETE FROM filetag WHERE tagid = (SELECT id FROM tag WHERE name = ?)");
assignmentDeleteQuery.addBindValue(tag);
if(!assignmentDeleteQuery.exec())
{
db.rollback();
Logger::error() << "Error while trying to delete tag: " << db.lastError() << Qt::endl;
return false;
}
QSqlQuery deleteTagQuery(db);
deleteTagQuery.prepare("DELETE FROM tag WHERE name = ?");
deleteTagQuery.addBindValue(tag);
if(!deleteTagQuery.exec())
{
db.rollback();
Logger::error() << "Error while trying to delete tag: " << db.lastError() << Qt::endl;
return false;
}
if(!db.commit())
{
db.rollback();
Logger::error() << "Error while trying to delete tag: " << db.lastError() << Qt::endl;
return false;
}
return true;
}

View File

@ -34,7 +34,10 @@ class SqliteDbService
bool addTag(QString tag, const QVector<QString> &paths); bool addTag(QString tag, const QVector<QString> &paths);
QVector<QString> getTags(); QVector<QString> getTags();
QVector<QString> getTagsForPath(QString path); QVector<QString> getTagsForPath(QString path);
QVector<QString> getPathsForTag(QString path);
bool setTags(QString path, const QSet<QString> &tags); bool setTags(QString path, const QSet<QString> &tags);
bool removePathsForTag(QString tag, const QVector<QString> &paths);
bool deleteTag(QString tag);
QVector<SearchResult> search(const LooqsQuery &query); QVector<SearchResult> search(const LooqsQuery &query);

View File

@ -28,6 +28,31 @@ bool TagManager::removeTagsForPath(QString path, const QSet<QString> &tags)
return this->dbService->setTags(path, newTags); return this->dbService->setTags(path, newTags);
} }
bool TagManager::removePathsForTag(QString tag, const QVector<QString> &paths)
{
return this->dbService->removePathsForTag(tag, paths);
}
bool TagManager::deleteTag(QString tag)
{
return this->dbService->deleteTag(tag);
}
QVector<QString> TagManager::getTags(QString path)
{
return this->dbService->getTagsForPath(path);
}
QVector<QString> TagManager::getTags()
{
return this->dbService->getTags();
}
QVector<QString> TagManager::getPaths(QString tag)
{
return this->dbService->getPathsForTag(tag);
}
bool TagManager::addTagsToPath(QString path, QString tagstring, QChar delim) bool TagManager::addTagsToPath(QString path, QString tagstring, QChar delim)
{ {
auto splitted = tagstring.split(delim); auto splitted = tagstring.split(delim);

View File

@ -17,9 +17,11 @@ class TagManager
bool addPathsToTag(QString tag, const QVector<QString> &paths); bool addPathsToTag(QString tag, const QVector<QString> &paths);
bool removeTagsForPath(QString path, const QSet<QString> &tags); bool removeTagsForPath(QString path, const QSet<QString> &tags);
bool removePathsForTag(QString tag, const QVector<QString> &paths);
bool deleteTag(QString tag); bool deleteTag(QString tag);
QVector<QString> getTags(QString path); QVector<QString> getTags(QString path);
QVector<QString> getTags();
QVector<QString> getPaths(QString tag); QVector<QString> getPaths(QString tag);
}; };

View File

@ -19,10 +19,10 @@ enum TokenType
FILTER_PATH_SIZE, FILTER_PATH_SIZE,
FILTER_PATH_ENDS, FILTER_PATH_ENDS,
FILTER_PATH_STARTS, FILTER_PATH_STARTS,
FILTER_CONTENT = 512, FILTER_TAG_ASSIGNED,
FILTER_CONTENT = 512, /* Everything below here is content search (except LIMIT) */
FILTER_CONTENT_CONTAINS, FILTER_CONTENT_CONTAINS,
FILTER_CONTENT_PAGE, FILTER_CONTENT_PAGE,
FILTER_TAG_ASSIGNED,
LIMIT = 1024 LIMIT = 1024
}; };