Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.28% covered (success)
98.28%
57 / 58
95.45% covered (success)
95.45%
21 / 22
CRAP
0.00% covered (danger)
0.00%
0 / 1
SearchQuery
98.28% covered (success)
98.28%
57 / 58
95.45% covered (success)
95.45%
21 / 22
31
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
1
 getDebugOptions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getParsedQuery
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getInitialNamespaces
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getInitialCrossSearchStrategy
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCrossSearchStrategy
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 getContextualFilters
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSearchEngineEntryPoint
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSort
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRandomSeed
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getForcedProfiles
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getOffset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLimit
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNamespaces
93.33% covered (success)
93.33%
14 / 15
0.00% covered (danger)
0.00%
0 / 1
8.02
 getSearchConfig
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getForcedProfile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasForcedProfile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isWithDYMSuggestion
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isAllowRewrite
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getProfileContextParameters
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getExtraFieldsToExtract
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 shouldProvideAllSnippets
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace CirrusSearch\Search;
4
5use CirrusSearch\CirrusDebugOptions;
6use CirrusSearch\CrossSearchStrategy;
7use CirrusSearch\Parser\AST\ParsedQuery;
8use CirrusSearch\SearchConfig;
9
10/**
11 * A search query, it contains all the necessary information to build and send a query to the backend.
12 * NOTE: Immutable value class.
13 */
14class SearchQuery {
15    /**
16     * Identifier for the fulltext SearchEngine entry point
17     * @see \SearchEngine::searchText()
18     */
19    public const SEARCH_TEXT = 'searchText';
20
21    /**
22     * @var ParsedQuery
23     */
24    private $parsedQuery;
25
26    /**
27     * @var int[]
28     */
29    private $initialNamespaces;
30
31    /**
32     * @var CrossSearchStrategy
33     */
34    private $initialCrossSearchStrategy;
35
36    /**
37     * @var CrossSearchStrategy|null (lazy loaded)
38     */
39    private $crossSearchStrategy;
40
41    /**
42     * @var \CirrusSearch\Query\Builder\ContextualFilter[]
43     */
44    private $contextualFilters;
45
46    /**
47     * Entry point from the SearchEngine
48     * TODO: clarify its usage and see whether or not another
49     * entry point var is needed to carry some provenance information
50     * from the UI.
51     * @var string
52     */
53    private $searchEngineEntryPoint;
54
55    /**
56     * @var string
57     */
58    private $sort;
59
60    /**
61     * @var int|null
62     */
63    private $randomSeed;
64
65    /**
66     * @var string[]
67     */
68    private $forcedProfiles;
69
70    /**
71     * @var int
72     */
73    private $offset;
74
75    /**
76     * @var int
77     */
78    private $limit;
79
80    /**
81     * @var CirrusDebugOptions
82     */
83    private $debugOptions;
84
85    /**
86     * @var SearchConfig
87     */
88    private $searchConfig;
89
90    /**
91     * @var bool
92     */
93    private $withDYMSuggestion;
94
95    /**
96     * @var bool
97     */
98    private $allowRewrite;
99
100    /**
101     * @var string[] parameters for SearchProfileService
102     * @see \CirrusSearch\Profile\ContextualProfileOverride
103     */
104    private $profileContextParameters;
105
106    /**
107     * @var string[] list of extra fields to extract
108     */
109    private $extraFieldsToExtract;
110
111    /**
112     * @var bool
113     */
114    private $provideAllSnippets;
115
116    /**
117     * @param ParsedQuery $parsedQuery
118     * @param int[] $initialNamespaces
119     * @param CrossSearchStrategy $initialCrosswikiStrategy
120     * @param \CirrusSearch\Query\Builder\ContextualFilter[] $contextualFilters
121     * @param string $searchEngineEntryPoint
122     * @param string $sort
123     * @param int|null $randomSeed
124     * @param string[] $forcedProfiles
125     * @param int $offset
126     * @param int $limit
127     * @param CirrusDebugOptions $debugOptions
128     * @param SearchConfig $searchConfig
129     * @param bool $withDYMSuggestion
130     * @param bool $allowRewrite
131     * @param string[] $profileContextParameters
132     * @param string[] $extraFieldsToExtract
133     * @param bool $provideAllSnippets
134     * @see SearchQueryBuilder
135     */
136    public function __construct(
137        ParsedQuery $parsedQuery,
138        array $initialNamespaces,
139        CrossSearchStrategy $initialCrosswikiStrategy,
140        array $contextualFilters,
141        $searchEngineEntryPoint,
142        $sort,
143        $randomSeed,
144        array $forcedProfiles,
145        $offset,
146        $limit,
147        CirrusDebugOptions $debugOptions,
148        SearchConfig $searchConfig,
149        $withDYMSuggestion,
150        $allowRewrite,
151        array $profileContextParameters,
152        array $extraFieldsToExtract,
153        bool $provideAllSnippets
154    ) {
155        $this->parsedQuery = $parsedQuery;
156        $this->initialNamespaces = $initialNamespaces;
157        $this->initialCrossSearchStrategy = $initialCrosswikiStrategy;
158        $this->contextualFilters = $contextualFilters;
159        $this->searchEngineEntryPoint = $searchEngineEntryPoint;
160        $this->sort = $sort;
161        $this->randomSeed = $randomSeed;
162        $this->forcedProfiles = $forcedProfiles;
163        $this->offset = $offset;
164        $this->limit = $limit;
165        $this->debugOptions = $debugOptions;
166        $this->searchConfig = $searchConfig;
167        $this->withDYMSuggestion = $withDYMSuggestion;
168        $this->allowRewrite = $allowRewrite;
169        $this->profileContextParameters = $profileContextParameters;
170        $this->extraFieldsToExtract = $extraFieldsToExtract;
171        $this->provideAllSnippets = $provideAllSnippets;
172    }
173
174    /**
175     * @return CirrusDebugOptions
176     */
177    public function getDebugOptions(): CirrusDebugOptions {
178        return $this->debugOptions;
179    }
180
181    /**
182     * @return ParsedQuery
183     */
184    public function getParsedQuery(): ParsedQuery {
185        return $this->parsedQuery;
186    }
187
188    /**
189     * @return int[]
190     */
191    public function getInitialNamespaces() {
192        return $this->initialNamespaces;
193    }
194
195    /**
196     * @return CrossSearchStrategy
197     */
198    public function getInitialCrossSearchStrategy(): CrossSearchStrategy {
199        return $this->initialCrossSearchStrategy;
200    }
201
202    /**
203     * @return CrossSearchStrategy
204     */
205    public function getCrossSearchStrategy(): CrossSearchStrategy {
206        if ( $this->crossSearchStrategy === null ) {
207            if ( $this->contextualFilters !== [] ) {
208                $this->crossSearchStrategy = CrossSearchStrategy::hostWikiOnlyStrategy();
209            } else {
210                $this->crossSearchStrategy = $this->parsedQuery
211                    ->getCrossSearchStrategy()
212                    ->intersect( $this->initialCrossSearchStrategy );
213            }
214        }
215        return $this->crossSearchStrategy;
216    }
217
218    /**
219     * @return \CirrusSearch\Query\Builder\ContextualFilter[]
220     */
221    public function getContextualFilters(): array {
222        return $this->contextualFilters;
223    }
224
225    /**
226     * From which SearchEngine method this query entered CirrusSearch
227     * @return string
228     */
229    public function getSearchEngineEntryPoint() {
230        return $this->searchEngineEntryPoint;
231    }
232
233    /**
234     * @return string
235     */
236    public function getSort() {
237        return $this->sort;
238    }
239
240    /**
241     * @return int|null
242     */
243    public function getRandomSeed(): ?int {
244        return $this->randomSeed;
245    }
246
247    /**
248     * @return string[]
249     */
250    public function getForcedProfiles(): array {
251        return $this->forcedProfiles;
252    }
253
254    /**
255     * @return int
256     */
257    public function getOffset() {
258        return $this->offset;
259    }
260
261    /**
262     * @return int
263     */
264    public function getLimit() {
265        return $this->limit;
266    }
267
268    /**
269     * List of namespaces required to run this query.
270     *
271     * @return int[] list of namespaces, empty array means that all namespaces
272     * are required.
273     */
274    public function getNamespaces(): array {
275        $additionalRequired = null;
276        if ( $this->initialNamespaces != [] && $this->contextualFilters != [] ) {
277            foreach ( $this->contextualFilters as $filter ) {
278                $additional = $filter->requiredNamespaces();
279                if ( $additional === null ) {
280                    continue;
281                }
282                if ( $additional === [] ) {
283                    $additionalRequired = [];
284                    break;
285                }
286                if ( $additionalRequired === null ) {
287                    $additionalRequired = $additional;
288                } else {
289                    $additionalRequired = array_merge( $additionalRequired, $additional );
290                }
291            }
292            if ( $additionalRequired !== null ) {
293                $additionalRequired = array_unique( $additionalRequired );
294            }
295        }
296        return $this->parsedQuery->getActualNamespaces( $this->initialNamespaces, $additionalRequired );
297    }
298
299    /**
300     * @return SearchConfig
301     */
302    public function getSearchConfig(): SearchConfig {
303        return $this->searchConfig;
304    }
305
306    /**
307     * @param string $profileType
308     * @see \CirrusSearch\Profile\SearchProfileService
309     * @return string|null name of the profile or null if nothing forced for this type
310     */
311    public function getForcedProfile( $profileType ) {
312        return $this->forcedProfiles[$profileType] ?? null;
313    }
314
315    /**
316     * @return bool
317     */
318    public function hasForcedProfile() {
319        return $this->forcedProfiles !== [];
320    }
321
322    /**
323     * @return bool
324     */
325    public function isWithDYMSuggestion() {
326        return $this->withDYMSuggestion;
327    }
328
329    /**
330     * @return bool
331     */
332    public function isAllowRewrite() {
333        return $this->allowRewrite;
334    }
335
336    /**
337     * @return string[]
338     * @see \CirrusSearch\Profile\ContextualProfileOverride
339     */
340    public function getProfileContextParameters() {
341        return $this->profileContextParameters;
342    }
343
344    /**
345     * @return string[]
346     * @see \CirrusSearch\Search\FullTextResultsType
347     */
348    public function getExtraFieldsToExtract(): array {
349        return $this->extraFieldsToExtract;
350    }
351
352    /**
353     * @return bool
354     */
355    public function shouldProvideAllSnippets(): bool {
356        return $this->provideAllSnippets;
357    }
358}