From 7d5707c4808bbffaca0b1642d4d238a4c28d2736 Mon Sep 17 00:00:00 2001 From: Zhang Sheng Date: Mon, 20 Jul 2026 10:49:01 +0800 Subject: [PATCH] fix: enhance Chinese numeral time parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Added support for Chinese numeral month specifications in time queries ("七月", "十二月") 2. Extended year-month and full date patterns to accept Chinese numerals ("2025年七月", "七月八号") 3. Improved number parsing logic to handle leading-ten Chinese numerals ("十一", "十五") 4. Updated test cases to verify new Chinese numeral time formats 5. Expanded regex patterns in time_rules.json to accommodate Chinese numerals The changes improve natural language processing for Chinese date/time queries by properly handling Chinese numeral representations alongside Arabic numerals. This makes the search functionality more intuitive for Chinese users who may use either number format when specifying dates. Log: Improved support for Chinese numeral time expressions in search queries Influence: 1. Test month queries with Chinese numerals ("七月", "十二月") 2. Verify year-month combinations with Chinese months ("2025年十一月") 3. Check date formats with Chinese day numbers ("七月八号") 4. Test full dates with Chinese numerals ("2025年七月三十日") 5. Verify mixed numeral formats ("最近3天" vs. "最近十五天") 6. Check edge cases like single-digit and leading-ten numbers fix: 增强中文数字时间解析功能 1. 新增支持中文数字月份查询 ("七月", "十二月") 2. 扩展年月和完整日期格式支持中文数字 ("2025年七月", "七月八号") 3. 改进数字解析逻辑以处理"十"开头的数字写法 ("十一", "十五") 4. 添加测试用例验证新的中文数字时间格式 5. 更新 time_rules.json 中的正则表达式以兼容中文数字 这些改动提升了中文时间查询的自然语言处理能力,正确处理中文数字与阿拉伯数 字表达,使搜索功能对使用中文数字表达时间的用户更加友好。 Log: 改进搜索查询中的中文数字时间表达式支持 Influence: 1. 测试中文数字月份查询 ("七月", "十二月") 2. 验证带中文月份的年份组合 ("2025年十一月") 3. 检查带中文日期的格式 ("七月八号") 4. 测试完整的带中文数字日期 ("2025年七月三十日") 5. 验证混合数字格式 ("最近3天" 与 "最近十五天") 6. 检查边界情况如个位数和"十"开头的数字 Fixes: #370419 --- .../dfm-search-tests/tst_chinese_nlp.cpp | 59 +++++++++++++++++++ .../semantic/extractors/timeextractor.cpp | 12 +++- .../semantic/rules/zh_CN/time_rules.json | 8 +-- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/autotests/dfm-search-tests/tst_chinese_nlp.cpp b/autotests/dfm-search-tests/tst_chinese_nlp.cpp index 2971e72d..d845c36e 100644 --- a/autotests/dfm-search-tests/tst_chinese_nlp.cpp +++ b/autotests/dfm-search-tests/tst_chinese_nlp.cpp @@ -959,6 +959,20 @@ void tst_ChineseNLP::timeCustom_month() m_parser->parse(QStringLiteral("5月份的图片"), intent2); QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent2.timeConstraint().customStart().date().month(), 5); + + // "七月" — Chinese numeral month should work too + ParsedIntent intent3; + m_parser->parse(QStringLiteral("七月的文件"), intent3); + QCOMPARE(intent3.timeConstraint().kind(), TimeConstraintKind::Custom); + QCOMPARE(intent3.timeConstraint().customStart().date().month(), 7); + QCOMPARE(intent3.timeConstraint().customEnd().date().month(), 7); + + // "十二月" — leading-ten Chinese numeral month should work too + ParsedIntent intent4; + m_parser->parse(QStringLiteral("十二月的文件"), intent4); + QCOMPARE(intent4.timeConstraint().kind(), TimeConstraintKind::Custom); + QCOMPARE(intent4.timeConstraint().customStart().date().month(), 12); + QCOMPARE(intent4.timeConstraint().customEnd().date().month(), 12); } void tst_ChineseNLP::timeCustom_yearMonth() @@ -988,6 +1002,22 @@ void tst_ChineseNLP::timeCustom_yearMonth() QCOMPARE(intent3.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent3.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent3.timeConstraint().customStart().date().day(), 1); + + // "2025年七月" — Chinese numeral month should also be parsed + ParsedIntent intent4; + m_parser->parse(QStringLiteral("2025年七月的文档"), intent4); + QCOMPARE(intent4.timeConstraint().kind(), TimeConstraintKind::Custom); + QCOMPARE(intent4.timeConstraint().customStart().date().year(), 2025); + QCOMPARE(intent4.timeConstraint().customStart().date().month(), 7); + QCOMPARE(intent4.timeConstraint().customStart().date().day(), 1); + + // "2025年十一月" — leading-ten Chinese numeral month in year-month form + ParsedIntent intent5; + m_parser->parse(QStringLiteral("2025年十一月的文档"), intent5); + QCOMPARE(intent5.timeConstraint().kind(), TimeConstraintKind::Custom); + QCOMPARE(intent5.timeConstraint().customStart().date().year(), 2025); + QCOMPARE(intent5.timeConstraint().customStart().date().month(), 11); + QCOMPARE(intent5.timeConstraint().customStart().date().day(), 1); } void tst_ChineseNLP::timeCustom_yearMonth_separators() @@ -1035,6 +1065,20 @@ void tst_ChineseNLP::timeCustom_dateSpoken() QCOMPARE(intent.timeConstraint().kind(), TimeConstraintKind::Custom); QCOMPARE(intent.timeConstraint().customStart().date().month(), 3); QCOMPARE(intent.timeConstraint().customStart().date().day(), 8); + + // "七月八号" — Chinese numeral month/day should work too + ParsedIntent intent2; + m_parser->parse(QStringLiteral("七月八号的图片"), intent2); + QCOMPARE(intent2.timeConstraint().kind(), TimeConstraintKind::Custom); + QCOMPARE(intent2.timeConstraint().customStart().date().month(), 7); + QCOMPARE(intent2.timeConstraint().customStart().date().day(), 8); + + // "十二月十五号" — leading-ten Chinese numeral month/day should work too + ParsedIntent intent3; + m_parser->parse(QStringLiteral("十二月十五号的图片"), intent3); + QCOMPARE(intent3.timeConstraint().kind(), TimeConstraintKind::Custom); + QCOMPARE(intent3.timeConstraint().customStart().date().month(), 12); + QCOMPARE(intent3.timeConstraint().customStart().date().day(), 15); } void tst_ChineseNLP::timeCustom_fullDate() @@ -1060,6 +1104,14 @@ void tst_ChineseNLP::timeCustom_fullDate() QCOMPARE(intent2.timeConstraint().customStart().date().year(), 2025); QCOMPARE(intent2.timeConstraint().customStart().date().month(), 12); QCOMPARE(intent2.timeConstraint().customStart().date().day(), 30); + + // "2025年七月三十日" — Chinese numeral month/day should work too + ParsedIntent intent3; + m_parser->parse(QStringLiteral("2025年七月三十日的文档"), intent3); + QCOMPARE(intent3.timeConstraint().kind(), TimeConstraintKind::Custom); + QCOMPARE(intent3.timeConstraint().customStart().date().year(), 2025); + QCOMPARE(intent3.timeConstraint().customStart().date().month(), 7); + QCOMPARE(intent3.timeConstraint().customStart().date().day(), 30); } void tst_ChineseNLP::timeCustom_fullDate_separators() @@ -1912,6 +1964,13 @@ void tst_ChineseNLP::timeDynamic_chineseNumerals() QCOMPARE(intent5.timeConstraint().kind(), TimeConstraintKind::Relative); QCOMPARE(intent5.timeConstraint().relativeValue(), 7); + // "最近十五天" — leading-ten Chinese numeral in dynamic relative time + ParsedIntent intent6; + m_parser->parse(QStringLiteral("最近十五天"), intent6); + QCOMPARE(intent6.timeConstraint().kind(), TimeConstraintKind::Relative); + QCOMPARE(intent6.timeConstraint().relativeValue(), 15); + QCOMPARE(intent6.timeConstraint().relativeUnit(), TimeUnit::Days); + // Mixed: Arabic + Chinese should still work // "最近3天" already tested above } diff --git a/src/dfm-search/dfm-search-lib/semantic/extractors/timeextractor.cpp b/src/dfm-search/dfm-search-lib/semantic/extractors/timeextractor.cpp index f7843fdb..712e01b8 100644 --- a/src/dfm-search/dfm-search-lib/semantic/extractors/timeextractor.cpp +++ b/src/dfm-search/dfm-search-lib/semantic/extractors/timeextractor.cpp @@ -270,10 +270,18 @@ int TimeExtractor::localeAwareToInt(const QString &input, return -1; } - // Two-character pattern: "XY" where Y is the tens unit (e.g., "十五" = 15) + // Leading tens-unit pattern: "十X" (e.g., "十一" = 11, "十五" = 15) + if (input.size() == 2 && input.left(1) == tensUnit) { + int suffix = digitMap.value(input.right(1), -1); + if (suffix >= 0) { + return 10 + suffix; + } + } + + // Two-character pattern: "XY" where Y is the tens unit (e.g., "二十" = 20) if (input.size() == 2 && input.mid(1) == tensUnit) { int prefix = digitMap.value(input.left(1), -1); - if (prefix > 1) { + if (prefix > 0) { return prefix * 10; } // "十" alone = 10 diff --git a/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN/time_rules.json b/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN/time_rules.json index 31e13dec..f56c947e 100644 --- a/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN/time_rules.json +++ b/src/dfm-search/dfm-search-lib/semantic/rules/zh_CN/time_rules.json @@ -132,7 +132,7 @@ }, { "id": "time_exact_month_current_year", - "pattern": "(?\\d{1,2})月份?", + "pattern": "(?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))月份?", "description": "Month this year (e.g. 12月, 5月份)", "enabled": true, "priority": 120, @@ -157,7 +157,7 @@ }, { "id": "time_exact_year_month", - "pattern": "(?\\d{2,4})[年\\./\\-](?\\d{1,2})(?:月份?)?", + "pattern": "(?\\d{2,4})[年\\./\\-](?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))(?:月份?)?", "description": "Exact year-month (e.g. 2025年12月, 2025-12)", "enabled": true, "priority": 140, @@ -182,7 +182,7 @@ }, { "id": "time_exact_date_current_year", - "pattern": "(?\\d{1,2})月(?\\d{1,2})[日号]", + "pattern": "(?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))月(?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))[日号]", "description": "Exact date this year (e.g. 12月5日, 3月8号)", "enabled": true, "priority": 140, @@ -207,7 +207,7 @@ }, { "id": "time_exact_full_date", - "pattern": "(?\\d{2,4})[年\\./\\-](?\\d{1,2})[月\\./\\-](?\\d{1,2})[日号]?", + "pattern": "(?\\d{2,4})[年\\./\\-](?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))[月\\./\\-](?(?:\\d{1,2}|[零一二两三四五六七八九十]{1,3}))[日号]?", "description": "Exact full date (e.g. 2025年12月5日, 2025-12-05)", "enabled": true, "priority": 160,