Passed
Push — master ( 5f60a5...391298 )
by Timo
24:49
created

QueryBuilder::useGroupingFromTypoScript()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
crap 1
1
<?php
2
namespace ApacheSolrForTypo3\Solr\Domain\Search\Query;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2017 <[email protected]>
8
 *  All rights reserved
9
 *
10
 *  This script is part of the TYPO3 project. The TYPO3 project is
11
 *  free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 3 of the License, or
14
 *  (at your option) any later version.
15
 *
16
 *  The GNU General Public License can be found at
17
 *  http://www.gnu.org/copyleft/gpl.html.
18
 *
19
 *  This script is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  This copyright notice MUST APPEAR in all copies of the script!
25
 ***************************************************************/
26
27
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\BigramPhraseFields;
28
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Elevation;
29
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Faceting;
30
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\FieldCollapsing;
31
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Filters;
32
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Grouping;
33
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Highlighting;
34
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Operator;
35
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\PhraseFields;
36
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\QueryFields;
37
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\ReturnFields;
38
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Slops;
39
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Spellchecking;
40
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\TrigramPhraseFields;
41
use ApacheSolrForTypo3\Solr\Domain\Site\SiteHashService;
42
use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository;
43
use ApacheSolrForTypo3\Solr\FieldProcessor\PageUidToHierarchy;
44
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
45
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
46
use ApacheSolrForTypo3\Solr\Util;
47
use TYPO3\CMS\Core\Utility\GeneralUtility;
48
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
49
50
/**
51
 * The QueryBuilder is responsible to build solr queries, that are used in the extension to query the solr server.
52
 *
53
 * @package ApacheSolrForTypo3\Solr\Domain\Search\Query
54
 */
55
class QueryBuilder {
56
57
    /**
58
     * Additional filters, which will be added to the query, as well as to
59
     * suggest queries.
60
     *
61
     * @var array
62
     */
63
    protected $additionalFilters = [];
64
65
    /**
66
     * @var TypoScriptConfiguration
67
     */
68
    protected $typoScriptConfiguration = null;
69
70
    /**
71
     * @var SolrLogManager;
72
     */
73
    protected $logger = null;
74
75
    /**
76
     * @var SiteHashService
77
     */
78
    protected $siteHashService = null;
79
80
    /**
81
     * @var Query
82
     */
83
    protected $queryToBuild = null;
84
85
    /**
86
     * QueryBuilder constructor.
87
     * @param TypoScriptConfiguration|null $configuration
88
     * @param SolrLogManager|null $solrLogManager
89
     * @param SiteHashService|null $siteHashService
90
     */
91 171
    public function __construct(TypoScriptConfiguration $configuration = null, SolrLogManager $solrLogManager = null, SiteHashService $siteHashService = null)
92
    {
93 171
        $this->typoScriptConfiguration = $configuration ?? Util::getSolrConfiguration();
94 171
        $this->logger = $solrLogManager ?? GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__);
95 171
        $this->siteHashService = $siteHashService ?? GeneralUtility::makeInstance(SiteHashService::class);
96 171
    }
97
98
    /**
99
     * @param Query $query
100
     * @return QueryBuilder
101
     */
102 59
    public function startFrom(Query $query): QueryBuilder
103
    {
104 59
        $this->queryToBuild = $query;
105 59
        return $this;
106
    }
107
108
    /**
109
     * @param string $queryString
110
     * @return QueryBuilder
111
     */
112 142
    public function newSearchQuery($queryString): QueryBuilder
113
    {
114 142
        $this->queryToBuild = $this->getQueryInstance($queryString);
115 142
        return $this;
116
    }
117
118
    /**
119
     * @param string $queryString
120
     * @return QueryBuilder
121
     */
122 2
    public function newSuggestQuery($queryString): QueryBuilder
123
    {
124 2
        $this->queryToBuild = $this->getSuggestQueryInstance($queryString);
125 2
        return $this;
126
    }
127
128
    /**
129
     * @return Query
130
     */
