ukui-search/libsearch/searchinterface/searchtasks/file-content-search-task.cpp

259 lines
8.0 KiB
C++

/*
*
* Copyright (C) 2023, KylinSoft Co., Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* Authors: iaom <zhangpengfei@kylinos.cn>
*/
//ukui-search
#include "file-content-search-task.h"
#include "index-status-recorder.h"
#include "chinese-segmentation.h"
#include "result-item.h"
#include "common.h"
#include "file-utils.h"
#include "file-indexer-config.h"
//Qt
#include <QIcon>
#include <QDebug>
#include <QDateTime>
using namespace UkuiSearch;
FileContentSearchTask::FileContentSearchTask(QObject *parent)
{
this->setParent(parent);
qRegisterMetaType<size_t>("size_t");
m_pool = new QThreadPool(this);
m_pool->setMaxThreadCount(1);
}
FileContentSearchTask::~FileContentSearchTask()
{
m_pool->clear();
m_pool->waitForDone();
}
void FileContentSearchTask::setController(const SearchController &searchController)
{
m_searchController = searchController;
}
PluginInterface::PluginType FileContentSearchTask::pluginType()
{
return PluginType::SearchTaskPlugin;
}
const QString FileContentSearchTask::name()
{
return tr("File Content");
}
const QString FileContentSearchTask::description()
{
return tr("File Content Search");
}
const QIcon FileContentSearchTask::icon()
{
return QIcon::fromTheme("folder", QIcon(":/unknown.svg"));
}
void FileContentSearchTask::setEnable(bool enable)
{
m_enable = enable;
}
bool FileContentSearchTask::isEnable()
{
int state = IndexStatusRecorder::getInstance()->getStatus(CONTENT_INDEX_DATABASE_STATE_KEY).toInt();
return m_enable && state == IndexStatusRecorder::State::Ready || state == IndexStatusRecorder::State::Updating || state == IndexStatusRecorder::State::RealTimeUpdating;
}
QString FileContentSearchTask::getCustomSearchType()
{
return tr("File Content");
}
SearchProperty::SearchType FileContentSearchTask::getSearchType()
{
return SearchProperty::SearchType::FileContent;
}
void FileContentSearchTask::startSearch()
{
FileContentSearchWorker *worker = new FileContentSearchWorker(this, &m_searchController);
m_pool->start(worker);
}
void FileContentSearchTask::stop()
{
}
bool FileContentSearchTask::isSearching()
{
return m_pool->activeThreadCount() > 0;
}
FileContentSearchWorker::FileContentSearchWorker(FileContentSearchTask *fileContentSearchTask, SearchController *searchController)
{
m_fileContentSearchTask = fileContentSearchTask;
m_searchController = searchController;
m_currentSearchId = m_searchController->getCurrentSearchId();
}
void FileContentSearchWorker::run()
{
QStringList searchDirs = m_searchController->getSearchDir();
searchDirs.removeDuplicates();
for (QString &dir : searchDirs) {
if (dir.endsWith("/") && dir != "/") {
dir = dir.mid(0, dir.length() - 1);
}
m_validDirectories.append(dir);
}
bool finished = true;
if (m_fileContentSearchTask->isEnable()) {
qDebug() << "content index enable";
finished = execSearch();
} else {
qWarning() << "content index incomplete";
sendErrorMsg(QObject::tr("Content index incomplete."));
finished = false;
}
if (finished) QMetaObject::invokeMethod(m_fileContentSearchTask, "searchFinished", Q_ARG(size_t, m_currentSearchId));
}
void FileContentSearchWorker::sendErrorMsg(const QString &msg)
{
QMetaObject::invokeMethod(m_fileContentSearchTask, "searchError",
Q_ARG(size_t, m_currentSearchId),
Q_ARG(QString, msg));
}
bool FileContentSearchWorker::execSearch()
{
try {
Xapian::Database db(CONTENT_INDEX_PATH.toStdString());
if(FileIndexerConfig::getInstance()->isOCREnable()) {
db.add_database(Xapian::Database(OCR_CONTENT_INDEX_PATH.toStdString()));
}
Xapian::Enquire enquire(db);
enquire.set_query(createQuery());
FileContentSearchFilter filter(this);
Xapian::MSet result = enquire.get_mset(0, m_searchController->maxResults(), nullptr, &filter);
for (auto it = result.begin(); it != result.end(); ++it) {
if (m_searchController->beginSearchIdCheck(m_currentSearchId)) {
QString path = QString::fromStdString(it.get_document().get_value(CONTENT_DATABASE_PATH_SLOT));
SearchResultProperties properties = m_searchController->getResultProperties(SearchProperty::SearchType::FileContent);
ResultItem resultItem(m_currentSearchId);
resultItem.setItemKey(path);
if(properties.contains(SearchProperty::SearchResultProperty::FilePath)) {
resultItem.setValue(SearchProperty::SearchResultProperty::FilePath, path);
}
if(properties.contains(SearchProperty::SearchResultProperty::FileIconName)) {
resultItem.setValue(SearchProperty::SearchResultProperty::FileIconName, FileUtils::getFileIcon(QUrl::fromLocalFile(path).toString()).name());
}
if(properties.contains(SearchProperty::SearchResultProperty::FileName)) {
resultItem.setValue(SearchProperty::SearchResultProperty::FileName, path.section("/", -1));
}
if(properties.contains(SearchProperty::SearchResultProperty::ModifiedTime)) {
resultItem.setValue(SearchProperty::SearchResultProperty::ModifiedTime,
QDateTime::fromString(QString::fromStdString(it.get_document().get_value(3)), "yyyyMMddHHmmsszzz"));
}
m_searchController->getDataQueue()->enqueue(resultItem);
if(++m_resultNum == m_searchController->informNum()) {
QMetaObject::invokeMethod(m_fileContentSearchTask, "reachInformNum");
m_resultNum = 0;
}
m_searchController->finishSearchIdCheck();
} else {
qDebug() << "Search id changed!";
m_searchController->finishSearchIdCheck();
return false;
}
}
return true;
} catch (const Xapian::Error &e) {
qWarning() << QString::fromStdString(e.get_description());
sendErrorMsg("Xapian Error: " + QString::fromStdString(e.get_description()));
return false;
}
}
inline Xapian::Query FileContentSearchWorker::createQuery()
{
std::vector<Xapian::Query> v;
for (auto &keyword : m_searchController->getKeyword()) {
std::vector<KeyWord> sKeyWord = ChineseSegmentation::getInstance()->callSegment(keyword);
for(const auto & c : sKeyWord) {
v.emplace_back(c.word);
}
}
Xapian::Query query;
if(FileIndexerConfig::getInstance()->isFuzzySearchEnable()) {
query = Xapian::Query(Xapian::Query::OP_OR, v.begin(), v.end());
} else {
query = Xapian::Query(Xapian::Query::OP_AND, v.begin(), v.end());
}
return query;
}
FileContentSearchFilter::FileContentSearchFilter(FileContentSearchWorker *worker) : m_worker(worker)
{
}
bool FileContentSearchFilter::operator()(const Xapian::Document &doc) const
{
//在此处对搜索结果进行过滤
QString path = QString::fromStdString(doc.get_value(CONTENT_DATABASE_PATH_SLOT));
if(!QFileInfo::exists(path)) {
return false;
}
//如果不指定搜索目录,那么搜索整个数据库
if (m_worker && !m_worker->m_validDirectories.empty()) {
for(const QString &dir : m_worker->m_validDirectories) {
return FileUtils::isOrUnder(path, dir);
}
}
return true;
}