Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
57.14% covered (warning)
57.14%
36 / 63
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
LocalSearchTaskSuggester
57.14% covered (warning)
57.14%
36 / 63
25.00% covered (danger)
25.00%
1 / 4
17.87
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 suggest
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 filter
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 search
68.09% covered (warning)
68.09%
32 / 47
0.00% covered (danger)
0.00%
0 / 1
8.59
1<?php
2
3namespace GrowthExperiments\NewcomerTasks\TaskSuggester;
4
5use GrowthExperiments\NewcomerTasks\NewcomerTasksUserOptionsLookup;
6use GrowthExperiments\NewcomerTasks\Task\TaskSet;
7use GrowthExperiments\NewcomerTasks\Task\TaskSetFilters;
8use GrowthExperiments\NewcomerTasks\TaskSuggester\SearchStrategy\SearchQuery;
9use GrowthExperiments\NewcomerTasks\TaskSuggester\SearchStrategy\SearchStrategy;
10use GrowthExperiments\NewcomerTasks\TaskType\TaskType;
11use GrowthExperiments\NewcomerTasks\TaskType\TaskTypeHandlerRegistry;
12use GrowthExperiments\NewcomerTasks\Topic\Topic;
13use ISearchResultSet;
14use MediaWiki\Api\ApiRawMessage;
15use MediaWiki\Cache\LinkBatchFactory;
16use MediaWiki\SpecialPage\SpecialPage;
17use MediaWiki\User\UserIdentity;
18use SearchEngine;
19use SearchEngineFactory;
20use StatusValue;
21use Wikimedia\Stats\IBufferingStatsdDataFactory;
22
23/**
24 * Suggest edits based on searching the wiki via SearchEngine.
25 */
26class LocalSearchTaskSuggester extends SearchTaskSuggester {
27
28    /** @var SearchEngineFactory */
29    private $searchEngineFactory;
30
31    /** @var IBufferingStatsdDataFactory */
32    private $statsdDataFactory;
33
34    /**
35     * @param TaskTypeHandlerRegistry $taskTypeHandlerRegistry
36     * @param SearchEngineFactory $searchEngineFactory
37     * @param SearchStrategy $searchStrategy
38     * @param NewcomerTasksUserOptionsLookup $newcomerTasksUserOptionsLookup
39     * @param LinkBatchFactory $linkBatchFactory
40     * @param TaskType[] $taskTypes
41     * @param Topic[] $topics
42     * @param IBufferingStatsdDataFactory $statsdDataFactory
43     */
44    public function __construct(
45        TaskTypeHandlerRegistry $taskTypeHandlerRegistry,
46        SearchEngineFactory $searchEngineFactory,
47        SearchStrategy $searchStrategy,
48        NewcomerTasksUserOptionsLookup $newcomerTasksUserOptionsLookup,
49        LinkBatchFactory $linkBatchFactory,
50        array $taskTypes,
51        array $topics,
52        IBufferingStatsdDataFactory $statsdDataFactory
53    ) {
54        parent::__construct( $taskTypeHandlerRegistry, $searchStrategy, $newcomerTasksUserOptionsLookup,
55            $linkBatchFactory, $taskTypes, $topics );
56        $this->searchEngineFactory = $searchEngineFactory;
57        $this->statsdDataFactory = $statsdDataFactory;
58    }
59
60    /** @inheritDoc */
61    public function suggest(
62        UserIdentity $user,
63        TaskSetFilters $taskSetFilters,
64        ?int $limit = null,
65        ?int $offset = null,
66        array $options = []
67    ) {
68        $start = microtime( true );
69        $suggest = parent::suggest( $user, $taskSetFilters, $limit, $offset, $options );
70        $this->statsdDataFactory->timing(
71            'timing.growthExperiments.SearchTaskSuggester.suggest', microtime( true ) - $start
72        );
73        return $suggest;
74    }
75
76    /** @inheritDoc */
77    public function filter( UserIdentity $user, TaskSet $taskSet ) {
78        $start = microtime( true );
79        $filter = parent::filter( $user, $taskSet );
80        $this->statsdDataFactory->timing(
81            'timing.growthExperiments.SearchTaskSuggester.filter', microtime( true ) - $start
82        );
83        return $filter;
84    }
85
86    /**
87     * @inheritDoc
88     */
89    protected function search(
90        SearchQuery $query,
91        int $limit,
92        int $offset,
93        bool $debug
94    ) {
95        $start = microtime( true );
96        $searchEngine = $this->searchEngineFactory->create();
97        $searchEngine->setLimitOffset( $limit, $offset );
98        $searchEngine->setNamespaces( [ NS_MAIN ] );
99        $searchEngine->setShowSuggestion( false );
100        $searchEngine->setFeatureData(
101            SearchEngine::FT_QUERY_INDEP_PROFILE_TYPE,
102            $query->getRescoreProfile() ?? 'classic_noboostlinks'
103        );
104        $sort = $query->getSort();
105        if ( $sort ) {
106            $searchEngine->setSort( $sort );
107        }
108        $matches = $searchEngine->searchText( $query->getQueryString() );
109        if ( !$matches ) {
110            $matches = StatusValue::newFatal( new ApiRawMessage(
111                'Full text searches are unsupported or disabled',
112                'grothexperiments-no-fulltext-search'
113            ) );
114        } elseif ( $matches instanceof StatusValue ) {
115            if ( $matches->isGood() ) {
116                $matches = $matches->getValue();
117                /** @var ISearchResultSet $matches */
118            } else {
119                // T302473 make sure the result is serializable
120                $matches->setResult( false, null );
121            }
122        }
123
124        if ( $debug ) {
125            $params = [
126                'search' => $query->getQueryString(),
127                'fulltext' => 1,
128                'ns0' => 1,
129                'limit' => $limit,
130                'offset' => $offset,
131                'cirrusRescoreProfile' => $query->getRescoreProfile() ?? 'classic_noboostlinks',
132                'cirrusDumpResult' => 1,
133                'cirrusExplain' => 'pretty',
134            ];
135            if ( $query->getSort() ) {
136                $params['sort'] = $query->getSort();
137            }
138            $query->setDebugUrl( SpecialPage::getTitleFor( 'Search' )
139                ->getFullURL( $params, false, PROTO_CANONICAL ) );
140        }
141        $elapsed = microtime( true ) - $start;
142        $this->logger->debug( 'LocalSearchTaskSuggester query', [
143            'query' => $query->getQueryString(),
144            'sort' => $query->getSort(),
145            'limit' => $limit,
146            'success' => $matches instanceof ISearchResultSet,
147            'elapsedTime' => $elapsed
148        ] );
149        $this->statsdDataFactory->timing( 'timing.growthExperiments.LocalSearchTaskSuggester.search', $elapsed );
150
151        return $matches;
152    }
153
154}