1<?php
2
3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4// See the LICENCE file in the repository root for full licence text.
5
6namespace App\Libraries\Search;
7
8use App\Libraries\Elasticsearch\BoolQuery;
9use App\Libraries\Elasticsearch\Highlight;
10use App\Libraries\Elasticsearch\Hit;
11use App\Libraries\Elasticsearch\QueryHelper;
12use App\Libraries\Elasticsearch\Search;
13use App\Libraries\Elasticsearch\SearchResponse;
14use App\Models\Forum\Post;
15use App\Models\Forum\Topic;
16use App\Models\User;
17use Illuminate\Database\Eloquent\Builder;
18
19class ForumSearch extends Search
20{
21 public static function getHighlights(Hit $hit, string $field)
22 {
23 $highlights = $hit->highlights($field, static::HIGHLIGHT_FRAGMENT_SIZE * 2);
24 $highlightsText = implode(' ... ', $highlights);
25
26 if ($highlightsText !== '') {
27 return blade_safe($highlightsText);
28 }
29 }
30
31 public function __construct(?ForumSearchParams $params = null)
32 {
33 parent::__construct(Post::esIndexName(), $params ?? new ForumSearchParams());
34
35 $this->source(['is_deleted','topic_id', 'post_id', 'post_time', 'poster_id', 'search_content', 'topic_title']);
36 $this->highlight(
37 (new Highlight())
38 ->field('topic_title')
39 ->field('search_content')
40 ->fragmentSize(static::HIGHLIGHT_FRAGMENT_SIZE)
41 ->numberOfFragments(3)
42 );
43 }
44
45 /**
46 * {@inheritdoc}
47 */
48 public function getQuery()
49 {
50 $query = (new BoolQuery())
51 ->filter(['terms' => ['forum_id' => $this->params->filteredForumIds()]]);
52
53 if (isset($this->params->topicId)) {
54 $query->filter(['term' => ['topic_id' => $this->params->topicId]]);
55 }
56
57 if ($this->params->queryString !== null) {
58 $query->must(QueryHelper::queryString($this->params->queryString, ['search_content', 'topic_title']));
59 }
60
61 if (isset($this->params->username)) {
62 $user = User::lookup($this->params->username);
63 $query->filter(['term' => ['poster_id' => $user ? $user->user_id : -1]]);
64 }
65
66 $query->mustNot(['terms' => ['poster_id' => $this->params->blockedUserIds()]]);
67
68 if (!$this->params->includeDeleted) {
69 $query->mustNot(['term' => ['is_deleted' => true]]);
70 }
71
72 return $query;
73 }
74
75 public function isTopicSpecificSearch()
76 {
77 return isset($this->params->topicId);
78 }
79
80 public function data()
81 {
82 return $this->response();
83 }
84
85 /**
86 * Returns a Builder for a Collection of all the posts that appeared in this query.
87 */
88 public function topics(): Builder
89 {
90 return Topic::withTrashed()->whereIn('topic_id', $this->response()->ids('topic_id'));
91 }
92
93 public function response(): SearchResponse
94 {
95 return parent::response()->recordType(Topic::class)->idField('topic_id');
96 }
97
98 /**
99 * Returns a Builder for a Collection of all the users that appeared in this query.
100 *
101 * @return Builder
102 */
103 public function users(): Builder
104 {
105 return User::whereIn('user_id', $this->response()->ids('poster_id'));
106 }
107}