Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
85.71% |
30 / 35 |
|
50.00% |
1 / 2 |
CRAP | |
0.00% |
0 / 1 |
RemoteSearchTaskSuggester | |
85.71% |
30 / 35 |
|
50.00% |
1 / 2 |
7.14 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
search | |
83.33% |
25 / 30 |
|
0.00% |
0 / 1 |
6.17 |
1 | <?php |
2 | |
3 | namespace GrowthExperiments\NewcomerTasks\TaskSuggester; |
4 | |
5 | use FauxSearchResultSet; |
6 | use GrowthExperiments\NewcomerTasks\FauxSearchResultWithScore; |
7 | use GrowthExperiments\NewcomerTasks\NewcomerTasksUserOptionsLookup; |
8 | use GrowthExperiments\NewcomerTasks\TaskSuggester\SearchStrategy\SearchQuery; |
9 | use GrowthExperiments\NewcomerTasks\TaskSuggester\SearchStrategy\SearchStrategy; |
10 | use GrowthExperiments\NewcomerTasks\TaskType\TaskType; |
11 | use GrowthExperiments\NewcomerTasks\TaskType\TaskTypeHandlerRegistry; |
12 | use GrowthExperiments\NewcomerTasks\Topic\Topic; |
13 | use GrowthExperiments\Util; |
14 | use MediaWiki\Cache\LinkBatchFactory; |
15 | use MediaWiki\Http\HttpRequestFactory; |
16 | use MediaWiki\Title\TitleFactory; |
17 | |
18 | /** |
19 | * Suggest edits based on searching a wiki (potentially a different one) via the API. |
20 | * Mainly meant for testing and development; it can in theory be used in production but |
21 | * it is less efficient than using SearchEngine internally. |
22 | */ |
23 | class RemoteSearchTaskSuggester extends SearchTaskSuggester { |
24 | |
25 | /** @var HttpRequestFactory */ |
26 | private $requestFactory; |
27 | |
28 | /** @var TitleFactory */ |
29 | private $titleFactory; |
30 | |
31 | /** @var string Remote API URL including api.php */ |
32 | private $apiUrl; |
33 | |
34 | /** |
35 | * @param TaskTypeHandlerRegistry $taskTypeHandlerRegistry |
36 | * @param SearchStrategy $searchStrategy |
37 | * @param NewcomerTasksUserOptionsLookup $newcomerTasksUserOptionsLookup |
38 | * @param LinkBatchFactory $linkBatchFactory |
39 | * @param HttpRequestFactory $requestFactory |
40 | * @param TitleFactory $titleFactory |
41 | * @param string $apiUrl Remote API URL including api.php |
42 | * @param TaskType[] $taskTypes |
43 | * @param Topic[] $topics |
44 | */ |
45 | public function __construct( |
46 | TaskTypeHandlerRegistry $taskTypeHandlerRegistry, |
47 | SearchStrategy $searchStrategy, |
48 | NewcomerTasksUserOptionsLookup $newcomerTasksUserOptionsLookup, |
49 | LinkBatchFactory $linkBatchFactory, |
50 | HttpRequestFactory $requestFactory, |
51 | TitleFactory $titleFactory, |
52 | $apiUrl, |
53 | array $taskTypes, |
54 | array $topics |
55 | ) { |
56 | parent::__construct( $taskTypeHandlerRegistry, $searchStrategy, $newcomerTasksUserOptionsLookup, |
57 | $linkBatchFactory, $taskTypes, $topics ); |
58 | $this->requestFactory = $requestFactory; |
59 | $this->titleFactory = $titleFactory; |
60 | $this->apiUrl = $apiUrl; |
61 | } |
62 | |
63 | /** @inheritDoc */ |
64 | protected function search( |
65 | SearchQuery $query, |
66 | int $limit, |
67 | int $offset, |
68 | bool $debug |
69 | ) { |
70 | // We randomize the results so offsets are meaningless. |
71 | // TODO use fixed random seed. |
72 | $params = [ |
73 | 'action' => 'query', |
74 | 'list' => 'search', |
75 | 'srsearch' => $query->getQueryString(), |
76 | 'srnamespace' => 0, |
77 | 'srlimit' => $limit, |
78 | 'srinfo' => 'totalhits', |
79 | 'srprop' => '', |
80 | 'srqiprofile' => $query->getRescoreProfile() ?? 'classic_noboostlinks', |
81 | // Convenient for debugging. Production setups should use LocalSearchTaskSuggester anyway. |
82 | 'errorlang' => 'en', |
83 | ]; |
84 | // FIXME quick fix: don't randomize if we use morelike, seems to conflict |
85 | if ( $query->getSort() ) { |
86 | $params['srsort'] = $query->getSort(); |
87 | } |
88 | $status = Util::getApiUrl( $this->requestFactory, $this->apiUrl, $params ); |
89 | if ( !$status->isOK() ) { |
90 | return $status; |
91 | } |
92 | $data = $status->getValue(); |
93 | |
94 | $results = []; |
95 | foreach ( $data['query']['search'] ?? [] as $i => $result ) { |
96 | $title = $this->titleFactory->newFromText( $result['title'], $result['ns'] ); |
97 | if ( !$title ) { |
98 | continue; |
99 | } |
100 | // The search API does not expose scores :( Put in something fake, just to ease testing. |
101 | $results[] = new FauxSearchResultWithScore( $title, 100 / ( $i + 1 ) ); |
102 | } |
103 | $resultSet = new FauxSearchResultSet( $results, (int)$data['query']['searchinfo']['totalhits'] ); |
104 | |
105 | if ( $debug ) { |
106 | // Add Cirrus debug dump URLs which show the details of how the scores were calculated. |
107 | $query->setDebugUrl( $this->apiUrl . '?' . wfArrayToCgi( $params, [ |
108 | 'cirrusDumpResult' => 1, |
109 | 'cirrusExplain' => 'pretty', |
110 | ] ) ); |
111 | } |
112 | |
113 | return $resultSet; |
114 | } |
115 | |
116 | } |