Search.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <?php
  2. namespace addons\cms\controller;
  3. use addons\cms\library\FulltextSearch;
  4. use addons\cms\library\Service;
  5. use addons\cms\model\Archives;
  6. use addons\cms\model\Modelx;
  7. use addons\cms\model\SearchLog;
  8. use think\Config;
  9. use think\Session;
  10. /**
  11. * 搜索控制器
  12. * Class Search
  13. * @package addons\cms\controller
  14. */
  15. class Search extends Base
  16. {
  17. public function index()
  18. {
  19. $config = get_addon_config('cms');
  20. $search = $this->request->request("search", $this->request->request("q", ""));
  21. $search = strip_tags($search);
  22. $search = mb_substr($search, 0, 100);
  23. if (!$search) {
  24. $this->error("关键字不能为空");
  25. }
  26. //搜索入库
  27. $token = $this->request->request("__searchtoken__");
  28. if ($search && $token && $token == Session::get("__searchtoken__")) {
  29. $log = SearchLog::getByKeywords($search);
  30. if ($log) {
  31. $log->setInc("nums");
  32. } else {
  33. SearchLog::create(['keywords' => $search, 'nums' => 1]);
  34. }
  35. }
  36. if ($config['searchtype'] == 'xunsearch') {
  37. return $this->xunsearch();
  38. }
  39. $channel = $model = null;
  40. $channel_id = $this->request->get('channel_id');
  41. $model_id = $this->request->get('model_id');
  42. if ($channel_id || $model_id) {
  43. $channel = \addons\cms\model\Channel::get($channel_id);
  44. $model_id = $channel ? $channel['model_id'] : $model_id;
  45. //加载模型数据
  46. $model = Modelx::get($model_id);
  47. if (!$model) {
  48. $this->error(__('No specified model found'));
  49. }
  50. }
  51. $filterList = [];
  52. $orderList = [];
  53. $orderby = $this->request->get('orderby', '');
  54. $orderway = $this->request->get('orderway', '');
  55. $orderway = $orderway && in_array(strtolower($orderway), ['asc', 'desc']) ? $orderway : 'desc';
  56. $params = ['q' => $search];
  57. if ($orderby) {
  58. $params['orderby'] = $orderby;
  59. }
  60. if ($orderway) {
  61. $params['orderway'] = $orderway;
  62. }
  63. //默认排序字段
  64. $orders = [
  65. ['name' => 'default', 'field' => 'weigh', 'title' => __('Default')],
  66. ['name' => 'views', 'field' => 'views', 'title' => __('Views')],
  67. ['name' => 'id', 'field' => 'id', 'title' => __('Post date')],
  68. ];
  69. //获取排序列表
  70. list($orderList, $orderby, $orderway) = Service::getOrderList($orderby, $orderway, $orders, $params);
  71. //模板名称
  72. $template = ($this->request->isAjax() ? '/ajax' : '/') . 'search';
  73. $pagelistParams = Service::getPagelistParams($template);
  74. //分页大小
  75. $pagesize = $pagelistParams['pagesize'] ?? 10;
  76. //过滤条件
  77. $filterPagelist = function ($query) use ($pagelistParams) {
  78. if (isset($pagelistParams['condition'])) {
  79. $query->where($pagelistParams['condition']);
  80. }
  81. };
  82. $pageList = new Archives();
  83. if ($model) {
  84. $pageList->join($model['table'] . ' n', 'a.id=n.id', 'LEFT')
  85. ->field('id,content', true, config('database.prefix') . $model['table'], 'n')
  86. ->where('model_id', $model->id);
  87. }
  88. $pageList = $pageList->with(['channel', 'user'])->alias('a')
  89. ->where('a.status', 'normal')
  90. ->where(function ($query) use ($search) {
  91. $keywordArr = explode(' ', $search);
  92. foreach ($keywordArr as $index => $item) {
  93. $query->where('a.title', 'like', '%' . $item . '%');
  94. }
  95. })
  96. ->whereNull('a.deletetime')
  97. ->field('a.*')
  98. ->where(function ($query) use ($channel) {
  99. if ($channel) {
  100. $query->where(function ($query) use ($channel) {
  101. if ($channel['listtype'] <= 2) {
  102. $query->whereOr("channel_id", $channel['id']);
  103. }
  104. if ($channel['listtype'] == 1 || $channel['listtype'] == 3) {
  105. $query->whereOr('channel_id', 'in', function ($query) use ($channel) {
  106. $query->name("cms_channel")->where('parent_id', $channel['id'])->field("id");
  107. });
  108. }
  109. if ($channel['listtype'] == 0 || $channel['listtype'] == 4) {
  110. $childrenIds = \addons\cms\model\Channel::getChannelChildrenIds($channel['id'], false);
  111. if ($childrenIds) {
  112. $query->whereOr('channel_id', 'in', $childrenIds);
  113. }
  114. }
  115. })
  116. ->whereOr("(`channel_ids`!='' AND FIND_IN_SET('{$channel['id']}', `channel_ids`))");
  117. }
  118. })
  119. ->where($filterPagelist)
  120. ->order($orderby, $orderway)
  121. ->paginate($pagesize, $config['pagemode'] == 'simple');
  122. $pageList->appends(array_filter($params));
  123. $this->view->assign("__FILTERLIST__", $filterList);
  124. $this->view->assign("__ORDERLIST__", $orderList);
  125. $this->view->assign("__PAGELIST__", $pageList);
  126. $this->view->assign("__SEARCHTERM__", $search);
  127. Config::set('cms.title', __("Search for %s", $search));
  128. if ($this->request->isAjax()) {
  129. $this->success("", "", $this->view->fetch($template));
  130. }
  131. return $this->view->fetch($template);
  132. }
  133. public function typeahead()
  134. {
  135. $search = $this->request->post("search", $this->request->post("q", ""));
  136. $search = mb_substr($search, 0, 100);
  137. $list = Archives
  138. ::where('status', 'normal')
  139. ->whereNull('deletetime')
  140. ->where('title', 'like', "%{$search}%")
  141. ->order('id', 'desc')
  142. ->field('id,title,diyname,channel_id,likes,dislikes,tags,createtime')
  143. ->limit(10)
  144. ->select();
  145. $result = collection($list)->toArray();
  146. $result[] = ['id' => 0, 'title' => __('Search more %s', $search), 'url' => addon_url("cms/search/index", [':search' => $search, 'search' => $search])];
  147. return json($result);
  148. }
  149. /**
  150. * Xunsearch搜索
  151. * @return string
  152. * @throws \think\Exception
  153. */
  154. public function xunsearch()
  155. {
  156. $orderList = [
  157. 'relevance' => '默认排序',
  158. 'createtime_desc' => '发布时间从新到旧',
  159. 'createtime_asc' => '发布时间从旧到新',
  160. 'views_desc' => '浏览次数从多到少',
  161. 'views_asc' => '浏览次数从少到多',
  162. 'comments_desc' => '评论次数从多到少',
  163. 'comments_asc' => '评论次数从少到多',
  164. ];
  165. $q = $this->request->request('q', $this->request->request('search', ''));
  166. $q = strip_tags($q);
  167. $q = mb_substr($q, 0, 100);
  168. $page = $this->request->get('page/d', '1');
  169. $order = $this->request->get('order', '');
  170. $fulltext = $this->request->get('fulltext/d', '1');
  171. $fuzzy = $this->request->get('fuzzy/d', '0');
  172. $synonyms = $this->request->get('synonyms/d', '0');
  173. $order = isset($orderList[$order]) ? $order : 'relevance';
  174. $total_begin = microtime(true);
  175. $search = null;
  176. $pagesize = 10;
  177. $error = '';
  178. $result = FulltextSearch::search($q, $page, $pagesize, $order, $fulltext, $fuzzy, $synonyms);
  179. if (!$result) {
  180. $this->error('请检查Xunsearch配置');
  181. }
  182. // 计算总耗时
  183. $total_cost = microtime(true) - $total_begin;
  184. //获取热门搜索
  185. $hot = FulltextSearch::hot();
  186. $data = [
  187. 'q' => $q,
  188. 'error' => $error,
  189. 'total' => $result['total'] ?? 0,
  190. 'count' => $result['count'] ?? 0,
  191. 'search_cost' => $result['microseconds'] ?? 0,
  192. 'docs' => $result['list'] ?? [],
  193. 'pager' => $result['pager'] ?? '',
  194. 'corrected' => $result['corrected'] ?? [],
  195. 'highlight' => $result['highlight'] ?? [],
  196. 'related' => $result['related'] ?? [],
  197. 'search' => $search,
  198. 'fulltext' => $fulltext,
  199. 'synonyms' => $synonyms,
  200. 'fuzzy' => $fuzzy,
  201. 'order' => $order,
  202. 'orderList' => $orderList,
  203. 'hot' => $hot,
  204. 'total_cost' => $total_cost,
  205. ];
  206. Config::set('cms.title', __("Search for %s", $q));
  207. $this->view->assign("title", $q);
  208. $this->view->assign($data);
  209. return $this->view->fetch('/xunsearch');
  210. }
  211. public function suggestion()
  212. {
  213. $q = trim($this->request->get('q', ''));
  214. $q = mb_substr($q, 0, 100);
  215. $terms = [];
  216. $config = get_addon_config('cms');
  217. if ($config['searchtype'] == 'xunsearch') {
  218. $terms = FulltextSearch::suggestion($q);
  219. } else {
  220. $terms = SearchLog::where("keywords", "LIKE", "{$q}%")->where("nums", ">", 0)->where("status", "normal")->column("keywords");
  221. }
  222. return json($terms);
  223. }
  224. }