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
52 changes: 52 additions & 0 deletions autotests/dfm-search-tests/tst_content_retriever.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ private Q_SLOTS:
void fetchPreview_withKeyword();
void fetchPreview_keywordNotFound();
void fetchPreview_offsetBeyondContent();
void fetchPreview_unlimitedNoKeyword();
void previewSnippet_basic();
};

Expand Down Expand Up @@ -329,6 +330,47 @@ void tst_ContentRetriever::fetchPreview_offsetBeyondContent()
QCOMPARE(result.keywordOffset(), -1);
}

void tst_ContentRetriever::fetchPreview_unlimitedNoKeyword()
{
QTemporaryDir tempDir;
QVERIFY(tempDir.isValid());

const QString contentIndexDir = tempDir.path() + "/content-index";
createIndex(contentIndexDir, SearchType::Content);

ContentRetriever retriever;
retriever.setIndexDirectory(SearchType::Content, contentIndexDir);

// No keyword + maxLength=0 (unlimited) → return all content from offset
// Content of doc-a.txt is "hello world from content index"
{
PreviewOptions options;
options.setKeyword(QString());
options.setOffset(0);
options.setMaxLength(0);

const PreviewResult result = retriever.fetchPreview("/tmp/doc-a.txt",
SearchType::Content,
options);
QCOMPARE(result.content(), QString("hello world from content index"));
QCOMPARE(result.keywordOffset(), -1);
}

// With offset, unlimited still returns all remaining content
{
PreviewOptions options;
options.setKeyword(QString());
options.setOffset(6); // skip "hello "
options.setMaxLength(0);

const PreviewResult result = retriever.fetchPreview("/tmp/doc-a.txt",
SearchType::Content,
options);
QCOMPARE(result.content(), QString("world from content index"));
QCOMPARE(result.keywordOffset(), -1);
}
}

void tst_ContentRetriever::previewSnippet_basic()
{
using DFMSEARCH::ContentHighlighter::previewSnippet;
Expand All @@ -353,6 +395,16 @@ void tst_ContentRetriever::previewSnippet_basic()

// Negative offset → empty
QVERIFY(previewSnippet(content, -1, 10, QString(), &kwOff).isEmpty());

// maxLength <= 0 means unlimited — returns all content from offset
QCOMPARE(previewSnippet(content, 6, 0, QString(), &kwOff), QString("world from content index"));
QCOMPARE(kwOff, -1);
QCOMPARE(previewSnippet(content, 0, 0, QString(), &kwOff), content);
QCOMPARE(kwOff, -1);

// With keyword + maxLength=0 (unlimited) — returns from keyword match to end
QCOMPARE(previewSnippet(content, 0, 0, "world", &kwOff), QString("world from content index"));
QCOMPARE(kwOff, 6);
}

QObject *create_tst_ContentRetriever()
Expand Down
19 changes: 13 additions & 6 deletions docs/dfm-searcher-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -481,17 +481,18 @@ dfm-searcher --method=realtime --time-last=30d --size-min=10M --file-types=video

## 十三、基本用法

### 37. 读取文件开头内容
### 37. 读取文件全部内容

```bash
dfm-searcher preview /home/user/notes.txt
```

默认读取前 200 个字符
无 keyword 且未设 `--max-preview` 时,输出文档**全部内容**

```
/home/user/notes.txt
2026年7月10日 工作计划:1.完成需求文档 2.代码评审 3.修复bug
今天重点完成代码评审部分,明天开始写自动化测试…
```

### 38. 读取文件内容(JSON 格式)
Expand All @@ -506,28 +507,34 @@ dfm-searcher preview /home/user/notes.txt -j
"searchType": "semantic",
"keyword": "",
"offset": 0,
"maxLength": 200,
"maxLength": 0,
"totalResults": 1,
"results": [
{
"path": "/home/user/notes.txt",
"content": "2026年7月10日 工作计划:1.完成需求文档 2.代码评审 3.修复bug",
"content": "2026年7月10日 工作计划:1.完成需求文档 2.代码评审 3.修复bug\n今天重点完成代码评审部分,明天开始写自动化测试…",
"keywordOffset": -1
}
]
}
```

> `maxLength: 0` 表示无限制,返回全文。

---

## 十四、用 --offset 和 --max-preview 控制读取范围

### 39. 从第 100 个字符开始读
无 keyword 时,默认输出全文。加 `--offset` 可以从指定位置开始读,加 `--max-preview` 可以限制读取长度。

### 39. 从第 100 个字符开始读(不限长度)

```bash
dfm-searcher preview /home/user/notes.txt --offset=100
```

从第 100 个字符开始,读到文件末尾。

### 40. 只读 50 个字符

```bash
Expand Down Expand Up @@ -828,7 +835,7 @@ dfm-searcher preview /home/user/doc.txt --offset=500 --max-preview=100 -j
| 选项 | 默认值 | 说明 |
|------|--------|------|
| `--offset=<n>` | `0` | 从文档第几个字符开始阅读/搜索 |
| `--max-preview=<n>` | `200` | 本次最多读取的字符数 |
| `--max-preview=<n>` | 无限制(无keyword) / `200`(有keyword) | 本次最多读取的字符数。设为 0 表示无限制 |
| `--type=<content\|ocr>` | 自动 | 强制指定搜索类型 |
| `-j` / `--json` | 关 | JSON 输出 |

