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
54 changes: 54 additions & 0 deletions autotests/dfm-search-tests/tst_chinese_nlp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ private Q_SLOTS:
void size_suffix_max();
void size_suffix_combined();
void size_suffix_chineseUnits();
void size_doesNotShadow_yearMonth();

// Relative time tests
void timeRelative_justNow();
Expand Down Expand Up @@ -971,6 +972,22 @@ void tst_ChineseNLP::timeCustom_yearMonth()
QCOMPARE(intent.timeConstraint().customStart().date().day(), 1);
QCOMPARE(intent.timeConstraint().customEnd().date().year(), 2025);
QCOMPARE(intent.timeConstraint().customEnd().date().month(), 12);

// "2025年12" — trailing 月 is optional
ParsedIntent intent2;
m_parser->parse(QStringLiteral("2025年12创建的文件"), intent2);
QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Custom);
QCOMPARE(intent2.timeConstraint().customStart().date().year(), 2025);
QCOMPARE(intent2.timeConstraint().customStart().date().month(), 12);
QCOMPARE(intent2.timeConstraint().customStart().date().day(), 1);

// "2025年12月份" — support 月份 suffix too
ParsedIntent intent3;
m_parser->parse(QStringLiteral("2025年12月份创建的文件"), intent3);
QCOMPARE(intent3.timeConstraint().kind(), TimeConstraintKind::Custom);
QCOMPARE(intent3.timeConstraint().customStart().date().year(), 2025);
QCOMPARE(intent3.timeConstraint().customStart().date().month(), 12);
QCOMPARE(intent3.timeConstraint().customStart().date().day(), 1);
}

void tst_ChineseNLP::timeCustom_yearMonth_separators()
Expand Down Expand Up @@ -1035,6 +1052,14 @@ void tst_ChineseNLP::timeCustom_fullDate()
// Verify time boundaries
QCOMPARE(intent.timeConstraint().customStart().time().hour(), 0);
QCOMPARE(intent.timeConstraint().customEnd().time().hour(), 23);

// "2025年12月30" — trailing 日号 is optional
ParsedIntent intent2;
m_parser->parse(QStringLiteral("2025年12月30创建的文件"), intent2);
QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Custom);
QCOMPARE(intent2.timeConstraint().customStart().date().year(), 2025);
QCOMPARE(intent2.timeConstraint().customStart().date().month(), 12);
QCOMPARE(intent2.timeConstraint().customStart().date().day(), 30);
}

void tst_ChineseNLP::timeCustom_fullDate_separators()
Expand Down Expand Up @@ -1579,6 +1604,35 @@ void tst_ChineseNLP::size_suffix_chineseUnits()
QCOMPARE(intent2.sizeConstraint().maxSize(), 51200LL); // 50KB
}

void tst_ChineseNLP::size_doesNotShadow_yearMonth()
{
ParsedIntent intent;
m_parser->parse(QStringLiteral("2025-5的音乐"), intent);

QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom);
QCOMPARE(intent.timeConstraint().customStart().date().year(), 2025);
QCOMPARE(intent.timeConstraint().customStart().date().month(), 5);
QVERIFY(!intent.sizeConstraint().isValid());
QVERIFY(setEquals(intent.fileExtensions(), audioExpectedExts()));

bool hasTimeYearMonth = false;
bool hasAudio = false;
bool hasSizeBetween = false;
for (const MatchSpan &span : intent.consumedSpans()) {
if (span.ruleId() == QLatin1String("time_exact_year_month")) {
hasTimeYearMonth = true;
} else if (span.ruleId() == QLatin1String("filetype_audio")) {
hasAudio = true;
} else if (span.ruleId() == QLatin1String("size_dynamic_between")) {
hasSizeBetween = true;
}
}

QVERIFY(hasTimeYearMonth);
QVERIFY(hasAudio);
QVERIFY(!hasSizeBetween);
}

// ===== Relative Time Tests =====

void tst_ChineseNLP::timeRelative_justNow()
Expand Down
60 changes: 49 additions & 11 deletions autotests/dfm-search-tests/tst_semantic_search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,15 +306,35 @@ void tst_TimeExtraction::customYearMonth()
meta["type"] = "custom";
meta["format"] = "year_month";
QByteArray json = makeRuleJson("time", "time_exact_year_month",
"(?<year>\\d{4})-(?<month>\\d{1,2})", 150, meta);
"(?<year>\\d{2,4})[年\\./\\-](?<month>\\d{1,2})(?:月份?)?", 150, meta);

RuleGroup group;
QVERIFY(buildGroupFromJson(json, group));

