SearchPlusController.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. <?php
  2. namespace App\Http\Controllers\V3;
  3. use App\DTO\Search\HitItemDTO;
  4. use App\Http\Controllers\Controller;
  5. use App\Http\Resources\V3\BaseResource;
  6. use App\Services\OpenSearchService;
  7. use Illuminate\Http\Request;
  8. use Illuminate\Http\Response;
  9. class SearchPlusController extends Controller
  10. {
  11. /**
  12. * 构造函数,注入 OpenSearchService
  13. */
  14. public function __construct(protected OpenSearchService $searchService) {}
  15. /**
  16. * 全文检索
  17. *
  18. * 支持 fuzzy / exact / semantic / hybrid 四种检索模式,底层走 OpenSearch。
  19. * 同样的参数也可以用 POST /v3/search 提交(参数多、URL 放不下时用)。
  20. *
  21. * @unauthenticated
  22. *
  23. * @queryParam q string required 搜索关键词
  24. * @queryParam search_mode string 检索模式。Enum: fuzzy,exact,semantic,hybrid Default: fuzzy
  25. * @queryParam resource_type string 资源类型。
  26. * Enum: article,term,dictionary,translation,origin_text,nissaya
  27. * @queryParam resource_id string 限定某个资源 id
  28. * @queryParam granularity string 文档颗粒度。
  29. * Enum: book,chapter,sutta,section,paragraph,sentence
  30. * @queryParam language string 语言。Example: zh-Hans
  31. * @queryParam category string 文档分类,逗号分隔。Enum: pali,commentary,subcommentary
  32. * @queryParam tags string 标签过滤,逗号分隔
  33. * @queryParam page_refs array 页码标记。Example: ["V3.81","M3.58"]
  34. * @queryParam related_id array 关联 id。Example: ["chapter_93-5","m.n. 38"]
  35. * @queryParam author string 按作者或译者过滤
  36. * @queryParam channel string 按来源 channel 过滤
  37. * @queryParam page integer 页码。Default: 1
  38. * @queryParam page_size integer 每页数量,上限 100。Default: 20
  39. */
  40. public function index(Request $request)
  41. {
  42. // 获取所有输入参数(自动兼容 GET 和 POST)
  43. $input = $request->all();
  44. // 基础参数 - 使用 $input 或直接用 $request->input() (已兼容 GET/POST)
  45. $query = $request->input('q', '');
  46. $page = max(1, (int) $request->input('page', 1));
  47. $pageSize = min(100, (int) $request->input('page_size', 20));
  48. $searchMode = $request->input('search_mode', 'fuzzy');
  49. $resourceType = $request->input('resource_type'); // 资源类型
  50. $resourceId = $request->input('resource_id'); // 资源类型
  51. $granularity = $request->input('granularity'); // 文档颗粒度
  52. $language = $request->input('language'); // 语言
  53. $category = $request->has('category') ? explode(',', $request->input('category')) : null; // 分类
  54. $tags = $request->has('tags') ? explode(',', $request->input('tags')) : []; // 标签
  55. $pageRefs = $request->input('page_refs', []); // 页码标记
  56. $relatedId = $request->input('related_id'); // 关联 ID
  57. $author = $request->input('author'); // 作者/译者 (metadata.author)
  58. $channel = $request->input('channel'); // 来源渠道 (metadata.channel)
  59. // 确保数组类型参数正确解析(POST 时可能是 JSON 字符串)
  60. $tags = is_array($tags) ? $tags : (is_string($tags) ? json_decode($tags, true) ?? [] : []);
  61. $pageRefs = is_array($pageRefs) ? $pageRefs : (is_string($pageRefs) ? json_decode($pageRefs, true) ?? [] : []);
  62. // 组装搜索参数
  63. $params = [
  64. 'query' => $query,
  65. 'page' => $page,
  66. 'pageSize' => $pageSize,
  67. 'searchMode' => $searchMode,
  68. 'resourceType' => $resourceType,
  69. 'resourceId' => $resourceId,
  70. 'granularity' => $granularity,
  71. 'language' => $language,
  72. 'category' => $category,
  73. 'tags' => $tags,
  74. 'pageRefs' => $pageRefs,
  75. 'relatedId' => $relatedId,
  76. 'author' => $author,
  77. 'channel' => $channel,
  78. ];
  79. try {
  80. // 调用 OpenSearchService 执行搜索
  81. $result = $this->searchService->search($params);
  82. return response()->json([
  83. 'success' => true,
  84. 'data' => $result,
  85. 'query_info' => [
  86. 'original_query' => $query,
  87. 'search_mode' => $searchMode,
  88. 'request_method' => $request->method(), // 可选:返回请求方法
  89. ],
  90. ]);
  91. } catch (\Exception $e) {
  92. return response()->json([
  93. 'success' => false,
  94. 'error' => $e->getMessage(),
  95. ], 500);
  96. }
  97. }
  98. /**
  99. * 全文检索(POST)
  100. *
  101. * 与 `GET /v3/search` 完全等价,只是把参数放在请求体里——参数多、
  102. * 尤其是 page_refs / related_id 这类数组时,URL 放不下。
  103. *
  104. * @unauthenticated
  105. *
  106. * @bodyParam q string required 搜索关键词
  107. * @bodyParam search_mode string 检索模式。Enum: fuzzy,exact,semantic,hybrid Default: fuzzy
  108. * @bodyParam resource_type string 资源类型
  109. * @bodyParam granularity string 文档颗粒度
  110. * @bodyParam language string 语言
  111. * @bodyParam category string 文档分类,逗号分隔
  112. * @bodyParam tags string 标签,逗号分隔
  113. * @bodyParam page_refs array 页码标记
  114. * @bodyParam related_id array 关联 id
  115. * @bodyParam author string 作者或译者
  116. * @bodyParam channel string 来源 channel
  117. * @bodyParam page integer 页码。Default: 1
  118. * @bodyParam page_size integer 每页数量,上限 100。Default: 20
  119. */
  120. public function store(Request $request)
  121. {
  122. // 直接调用 index 方法,复用搜索逻辑
  123. return $this->index($request);
  124. }
  125. /**
  126. * 按 id 取单条检索结果
  127. *
  128. * 直接从 OpenSearch 按文档 id 取回,不走检索。取不到返回 404。
  129. *
  130. * @unauthenticated
  131. *
  132. * @urlParam search string required OpenSearch 文档 id
  133. */
  134. public function show($id)
  135. {
  136. //
  137. try {
  138. return BaseResource::make(HitItemDTO::fromArray($this->searchService->get($id)));
  139. } catch (\Throwable $th) {
  140. abort(404, __('site.not_found'));
  141. }
  142. }
  143. /**
  144. * 更新资源
  145. *
  146. * @route PUT /api/search/{uid}
  147. *
  148. * @param JSON: OpenSearch 格式数据
  149. * @return Response
  150. */
  151. public function update(Request $request, $uid) {}
  152. /**
  153. * Remove the specified resource from storage.
  154. *
  155. * 删除资源
  156. *
  157. * @route DELETE /api/search/{uid}
  158. *
  159. * @return Response
  160. */
  161. public function destroy($uid) {}
  162. }