131 155
    public function getQuery(): Query
132
    {
133 155
        return $this->queryToBuild;
134
    }
135
136
    /**
137
     * Initializes the Query object and SearchComponents and returns
138
     * the initialized query object, when a search should be executed.
139
     *
140
     * @param string|null $rawQuery
141
     * @param int $resultsPerPage
142
     * @return Query
143
     */
144 141
    public function buildSearchQuery($rawQuery, $resultsPerPage = 10) : Query
145
    {
146 141
        if ($this->typoScriptConfiguration->getLoggingQuerySearchWords()) {
147
            $this->logger->log(SolrLogManager::INFO, 'Received search query', [$rawQuery]);
148
        }
149
150
        /* @var $query Query */
151 141
        return $this->newSearchQuery($rawQuery)
152 141
                ->useResultsPerPage($resultsPerPage)
153 141
                ->useReturnFieldsFromTypoScript()
154 141
                ->useQueryFieldsFromTypoScript()
155 141
                ->useInitialQueryFromTypoScript()
156 141
                ->useFiltersFromTypoScript()
157 141
                ->useFacetingFromTypoScript()
158 141
                ->useVariantsFromTypoScript()
159 141
                ->useGroupingFromTypoScript()
160 141
                ->useHighlightingFromTypoScript()
161 141
                ->usePhraseFieldsFromTypoScript()
162 141
                ->useBigramPhraseFieldsFromTypoScript()
163 141
                ->useTrigramPhraseFieldsFromTypoScript()
164 141
                ->getQuery();
165
    }
166
167
    /**
168
     * Builds a SuggestQuery with all applied filters.
169
     *
170
     * @param string $queryString
171
     * @param string $additionalFilters
172
     * @param integer $requestedPageId
173
     * @param string $groupList
174
     * @return SuggestQuery
175
     */
176 2
    public function buildSuggestQuery(string $queryString, string $additionalFilters, int $requestedPageId, string $groupList) : SuggestQuery
177
    {
178 2
        $this->newSuggestQuery($queryString)
179 2
            ->useFiltersFromTypoScript()
180 2
            ->useSiteHashFromTypoScript($requestedPageId)
181 2
            ->useUserAccessGroups(explode(',', $groupList));
182
183 2
        $this->queryToBuild->setOmitHeader();
184
185 2
        if (!empty($additionalFilters)) {
186
            $additionalFilters = (array)json_decode($additionalFilters);
187
            $this->useFilterArray($additionalFilters);
188
        }
189
190
191 2
        return $this->queryToBuild;
192
    }
193
194
    /**
195
     * Uses an array of filters and applies them to the query.
196
     *
197
     * @param array $filterArray
198
     * @return QueryBuilder
199
     */
200 143
    public function useFilterArray(array $filterArray): QueryBuilder
201
    {
202 143
        foreach ($filterArray as $additionalFilter) {
203 3
            $this->useFilter($additionalFilter);
204
        }
205
206 143
        return $this;
207
    }
208
209
    /**
210
     * Returns Query for Search which finds document for given page.
211
     * Note: The Connection is per language as recommended in ext-solr docs.
212
     *
213
     * @return Query
214
     */
215 1
    public function buildPageQuery($pageId)
216
    {
217 1
        $siteRepository = GeneralUtility::makeInstance(SiteRepository::class);
218 1
        $site = $siteRepository->getSiteByPageId($pageId);
219
220 1
        return $this->newSearchQuery('')
221 1
            ->useQueryString('*:*')
222 1
            ->useFilter('(type:pages AND uid:' . $pageId . ') OR (*:* AND pid:' . $pageId . ' NOT type:pages)', 'type')
223 1
            ->useFilter('siteHash:' . $site->getSiteHash(), 'siteHash')
224 1
            ->useReturnFields(ReturnFields::fromString('*'))
225 1
            ->useSorting('type asc, title asc')
226 1
            ->useQueryType('standard')
227 1
            ->useRawQueryString()
228 1
            ->getQuery();
229
    }
