Searcher: use async first phase search

This commit is contained in:
Le Tan 2022-02-22 19:49:22 +08:00
parent 0e4442f513
commit 486950c1aa
8 changed files with 191 additions and 89 deletions

View File

@ -9,7 +9,7 @@
using namespace vnotex;
FileSearchEngineWorker::FileSearchEngineWorker(QObject *p_parent)
: QThread(p_parent)
: AsyncWorker(p_parent)
{
}
@ -22,16 +22,6 @@ void FileSearchEngineWorker::setData(const QVector<SearchSecondPhaseItem> &p_ite
m_token = p_token;
}
void FileSearchEngineWorker::stop()
{
m_askedToStop.store(1);
}
bool FileSearchEngineWorker::isAskedToStop() const
{
return m_askedToStop.load() == 1;
}
void FileSearchEngineWorker::run()
{
const int c_batchSize = 100;

View File

@ -8,6 +8,8 @@
#include <QAtomicInt>
#include <QVector>
#include <utils/asyncworker.h>
#include "searchtoken.h"
#include "searchdata.h"
@ -15,7 +17,7 @@ namespace vnotex
{
struct SearchResultItem;
class FileSearchEngineWorker : public QThread
class FileSearchEngineWorker : public AsyncWorker
{
Q_OBJECT
friend class FileSearchEngine;
@ -28,9 +30,6 @@ namespace vnotex
const QSharedPointer<SearchOption> &p_option,
const SearchToken &p_token);
public slots:
void stop();
signals:
void resultItemsReady(const QVector<QSharedPointer<SearchResultItem>> &p_items);
@ -44,10 +43,6 @@ namespace vnotex
void processBatchResults();
bool isAskedToStop() const;
QAtomicInt m_askedToStop = 0;
QVector<SearchSecondPhaseItem> m_items;
SearchToken m_token;

View File

@ -8,6 +8,7 @@
#include <core/exception.h>
#include <notebook/node.h>
#include <notebook/notebook.h>
#include <utils/asyncworker.h>
#include "searchresultitem.h"
#include "filesearchengine.h"
@ -53,25 +54,31 @@ SearchState Searcher::search(const QSharedPointer<SearchOption> &p_option, const
emit logRequested(tr("Searching %n buffer(s)", "", p_buffers.size()));
emit progressUpdated(0, p_buffers.size());
for (int i = 0; i < p_buffers.size(); ++i) {
if (!p_buffers[i]) {
continue;
m_firstPhaseWorker->doWork([this, p_buffers]() {
emit progressUpdated(0, p_buffers.size());
for (int i = 0; i < p_buffers.size(); ++i) {
if (!p_buffers[i]) {
continue;
}
if (isAskedToStop()) {
emit finished(SearchState::Stopped);
return;
}
auto file = p_buffers[i]->getFile();
if (!firstPhaseSearch(file.data())) {
emit finished(SearchState::Failed);
return;
}
emit progressUpdated(i + 1, p_buffers.size());
}
if (isAskedToStop()) {
return SearchState::Stopped;
}
emit finished(SearchState::Finished);
});
auto file = p_buffers[i]->getFile();
if (!firstPhaseSearch(file.data())) {
return SearchState::Failed;
}
emit progressUpdated(i + 1, p_buffers.size());
}
return SearchState::Finished;
return SearchState::Busy;
}
SearchState Searcher::search(const QSharedPointer<SearchOption> &p_option, Node *p_folder)
@ -88,29 +95,19 @@ SearchState Searcher::search(const QSharedPointer<SearchOption> &p_option, Node
emit logRequested(tr("Searching folder (%1)").arg(p_folder->getName()));
QVector<SearchSecondPhaseItem> secondPhaseItems;
if (!firstPhaseSearchFolder(p_folder, secondPhaseItems)) {
return SearchState::Failed;
}
if (isAskedToStop()) {
return SearchState::Stopped;
}
if (!secondPhaseItems.isEmpty()) {
// Do second phase search.
if (!secondPhaseSearch(secondPhaseItems)) {
return SearchState::Failed;
m_firstPhaseWorker->doWork([this, p_folder]() {
if (!firstPhaseSearchFolder(p_folder, m_secondPhaseItems)) {
m_secondPhaseItems.clear();
emit finished(SearchState::Failed);
return;
}
if (isAskedToStop()) {
return SearchState::Stopped;
if (m_secondPhaseItems.isEmpty()) {
emit finished(SearchState::Finished);
}
});
return SearchState::Busy;
}
return SearchState::Finished;
return SearchState::Busy;
}
SearchState Searcher::search(const QSharedPointer<SearchOption> &p_option, const QVector<Notebook *> &p_notebooks)
@ -119,41 +116,32 @@ SearchState Searcher::search(const QSharedPointer<SearchOption> &p_option, const
return SearchState::Failed;
}
QVector<SearchSecondPhaseItem> secondPhaseItems;
m_firstPhaseWorker->doWork([this, p_notebooks]() {
emit progressUpdated(0, p_notebooks.size());
for (int i = 0; i < p_notebooks.size(); ++i) {
if (isAskedToStop()) {
m_secondPhaseItems.clear();
emit finished(SearchState::Stopped);
return;
}
emit progressUpdated(0, p_notebooks.size());
for (int i = 0; i < p_notebooks.size(); ++i) {
if (isAskedToStop()) {
return SearchState::Stopped;
emit logRequested(tr("Searching notebook (%1)").arg(p_notebooks[i]->getName()));
if (!firstPhaseSearch(p_notebooks[i], m_secondPhaseItems)) {
m_secondPhaseItems.clear();
emit finished(SearchState::Failed);
return;
}
emit progressUpdated(i + 1, p_notebooks.size());
}
emit logRequested(tr("Searching notebook (%1)").arg(p_notebooks[i]->getName()));
if (!firstPhaseSearch(p_notebooks[i], secondPhaseItems)) {
return SearchState::Failed;
if (m_secondPhaseItems.isEmpty()) {
emit finished(SearchState::Finished);
}
});
emit progressUpdated(i + 1, p_notebooks.size());
}
if (isAskedToStop()) {
return SearchState::Stopped;
}
if (!secondPhaseItems.isEmpty()) {
// Do second phase search.
if (!secondPhaseSearch(secondPhaseItems)) {
return SearchState::Failed;
}
if (isAskedToStop()) {
return SearchState::Stopped;
}
return SearchState::Busy;
}
return SearchState::Finished;
return SearchState::Busy;
}
bool Searcher::prepare(const QSharedPointer<SearchOption> &p_option)
@ -172,12 +160,24 @@ bool Searcher::prepare(const QSharedPointer<SearchOption> &p_option)
m_filePattern = QRegularExpression(QRegularExpression::wildcardToRegularExpression(m_option->m_filePattern), QRegularExpression::CaseInsensitiveOption);
}
if (!m_firstPhaseWorker) {
m_firstPhaseWorker = new AsyncWorkerWithFunctor(this);
connect(m_firstPhaseWorker, &AsyncWorkerWithFunctor::finished,
this, &Searcher::doSecondPhaseSearch);
}
if (m_firstPhaseWorker->isRunning()) {
emit logRequested(tr("Failed to search due to worker is busy"));
return false;
}
m_secondPhaseItems.clear();
return true;
}
bool Searcher::isAskedToStop() const
{
QCoreApplication::sendPostedEvents();
return m_askedToStop;
}
@ -357,7 +357,7 @@ bool Searcher::firstPhaseSearchFolder(Node *p_node, QVector<SearchSecondPhaseIte
return false;
}
if (testTarget(SearchTarget::SearchFolder)) {
if (testTarget(SearchTarget::SearchFolder) && !p_node->isRoot()) {
const auto name = p_node->getName();
const auto folderPath = p_node->fetchAbsolutePath();
const auto relativePath = p_node->fetchPath();
@ -531,3 +531,26 @@ bool Searcher::searchTag(const Node *p_node) const
return false;
}
void Searcher::doSecondPhaseSearch()
{
if (m_secondPhaseItems.isEmpty()) {
return;
}
if (isAskedToStop()) {
emit finished(SearchState::Stopped);
return;
}
// Do second phase search.
if (!secondPhaseSearch(m_secondPhaseItems)) {
emit finished(SearchState::Failed);
return;
}
if (isAskedToStop()) {
emit finished(SearchState::Stopped);
return;
}
}

View File

@ -5,6 +5,7 @@
#include <QSharedPointer>
#include <QScopedPointer>
#include <QRegularExpression>
#include <QThread>
#include "searchdata.h"
#include "searchtoken.h"
@ -17,6 +18,7 @@ namespace vnotex
struct SearchResultItem;
class Node;
class Notebook;
class AsyncWorkerWithFunctor;
class Searcher : public QObject
{
@ -83,6 +85,9 @@ namespace vnotex
void createSearchEngine();
// Will be called after m_firstPhaseWorker finished.
void doSecondPhaseSearch();
QSharedPointer<SearchOption> m_option;
SearchToken m_token;
@ -92,6 +97,11 @@ namespace vnotex
bool m_askedToStop = false;
QScopedPointer<ISearchEngine> m_engine;
AsyncWorkerWithFunctor *m_firstPhaseWorker = nullptr;
// Pending second phase items.
QVector<SearchSecondPhaseItem> m_secondPhaseItems;
};
}

37
src/utils/asyncworker.cpp Normal file
View File

@ -0,0 +1,37 @@
#include "asyncworker.h"
using namespace vnotex;
AsyncWorker::AsyncWorker(QObject *p_parent)
: QThread(p_parent)
{
}
void AsyncWorker::stop()
{
m_askedToStop.store(1);
}
bool AsyncWorker::isAskedToStop() const
{
return m_askedToStop.load() == 1;
}
AsyncWorkerWithFunctor::AsyncWorkerWithFunctor(QObject *p_parent)
: QThread(p_parent)
{
}
void AsyncWorkerWithFunctor::doWork(const Functor &p_functor)
{
Q_ASSERT(!isRunning());
m_functor = p_functor;
start();
}
void AsyncWorkerWithFunctor::run()
{
Q_ASSERT(m_functor);
m_functor();
}

44
src/utils/asyncworker.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef ASYNCWORKER_H
#define ASYNCWORKER_H
#include <QThread>
#include <QAtomicInt>
namespace vnotex
{
class AsyncWorker : public QThread
{
Q_OBJECT
public:
explicit AsyncWorker(QObject *p_parent = nullptr);
public slots:
void stop();
protected:
bool isAskedToStop() const;
private:
QAtomicInt m_askedToStop = 0;
};
class AsyncWorkerWithFunctor : public QThread
{
Q_OBJECT
public:
typedef std::function<void()> Functor;
explicit AsyncWorkerWithFunctor(QObject *p_parent = nullptr);
void doWork(const Functor &p_functor);
protected:
void run() Q_DECL_OVERRIDE;
private:
Functor m_functor;
};
}
#endif // ASYNCWORKER_H

View File

@ -1,6 +1,7 @@
QT += widgets svg
SOURCES += \
$$PWD/asyncworker.cpp \
$$PWD/callbackpool.cpp \
$$PWD/contentmediautils.cpp \
$$PWD/docsutils.cpp \
@ -18,6 +19,7 @@ SOURCES += \
$$PWD/clipboardutils.cpp
HEADERS += \
$$PWD/asyncworker.h \
$$PWD/callbackpool.h \
$$PWD/contentmediautils.h \
$$PWD/docsutils.h \

View File

@ -40,6 +40,10 @@ SearchPanel::SearchPanel(const QSharedPointer<ISearchInfoProvider> &p_provider,
{
qRegisterMetaType<QVector<QSharedPointer<SearchResultItem>>>("QVector<QSharedPointer<SearchResultItem>>");
qRegisterMetaType<QSharedPointer<SearchResultItem>>("QSharedPointer<SearchResultItem>");
qRegisterMetaType<SearchState>("SearchState");
setupUI();
initOptions();
@ -442,9 +446,6 @@ SearchState SearchPanel::search(const QSharedPointer<SearchOption> &p_option)
break;
}
auto folder = m_provider->getCurrentFolder();
if (folder && (folder->isRoot())) {
folder = nullptr;
}
if (!folder) {
break;
}