Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
#include "semantic/semanticruleengine.h"
#include "locationextractor.h"

#include <QDir>

Check warning on line 10 in src/dfm-search/dfm-search-lib/semantic/extractors/actionextractor.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDir> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 10 in src/dfm-search/dfm-search-lib/semantic/extractors/actionextractor.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QDir> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QFileInfo>

Check warning on line 11 in src/dfm-search/dfm-search-lib/semantic/extractors/actionextractor.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QFileInfo> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 11 in src/dfm-search/dfm-search-lib/semantic/extractors/actionextractor.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QFileInfo> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QDebug>

Check warning on line 12 in src/dfm-search/dfm-search-lib/semantic/extractors/actionextractor.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDebug> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 12 in src/dfm-search/dfm-search-lib/semantic/extractors/actionextractor.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QDebug> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QVariant>

Check warning on line 13 in src/dfm-search/dfm-search-lib/semantic/extractors/actionextractor.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QVariant> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 13 in src/dfm-search/dfm-search-lib/semantic/extractors/actionextractor.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QVariant> not found. Please note: Cppcheck does not need standard library headers to get proper results.

DFM_SEARCH_BEGIN_NS

Expand Down Expand Up @@ -137,16 +138,29 @@
}

const QString xdgType = meta.value("xdg_type").toString();
const QString customSubdir = meta.value("custom_path").toString();
const QVariant customPathVar = meta.value("custom_path");