230
231
    /**
232
     * Returns a query for single record
233
     *
234
     * @return Query
235
     */
236
    public function buildRecordQuery($type, $uid, $pageId): Query
237
    {
238
        $siteRepository = GeneralUtility::makeInstance(SiteRepository::class);
239
        $site = $siteRepository->getSiteByPageId($pageId);
240
241
        return $this->newSearchQuery('')
242
            ->useQueryString('*:*')
243
            ->useFilter('type:' . $type . ' AND uid:' . $uid, 'type')
244
            ->useFilter('siteHash:' . $site->getSiteHash(), 'siteHash')
245
            ->useReturnFields(ReturnFields::fromString('*'))
246
            ->useSorting('type asc, title asc')
247
            ->useQueryType('standard')
248
            ->useRawQueryString()
249
            ->getQuery();
250
    }
251
252
    /**
253
     * Applies the queryString that is used to search
254
     *
255
     * @param string $queryString
256
     * @return QueryBuilder
257
     */
258 1
    public function useQueryString($queryString): QueryBuilder
259
    {
260 1
        $this->queryToBuild->getQueryStringContainer()->setQueryString($queryString);
261 1
        return $this;
262
    }
263
264
    /**
265
     * Applies the useRawQueryString flag to the queryString.
266
     *
267
     * @param boolean $boolean
268
     * @return QueryBuilder
269
     */
270 1
    public function useRawQueryString($boolean = true): QueryBuilder
271
    {
272 1
        $this->queryToBuild->getQueryStringContainer()->useRawQueryString($boolean);
273 1
        return $this;
274
    }
275
276
    /**
277
     * Applies the passed queryType to the query.
278
     *
279
     * @param string $queryType
280
     * @return QueryBuilder
281
     */
282 1
    public function useQueryType($queryType): QueryBuilder
283
    {
284 1
        $this->queryToBuild->setQueryType($queryType);
285 1
        return $this;
286
    }
287
288
    /**
289
     * Applies the passed sorting to the query.
290
     *
291
     * @param string $sorting
292
     * @return QueryBuilder
293
     */
294 1
    public function useSorting($sorting): QueryBuilder
295
    {
296 1
        $this->queryToBuild->setSorting($sorting);
297 1
        return $this;
298
    }
299
300
    /**
301
     * @param int $resultsPerPage
302
     * @return QueryBuilder
303
     */
304 141
    public function useResultsPerPage($resultsPerPage): QueryBuilder
305
    {
306 141
        $this->queryToBuild->getPagination()->setResultsPerPage($resultsPerPage);
307 141
        return $this;
308
    }
309
310
    /**
311
     * @param int $page
312
     * @return QueryBuilder
313
     */
314
    public function usePage($page): QueryBuilder
315
    {
316
        $this->queryToBuild->getPagination()->setPage($page);
317
        return $this;
318
    }
319
320
    /**
321
     * @param Operator $operator
322
     * @return QueryBuilder
323
     */
324
    public function useOperator(Operator $operator): QueryBuilder
325
    {
326
        $this->queryToBuild->setOperator($operator);
327
        return $this;
328
    }
329
330
    /**
331
     * @return QueryBuilder
332
     */
333 50
    public function useSlopsFromTypoScript(): QueryBuilder
334
    {
335 50
        return $this->useSlops(Slops::fromTypoScriptConfiguration($this->typoScriptConfiguration));
336
    }
337
338
    /**
339
     * @param Slops $slops
340
     * @return QueryBuilder
341
     */
342 50
    public function useSlops(Slops $slops): QueryBuilder
343
    {
344 50
        $this->queryToBuild->setSlops($slops);
345 50
        return $this;
346
    }
347
348
    /**
349
     * Uses the configured boost queries from typoscript
350
     *
351
     * @return QueryBuilder
352
     */
353 50
    public function useBoostQueriesFromTypoScript(): QueryBuilder