Expand Down
3 changes: 2 additions & 1 deletion src/dfm-search/dfm-search-client/cli_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ void CliOptions::printHelp() const
std::cout << " dfm-searcher preview [<keyword>] <path1> [path2 ...] [--type=<content|ocr>] [--offset=<n>] [--max-preview=<length>] [-j]" << std::endl;
std::cout << " Fetch content snippets for specific files without running a full search." << std::endl;
std::cout << " With keyword: returns snippet from the keyword match position (search starts at --offset)." << std::endl;
std::cout << " Without keyword: returns exact content starting from --offset, up to --max-preview characters." << std::endl;
std::cout << " Without keyword: returns exact content starting from --offset (full content if --max-preview is omitted)." << std::endl;
std::cout << " Search type is auto-detected by file extension; use --type to force a specific index." << std::endl;
std::cout << std::endl;
std::cout << " # Fetch highlighted snippet for a single file" << std::endl;
Expand Down Expand Up @@ -281,6 +281,7 @@ bool CliOptions::parse(QCoreApplication &app, SearchCliConfig &config)
if (ok && previewLength > 0) {
config.maxPreviewLength = previewLength;
}
config.maxPreviewSet = true;
}
if (m_parser.isSet(m_offsetOption)) {
bool ok;
Expand Down
1 change: 1 addition & 0 deletions src/dfm-search/dfm-search-client/cli_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ struct SearchCliConfig
int maxResults = 0; // 0 表示不限制
int maxPreviewLength = 200;
int offset = 0; // 内容偏移值,preview 子命令使用
bool maxPreviewSet = false; // --max-preview 是否被显式设置(preview 子命令使用)

// 文件名搜索选项
QString filenameKeyword;
Expand Down
10 changes: 8 additions & 2 deletions src/dfm-search/dfm-search-client/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,13 @@ int main(int argc, char *argv[])
DFMSEARCH::ContentRetriever retriever;
DFMSEARCH::PreviewOptions previewOptions;
previewOptions.setOffset(config.offset);
previewOptions.setMaxLength(config.maxPreviewLength);
// 无 keyword 且未显式设置 --max-preview:maxLength=0 表示无限制(返回全文)
// 有 keyword 或显式设置了 --max-preview:使用 maxPreviewLength 值
if (config.keyword.isEmpty() && !config.maxPreviewSet) {
previewOptions.setMaxLength(0);
} else {
previewOptions.setMaxLength(config.maxPreviewLength);
}
previewOptions.setKeyword(config.keyword);

// Paths are stored as comma-separated in config.searchPath
Expand All @@ -206,7 +212,7 @@ int main(int argc, char *argv[])
: (config.searchType == SearchType::Ocr) ? "ocr" : "semantic";
root["keyword"] = config.keyword;
root["offset"] = config.offset;
root["maxLength"] = config.maxPreviewLength;
root["maxLength"] = previewOptions.maxLength();

QJsonArray results;
for (const QString &path : paths) {
Expand Down
8 changes: 4 additions & 4 deletions src/dfm-search/dfm-search-lib/utils/contenthighlighter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,13 +249,13 @@ QString previewSnippet(const QString &content, int offset, int maxLength,
*keywordOffset = -1;
}

if (content.isEmpty() || maxLength <= 0 || offset < 0) {
if (content.isEmpty() || offset < 0) {
return {};
}

if (keyword.isEmpty()) {
// 无 keyword:直接从 offset 截取 maxLength
return content.mid(offset, maxLength);
// 无 keyword:直接从 offset 截取
return maxLength > 0 ? content.mid(offset, maxLength) : content.mid(offset);
}

// 有 keyword:从 offset 开始搜索,找到后从匹配位置截取
Expand All @@ -268,7 +268,7 @@ QString previewSnippet(const QString &content, int offset, int maxLength,
*keywordOffset = pos;
}

return content.mid(pos, maxLength);
return maxLength > 0 ? content.mid(pos, maxLength) : content.mid(pos);
}

} // namespace ContentHighlighter
Expand Down
4 changes: 3 additions & 1 deletion src/dfm-search/dfm-search-lib/utils/contenthighlighter.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ QString customHighlight(const QStringList &keywords, const QString &content, int
* 有 keyword: content.indexOf(keyword, offset) → 从匹配位置截取 maxLength
* 不做 simplified,不加省略号,不高亮
*
* maxLength <= 0 表示无限制,返回从 offset(或 keyword 匹配位置)开始的全部剩余内容。
*
* @param content 原始文档内容
* @param offset 内容偏移值(默认0)
* @param maxLength 最大截取长度
* @param maxLength 最大截取长度,<= 0 表示无限制
* @param keyword 搜索关键词(可选)
* @param keywordOffset 输出 keyword 在全文中的匹配位置(-1 if 无 keyword 或未匹配)
* @return 截取的内容片段,找不到 keyword 或 offset 越界时返回空字符串
Expand Down
Loading