forked from openkylin/ukui-search
Merge branch 'ukss-dev' into 'ukss-dev'
文件搜索插件 See merge request kylin-desktop/ukui-search!281
This commit is contained in:
commit
777b5c06ef
|
@ -34,6 +34,18 @@ public:
|
|||
QStringList getFileLabel();
|
||||
bool isSearchFileOnly();
|
||||
bool isSearchDirOnly();
|
||||
void clearAllConditions();
|
||||
void clearKeyWords();
|
||||
void clearSearchDir();
|
||||
|
||||
/**
|
||||
* @brief 分页选项
|
||||
* @param first 指定起始位置
|
||||
* @param maxResults 每次搜索结果集的数量
|
||||
*/
|
||||
void setPagination(unsigned int first, unsigned int maxResults);
|
||||
unsigned int first() const;
|
||||
unsigned int maxResults() const;
|
||||
|
||||
private:
|
||||
void copyData();
|
||||
|
@ -53,6 +65,8 @@ private:
|
|||
bool m_onlySearchFile = false;
|
||||
bool m_onlySearchDir = false;
|
||||
|
||||
unsigned int m_first = 0;
|
||||
unsigned int m_maxResults = 100; //默认取100条结果
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,11 @@ void SearchControllerPrivate::setOnlySearchDir(bool onlySearchDir)
|
|||
|
||||
size_t SearchControllerPrivate::getCurrentSearchId()
|
||||
{
|
||||
return m_searchId;
|
||||
m_searchIdMutex.lock();
|
||||
size_t searchId = m_searchId;
|
||||
m_searchIdMutex.unlock();
|
||||
|
||||
return searchId;
|
||||
}
|
||||
|
||||
DataQueue<ResultItem> *SearchControllerPrivate::getDataQueue()
|
||||
|
@ -164,6 +168,38 @@ void SearchControllerPrivate::copyData()
|
|||
}
|
||||
}
|
||||
|
||||
void SearchControllerPrivate::clearAllConditions()
|
||||
{
|
||||
clearKeyWords();
|
||||
clearSearchDir();
|
||||
}
|
||||
|
||||
void SearchControllerPrivate::clearKeyWords()
|
||||
{
|
||||
m_keywords.clear();
|
||||
}
|
||||
|
||||
void SearchControllerPrivate::clearSearchDir()
|
||||
{
|
||||
m_searchDirs.clear();
|
||||
}
|
||||
|
||||
void SearchControllerPrivate::setPagination(unsigned int first, unsigned int maxResults)
|
||||
{
|
||||
m_first = first;
|
||||
m_maxResults = maxResults;
|
||||
}
|
||||
|
||||
unsigned int SearchControllerPrivate::first() const
|
||||
{
|
||||
return m_first;
|
||||
}
|
||||
|
||||
unsigned int SearchControllerPrivate::maxResults() const
|
||||
{
|
||||
return m_maxResults;
|
||||
}
|
||||
|
||||
SearchController::SearchController(std::shared_ptr<SearchController> parent) : m_parent(parent), d(new SearchControllerPrivate(this))
|
||||
{
|
||||
}
|
||||
|
@ -285,3 +321,33 @@ void SearchController::stop()
|
|||
{
|
||||
d->stop();
|
||||
}
|
||||
|
||||
void SearchController::clearAllConditions()
|
||||
{
|
||||
d->clearAllConditions();
|
||||
}
|
||||
|
||||
void SearchController::clearKeyWords()
|
||||
{
|
||||
d->clearKeyWords();
|
||||
}
|
||||
|
||||
void SearchController::clearSearchDir()
|
||||
{
|
||||
d->clearSearchDir();
|
||||
}
|
||||
|
||||
void SearchController::setPagination(unsigned int first, unsigned int maxResults)
|
||||
{
|
||||
d->setPagination(first, maxResults);
|
||||
}
|
||||
|
||||
unsigned int SearchController::first() const
|
||||
{
|
||||
return d->first();
|
||||
}
|
||||
|
||||
unsigned int SearchController::maxResults() const
|
||||
{
|
||||
return d->maxResults();
|
||||
}
|
||||
|
|
|
@ -49,6 +49,14 @@ public:
|
|||
QStringList getFileLabel();
|
||||
bool isSearchFileOnly();
|
||||
bool isSearchDirOnly();
|
||||
void clearAllConditions();
|
||||
void clearKeyWords();
|
||||
void clearSearchDir();
|
||||
|
||||
void setPagination(unsigned int first, unsigned int maxResults);
|
||||
unsigned int first() const;
|
||||
unsigned int maxResults() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<SearchController> m_parent = nullptr;
|
||||
SearchControllerPrivate *d = nullptr;
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
#include "file-search-task.h"
|
||||
#include "index-status-recorder.h"
|
||||
#include "dir-watcher.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QQueue>
|
||||
#include <QDebug>
|
||||
|
||||
using namespace UkuiSearch;
|
||||
FileSearchTask::FileSearchTask(QObject *parent)
|
||||
{
|
||||
|
@ -51,49 +56,196 @@ FileSearchWorker::FileSearchWorker(FileSearchTask *fileSarchTask, std::shared_pt
|
|||
|
||||
void FileSearchWorker::run()
|
||||
{
|
||||
qDebug() << "File search start";
|
||||
//TODO do search here
|
||||
QQueue<QString> bfs;
|
||||
bfs.enqueue(QDir::homePath());
|
||||
QFileInfoList list;
|
||||
m_currentSearchId = m_searchController->getCurrentSearchId();
|
||||
//1.检查是否为不可搜索目录
|
||||
QStringList searchDirs = m_searchController->getSearchDir();
|
||||
searchDirs.removeDuplicates();
|
||||
|
||||
for (QString &dir : searchDirs) {
|
||||
if (dir.endsWith("/")) {
|
||||
dir = dir.mid(0, dir.length() - 1);
|
||||
}
|
||||
|
||||
QStringList blackListTmp = DirWatcher::getDirWatcher()->blackListOfDir(dir);
|
||||
if (!blackListTmp.contains(dir)) {
|
||||
m_validDirectories.append(dir);
|
||||
m_blackList.append(blackListTmp);
|
||||
}
|
||||
}
|
||||
|
||||
bool finished = true;
|
||||
//TODO 还需要判断是否为不能建立索引的目录
|
||||
if (IndexStatusRecorder::getInstance()->indexDatabaseEnable()) {
|
||||
qDebug() << "index ready";
|
||||
finished = searchWithIndex();
|
||||
|
||||
} else {
|
||||
qDebug() << "direct search";
|
||||
finished = directSearch();
|
||||
}
|
||||
|
||||
if (finished) QMetaObject::invokeMethod(m_FileSearchTask, "searchFinished", Q_ARG(size_t, m_currentSearchId));
|
||||
}
|
||||
|
||||
Xapian::Query FileSearchWorker::creatQueryForFileSearch() {
|
||||
Xapian::Query fileOrDir = Xapian::Query::MatchAll;
|
||||
if (!m_searchController->isSearchDirOnly() || !m_searchController->isSearchFileOnly()) {
|
||||
if (m_searchController->isSearchDirOnly()) {
|
||||
fileOrDir = Xapian::Query(Xapian::Query::OP_VALUE_RANGE, 1, "1", "1");
|
||||
|
||||
} else if (m_searchController->isSearchFileOnly()) {
|
||||
fileOrDir = Xapian::Query(Xapian::Query::OP_VALUE_RANGE, 1, "0", "0");
|
||||
}
|
||||
|
||||
} else {
|
||||
//同时指定只搜索目录和只搜索文件。。。
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Xapian::Query> queries;
|
||||
for (QString &keyword : m_searchController->getKeyword()) {
|
||||
if (!keyword.isEmpty()) {
|
||||
std::vector<Xapian::Query> queryOfKeyword;
|
||||
for(auto &c : keyword) {
|
||||
queryOfKeyword.emplace_back(Xapian::Query(QUrl::toPercentEncoding(c).toStdString()));
|
||||
}
|
||||
queries.emplace_back(Xapian::Query(Xapian::Query::OP_PHRASE, queryOfKeyword.begin(), queryOfKeyword.end()));
|
||||
}
|
||||
}
|
||||
|
||||
return {Xapian::Query::OP_AND, {Xapian::Query::OP_AND, queries.begin(), queries.end()}, fileOrDir};
|
||||
}
|
||||
|
||||
bool FileSearchWorker::searchWithIndex()
|
||||
{
|
||||
try {
|
||||
Xapian::Database db(INDEX_PATH.toStdString());
|
||||
Xapian::Enquire enquire(db);
|
||||
// qDebug() << "===" << QString::fromStdString(creatQueryForFileSearch().get_description());
|
||||
enquire.set_query(creatQueryForFileSearch());
|
||||
FileSearchFilter fileSearchFilter(this);
|
||||
|
||||
Xapian::MSet result = enquire.get_mset(m_searchController->first(), m_searchController->maxResults(), 0, &fileSearchFilter);
|
||||
|
||||
for (auto it = result.begin(); it != result.end(); ++it) {
|
||||
if (m_searchController->beginSearchIdCheck(m_currentSearchId)) {
|
||||
std::string data = it.get_document().get_data();
|
||||
|
||||
ResultItem resultItem(m_currentSearchId, QString::fromStdString(data));
|
||||
m_searchController->getDataQueue()->enqueue(resultItem);
|
||||
m_searchController->finishSearchIdCheck();
|
||||
|
||||
} else {
|
||||
qDebug() << "Search id changed!";
|
||||
m_searchController->finishSearchIdCheck();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} catch(const Xapian::Error &e) {
|
||||
qWarning() << QString::fromStdString(e.get_description());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileSearchWorker::directSearch()
|
||||
{
|
||||
unsigned int maxResults = m_searchController->maxResults();
|
||||
QQueue<QString> searchPathQueue;
|
||||
for (QString &dir : m_validDirectories) {
|
||||
searchPathQueue.enqueue(dir);
|
||||
}
|
||||
|
||||
QDir dir;
|
||||
if(true == m_searchController.get()->isSearchDirOnly()) {
|
||||
dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
QFileInfoList infoList;
|
||||
if (m_searchController->isSearchDirOnly()) {
|
||||
dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
} else {
|
||||
dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
|
||||
dir.setSorting(QDir::DirsFirst);
|
||||
}
|
||||
while(!bfs.empty()) {
|
||||
dir.setPath(bfs.dequeue());
|
||||
list = dir.entryInfoList();
|
||||
for (auto i : list) {
|
||||
if (i.isDir() && (!(i.isSymLink()))) {
|
||||
bfs.enqueue(i.absoluteFilePath());
|
||||
}
|
||||
bool matched = true;
|
||||
for(QString word : m_searchController.get()->getKeyword()) {
|
||||
if(!i.fileName().contains(word, Qt::CaseInsensitive)) {
|
||||
matched = false;
|
||||
// return;
|
||||
|
||||
while (!searchPathQueue.empty()) {
|
||||
dir.setPath(searchPathQueue.dequeue());
|
||||
infoList = dir.entryInfoList();
|
||||
|
||||
for (const auto &fileInfo : infoList) {
|
||||
if (fileInfo.isDir() && !fileInfo.isSymLink()) {
|
||||
QString newPath = fileInfo.absoluteFilePath();
|
||||
if (m_blackList.contains(newPath)) {
|
||||
//在黑名单的路径忽略搜索
|
||||
continue;
|
||||
}
|
||||
if (m_searchController->isRecurse()) {
|
||||
searchPathQueue.enqueue(newPath);
|
||||
}
|
||||
}
|
||||
if(matched) {
|
||||
if((i.isDir() && true == m_searchController.get()->isSearchFileOnly()) ||
|
||||
(i.isFile() && true == m_searchController.get()->isSearchDirOnly())) {
|
||||
continue;
|
||||
} else {
|
||||
ResultItem ri(m_searchController.get()->getCurrentSearchId(), i.absoluteFilePath());
|
||||
if(m_searchController.get()->beginSearchIdCheck(m_searchController.get()->getCurrentSearchId())) {
|
||||
m_searchController.get()->getDataQueue()->enqueue(ri);
|
||||
m_searchController.get()->finishSearchIdCheck();
|
||||
} else {
|
||||
qDebug() << "Search id changed!";
|
||||
m_searchController.get()->finishSearchIdCheck();
|
||||
return;
|
||||
|
||||
bool matched = false;
|
||||
//同时包含几个key为成功匹配
|
||||
for (const QString &keyword: m_searchController->getKeyword()) {
|
||||
if (!keyword.isEmpty()) {
|
||||
//TODO 修改匹配方式,对结果进行排序
|
||||
if (fileInfo.fileName().contains(keyword, Qt::CaseInsensitive)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_searchController->beginSearchIdCheck(m_currentSearchId)) {
|
||||
if (matched) {
|
||||
ResultItem ri(m_currentSearchId, fileInfo.absoluteFilePath());
|
||||
m_searchController->getDataQueue()->enqueue(ri);
|
||||
--maxResults;
|
||||
}
|
||||
m_searchController->finishSearchIdCheck();
|
||||
if (maxResults == 0) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Search id changed!";
|
||||
m_searchController->finishSearchIdCheck();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
QMetaObject::invokeMethod(m_FileSearchTask, "searchFinished", Q_ARG(size_t, m_searchController.get()->getCurrentSearchId()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FileSearchFilter::FileSearchFilter(FileSearchWorker *parent) : parent(parent) {}
|
||||
|
||||
bool FileSearchFilter::operator ()(const Xapian::Document &doc) const
|
||||
{
|
||||
if (parent) {
|
||||
QString path = QString::fromStdString(doc.get_data());
|
||||
bool isRecurse = parent->m_searchController->isRecurse();
|
||||
bool inSearchDir = std::any_of(parent->m_validDirectories.begin(), parent->m_validDirectories.end(), [&](QString &dir) {
|
||||
bool startWithDir = path.startsWith(dir);
|
||||
if (!startWithDir) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (path.length() == dir.length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isRecurse) {
|
||||
//去除搜索路径后,是否包含 "/"
|
||||
return !path.midRef((dir.length() + 1), (path.length() - dir.length() - 1)).contains("/");
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
bool inBlackList = std::any_of(parent->m_blackList.begin(), parent->m_blackList.end(), [&](QString &dir) {
|
||||
return path.startsWith(dir);
|
||||
});
|
||||
|
||||
return inSearchDir && !inBlackList;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -4,9 +4,12 @@
|
|||
#include <QIcon>
|
||||
#include <QThreadPool>
|
||||
#include <QRunnable>
|
||||
#include <xapian.h>
|
||||
|
||||
#include "search-task-plugin-iface.h"
|
||||
#include "search-controller.h"
|
||||
#include "result-item.h"
|
||||
|
||||
namespace UkuiSearch {
|
||||
/*
|
||||
* 这里只写了大概框架,具体逻辑未实现,可以当成伪代码参考。
|
||||
|
@ -35,13 +38,43 @@ private:
|
|||
|
||||
class FileSearchWorker : public QRunnable
|
||||
{
|
||||
friend class FileSearchFilter;
|
||||
|
||||
public:
|
||||
explicit FileSearchWorker(FileSearchTask *fileSarchTask, std::shared_ptr<SearchController> searchController);
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 通过索引进行搜索,如果搜索过程正常,返回true
|
||||
* 如果搜索被打断,返回false.
|
||||
* 搜索被打断是指用户使用同一个task发起多次搜索,导致searchId发生变化,那么上一次搜索即被打断。
|
||||
* @return
|
||||
*/
|
||||
bool searchWithIndex();
|
||||
//同上
|
||||
bool directSearch();
|
||||
Xapian::Query creatQueryForFileSearch();
|
||||
|
||||
private:
|
||||
FileSearchTask *m_FileSearchTask;
|
||||
std::shared_ptr<SearchController> m_searchController;
|
||||
|
||||
size_t m_currentSearchId = 0;
|
||||
QStringList m_validDirectories;
|
||||
QStringList m_blackList;
|
||||
};
|
||||
|
||||
class FileSearchFilter : public Xapian::MatchDecider {
|
||||
public:
|
||||
explicit FileSearchFilter(FileSearchWorker *parent);
|
||||
bool operator ()(const Xapian::Document &doc) const;
|
||||
|
||||
private:
|
||||
FileSearchWorker *parent = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // FILESEARCHTASK_H
|
||||
|
|
|
@ -24,6 +24,11 @@ public:
|
|||
void setOnlySearchDir(bool onlySearchDir);
|
||||
void setSearchOnlineApps(bool searchOnlineApps);
|
||||
void initSearchPlugin(SearchType searchType);
|
||||
void clearAllConditions();
|
||||
void clearKeyWords();
|
||||
void clearSearchDir();
|
||||
void setPagination(unsigned int first, unsigned int maxResults);
|
||||
|
||||
size_t startSearch(SearchType searchtype, QString customSearchType = QString());
|
||||
void stop();
|
||||
|
||||
|
|
|
@ -84,6 +84,26 @@ void UkuiSearchTaskPrivate::stop()
|
|||
m_searchCotroller->stop();
|
||||
}
|
||||
|
||||
void UkuiSearchTaskPrivate::clearKeyWords()
|
||||
{
|
||||
m_searchCotroller->clearKeyWords();
|
||||
}
|
||||
|
||||
void UkuiSearchTaskPrivate::clearAllConditions()
|
||||
{
|
||||
m_searchCotroller->clearAllConditions();
|
||||
}
|
||||
|
||||
void UkuiSearchTaskPrivate::clearSearchDir()
|
||||
{
|
||||
m_searchCotroller->clearSearchDir();
|
||||
}
|
||||
|
||||
void UkuiSearchTaskPrivate::setPagination(unsigned int first, unsigned int maxResults)
|
||||
{
|
||||
m_searchCotroller->setPagination(first, maxResults);
|
||||
}
|
||||
|
||||
UkuiSearchTask::UkuiSearchTask(QObject *parent) : QObject(parent), d(new UkuiSearchTaskPrivate(this))
|
||||
{
|
||||
connect(d, &UkuiSearchTaskPrivate::searchFinished, this, &UkuiSearchTask::searchFinished);
|
||||
|
@ -147,3 +167,23 @@ void UkuiSearchTask::stop()
|
|||
{
|
||||
d->stop();
|
||||
}
|
||||
|
||||
void UkuiSearchTask::clearAllConditions()
|
||||
{
|
||||
d->clearAllConditions();
|
||||
}
|
||||
|
||||
void UkuiSearchTask::clearKeyWords()
|
||||
{
|
||||
d->clearKeyWords();
|
||||
}
|
||||
|
||||
void UkuiSearchTask::clearSearchDir()
|
||||
{
|
||||
d->clearSearchDir();
|
||||
}
|
||||
|
||||
void UkuiSearchTask::setPagination(unsigned int first, unsigned int maxResults)
|
||||
{
|
||||
d->setPagination(first, maxResults);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,11 @@ public:
|
|||
void setOnlySearchDir(bool onlySearchDir);
|
||||
void setSearchOnlineApps(bool searchOnlineApps);
|
||||
void initSearchPlugin(SearchType searchType);
|
||||
void clearAllConditions();
|
||||
void clearKeyWords();
|
||||
void clearSearchDir();
|
||||
void setPagination(unsigned int first, unsigned int maxResults);
|
||||
|
||||
size_t startSearch(SearchType searchtype, QString customSearchType = QString());
|
||||
void stop();
|
||||
|
||||
|
|
Loading…
Reference in New Issue