354
    {
355 50
        $searchConfiguration = $this->typoScriptConfiguration->getSearchConfiguration();
356
357 50
        if (!empty($searchConfiguration['query.']['boostQuery'])) {
358 1
            return $this->useBoostQueries($searchConfiguration['query.']['boostQuery']);
359
        }
360
361 49
        if (!empty($searchConfiguration['query.']['boostQuery.'])) {
362 1
            $boostQueries = $searchConfiguration['query.']['boostQuery.'];
363 1
            return $this->useBoostQueries(array_values($boostQueries));
364
        }
365
366 48
        return $this;
367
    }
368
369
    /**
370
     * Uses the passed boostQuer(y|ies) for the query.
371
     *
372
     * @param string|array $boostQueries
373
     * @return QueryBuilder
374
     */
375 2
    public function useBoostQueries($boostQueries): QueryBuilder
376
    {
377 2
        $this->queryToBuild->setBoostQuery($boostQueries);
378 2
        return $this;
379
    }
380
381
    /**
382
     * Uses the configured boostFunction from the typoscript configuration.
383
     *
384
     * @return QueryBuilder
385
     */
386 50
    public function useBoostFunctionFromTypoScript(): QueryBuilder
387
    {
388 50
        $searchConfiguration = $this->typoScriptConfiguration->getSearchConfiguration();
389 50
        if (!empty($searchConfiguration['query.']['boostFunction'])) {
390 1
            return $this->useBoostFunction($searchConfiguration['query.']['boostFunction']);
391
        }
392
393 49
        return $this;
394
    }
395
396
    /**
397
     * Uses the passed boostFunction for the query.
398
     *
399
     * @param string $boostFunction
400
     * @return QueryBuilder
401
     */
402 1
    public function useBoostFunction($boostFunction): QueryBuilder
403
    {
404 1
        $this->queryToBuild->setBoostFunction($boostFunction);
405 1
        return $this;
406
    }
407
408
    /**
409
     * Uses the configured minimumMatch from the typoscript configuration.
410
     *
411
     * @return QueryBuilder
412
     */
413 50
    public function useMinimumMatchFromTypoScript(): QueryBuilder
414
    {
415 50
        $searchConfiguration = $this->typoScriptConfiguration->getSearchConfiguration();
416 50
        if (!empty($searchConfiguration['query.']['minimumMatch'])) {
417 1
            return $this->useMinimumMatch($searchConfiguration['query.']['minimumMatch']);
418
        }
419
420 49
        return $this;
421
    }
422
423
    /**
424
     * Uses the passed minimumMatch(mm) for the query.
425
     *
426
     * @param string $boostFunction
427
     * @return QueryBuilder
428
     */
429 1
    public function useMinimumMatch($boostFunction): QueryBuilder
430
    {
431 1
        $this->queryToBuild->setMinimumMatch($boostFunction);
432 1
        return $this;
433
    }
434
435
    /**
436
     * @return QueryBuilder
437
     */
438 50
    public function useTieParameterFromTypoScript(): QueryBuilder
439
    {
440 50
        $searchConfiguration = $this->typoScriptConfiguration->getSearchConfiguration();
441 50
        if (empty($searchConfiguration['query.']['tieParameter'])) {
442 49
            return $this;
443
        }
444
445 1
        return $this->useTieParameter($searchConfiguration['query.']['tieParameter']);
446
    }
447
448
    /**
449
     * Applies the tie parameter to the query.
450
     *
451
     * @param mixed $tie
452
     * @return QueryBuilder
453
     */
454 1
    public function useTieParameter($tie): QueryBuilder
455
    {
456 1
        $this->queryToBuild->setTieParameter($tie);
457 1
        return $this;
458
    }
459
460
    /**
461
     * Applies the configured query fields from the typoscript configuration.
462
     *
463
     * @return QueryBuilder
464
     */
465 141
    public function useQueryFieldsFromTypoScript(): QueryBuilder
466
    {
467 141
        return $this->useQueryFields(QueryFields::fromString($this->typoScriptConfiguration->getSearchQueryQueryFields()));
468
    }
