|
|
@@ -1,39 +1,44 @@
|
|
|
<?php
|
|
|
-
|
|
|
+// api-v8/app/Http/Controllers/SearchSuggestController.php
|
|
|
namespace App\Http\Controllers;
|
|
|
|
|
|
use App\Http\Controllers\Controller;
|
|
|
use Illuminate\Http\Request;
|
|
|
use App\Services\OpenSearchService;
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
- * 返回示例
|
|
|
- * 请求:GET /api/v2/suggest?q=慈&type=term&limit=5
|
|
|
+ * 搜索自动建议控制器
|
|
|
+ *
|
|
|
+ * 返回示例:
|
|
|
+ *
|
|
|
+ * 请求:GET /api/v2/suggest?q=dhamma&fields=title,content&limit=10
|
|
|
+ *
|
|
|
* 返回:
|
|
|
* {
|
|
|
- "success": true,
|
|
|
- "data": {
|
|
|
- "suggestions": [
|
|
|
- {
|
|
|
- "text": "慈悲",
|
|
|
- "resource_type": "translation",
|
|
|
- "language": "zh-Hans"
|
|
|
- },
|
|
|
- {
|
|
|
- "text": "mettā",
|
|
|
- "resource_type": "origin_text",
|
|
|
- "language": "pali"
|
|
|
- },
|
|
|
- {
|
|
|
- "text": "compassion",
|
|
|
- "resource_type": "translation",
|
|
|
- "language": "en-US"
|
|
|
- }
|
|
|
- ]
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
+ * "success": true,
|
|
|
+ * "data": {
|
|
|
+ * "query": "dhamma",
|
|
|
+ * "suggestions": [
|
|
|
+ * {
|
|
|
+ * "text": "dhammapada",
|
|
|
+ * "source": "title",
|
|
|
+ * "score": 5.2,
|
|
|
+ * "resource_type": "sutta",
|
|
|
+ * "language": "pali",
|
|
|
+ * "doc_id": "doc_123"
|
|
|
+ * },
|
|
|
+ * {
|
|
|
+ * "text": "dhammapadā",
|
|
|
+ * "source": "content",
|
|
|
+ * "score": 4.8,
|
|
|
+ * "resource_type": "commentary",
|
|
|
+ * "language": "pali",
|
|
|
+ * "doc_id": "doc_456"
|
|
|
+ * }
|
|
|
+ * ],
|
|
|
+ * "total": 2
|
|
|
+ * }
|
|
|
+ * }
|
|
|
*/
|
|
|
class SearchSuggestController extends Controller
|
|
|
{
|
|
|
@@ -50,56 +55,140 @@ class SearchSuggestController extends Controller
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Display a listing of suggestions.
|
|
|
+ * # 1. 查询所有字段(默认)
|
|
|
+GET /api/v2/suggest?q=dhamma&limit=10
|
|
|
+
|
|
|
+# 2. 只查询标题
|
|
|
+GET /api/v2/suggest?q=dhamma&fields=title&limit=10
|
|
|
+
|
|
|
+# 3. 查询标题和内容
|
|
|
+GET /api/v2/suggest?q=dhamma&fields=title,content&limit=10
|
|
|
+
|
|
|
+# 4. 查询页面引用,带语言过滤
|
|
|
+GET /api/v2/suggest?q=M.1&fields=page_refs&language=pali&limit=5
|
|
|
+
|
|
|
+# 5. 数组形式传递多个字段
|
|
|
+GET /api/v2/suggest?q=dhamma&fields[]=title&fields[]=content&limit=10
|
|
|
+ */
|
|
|
+ /**
|
|
|
+ * 自动建议接口
|
|
|
*
|
|
|
- * 自动建议接口,基于 OpenSearch completion suggester。
|
|
|
- * 支持术语、巴利罗马化拼写、页码等建议。
|
|
|
+ * 基于 OpenSearch completion suggester,支持从不同字段获取建议。
|
|
|
*
|
|
|
* @param \Illuminate\Http\Request $request
|
|
|
* - q (string): 输入的部分文本(必填)
|
|
|
- * - type (string): 建议类型,可选值 term|pali_romanized|page_ref,默认 term
|
|
|
- * - language (string): 语言过滤,可选
|
|
|
- * - limit (int): 返回数量,默认 10,最大 50
|
|
|
+ * - fields (string|array): 要查询的字段,可选值:
|
|
|
+ * - 不传:查询所有字段 (title, content, page_refs)
|
|
|
+ * - 单个字段:'title' | 'content' | 'page_refs'
|
|
|
+ * - 多个字段:'title,content' 或 ['title', 'content']
|
|
|
+ * - language (string): 语言过滤,可选(如:pali, zh, en)
|
|
|
+ * - limit (int): 每个字段返回的建议数量,默认 10,最大 50
|
|
|
*
|
|
|
* @return \Illuminate\Http\JsonResponse
|
|
|
*/
|
|
|
public function index(Request $request)
|
|
|
{
|
|
|
- $query = $request->input('q', '');
|
|
|
- $type = $request->input('type', 'term');
|
|
|
- $language = $request->input('language');
|
|
|
- $limit = min(50, (int) $request->input('limit', 10));
|
|
|
-
|
|
|
+ // 验证必填参数
|
|
|
+ $query = $request->input('q', '');
|
|
|
if (empty($query)) {
|
|
|
return response()->json([
|
|
|
'success' => false,
|
|
|
- 'error' => '缺少参数 q'
|
|
|
+ 'error' => '缺少参数 q(查询文本)'
|
|
|
], 400);
|
|
|
}
|
|
|
|
|
|
+ // 解析 fields 参数
|
|
|
+ $fields = $this->parseFields($request->input('fields'));
|
|
|
+
|
|
|
+ // 获取其他参数
|
|
|
+ $language = $request->input('language', null);
|
|
|
+ $limit = min(50, max(1, (int) $request->input('limit', 10)));
|
|
|
+
|
|
|
try {
|
|
|
- $rawSuggestions = $this->searchService->suggest($query, $type, $language, $limit);
|
|
|
+ // 调用搜索服务
|
|
|
+ $rawSuggestions = $this->searchService->suggest(
|
|
|
+ $query,
|
|
|
+ $fields,
|
|
|
+ $language,
|
|
|
+ $limit
|
|
|
+ );
|
|
|
|
|
|
- // 格式化返回结果:包含 text + resource_type + language
|
|
|
- $suggestions = collect($rawSuggestions)->map(function ($item) {
|
|
|
- return [
|
|
|
- 'text' => $item['text'] ?? '',
|
|
|
- 'resource_type' => $item['resource_type'] ?? null,
|
|
|
- 'language' => $item['language'] ?? null,
|
|
|
- ];
|
|
|
- })->all();
|
|
|
+ // 格式化返回结果
|
|
|
+ $suggestions = $this->formatSuggestions($rawSuggestions);
|
|
|
|
|
|
return response()->json([
|
|
|
'success' => true,
|
|
|
'data' => [
|
|
|
- 'suggestions' => $suggestions
|
|
|
+ 'query' => $query,
|
|
|
+ 'suggestions' => $suggestions,
|
|
|
+ 'total' => count($suggestions)
|
|
|
]
|
|
|
]);
|
|
|
+ } catch (\InvalidArgumentException $e) {
|
|
|
+ return response()->json([
|
|
|
+ 'success' => false,
|
|
|
+ 'error' => '无效的字段参数:' . $e->getMessage(),
|
|
|
+ 'hint' => '有效的字段值:title, content, page_refs'
|
|
|
+ ], 400);
|
|
|
} catch (\Exception $e) {
|
|
|
return response()->json([
|
|
|
'success' => false,
|
|
|
- 'error' => $e->getMessage(),
|
|
|
+ 'error' => '搜索建议失败:' . $e->getMessage(),
|
|
|
], 500);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析 fields 参数
|
|
|
+ *
|
|
|
+ * @param mixed $fields
|
|
|
+ * @return string|array|null
|
|
|
+ */
|
|
|
+ protected function parseFields($fields)
|
|
|
+ {
|
|
|
+ if ($fields === null) {
|
|
|
+ return null; // 查询所有字段
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is_string($fields)) {
|
|
|
+ // 如果是逗号分隔的字符串,转换为数组
|
|
|
+ if (strpos($fields, ',') !== false) {
|
|
|
+ $fieldsArray = array_map('trim', explode(',', $fields));
|
|
|
+ return $fieldsArray;
|
|
|
+ }
|
|
|
+ // 单个字段
|
|
|
+ return $fields;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is_array($fields)) {
|
|
|
+ return $fields;
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 格式化建议结果
|
|
|
+ *
|
|
|
+ * @param array $rawSuggestions
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ protected function formatSuggestions(array $rawSuggestions): array
|
|
|
+ {
|
|
|
+ return collect($rawSuggestions)->map(function ($item) {
|
|
|
+ $docSource = $item['doc_source'] ?? [];
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'text' => $item['text'] ?? '',
|
|
|
+ 'source' => $item['source'] ?? null,
|
|
|
+ 'score' => round($item['score'] ?? 0, 2),
|
|
|
+ 'resource_type' => $docSource['resource_type'] ?? null,
|
|
|
+ 'language' => $docSource['language'] ?? null,
|
|
|
+ 'doc_id' => $item['doc_id'] ?? null,
|
|
|
+ // 可选:添加更多元数据
|
|
|
+ 'category' => $docSource['category'] ?? null,
|
|
|
+ 'granularity' => $docSource['granularity'] ?? null,
|
|
|
+ ];
|
|
|
+ })->all();
|
|
|
+ }
|
|
|
}
|