# 文档搜索功能指南

## 概述

文档搜索功能使用 Laravel Scout 和 Meilisearch 提供强大的全文搜索能力。系统会自动将转换完成的文档索引到 Meilisearch，用户可以通过关键词快速检索文档内容。

## 核心组件

### DocumentSearchService

文档搜索服务类，提供以下功能：

1. **search()** - 执行全文搜索
   - 搜索文档标题、描述和 Markdown 内容
   - 支持额外的筛选条件（类型、分组、上传者）
   - 自动应用用户权限过滤

2. **filterByUserPermissions()** - 权限过滤
   - 确保用户只能看到有权限访问的文档
   - 全局文档对所有用户可见
   - 专用文档只对所属分组的用户可见

3. **prepareSearchableData()** - 准备索引数据
   - 包含完整的 Markdown 内容用于搜索
   - 包含文档元数据（标题、描述、类型、分组等）

4. **indexDocument()** - 索引文档
   - 将文档添加到 Meilisearch 索引
   - 只索引转换完成的文档

5. **updateDocumentIndex()** - 更新索引
   - 更新已索引文档的信息
   - 如果文档不应被索引，则移除索引

6. **removeDocumentFromIndex()** - 移除索引
   - 从 Meilisearch 中删除文档索引

### DocumentObserver

文档观察者，自动管理文档的搜索索引：

- **created** - 文档创建时不立即索引（等待转换完成）
- **updated** - 文档更新时检查转换状态并更新索引
  - 当 conversion_status 变为 'completed' 时自动索引
  - 当其他重要字段更新时也更新索引
- **deleted** - 文档删除时移除索引
- **restored** - 文档恢复时重新索引
- **forceDeleted** - 强制删除时移除索引

## 工作流程

### 文档索引流程

1. 用户上传 Word 文档
2. 文档保存到数据库，conversion_status 设置为 'pending'
3. 转换任务加入队列
4. 转换完成后，updateDocumentMarkdown() 将 conversion_status 更新为 'completed'
5. DocumentObserver 监听到 updated 事件
6. 自动调用 DocumentSearchService::indexDocument() 创建索引
7. 文档可以被搜索

### 搜索流程

1. 用户输入搜索关键词
2. 调用 DocumentSearchService::search()
3. 使用 Scout 在 Meilisearch 中搜索
4. 应用额外的筛选条件
5. 使用 filterByUserPermissions() 过滤结果
6. 返回用户有权限访问的文档列表

## 配置

### Meilisearch 配置

在 `config/scout.php` 中配置：

```php
'meilisearch' => [
    'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
    'key' => env('MEILISEARCH_KEY'),
    'index-settings' => [
        'documents' => [
            'filterableAttributes' => ['type', 'group_id', 'uploaded_by', 'conversion_status'],
            'sortableAttributes' => ['created_at', 'title', 'updated_at'],
            'searchableAttributes' => ['title', 'description', 'markdown_content'],
        ],
    ],
],
```

### 环境变量

在 `.env` 文件中配置：

```env
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=your-master-key
```

## 使用示例

### 基本搜索

```php
use App\Services\DocumentSearchService;

$searchService = app(DocumentSearchService::class);
$user = auth()->user();

// 搜索关键词
$results = $searchService->search('Laravel', $user);

// 带筛选条件的搜索
$results = $searchService->search('Laravel', $user, [
    'type' => 'global',
    'group_id' => 1,
]);
```

### 手动索引管理

```php
use App\Services\DocumentSearchService;
use App\Models\Document;

$searchService = app(DocumentSearchService::class);
$document = Document::find(1);

// 索引文档
$searchService->indexDocument($document);

// 更新索引
$searchService->updateDocumentIndex($document);

// 移除索引
$searchService->removeDocumentFromIndex($document);
```

### 批量重建索引

```php
// 重建所有文档的索引
php artisan scout:import "App\Models\Document"

// 清空索引
php artisan scout:flush "App\Models\Document"
```

## 错误处理

所有索引操作都包含错误处理：

- 索引失败不会影响文档的正常保存和使用
- 所有错误都会记录到日志中
- 搜索失败时返回空集合

## 性能考虑

1. **异步索引** - 文档转换和索引都在队列中异步处理
2. **权限过滤** - 在应用层过滤搜索结果，确保数据安全
3. **缓存策略** - 可以缓存热门搜索结果（待实现）
4. **分页** - 搜索结果应该分页显示（在前端实现）

## 故障排查

### 文档无法被搜索

1. 检查 conversion_status 是否为 'completed'
2. 检查 Meilisearch 服务是否运行
3. 检查文档是否已索引：`php artisan scout:import "App\Models\Document"`
4. 查看日志文件中的错误信息

### Meilisearch 连接失败

1. 确认 Meilisearch 服务正在运行
2. 检查 .env 中的 MEILISEARCH_HOST 和 MEILISEARCH_KEY
3. 测试连接：`curl http://localhost:7700/health`

### 搜索结果为空

1. 确认文档已完成转换
2. 确认用户有权限访问文档
3. 检查搜索关键词是否正确
4. 查看 Meilisearch 日志

## 下一步

- 实现搜索页面 UI（任务 25）
- 添加搜索结果高亮显示
- 实现搜索建议和自动补全
- 添加高级搜索语法支持