469
470
    /**
471
     * Applies custom QueryFields to the query.
472
     *
473
     * @param QueryFields $queryFields
474
     * @return QueryBuilder
475
     */
476 141
    public function useQueryFields(QueryFields $queryFields): QueryBuilder
477
    {
478 141
        $this->queryToBuild->setQueryFields($queryFields);
479 141
        return $this;
480
    }
481
482
    /**
483
     * Applies the configured return fields from the typoscript configuration.
484
     *
485
     * @return QueryBuilder
486
     */
487 141
    public function useReturnFieldsFromTypoScript(): QueryBuilder
488
    {
489 141
        $returnFieldsArray = (array)$this->typoScriptConfiguration->getSearchQueryReturnFieldsAsArray(['*', 'score']);
490 141
        return $this->useReturnFields(ReturnFields::fromArray($returnFieldsArray));
491
    }
492
493
    /**
494
     * Applies custom ReturnFields to the query.
495
     *
496
     * @param ReturnFields $returnFields
497
     * @return QueryBuilder
498
     */
499 142
    public function useReturnFields(ReturnFields $returnFields): QueryBuilder
500
    {
501 142
        $this->queryToBuild->setReturnFields($returnFields);
502 142
        return $this;
503
    }
504
505
    /**
506
     * Can be used to apply the allowed sites from plugin.tx_solr.search.query.allowedSites to the query.
507
     *
508
     * @param int $requestedPageId
509
     * @return QueryBuilder
510
     */
511 39
    public function useSiteHashFromTypoScript(int $requestedPageId): QueryBuilder
512
    {
513 39
        $queryConfiguration = $this->typoScriptConfiguration->getObjectByPathOrDefault('plugin.tx_solr.search.query.', []);
514 39
        $allowedSites = $this->siteHashService->getAllowedSitesForPageIdAndAllowedSitesConfiguration($requestedPageId, $queryConfiguration['allowedSites']);
515 39
        return $this->useSiteHashFromAllowedSites($allowedSites);
516
    }
517
518
    /**
519
     * Can be used to apply a list of allowed sites to the query.
520
     *
521
     * @param string $allowedSites
522
     * @return QueryBuilder
523
     */
524 39
    public function useSiteHashFromAllowedSites($allowedSites): QueryBuilder
525
    {
526 39
        $isAnySiteAllowed = trim($allowedSites) === '*';
527 39
        if ($isAnySiteAllowed) {
528
            // no filter required
529 1
            return $this;
530
        }
531
532 38
        $allowedSites = GeneralUtility::trimExplode(',', $allowedSites);
533 38
        $filters = [];
534 38
        foreach ($allowedSites as $site) {
535 38
            $siteHash = $this->siteHashService->getSiteHashForDomain($site);
536 38
            $filters[] = 'siteHash:"' . $siteHash . '"';
537
        }
538
539 38
        $siteHashFilterString = implode(' OR ', $filters);
540 38
        return $this->useFilter($siteHashFilterString, 'siteHash');
541
    }
542
543
    /**
544
     * Can be used to use a specific filter string in the solr query.
545
     *
546
     * @param string $filterString
547
     * @param string $filterName
548
     * @return QueryBuilder
549
     */
550 47
    public function useFilter($filterString, $filterName = ''): QueryBuilder
551
    {
552 47
        $this->queryToBuild->getFilters()->add($filterString, $filterName);
553 47
        return $this;
554
    }
555
556
    /**
557
     * Can be used to filter the result on an applied list of user groups.
558
     *
559
     * @param array $groups
560
     * @return QueryBuilder
561
     */
562 43
    public function useUserAccessGroups(array $groups): QueryBuilder
563
    {
564 43
        $groups = array_map('intval', $groups);
565 43
        $groups[] = 0; // always grant access to public documents
566 43
        $groups = array_unique($groups);
567 43
        sort($groups, SORT_NUMERIC);
568
569 43
        $accessFilter = '{!typo3access}' . implode(',', $groups);
570 43
        return $this->useFilter($accessFilter, 'accessFilter');
571
    }