auto match = group.rules[0].regex.match("2025-12");
QVERIFY(match.hasMatch());
QCOMPARE(match.captured("year"), QString("2025"));
QCOMPARE(match.captured("month"), QString("12"));
auto match1 = group.rules[0].regex.match("2025年12月");
QVERIFY(match1.hasMatch());
QCOMPARE(match1.captured("year"), QString("2025"));
QCOMPARE(match1.captured("month"), QString("12"));

auto match2 = group.rules[0].regex.match("2025年12");
QVERIFY(match2.hasMatch());
QCOMPARE(match2.captured("year"), QString("2025"));
QCOMPARE(match2.captured("month"), QString("12"));

auto match3 = group.rules[0].regex.match("2025年12月份");
QVERIFY(match3.hasMatch());
QCOMPARE(match3.captured("year"), QString("2025"));
QCOMPARE(match3.captured("month"), QString("12"));

auto match4 = group.rules[0].regex.match("2025-12");
QVERIFY(match4.hasMatch());
QCOMPARE(match4.captured("year"), QString("2025"));
QCOMPARE(match4.captured("month"), QString("12"));

auto match5 = group.rules[0].regex.match("25.12");
QVERIFY(match5.hasMatch());
QCOMPARE(match5.captured("year"), QString("25"));
QCOMPARE(match5.captured("month"), QString("12"));
}

void tst_TimeExtraction::customFullDate()
Expand All @@ -323,17 +343,35 @@ void tst_TimeExtraction::customFullDate()
meta["type"] = "custom";
meta["format"] = "full_date";
QByteArray json = makeRuleJson("time", "time_exact_full_date",
"(?<year>\\d{4})-(?<month>\\d{1,2})-(?<day>\\d{1,2})",
"(?<year>\\d{2,4})[年\\./\\-](?<month>\\d{1,2})[月\\./\\-](?<day>\\d{1,2})[日号]?",
140, meta);

RuleGroup group;
QVERIFY(buildGroupFromJson(json, group));

auto match = group.rules[0].regex.match("2025-03-15");
QVERIFY(match.hasMatch());
QCOMPARE(match.captured("year"), QString("2025"));
QCOMPARE(match.captured("month"), QString("03"));
QCOMPARE(match.captured("day"), QString("15"));
auto match1 = group.rules[0].regex.match("2025年12月5日");
QVERIFY(match1.hasMatch());
QCOMPARE(match1.captured("year"), QString("2025"));
QCOMPARE(match1.captured("month"), QString("12"));
QCOMPARE(match1.captured("day"), QString("5"));

auto match2 = group.rules[0].regex.match("2025年12月5");
QVERIFY(match2.hasMatch());
QCOMPARE(match2.captured("year"), QString("2025"));
QCOMPARE(match2.captured("month"), QString("12"));
QCOMPARE(match2.captured("day"), QString("5"));

auto match3 = group.rules[0].regex.match("2025-03-15");
QVERIFY(match3.hasMatch());
QCOMPARE(match3.captured("year"), QString("2025"));
QCOMPARE(match3.captured("month"), QString("03"));
QCOMPARE(match3.captured("day"), QString("15"));

auto match4 = group.rules[0].regex.match("2025/12/5");
QVERIFY(match4.hasMatch());
QCOMPARE(match4.captured("year"), QString("2025"));
QCOMPARE(match4.captured("month"), QString("12"));
QCOMPARE(match4.captured("day"), QString("5"));
}

void tst_TimeExtraction::noMatch()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@
return;
}

// Size extraction runs after time/filetype. If this size span is already
// fully consumed by an earlier, higher-confidence dimension such as a
// year-month time expression ("2025-5"), ignore it to avoid overwriting
// the intent with a bogus size constraint.
for (const MatchSpan &existing : intent.consumedSpans()) {
if (match.capturedStart() >= existing.start()
&& match.capturedEnd() <= existing.end()) {

Check warning on line 38 in src/dfm-search/dfm-search-lib/semantic/extractors/sizeextractor.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

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

Check warning on line 38 in src/dfm-search/dfm-search-lib/semantic/extractors/sizeextractor.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Consider using std::any_of algorithm instead of a raw loop.
return;
}
}

const QVariantMap metadata = m_engine->ruleMetadata("size", ruleId);
const QString typeStr = metadata.value("type").toString();
SizeConstraint sc;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@
},
{
"id": "time_exact_year_month",
"pattern": "(?<year>\\d{2,4})[年\\./\\-](?<month>\\d{1,2})月份?",
"pattern": "(?<year>\\d{2,4})[年\\./\\-](?<month>\\d{1,2})(?:月份?)?",
"description": "Exact year-month (e.g. 2025年12月, 2025-12)",
"enabled": true,
"priority": 140,
Expand Down Expand Up @@ -337,4 +337,4 @@
]
}
]
}
}
Loading