checks on input, exceptions, improved contains query performance

This commit is contained in:
Albert S. 2018-09-02 13:54:27 +02:00
parent 802609da12
commit 5d8de075be
4 changed files with 73 additions and 10 deletions

View File

@ -37,6 +37,7 @@ void MainWindow::connectSignals()
connect(this, &MainWindow::beginSearch, searchWorker, &SearchWorker::search); connect(this, &MainWindow::beginSearch, searchWorker, &SearchWorker::search);
connect(searchWorker, &SearchWorker::searchResultsReady, this, &MainWindow::handleSearchResults); connect(searchWorker, &SearchWorker::searchResultsReady, this, &MainWindow::handleSearchResults);
connect(searchWorker, &SearchWorker::searchCancelled, this, &MainWindow::handleCancelledSearch); connect(searchWorker, &SearchWorker::searchCancelled, this, &MainWindow::handleCancelledSearch);
connect(searchWorker, &SearchWorker::searchError, this, &MainWindow::handleSearchError);
connect(ui->treeResultsList, &QTreeWidget::itemActivated, this, &MainWindow::treeSearchItemActivated); connect(ui->treeResultsList, &QTreeWidget::itemActivated, this, &MainWindow::treeSearchItemActivated);
connect(ui->treeResultsList, &QTreeWidget::customContextMenuRequested, this, &MainWindow::showSearchResultsContextMenu); connect(ui->treeResultsList, &QTreeWidget::customContextMenuRequested, this, &MainWindow::showSearchResultsContextMenu);
connect(ui->tabWidget, &QTabWidget::currentChanged, this, &MainWindow::tabChanged); connect(ui->tabWidget, &QTabWidget::currentChanged, this, &MainWindow::tabChanged);
@ -98,6 +99,11 @@ void MainWindow::pdfPreviewReceived(PdfPreview preview)
void MainWindow::lineEditReturnPressed() void MainWindow::lineEditReturnPressed()
{ {
QString q = ui->txtSearch->text(); QString q = ui->txtSearch->text();
if(!searchWorker->checkParanthesis(q))
{
ui->lblSearchResults->setText("Invalid paranthesis");
return;
}
//TODO: validate q; //TODO: validate q;
ui->lblSearchResults->setText("Searching..."); ui->lblSearchResults->setText("Searching...");
emit beginSearch(q); emit beginSearch(q);
@ -158,6 +164,11 @@ void MainWindow::handleCancelledSearch()
} }
void MainWindow::handleSearchError(QString error)
{
ui->lblSearchResults->setText("Error:" + error);
}
void MainWindow::treeSearchItemActivated(QTreeWidgetItem *item, int i) void MainWindow::treeSearchItemActivated(QTreeWidgetItem *item, int i)
{ {
QDesktopServices::openUrl(QUrl::fromLocalFile(item->text(1))); QDesktopServices::openUrl(QUrl::fromLocalFile(item->text(1)));

View File

@ -40,8 +40,8 @@ private slots:
void lineEditReturnPressed(); void lineEditReturnPressed();
void handleSearchResults(const QVector<SearchResult> &results); void handleSearchResults(const QVector<SearchResult> &results);
void handleCancelledSearch(); void handleCancelledSearch();
void handleSearchError(QString error);
void treeSearchItemActivated(QTreeWidgetItem *item, int i); void treeSearchItemActivated(QTreeWidgetItem *item, int i);
void showSearchResultsContextMenu(const QPoint &point); void showSearchResultsContextMenu(const QPoint &point);
void tabChanged(); void tabChanged();
void pdfPreviewReceived(PdfPreview preview); void pdfPreviewReceived(PdfPreview preview);

View File

@ -3,6 +3,7 @@
#include <QRegularExpression> #include <QRegularExpression>
#include <QDebug> #include <QDebug>
#include <QSqlError> #include <QSqlError>
#include <QStack>
SearchWorker::SearchWorker() SearchWorker::SearchWorker()
{ {
@ -19,6 +20,10 @@ SearchWorker::SearchWorker(const QString &dbpath)
QVector<SearchWorker::Command> SearchWorker::tokenize(QString expression) QVector<SearchWorker::Command> SearchWorker::tokenize(QString expression)
{ {
if(!checkParanthesis(expression))
{
throw std::invalid_argument("Invalid paranthesis");
}
//TODO: merge lonewords //TODO: merge lonewords
QVector<Command> result; QVector<Command> result;
QRegularExpression rx("((?<filtername>(\\.|\\w)+):(?<args>\\((?<innerargs>[^\\)]+)\\)|(\\w)+)|(?<boolean>AND|OR|!)|(?<bracket>\\(|\\))|(?<loneword>\\w+))"); QRegularExpression rx("((?<filtername>(\\.|\\w)+):(?<args>\\((?<innerargs>[^\\)]+)\\)|(\\w)+)|(?<boolean>AND|OR|!)|(?<bracket>\\(|\\))|(?<loneword>\\w+))");
@ -111,11 +116,9 @@ QString SearchWorker::createSql(const SearchWorker::Command &cmd)
} }
if(key == "contains" || key == "c") if(key == "contains" || key == "c")
{ {
return " ( COALESCE( (SELECT 1 FROM content_fts WHERE content_fts.content MATCH '" + value + "' AND content_fts.ROWID= content.id), 0 ) )"; return " content.id IN (SELECT content_fts.ROWID FROM content_fts WHERE content_fts.content MATCH '" + value + "' )";
} }
qDebug() << "NOHIT" << key; throw std::invalid_argument("Unknown filter: " + key.toStdString());
//TODO: exception?
return "NOTHING";
} }
QString SearchWorker::makeSql(const QVector<SearchWorker::Command> &tokens) QString SearchWorker::makeSql(const QVector<SearchWorker::Command> &tokens)
@ -128,26 +131,73 @@ QString SearchWorker::makeSql(const QVector<SearchWorker::Command> &tokens)
return result; return result;
} }
bool SearchWorker::checkParanthesis(QString expression)
{
QStack<QChar> open;
QStack<QChar> close;
for(QChar &c : expression)
{
if(c == '(')
{
open.push(c);
}
if(c == ')')
{
close.push(c);
}
}
if(open.size() != close.size())
{
return false;
}
while(!open.empty() && !close.empty())
{
QChar o = open.pop();
QChar c = close.pop();
if(o != '(' && c != ')' )
{
return false;
}
}
return true;
}
void SearchWorker::search(const QString &query) void SearchWorker::search(const QString &query)
{ {
QSqlQuery dbquery(db); QSqlQuery dbquery(db);
QVector<SearchResult> results; QVector<SearchResult> results;
QString whereSql = makeSql(tokenize(query)); QString whereSql;
try
{
whereSql = makeSql(tokenize(query));
}
catch(const std::exception &e)
{
emit searchError(e.what());
return;
}
QString prep; QString prep;
//TODO: hack, as we don't wanna look into content and get redundant results, when we don't even care about content //TODO: hack, as we don't wanna look into content and get redundant results, when we don't even care about content
if(whereSql.contains("content.")) if(whereSql.contains("content."))
{ {
prep = "SELECT file.path AS path, content.page AS page, file.mtime AS mtime FROM file INNER JOIN content ON file.id = content.fileid INNER JOIN content_fts ON content.id = content_fts.ROWID WHERE 1=1 AND " + whereSql + " ORDER By file.mtime DESC, content.page ASC"; prep = "SELECT file.path AS path, content.page AS page, file.mtime AS mtime FROM file INNER JOIN content ON file.id = content.fileid WHERE 1=1 AND " + whereSql + " ORDER By file.mtime DESC, content.page ASC";
} }
else else
{ {
prep = "SELECT file.path AS path, 0 as page, file.mtime AS mtime FROM file WHERE " + whereSql + " ORDER by file.mtime DESC"; prep = "SELECT file.path AS path, 0 as page, file.mtime AS mtime FROM file WHERE " + whereSql + " ORDER by file.mtime DESC";
} }
dbquery.prepare(prep); dbquery.prepare(prep);
dbquery.exec(); bool success = dbquery.exec();
qDebug() << "prepped: " << prep; if(!success)
qDebug() << dbquery.lastError(); {
qDebug() << "prepped: " << prep;
qDebug() << dbquery.lastError();
emit searchError(dbquery.lastError().text());
return;
}
while(dbquery.next()) while(dbquery.next())
{ {
SearchResult result; SearchResult result;

View File

@ -33,11 +33,13 @@ private:
public: public:
SearchWorker(); SearchWorker();
SearchWorker(const QString &dbpath); SearchWorker(const QString &dbpath);
bool checkParanthesis(QString expression);
public slots: public slots:
void search(const QString &query); void search(const QString &query);
signals: signals:
void searchResultsReady(const QVector<SearchResult> &results); void searchResultsReady(const QVector<SearchResult> &results);
void searchCancelled(); void searchCancelled();
void searchError(QString e);
}; };
#endif // SEARCHWORKER_H #endif // SEARCHWORKER_H