572
573
    /**
574
     * Applies the configured initial query settings to set the alternative query for solr as required.
575
     *
576
     * @return QueryBuilder
577
     */
578 141
    public function useInitialQueryFromTypoScript(): QueryBuilder
579
    {
580 141
        if ($this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchQueryAllowEmptyQuery()) {
581
            // empty main query, but using a "return everything"
582
            // alternative query in q.alt
583 38
            $this->queryToBuild->setAlternativeQuery('*:*');
584
        }
585
586 141
        if ($this->typoScriptConfiguration->getSearchInitializeWithQuery()) {
587 4
            $this->queryToBuild->setAlternativeQuery($this->typoScriptConfiguration->getSearchInitializeWithQuery());
588
        }
589
590 141
        return $this;
591
    }
592
593
    /**
594
     * Applies the configured facets from the typoscript configuration on the query.
595
     *
596
     * @return QueryBuilder
597
     */
598 141
    public function useFacetingFromTypoScript(): QueryBuilder
599
    {
600 141
        return $this->useFaceting(Faceting::fromTypoScriptConfiguration($this->typoScriptConfiguration));
601
    }
602
603
    /**
604
     * Applies a custom Faceting configuration to the query.
605
     *
606
     * @param Faceting $faceting
607
     * @return QueryBuilder
608
     */
609 141
    public function useFaceting(Faceting $faceting): QueryBuilder
610
    {
611 141
        $this->queryToBuild->setFaceting($faceting);
612 141
        return $this;
613
    }
614
615
    /**
616
     * Applies the configured variants from the typoscript configuration on the query.
617
     *
618
     * @return QueryBuilder
619
     */
620 141
    public function useVariantsFromTypoScript(): QueryBuilder
621
    {
622 141
        return $this->useFieldCollapsing(FieldCollapsing::fromTypoScriptConfiguration($this->typoScriptConfiguration));
623
    }
624
625
    /**
626
     * @param FieldCollapsing $fieldCollapsing
627
     * @return QueryBuilder
628
     */
629 141
    public function useFieldCollapsing(FieldCollapsing $fieldCollapsing): QueryBuilder
630
    {
631 141
        $this->queryToBuild->setFieldCollapsing($fieldCollapsing);
632 141
        return $this;
633
    }
634
635
    /**
636
     * Applies the configured groupings from the typoscript configuration to the query.
637
     *
638
     * @return QueryBuilder
639
     */
640 141
    public function useGroupingFromTypoScript(): QueryBuilder
641
    {
642 141
        return $this->useGrouping(Grouping::fromTypoScriptConfiguration($this->typoScriptConfiguration));
643
    }
644
645
    /**
646
     * Applies a custom initialized grouping to the query.
647
     *
648
     * @param Grouping $grouping
649
     * @return QueryBuilder
650
     */
651 141
    public function useGrouping(Grouping $grouping): QueryBuilder
652
    {
653 141
        $this->queryToBuild->setGrouping($grouping);
654 141
        return $this;
655
    }
656
657
    /**
658
     * Applies the configured highlighting from the typoscript configuration to the query.
659
     *
660
     * @return QueryBuilder
661
     */
662 141
    public function useHighlightingFromTypoScript(): QueryBuilder
663
    {
664 141
        return $this->useHighlighting(Highlighting::fromTypoScriptConfiguration($this->typoScriptConfiguration));
665
    }
666
667
    /**
668
     * @param Highlighting $highlighting
669
     * @return QueryBuilder
670
     */
671 141
    public function useHighlighting(Highlighting $highlighting): QueryBuilder
672
    {
673 141
        $this->queryToBuild->setHighlighting($highlighting);
674 141
        return $this;
675
    }
676
677
    /**
678
     * Applies the configured filters (page section and other from typoscript).
679
     *
680
     * @return QueryBuilder
681
     */
682 143
    public function useFiltersFromTypoScript(): QueryBuilder