QStringList resolved;
if (!customSubdir.isEmpty()) {
resolved = LocationExtractor::expandCustomPath(
QDir::homePath(), customSubdir);
} else if (!xdgType.isEmpty()) {
const QString path = LocationExtractor::resolveXdgPath(xdgType);
if (!path.isEmpty()) {
resolved = { path };
if (customPathVar.userType() == QMetaType::QVariantList) {
for (const QVariant &pathVar : customPathVar.toList()) {
const QString relPath = pathVar.toString();
if (relPath.isEmpty()) continue;
const QStringList expanded = LocationExtractor::expandCustomPath(
QDir::homePath(), relPath);
for (const QString &p : expanded) {
if (!resolved.contains(p)) resolved.append(p);
}
}
} else {
const QString customSubdir = customPathVar.toString();
if (!customSubdir.isEmpty()) {
resolved = LocationExtractor::expandCustomPath(
QDir::homePath(), customSubdir);
} else if (!xdgType.isEmpty()) {
const QString path = LocationExtractor::resolveXdgPath(xdgType);
if (!path.isEmpty()) {
resolved = { path };
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@

#include "semantic/semanticruleengine.h"

#include <QDir>

Check warning on line 9 in src/dfm-search/dfm-search-lib/semantic/extractors/locationextractor.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDir> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 9 in src/dfm-search/dfm-search-lib/semantic/extractors/locationextractor.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QDir> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QFileInfo>

Check warning on line 10 in src/dfm-search/dfm-search-lib/semantic/extractors/locationextractor.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QFileInfo> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 10 in src/dfm-search/dfm-search-lib/semantic/extractors/locationextractor.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QFileInfo> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QStandardPaths>

Check warning on line 11 in src/dfm-search/dfm-search-lib/semantic/extractors/locationextractor.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QStandardPaths> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 11 in src/dfm-search/dfm-search-lib/semantic/extractors/locationextractor.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QStandardPaths> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QVariant>

Check warning on line 12 in src/dfm-search/dfm-search-lib/semantic/extractors/locationextractor.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QVariant> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 12 in src/dfm-search/dfm-search-lib/semantic/extractors/locationextractor.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QVariant> not found. Please note: Cppcheck does not need standard library headers to get proper results.

DFM_SEARCH_BEGIN_NS

Expand All @@ -34,7 +35,7 @@
const QVariantMap metadata = m_engine->ruleMetadata("location", ruleIds[i]);

const QString xdgType = metadata.value("xdg_type").toString();
const QString customSubdir = metadata.value("custom_path").toString();
const QVariant customPathVar = metadata.value("custom_path");
const bool includeHidden = metadata.value("include_hidden", false).toBool();

// Always record the consumed span when a rule matches,
Expand All @@ -47,12 +48,24 @@
intent.consumedSpans().append(span);

QStringList resolvedPaths;
if (!customSubdir.isEmpty()) {
resolvedPaths = expandCustomPath(QDir::homePath(), customSubdir);
if (customPathVar.userType() == QMetaType::QVariantList) {
for (const QVariant &pathVar : customPathVar.toList()) {
const QString relPath = pathVar.toString();
if (relPath.isEmpty()) continue;
const QStringList expanded = expandCustomPath(QDir::homePath(), relPath);
for (const QString &p : expanded) {
if (!resolvedPaths.contains(p)) resolvedPaths.append(p);
}
}
} else {
const QString path = resolveXdgPath(xdgType);
if (!path.isEmpty()) {
resolvedPaths = { path };
const QString customSubdir = customPathVar.toString();
if (!customSubdir.isEmpty()) {
resolvedPaths = expandCustomPath(QDir::homePath(), customSubdir);
} else {
const QString path = resolveXdgPath(xdgType);
if (!path.isEmpty()) {
resolvedPaths = { path };
}
}
}
if (resolvedPaths.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@
"enabled": true,
"priority": 180,
"metadata": {
"custom_path": "Documents/xwechat_files/*/msg/file",
"custom_path": [
"Documents/xwechat_files/*/msg/file",
"Documents/xwechat_files/*/msg/video"
],
"exclude_patterns": ["*_thumb.jpg"],
"include_hidden": false,
"im_received": true
}
Expand Down
12 changes: 12 additions & 0 deletions src/dfm-search/dfm-search-lib/semantic/semanticruleengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,18 @@
return {};
}

QVariantMap SemanticRuleEngine::ruleMetadataById(const QString &ruleId) const
{
for (const RuleGroup &group : m_groups) {
for (const Rule &rule : group.rules) {
if (rule.id == ruleId) {

Check warning on line 185 in src/dfm-search/dfm-search-lib/semantic/semanticruleengine.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Consider using std::find_if algorithm instead of a raw loop.

Check warning on line 185 in src/dfm-search/dfm-search-lib/semantic/semanticruleengine.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Consider using std::find_if algorithm instead of a raw loop.
return rule.metadata;
}
}
}
return {};
}

bool SemanticRuleEngine::hasGroup(const QString &group) const
{
return m_groups.contains(group);
Expand Down
11 changes: 11 additions & 0 deletions src/dfm-search/dfm-search-lib/semantic/semanticruleengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ class SemanticRuleEngine : public QObject
*/
QVariantMap ruleMetadata(const QString &group, const QString &ruleId) const;

/**
* @brief Get a rule's metadata by rule ID, searching across all groups.
* @param ruleId The rule ID to look up
* @return The metadata map, or empty if not found
*
* Unlike ruleMetadata(group, ruleId), this searches all groups.
* Useful when the caller has a ruleId from consumedSpans but doesn't
* know which group it belongs to.
*/
QVariantMap ruleMetadataById(const QString &ruleId) const;

/**
* @brief Get all rule IDs in a group.
*/
Expand Down
112 changes: 66 additions & 46 deletions src/dfm-search/dfm-search-lib/semantic/semanticsearcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@
#include <dfm-search/dsearch_global.h>
#include <dfm-search/semantic_types.h>

#include <QDebug>

Check warning on line 14 in src/dfm-search/dfm-search-lib/semantic/semanticsearcher.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDebug> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 14 in src/dfm-search/dfm-search-lib/semantic/semanticsearcher.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QDebug> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QDir>
#include <QEventLoop>
#include <QFileInfo>
#include <QTimer>
#include <QVariant>

#include <algorithm>

DFM_SEARCH_BEGIN_NS

Expand Down Expand Up @@ -47,13 +51,13 @@
{
int count = 0;
if (!intent.searchDirectories().isEmpty()) ++count; // location
if (intent.hiddenOnly()) ++count; // hiddenOnly 属性
if (intent.recentOnly()) ++count; // recentOnly 属性
if (intent.timeConstraint().isValid()) ++count; // time 约束
if (intent.sizeConstraint().isValid()) ++count; // size 约束
if (!intent.fileExtensions().isEmpty()) ++count; // fileType
if (!intent.keywords().isEmpty()) ++count; // keyword (unconsumed text)
if (hasTargetRuleSpan(intent)) ++count; // target 名词 (文件/文件名/文件夹)
if (intent.hiddenOnly()) ++count; // hiddenOnly 属性
if (intent.recentOnly()) ++count; // recentOnly 属性
if (intent.timeConstraint().isValid()) ++count; // time 约束
if (intent.sizeConstraint().isValid()) ++count; // size 约束
if (!intent.fileExtensions().isEmpty()) ++count; // fileType
if (!intent.keywords().isEmpty()) ++count; // keyword (unconsumed text)
if (hasTargetRuleSpan(intent)) ++count; // target 名词 (文件/文件名/文件夹)

// 当 specific filetype 与 general filetype 同时命中(如 "PPT文档"、"pdf文档"),
// general 类型词(文档/表格/幻灯片)充当目标名词,贡献额外维度。
Expand Down Expand Up @@ -161,48 +165,22 @@
dirs = QStringList { QDir::homePath() };
}

// Step 5: Set up signal/slot handlers
auto onFinished = [this](const SearchResultList &results) {
// Collect and deduplicate results from each engine's final result list
for (const SearchResult &r : results) {
if (!seenPaths.contains(r.path())) {
seenPaths.insert(r.path());
allResults.append(r);
}
}

if (pendingFinishCount.fetch_sub(1) == 1) {
// All engines finished
timeoutTimer->stop();

// Truncate final deduplicated results to maxResults
if (maxResults > 0 && allResults.size() > maxResults) {
allResults = allResults.mid(0, maxResults);
}

if (cancelled.load()) {
status.store(SearchStatus::Cancelled);
Q_EMIT q->statusChanged(SearchStatus::Cancelled);
Q_EMIT q->searchCancelled();
} else {
status.store(SearchStatus::Finished);
Q_EMIT q->statusChanged(SearchStatus::Finished);
Q_EMIT q->searchFinished(allResults);
excludeNamePatterns.clear();
for (const MatchSpan &span : intent.consumedSpans()) {
const QVariantMap meta = ruleEngine->ruleMetadataById(span.ruleId());
const QVariant ep = meta.value("exclude_patterns");
if (ep.userType() == QMetaType::QVariantList) {
for (const auto &v : ep.toList()) {
const QString p = v.toString();
if (!p.isEmpty() && !excludeNamePatterns.contains(p))
excludeNamePatterns.append(p);
}
}
};
}

// 引擎错误也必须触发计数归零检查,否则上层会一直阻塞到 60s 超时。
// 校验失败(如 KeywordIsEmpty)会让 GenericSearchEngine::search 在
// emit errorOccurred 后直接 return,不会 emit searchFinished ——
// 此时必须由 onError 兜底减计数,把错误视为引擎完成。
auto onError = [this, onFinished](const SearchError &error) {
qWarning() << "Search error:" << error.message();
// Don't propagate individual engine errors to caller
// The other engines may still produce valid results
// 但仍需减计数,否则 pendingFinishCount 永不到 0
onFinished({});
};
// Step 5: Set up signal/slot handlers
auto onFinished = [this](const SearchResultList &r) { onEngineFinished(r); };
auto onError = [this](const SearchError &e) { onEngineError(e); };

// Step 6: Clean up any previous search engines
pendingFinishCount.store(0);
Expand Down Expand Up @@ -307,6 +285,48 @@
engine->search(query);
}

void SemanticSearcherData::onEngineFinished(const SearchResultList &results)
{
for (const SearchResult &r : results) {
if (!excludeNamePatterns.isEmpty()) {
const QString fileName = QFileInfo(r.path()).fileName();
bool excluded = std::any_of(excludeNamePatterns.cbegin(), excludeNamePatterns.cend(),
[&fileName](const QString &pattern) {
return QDir::match(pattern, fileName);
});
if (excluded) continue;
}
if (!seenPaths.contains(r.path())) {
seenPaths.insert(r.path());
allResults.append(r);
}
}

if (pendingFinishCount.fetch_sub(1) == 1) {
timeoutTimer->stop();

if (maxResults > 0 && allResults.size() > maxResults) {
allResults = allResults.mid(0, maxResults);
}

if (cancelled.load()) {
status.store(SearchStatus::Cancelled);
Q_EMIT q->statusChanged(SearchStatus::Cancelled);
Q_EMIT q->searchCancelled();
} else {
status.store(SearchStatus::Finished);
Q_EMIT q->statusChanged(SearchStatus::Finished);
Q_EMIT q->searchFinished(allResults);
}
}
}

void SemanticSearcherData::onEngineError(const SearchError &error)
{
qWarning() << "Search error:" << error.message();
onEngineFinished({});
}

void SemanticSearcherData::doCancel()
{
cancelled.store(true);
Expand Down
4 changes: 4 additions & 0 deletions src/dfm-search/dfm-search-lib/semantic/semanticsearcher_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ class SemanticSearcherData
std::function<void(const SearchResultList &)> onFinished,
std::function<void(const SearchError &)> onError);

void onEngineFinished(const SearchResultList &results);
void onEngineError(const SearchError &error);

SemanticSearcher *q = nullptr;

// State
Expand Down Expand Up @@ -89,6 +92,7 @@ class SemanticSearcherData
bool detailedResultsEnabled = false;
int maxResults = 0; // 0 = unlimited
QStringList excludedPaths; // directories to exclude from search
QStringList excludeNamePatterns; // glob patterns to filter from results (e.g. *_thumb.jpg)
};

DFM_SEARCH_END_NS
Expand Down
Loading