diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 3da126f..6a75bc0 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -33,10 +33,8 @@ MainWindow::MainWindow(QWidget *parent) : void MainWindow::connectSignals() { - connect(ui->txtSearch, &QLineEdit::textChanged, this, &MainWindow::lineEditTextChanged); connect(ui->txtSearch, &QLineEdit::returnPressed, this, &MainWindow::lineEditReturnPressed); - connect(this, &MainWindow::beginFileSearch, searchWorker, &SearchWorker::searchForFile); - connect(this, &MainWindow::beginContentSearch, searchWorker, &SearchWorker::searchForContent); + connect(this, &MainWindow::beginSearch, searchWorker, &SearchWorker::search); connect(searchWorker, &SearchWorker::searchResultsReady, this, &MainWindow::handleSearchResults); connect(searchWorker, &SearchWorker::searchCancelled, this, &MainWindow::handleCancelledSearch); connect(ui->treeResultsList, &QTreeWidget::itemActivated, this, &MainWindow::treeSearchItemActivated); @@ -98,26 +96,10 @@ void MainWindow::pdfPreviewReceived(PdfPreview preview) } void MainWindow::lineEditReturnPressed() -{ - if(pdfTabActive() && pdfDirty) - { - makePdfPreview(); - } -} - -void MainWindow::lineEditTextChanged() { QString q = ui->txtSearch->text(); - if(q.startsWith("|")) - { - q = q.mid(1); - emit beginContentSearch(q); - } - else - { - emit beginFileSearch(q); - } - + //TODO: validate q; + emit beginSearch(q); } void MainWindow::handleSearchResults(const QVector &results) @@ -147,6 +129,10 @@ void MainWindow::handleSearchResults(const QVector &results) ui->treeResultsList->resizeColumnToContents(0); ui->treeResultsList->resizeColumnToContents(1); pdfDirty = ! this->pdfSearchResults.empty(); + if(pdfTabActive() && pdfDirty) + { + makePdfPreview(); + } } diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 499a981..0a5dd85 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -20,8 +20,7 @@ public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); signals: - void beginFileSearch(const QString &query); - void beginContentSearch(const QString &query); + void beginSearch(const QString &query); void startPdfPreviewGeneration(QVector paths, double scalefactor); private: Ui::MainWindow *ui; @@ -39,7 +38,6 @@ private: unsigned int processedPdfPreviews; private slots: void lineEditReturnPressed(); - void lineEditTextChanged(); void handleSearchResults(const QVector &results); void handleCancelledSearch(); void treeSearchItemActivated(QTreeWidgetItem *item, int i); diff --git a/gui/searchworker.cpp b/gui/searchworker.cpp index 1231782..2ea03c8 100644 --- a/gui/searchworker.cpp +++ b/gui/searchworker.cpp @@ -1,13 +1,15 @@ #include "searchworker.h" +#include #include +#include SearchWorker::SearchWorker() { } SearchWorker::SearchWorker(const QString &dbpath) { - QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); + db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName(dbpath); if(!db.open()) { @@ -20,12 +22,115 @@ SearchWorker::SearchWorker(const QString &dbpath) queryContent->prepare("SELECT file.path, content.page, file.mtime FROM file INNER JOIN content ON file.id = content.fileid INNER JOIN content_fts ON content.id = content_fts.ROWID WHERE content_fts.content MATCH ? ORDER By file.mtime DESC, content.page ASC"); } - -QString normalize(QString str) +QVector SearchWorker::tokenize(QString expression) { - str = str.replace(" ", " AND "); - str = str.replace("|", " OR "); - return str; + //TODO: merge lonewords + QVector result; + QRegularExpression rx("((?(\\.|\\w)+):(?\\((?[^\\)]+)\\)|(\\w)+)|(?AND|OR|!)|(?\\(|\\))|(?\\w+))"); + QRegularExpressionMatchIterator i = rx.globalMatch(expression); + bool wasbool = true; + while(i.hasNext()) + { + QRegularExpressionMatch m = i.next(); + QString boolean = m.captured("boolean"); + QString filtername = m.captured("filtername"); + QString bracket = m.captured("bracket"); + QString loneword = m.captured("loneword"); + if(boolean != "") + { + /* if(wasbool) + { + throw new std::runtime_error("Bool after Bool is invalid"); + }*/ + wasbool = true; + result.append(Command(boolean)); + } + + if(bracket != "") + { + if(!wasbool) + { + if(bracket == "(") + { + result.append(Command("AND")); + } + } + result.append(Command(bracket)); + } + + + if(loneword != "") + { + if(!wasbool) + { + result.append(Command("AND")); + } + wasbool = false; + result.append(Command("contains", loneword)); + } + if(filtername != "") + { + if(!wasbool) + { + result.append(Command("AND")); + } + wasbool = false; + QString value = m.captured("innerargs"); + if(value == "") + value = m.captured("args"); + result.append(Command(filtername, value)); + + } + + } + return result; +} + +QString SearchWorker::createSql(const SearchWorker::Command &cmd) +{ + QString key = cmd.key; + QString value = cmd.value; + if(key == "AND" || key == "OR" || key=="(" || key==")") + { + return " " + key + " "; + } + if(key == "!") + { + return " NOT "; + } + if(key == "path.starts") + { + return " file.path LIKE '" + value + "%' "; + } + if(key == "path.ends") + { + return " file.path LIKE '%" + value + "' "; + } + if(key == "path.contains" || key == "inpath") + { + return " file.path LIKE '%" + value + "%' "; + } + if(key == "page") + { + return " content.page = " + value; + } + if(key == "contains") + { + return " ( COALESCE( (SELECT 1 FROM content_fts WHERE content_fts.content MATCH '" + value + "' AND content_fts.ROWID= content.id), 0 ) )"; + } + qDebug() << "NOHIT" << key; + //TODO: exception? + return "NOTHING"; +} + +QString SearchWorker::makeSql(const QVector &tokens) +{ + QString result; + for(const Command &c : tokens) + { + result += createSql(c); + } + return result; } void SearchWorker::searchForFile(const QString &query) { @@ -60,3 +165,25 @@ void SearchWorker::searchForContent(const QString &query) emit searchResultsReady(results); } +void SearchWorker::search(const QString &query) +{ + QSqlQuery dbquery(db); + QVector results; + QString whereSql = makeSql(tokenize(query)); + QString 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"; + dbquery.prepare(prep); + dbquery.exec(); + qDebug() << "prepped: " << prep; + qDebug() << dbquery.lastError(); + while(dbquery.next()) + { + SearchResult result; + + result.path = dbquery.value("path").toString(); + result.page = dbquery.value("page").toUInt(); + result.mtime = dbquery.value("mtime").toUInt(); + results.append(result); + } + emit searchResultsReady(results); +} + diff --git a/gui/searchworker.h b/gui/searchworker.h index 3429056..e0f021b 100644 --- a/gui/searchworker.h +++ b/gui/searchworker.h @@ -9,16 +9,36 @@ class SearchWorker : public QObject { + class Command + { + public: + QString key; + QString value; + + Command(QString key="", QString value="") + { + this->key = key; + this->value = value; + } + + }; + + Q_OBJECT private: QSqlQuery *queryFile; QSqlQuery *queryContent; + QVector tokenize(QString expression); + QString createSql(const Command &cmd); + QString makeSql(const QVector &tokens); + QSqlDatabase db; public: SearchWorker(); SearchWorker(const QString &dbpath); public slots: void searchForFile(const QString &query); void searchForContent(const QString &query); + void search(const QString &query); signals: void searchResultsReady(const QVector &results); void searchCancelled();