683
    {
684 143
        $filters = Filters::fromTypoScriptConfiguration($this->typoScriptConfiguration);
685 143
        $this->queryToBuild->setFilters($filters);
686
687 143
        $this->useFilterArray($this->getAdditionalFilters());
688
689 143
        $searchQueryFilters = $this->typoScriptConfiguration->getSearchQueryFilterConfiguration();
690
691 143
        if (!is_array($searchQueryFilters) || count($searchQueryFilters) <= 0) {
692 139
            return $this;
693
        }
694
695
        // special filter to limit search to specific page tree branches
696 4
        if (array_key_exists('__pageSections', $searchQueryFilters)) {
697 1
            $pageIds = GeneralUtility::trimExplode(',', $searchQueryFilters['__pageSections']);
698 1
            $this->usePageSectionsFromPageIds($pageIds);
699 1
            $this->typoScriptConfiguration->removeSearchQueryFilterForPageSections();
700
        }
701
702 4
        return $this;
703
    }
704
705
    /**
706
     * Applies the configured elevation from the typoscript configuration.
707
     *
708
     * @return QueryBuilder
709
     */
710 37
    public function useElevationFromTypoScript(): QueryBuilder
711
    {
712 37
        return $this->useElevation(Elevation::fromTypoScriptConfiguration($this->typoScriptConfiguration));
713
    }
714
715
    /**
716
     * @param Elevation $elevation
717
     * @return QueryBuilder
718
     */
719 37
    public function useElevation(Elevation $elevation): QueryBuilder
720
    {
721 37
        $this->queryToBuild->setElevation($elevation);
722 37
        return $this;
723
    }
724
725
    /**
726
     * Applies the configured spellchecking from the typoscript configuration.
727
     *
728
     * @return QueryBuilder
729
     */
730 33
    public function useSpellcheckingFromTypoScript(): QueryBuilder
731
    {
732 33
        return $this->useSpellchecking(Spellchecking::fromTypoScriptConfiguration($this->typoScriptConfiguration));
733
    }
734
735
    /**
736
     * @param Spellchecking $spellchecking
737
     * @return QueryBuilder
738
     */
739 33
    public function useSpellchecking(Spellchecking $spellchecking): QueryBuilder
740
    {
741 33
        $this->queryToBuild->setSpellchecking($spellchecking);
742 33
        return $this;
743
    }
744
745
    /**
746
     * Applies the passed pageIds as __pageSection filter.
747
     *
748
     * @param array $pageIds
749
     * @return QueryBuilder
750
     */
751 1
    public function usePageSectionsFromPageIds(array $pageIds = []): QueryBuilder
752
    {
753 1
        $filters = [];
754
755
        /** @var $processor PageUidToHierarchy */
756 1
        $processor = GeneralUtility::makeInstance(PageUidToHierarchy::class);
757 1
        $hierarchies = $processor->process($pageIds);
758
759 1
        foreach ($hierarchies as $hierarchy) {
760 1
            $lastLevel = array_pop($hierarchy);
761 1
            $filters[] = 'rootline:"' . $lastLevel . '"';
762
        }
763
764 1
        $pageSectionsFilterString = implode(' OR ', $filters);
765 1
        return $this->useFilter($pageSectionsFilterString, 'pageSections');
766
    }
767
768
    /**
769
     * Applies the configured phrase fields from the typoscript configuration to the query.
770
     *
771
     * @return QueryBuilder
772
     */
773 141
    public function usePhraseFieldsFromTypoScript(): QueryBuilder
774
    {
775 141
        return $this->usePhraseFields(PhraseFields::fromTypoScriptConfiguration($this->typoScriptConfiguration));
776
    }
777
778
    /**
779
     * Applies a custom configured PhraseFields to the query.
780
     *
781
     * @param PhraseFields $phraseFields
782
     * @return QueryBuilder
783
     */
784 141
    public function usePhraseFields(PhraseFields $phraseFields): QueryBuilder
785
    {
786 141
        $this->queryToBuild->setPhraseFields($phraseFields);
787 141
        return $this;
788
    }
789
790
    /**
791
     * Applies the configured bigram phrase fields from the typoscript configuration to the query.
792
     *
793
     * @return QueryBuilder
794
     */
795 141
    public function useBigramPhraseFieldsFromTypoScript(): QueryBuilder
796
    {
797 141
        return $this->useBigramPhraseFields(BigramPhraseFields::fromTypoScriptConfiguration($this->typoScriptConfiguration));
798
    }
799
800
    /**
801
     * Applies a custom configured BigramPhraseFields to the query.
802
     *
803
     * @param BigramPhraseFields $bigramPhraseFields
804
     * @return QueryBuilder
805
     */
806 141
    public function useBigramPhraseFields(BigramPhraseFields $bigramPhraseFields): QueryBuilder
807
    {
808 141
        $this->queryToBuild->setBigramPhraseFields($bigramPhraseFields);
809 141
        return $this;
810
    }
811
812
    /**
813
     * Applies the configured trigram phrase fields from the typoscript configuration to the query.
814
     *
815
     * @return QueryBuilder
816
     */
817 141
    public function useTrigramPhraseFieldsFromTypoScript(): QueryBuilder
818
    {
819 141
        return $this->useTrigramPhraseFields(TrigramPhraseFields::fromTypoScriptConfiguration($this->typoScriptConfiguration));
820
    }
821
822
    /**
823
     * Applies a custom configured TrigramPhraseFields to the query.
824
     *
825
     * @param TrigramPhraseFields $trigramPhraseFields
826
     * @return QueryBuilder
827
     */
828 141
    public function useTrigramPhraseFields(TrigramPhraseFields $trigramPhraseFields): QueryBuilder
829
    {
830 141
        $this->queryToBuild->setTrigramPhraseFields($trigramPhraseFields);
831 141
        return $this;
832
    }
833
834
    /**
835
     * Retrieves the configuration filters from the TypoScript configuration, except the __pageSections filter.
836
     *
837
     * @return array
838
     */
839 148
    public function getAdditionalFilters() : array
840
    {
841
        // when we've build the additionalFilter once, we could return them
842 148
        if (count($this->additionalFilters) > 0) {
843 2
            return $this->additionalFilters;
844
        }
845
846 148
        $searchQueryFilters = $this->typoScriptConfiguration->getSearchQueryFilterConfiguration();
847 148
        if (!is_array($searchQueryFilters) || count($searchQueryFilters) <= 0) {
848 145
            return [];
849
        }
850
851 4
        $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
852
853
        // all other regular filters
854 4
        foreach ($searchQueryFilters as $filterKey => $filter) {
855
            // the __pageSections filter should not be handled as additional filter
856 4
            if ($filterKey === '__pageSections') {
857 1
                continue;
858
            }
859
860 3
            $filterIsArray = is_array($searchQueryFilters[$filterKey]);
861 3
            if ($filterIsArray) {
862
                continue;
863
            }
864
865 3
            $hasSubConfiguration = is_array($searchQueryFilters[$filterKey . '.']);
866 3
            if ($hasSubConfiguration) {
867
                $filter = $cObj->stdWrap($searchQueryFilters[$filterKey], $searchQueryFilters[$filterKey . '.']);
868
            }
869
870 3
            $this->additionalFilters[$filterKey] = $filter;
871
        }
872
873 4
        return $this->additionalFilters;
874
    }
875
876
    /**
877
     * @param string $rawQuery
878
     * @return Query|object
879
     */
880 142
    protected function getQueryInstance($rawQuery): Query
881
    {
882 142
        $query = GeneralUtility::makeInstance(Query::class, $rawQuery);
883 142
        return $query;
884
    }
885
886
887
    /**
888
     * @param string $rawQuery
889
     * @return SuggestQuery
890
     */
891 2
    protected function getSuggestQueryInstance($rawQuery): SuggestQuery
892
    {
893 2
        $query = GeneralUtility::makeInstance(SuggestQuery::class, $rawQuery, $this->typoScriptConfiguration);
894 2
        return $query;
895
    }
896
}