Passed
Push — master ( aa570e...188b42 )
by Timo
22:45 queued 03:50
created

Query::addGroupSorting()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
namespace ApacheSolrForTypo3\Solr;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2009-2015 Ingo Renner <[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 2 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\Helper\EscapeService;
28
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Faceting;
29
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Filters;
30
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Grouping;
31
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Highlighting;
32
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\QueryFields;
33
use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\ReturnFields;
34
use ApacheSolrForTypo3\Solr\Domain\Site\SiteHashService;
35
use ApacheSolrForTypo3\Solr\FieldProcessor\PageUidToHierarchy;
36
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
37
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
38
use TYPO3\CMS\Core\Utility\GeneralUtility;
39
40
/**
41
 * A Solr search query
42
 *
43
 * @author Ingo Renner <[email protected]>
44
 * @author Timo Hund <[email protected]>
45
 */
46
class Query
47
{
48
49
    // FIXME extract link building from the query, it's not the query's domain
50
51
    const SORT_ASC = 'ASC';
52
    const SORT_DESC = 'DESC';
53
54
    const OPERATOR_AND = 'AND';
55
    const OPERATOR_OR = 'OR';
56
57
    /**
58
     * Used to identify the queries.
59
     *
60
     * @var int
61
     */
62
    protected static $idCount = 0;
63
64
    /**
65
     * @var int
66
     */
67
    protected $id;
68
69
    /**
70
     * @var TypoScriptConfiguration
71
     */
72
    protected $solrConfiguration;
73
74
    /**
75
     * @var string
76
     */
77
    protected $keywords;
78
79
    /**
80
     * @var string
81
     */
82
    protected $keywordsRaw;
83
84
    /**
85
     * ParameterBuilder for filters.
86
     *
87
     * @var Filters
88
     */
89
    protected $filters = null;
90
91
    /**
92
     * @var string
93
     */
94
    protected $sorting;
95
96
    // TODO check usage of these two variants, especially the check for $rawQueryString in getQueryString()
97
    /**
98
     * @var
99
     */
100
    protected $queryString;
101
102
    /**
103
     * @var array
104
     */
105
    protected $queryParameters = [];
106
107
    /**
108
     * @var int
109
     */
110
    protected $resultsPerPage;
111
112
    /**
113
     * @var int
114
     */
115
    protected $page;
116
117
    /**
118
     * @var int
119
     */
120
    protected $linkTargetPageId;
121
122
    /**
123
     * Holds the query fields with their associated boosts. The key represents
124
     * the field name, value represents the field's boost. These are the fields
125
     * that will actually be searched.
126
     *
127
     * Used in Solr's qf parameter
128
     *
129
     * @var QueryFields
130
     * @see http://wiki.apache.org/solr/DisMaxQParserPlugin#qf_.28Query_Fields.29
131
     */
132
    protected $queryFields = null;
133
134
    /**
135
     * List of fields that will be returned in the result documents.
136
     *
137
     * used in Solr's fl parameter
138
     *
139
     * @var ReturnFields
140
     * @see http://wiki.apache.org/solr/CommonQueryParameters#fl
141
     */
142
    protected $returnFields = null;
143
144
    /**
145
     * ParameterBuilder for the highlighting.
146
     *
147
     * @var Highlighting
148
     */
149
    protected $highlighting = null;
150
151
    /**
152
     * ParameterBuilder for the faceting.
153
     *
154
     * @var Faceting
155
     */
156
    protected $faceting = null;
157
158
    /**
159
     * ParameterBuilder for the grouping.
160
     *
161
     * @var Grouping
162
     */
163
    protected $grouping = null;
164
165
    /**
166
     * @var bool
167
     */
168
    private $rawQueryString = false;
169
170
    /**
171
     * The field by which the result will be collapsed
172
     * @var string
173
     */
174
    protected $variantField = 'variantId';
175
176
    /**
177
     * @var SiteHashService
178
     */
179
    protected $siteHashService = null;
180
181
    /**
182
     * @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager
183
     */
184
    protected $logger = null;
185
186
    /**
187
     * @var EscapeService
188
     */
189
    protected $escapeService = null;
190
191
    /**
192
     * Query constructor.
193
     * @param string $keywords
194
     * @param TypoScriptConfiguration $solrConfiguration
195
     * @param SiteHashService|null $siteHashService
196
     * @param EscapeService|null $escapeService
197
     * @param SolrLogManager|null $solrLogManager
198
     */
199 129
    public function __construct($keywords, $solrConfiguration = null, SiteHashService $siteHashService = null, EscapeService $escapeService = null, SolrLogManager $solrLogManager = null)
200
    {
201 129
        $keywords = (string)$keywords;
202
203 129
        $this->logger = is_null($solrLogManager) ? GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__) : $solrLogManager;
204 129
        $this->solrConfiguration = is_null($solrConfiguration) ? Util::getSolrConfiguration() : $solrConfiguration;
205 129
        $this->siteHashService = is_null($siteHashService) ? GeneralUtility::makeInstance(SiteHashService::class) : $siteHashService;
206 129
        $this->escapeService = is_null($escapeService) ? GeneralUtility::makeInstance(EscapeService::class) : $escapeService;
207 129
        $this->setKeywords($keywords);
208 129
        $this->sorting = '';
209
210 129
        $this->linkTargetPageId = $this->solrConfiguration->getSearchTargetPage();
211
212 129
        $this->initializeQuery();
213
214 129
        $this->id = ++self::$idCount;
215 129
    }
216
217
    /**
218
     * @return void
219
     */
220 128
    protected function initializeQuery()
221
    {
222
        // Filters
223 128
        $this->initializeFilters();
224
225
        // What fields to search
226 128
        $queryFields = QueryFields::fromString($this->solrConfiguration->getSearchQueryQueryFields());
227 128
        $this->setQueryFields($queryFields);
228
229
        // What fields to return from Solr
230 128
        $returnFieldsArray = $this->solrConfiguration->getSearchQueryReturnFieldsAsArray(['*', 'score']);
231 128
        $returnFields = ReturnFields::fromArray($returnFieldsArray);
232 128
        $this->setReturnFields($returnFields);
233
234
        // Configure highlighting
235 128
        $highlighting = Highlighting::fromTypoScriptConfiguration($this->solrConfiguration);
236 128
        $this->setHighlighting($highlighting);
237
238
        // Configure faceting
239 128
        $this->initializeFaceting();
240
241
        // Initialize grouping
242 128
        $this->initializeGrouping();
243
244
        // Configure collapsing
245 128
        $this->initializeCollapsingFromConfiguration();
246 128
    }
247
248
    /**
249
     * @param QueryFields $queryFields
250
     */
251
    public function setQueryFields(QueryFields $queryFields)
252
    {
253
        $this->queryFields = $queryFields;
254
    }
255
256
    /**
257
     * @return QueryFields
258
     */
259
    public function getQueryFields()
260
    {
261
        return $this->queryFields;
262 1
    }
263
264 1
    /**
265 1
     * magic implementation for clone(), makes sure that the id counter is
266 1
     * incremented
267
     *
268
     * @return void
269
     */
270
    public function __clone()
271
    {
272
        $this->id = ++self::$idCount;
273
    }
274
275
    /**
276
     * returns a string representation of the query
277
     *
278 1
     * @return string the string representation of the query
279
     */
280 1
    public function __toString()
281 1
    {
282 1
        return $this->getQueryString();
283
    }
284
285
    /**
286
     * Builds the query string which is then used for Solr's q parameters
287 128
     *
288
     * @return string Solr query string
289 128
     */
290 128
    public function getQueryString()
291
    {
292
        if (!$this->rawQueryString) {
293
            $this->buildQueryString();
294
        }
295 82
296
        return $this->queryString;
297 82
    }
298
299
    /**
300
     * Sets the query string without any escaping.
301
     *
302
     * Be cautious with this function!
303
     * TODO remove this method as it basically just sets the q parameter / keywords
304
     *
305
     * @param string $queryString The raw query string.
306
     */
307
    public function setQueryString($queryString)
308
    {
309
        $this->queryString = $queryString;
310
    }
311
312
    /**
313
     * Creates the string that is later used as the q parameter in the solr query
314
     *
315
     * @return void
316 1
     */
317
    protected function buildQueryString()
318 1
    {
319
        // very simple for now
320
        $this->queryString = $this->keywords;
321
    }
322
323
    /**
324
     * Sets whether a raw query sting should be used, that is, whether the query
325
     * string should be escaped or not.
326 39
     *
327
     * @param bool $useRawQueryString TRUE to use raw queries (like Lucene Query Language) or FALSE for regular, escaped queries
328 39
     */
329 36
    public function useRawQueryString($useRawQueryString)
330
    {
331
        $this->rawQueryString = (boolean)$useRawQueryString;
332 39
    }
333
334
    /**
335
     * Returns the query's ID.
336
     *
337
     * @return int The query's ID.
338
     */
339
    public function getId()
340
    {
341
        return $this->id;
342
    }
343 4
344
    /**
345 4
     * Gets the currently showing page's number
346 4
     *
347
     * @return int page number currently showing
348
     */
349
    public function getPage()
350
    {
351
        return $this->page;
352
    }
353 36
354
    /**
355
     * Sets the page that should be shown
356 36
     *
357 36
     * @param int $page page number to show
358
     * @return void
359
     */
360
    public function setPage($page)
361
    {
362
        $this->page = max(intval($page), 0);
363
    }
364
365 4
    /**
366
     * Gets the index of the first result document we're showing
367 4
     *
368 4
     * @return int index of the currently first document showing
369
     */
370
    public function getStartIndex()
371
    {
372
        return ($this->page - 1) * $this->resultsPerPage;
373
    }
374
375
    /**
376
     * Gets the index of the last result document we're showing
377
     *
378
     * @return int index of the currently last document showing
379
     */
380
    public function getEndIndex()
381
    {
382
        return $this->page * $this->resultsPerPage;
383
    }
384
385
    // query elevation
386
387
    /**
388
     * Activates and deactivates query elevation for the current query.
389
     *
390
     * @param bool $elevation True to enable query elevation (default), FALSE to disable query elevation.
391
     * @param bool $forceElevation Optionally force elevation so that the elevated documents are always on top regardless of sorting, default to TRUE.
392
     * @param bool $markElevatedResults Mark elevated results
393
     * @return void
394
     */
395
    public function setQueryElevation($elevation = true, $forceElevation = true, $markElevatedResults = true)
396
    {
397
        if ($elevation) {
398
            $this->queryParameters['enableElevation'] = 'true';
399
            $this->setForceElevation($forceElevation);
400 1
            if ($markElevatedResults) {
401
                $this->getReturnFields()->add('isElevated:[elevated]');
402 1
            }
403
        } else {
404
            $this->queryParameters['enableElevation'] = 'false';
405
            unset($this->queryParameters['forceElevation']);
406
            $this->getReturnFields()->remove('isElevated:[elevated]');
407
            $this->getReturnFields()->remove('[elevated]'); // fallback
408
        }
409
    }
410
411 2
    /**
412
     * Enables or disables the forceElevation query parameter.
413 2
     *
414 2
     * @param bool $forceElevation
415
     */
416
    protected function setForceElevation($forceElevation)
417
    {
418
        if ($forceElevation) {
419
            $this->queryParameters['forceElevation'] = 'true';
420
        } else {
421
            $this->queryParameters['forceElevation'] = 'false';
422
        }
423
    }
424
425
    // collapsing
426
427
    /**
428
     * Check whether collapsing is active
429
     *
430
     * @return bool
431
     */
432
    public function getIsCollapsing()
433
    {
434
        return $this->getFilters()->hasWithName('collapsing');
435
    }
436
437
    /**
438
     * @param string $fieldName
439
     */
440
    public function setVariantField($fieldName)
441
    {
442
        $this->variantField = $fieldName;
443
    }
444
445
    /**
446 33
     * @return string
447
     */
448 33
    public function getVariantField()
449 29
    {
450 29
        return $this->variantField;
451 29
    }
452 29
453
    /**
454
     * @param bool $collapsing
455 5
     */
456 5
    public function setCollapsing($collapsing = true)
457 5
    {
458 5
        if ($collapsing) {
459
            $this->getFilters()->add('{!collapse field=' . $this->variantField . '}', 'collapsing');
460 33
            if ($this->solrConfiguration->getSearchVariantsExpand()) {
461
                $this->queryParameters['expand'] = 'true';
462
                $this->queryParameters['expand.rows'] = $this->solrConfiguration->getSearchVariantsLimit();
463
            }
464
        } else {
465
            $this->getFilters()->removeByName('collapsing');
466
            unset($this->queryParameters['expand']);
467 29
            unset($this->queryParameters['expand.rows']);
468
        }
469 29
    }
470 28
471
    // grouping
472 1
473
    /**
474 29
     * Activates and deactivates grouping for the current query.
475
     *
476
     * @param Grouping $grouping TRUE to enable grouping, FALSE to disable grouping
477
     * @return void
478
     */
479
    public function setGrouping(Grouping $grouping)
480
    {
481
        $this->grouping = $grouping;
482
    }
483 3
484
    /**
485 3
     * @return Grouping
486
     */
487
    public function getGrouping()
488
    {
489
        return $this->grouping;
490
    }
491 4
492
    /**
493 4
     * Returns the number of results that should be shown per page
494 4
     *
495
     * @return int number of results to show per page
496
     */
497
    public function getResultsPerPage()
498
    {
499 1
        if ($this->getGrouping() instanceof Grouping && $this->getGrouping()->getIsEnabled()) {
500
            return $this->getGrouping()->getNumberOfGroups();
501 1
        }
502
503
        return $this->resultsPerPage;
504
    }
505
506
    /**
507 6
     * Sets the number of results that should be shown per page
508
     *
509 6
     * @param int $resultsPerPage Number of results to show per page
510 6
     * @return void
511 6
     */
512 2
    public function setResultsPerPage($resultsPerPage)
513 6
    {
514
        $this->resultsPerPage = max(intval($resultsPerPage), 0);
515
    }
516 1
517 1
    // faceting
518 1
519
    /**
520 6
     * Activates and deactivates faceting for the current query.
521
     *
522
     * @param Faceting $faceting TRUE to enable faceting, FALSE to disable faceting
523
     * @return void
524
     */
525
    public function setFaceting(Faceting $faceting)
526
    {
527
        $this->faceting = $faceting;
528
    }
529
530
    /**
531
     * @return Faceting
532
     */
533
    public function getFaceting()
534
    {
535
        return $this->faceting;
536
    }
537
538
539
    /**
540
     * Gets all currently applied filters.
541
     *
542
     * @return Filters Array of filters
543
     */
544
    public function getFilters()
545
    {
546
        return $this->filters;
547
    }
548
549
    /**
550
     * Sets the filters to use.
551
     *
552
     * @param Filters $filters
553
     */
554
    public function setFilters(Filters $filters)
555
    {
556 129
        $this->filters = $filters;
557
    }
558 129
559 129
    // sorting
560 129
561
    /**
562
     * Sets access restrictions for a frontend user.
563
     *
564
     * @param array $groups Array of groups a user has been assigned to
565
     */
566
    public function setUserAccessGroups(array $groups)
567 3
    {
568
        $groups = array_map('intval', $groups);
569 3
        $groups[] = 0; // always grant access to public documents
570 2
        $groups = array_unique($groups);
571 2
        sort($groups, SORT_NUMERIC);
572
573 2
        $accessFilter = '{!typo3access}' . implode(',', $groups);
574
        $this->getFilters()->removeByPrefix('{!typo3access}');
575 3
        $this->getFilters()->add($accessFilter);
576
    }
577
578
    // query parameters
579
580 91
    /**
581
     * Limits the query to certain sites
582 91
     *
583
     * @param string $allowedSites Comma-separated list of domains
584
     */
585
    public function setSiteHashFilter($allowedSites)
586
    {
587
        if (trim($allowedSites) === '*') {
588
            return;
589
        }
590
591
        $allowedSites = GeneralUtility::trimExplode(',', $allowedSites);
592
        $filters = [];
593 2
594
        foreach ($allowedSites as $site) {
595 2
            $siteHash = $this->siteHashService->getSiteHashForDomain($site);
596 2
            $filters[] = 'siteHash:"' . $siteHash . '"';
597 2
        }
598
599
        $this->getFilters()->add(implode(' OR ', $filters));
600
    }
601
602
    /**
603
     * Limits the query to certain page tree branches
604
     *
605
     * @param string $pageIds Comma-separated list of page IDs
606
     */
607 1
    public function setRootlineFilter($pageIds)
608
    {
609 1
        $pageIds = GeneralUtility::trimExplode(',', $pageIds);
610 1
        $filters = [];
611
612
            /** @var $processor PageUidToHierarchy */
613
        $processor = GeneralUtility::makeInstance(PageUidToHierarchy::class);
614
        $hierarchies = $processor->process($pageIds);
615
616
        foreach ($hierarchies as $hierarchy) {
617
            $lastLevel = array_pop($hierarchy);
618 33
            $filters[] = 'rootline:"' . $lastLevel . '"';
619
        }
620 33
621 2
        $this->getFilters()->add(implode(' OR ', $filters));
622
    }
623
624 33
    /**
625
     * @param ReturnFields $returnFields
626
     */
627
    public function setReturnFields(ReturnFields $returnFields)
628
    {
629
        $this->returnFields = $returnFields;
630
    }
631
632
    /**
633 40
     * @return ReturnFields
634
     */
635 40
    public function getReturnFields()
636 40
    {
637
        return $this->returnFields;
638
    }
639
640
    /**
641
     * Gets the query type, Solr's qt parameter.
642
     *
643
     * @return string Query type, qt parameter.
644 1
     */
645
    public function getQueryType()
646 1
    {
647 1
        return $this->queryParameters['qt'];
648 1
    }
649
650
    /**
651
     * Sets the query type, Solr's qt parameter.
652
     *
653
     * @param string|bool $queryType String query type or boolean FALSE to disable / reset the qt parameter.
654
     * @see http://wiki.apache.org/solr/CoreQueryParameters#qt
655
     */
656 1
    public function setQueryType($queryType)
657
    {
658 1
        $this->setQueryParameterWhenStringOrUnsetWhenEmpty('qt', $queryType);
659 1
    }
660
661
    /**
662
     * Sets the query operator to AND or OR. Unsets the query operator (actually
663
     * sets it back to default) for FALSE.
664
     *
665
     * @param string|bool $operator AND or OR, FALSE to unset
666
     */
667
    public function setOperator($operator)
668
    {
669 1
        if (in_array($operator, [self::OPERATOR_AND, self::OPERATOR_OR])) {
670
            $this->queryParameters['q.op'] = $operator;
671 1
        }
672 1
673 1
        if ($operator === false) {
674
            unset($this->queryParameters['q.op']);
675
        }
676
    }
677
678
    /**
679
     * Gets the alternative query, Solr's q.alt parameter.
680
     *
681 2
     * @return string Alternative query, q.alt parameter.
682
     */
683 2
    public function getAlternativeQuery()
684 2
    {
685
        return $this->queryParameters['q.alt'];
686
    }
687
688
    /**
689
     * Sets an alternative query, Solr's q.alt parameter.
690
     *
691
     * This query supports the complete Lucene Query Language.
692
     *
693
     * @param string $alternativeQuery String alternative query or boolean FALSE to disable / reset the q.alt parameter.
694
     * @see http://wiki.apache.org/solr/DisMaxQParserPlugin#q.alt
695 1
     */
696
    public function setAlternativeQuery($alternativeQuery)
697 1
    {
698 1
        $this->setQueryParameterWhenStringOrUnsetWhenEmpty('q.alt', $alternativeQuery);
699 1
    }
700
701
    // keywords
702
703
    /**
704
     * Set the query to omit the response header
705
     *
706
     * @param bool $omitHeader TRUE (default) to omit response headers, FALSE to re-enable
707 1
     */
708
    public function setOmitHeader($omitHeader = true)
709 1
    {
710 1
        $omitHeader = ($omitHeader === true) ? 'true' : $omitHeader;
711
        $this->setQueryParameterWhenStringOrUnsetWhenEmpty('omitHeader', $omitHeader);
712
    }
713
714
    /**
715
     * Get the query keywords, keywords are escaped.
716
     *
717
     * @return string query keywords
718
     */
719 1
    public function getKeywords()
720
    {
721 1
        return $this->keywords;
722 1
    }
723 1
724
    /**
725
     * Sets the query keywords, escapes them as needed for Solr/Lucene.
726
     *
727
     * @param string $keywords user search terms/keywords
728
     */
729
    public function setKeywords($keywords)
730
    {
731
        $this->keywords = $this->escapeService->escape($keywords);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->escapeService->escape($keywords) can also be of type integer or double. However, the property $keywords is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
732 1
        $this->keywordsRaw = $keywords;
733
    }
734 1
735 1
    /**
736
     * Gets the cleaned keywords so that it can be used in templates f.e.
737
     *
738
     * @return string The cleaned keywords.
739
     */
740
    public function getKeywordsCleaned()
741
    {
742
        return $this->cleanKeywords($this->keywordsRaw);
743
    }
744 128
745
    /**
746 128
     * Helper method to escape/encode keywords for use in HTML
747 128
     *
748 128
     * @param string $keywords Keywords to prepare for use in HTML
749
     * @return string Encoded keywords
750
     */
751
    public static function cleanKeywords($keywords)
752
    {
753
        $keywords = trim($keywords);
754
        $keywords = htmlspecialchars($keywords);
755 4
        return $keywords;
756
    }
757 4
758 3
    // relevance, matching
759 3
760
    /**
761 1
     * Gets the raw, unescaped, unencoded keywords.
762
     *
763 4
     * USE WITH CAUTION!
764
     *
765
     * @return string raw keywords
766
     */
767
    public function getKeywordsRaw()
768 79
    {
769
        return $this->keywordsRaw;
770 79
    }
771
772
    /**
773
     * Sets the minimum match (mm) parameter
774
     *
775
     * @param mixed $minimumMatch Minimum match parameter as string or boolean FALSE to disable / reset the mm parameter
776
     * @see http://wiki.apache.org/solr/DisMaxRequestHandler#mm_.28Minimum_.27Should.27_Match.29
777
     */
778
    public function setMinimumMatch($minimumMatch)
779 1
    {
780
        $this->setQueryParameterWhenStringOrUnsetWhenEmpty('mm', $minimumMatch);
781 1
    }
782 1
783 1
    /**
784 1
     * Sets the boost function (bf) parameter
785
     *
786
     * @param mixed $boostFunction boost function parameter as string or boolean FALSE to disable / reset the bf parameter
787
     * @see http://wiki.apache.org/solr/DisMaxRequestHandler#bf_.28Boost_Functions.29
788
     */
789
    public function setBoostFunction($boostFunction)
790
    {
791
        $this->setQueryParameterWhenStringOrUnsetWhenEmpty('bf', $boostFunction);
792 1
    }
793
794 1
    // query fields
795 1
    // TODO move up to field list methods
796 1
797 1
    /**
798
     * Sets the boost query (bq) parameter
799
     *
800
     * @param mixed $boostQuery boost query parameter as string or array to set a boost query or boolean FALSE to disable / reset the bq parameter
801
     * @see http://wiki.apache.org/solr/DisMaxQParserPlugin#bq_.28Boost_Query.29
802
     */
803
    public function setBoostQuery($boostQuery)
804
    {
805
        if (is_array($boostQuery)) {
806 1
            $this->queryParameters['bq'] = $boostQuery;
807
            return;
808 1
        }
809 1
        $this->setQueryParameterWhenStringOrUnsetWhenEmpty('bq', $boostQuery);
810 1
    }
811
812
    /**
813
     * Gets a specific query parameter by its name.
814
     *
815
     * @param string $parameterName The parameter to return
816
     * @param mixed $defaultIfEmpty
817
     * @return mixed The parameter's value or $defaultIfEmpty if not set
818 1
     */
819
    public function getQueryParameter($parameterName, $defaultIfEmpty = null)
820 1
    {
821 1
        $parameters = $this->getQueryParameters();
822 1
        return isset($parameters[$parameterName]) ? $parameters[$parameterName] : $defaultIfEmpty;
823
    }
824
825
    /**
826
     * Builds an array of query parameters to use for the search query.
827
     *
828
     * @return array An array ready to use with query parameters
829
     */
830
    public function getQueryParameters()
831
    {
832
        $queryParameters = $this->getReturnFields()->build();
833
        $queryParameters = array_merge($queryParameters, $this->getFilters()->build());
834
        $queryParameters = array_merge($queryParameters, $this->queryParameters);
835
        $queryParameters = array_merge($queryParameters, $this->getQueryFields()->build());
836
        $queryParameters = array_merge($queryParameters, $this->getHighlighting()->build());
837
        $queryParameters = array_merge($queryParameters, $this->getFaceting()->build());
838
        $queryParameters = array_merge($queryParameters, $this->getGrouping()->build());
839
840
        return $queryParameters;
841
    }
842
843 95
    // general query parameters
844
845 95
    /**
846
     * Enables or disables highlighting of search terms in result teasers.
847
     *
848
     * @param Highlighting $highlighting
849
     * @see http://wiki.apache.org/solr/HighlightingParameters
850
     * @return void
851
     */
852
    public function setHighlighting(Highlighting $highlighting)
853 129
    {
854
        $this->highlighting = $highlighting;
855 129
    }
856 129
857
    /**
858
     * @return Highlighting
859
     */
860
    public function getHighlighting()
861
    {
862
        return $this->highlighting;
863
    }
864
865 37
    // misc
866
867 37
    /**
868 37
     * Enables or disables spellchecking for the query.
869 37
     *
870 37
     * @param bool $spellchecking Enables spellchecking when set to TRUE, deactivates spellchecking when set to FALSE, defaults to TRUE.
871
     */
872 37
    public function setSpellchecking($spellchecking = true)
873 37
    {
874 37
        if ($spellchecking) {
875 37
            $this->queryParameters['spellcheck'] = 'true';
876
            $this->queryParameters['spellcheck.collate'] = 'true';
877
            $maxCollationTries = $this->solrConfiguration->getSearchSpellcheckingNumberOfSuggestionsToTry();
878
            $this->addQueryParameter('spellcheck.maxCollationTries', $maxCollationTries);
879
        } else {
880
            unset($this->queryParameters['spellcheck']);
881
            unset($this->queryParameters['spellcheck.collate']);
882
            unset($this->queryParameters['spellcheck.maxCollationTries']);
883
        }
884 1
    }
885
886 1
    /**
887
     * This method can be used to set a query parameter when the value is a string and not empty or unset it
888 1
     * in any other case. Extracted to avoid duplicate code.
889 1
     *
890
     * @param string $parameterName
891
     * @param mixed $value
892
     */
893
    private function setQueryParameterWhenStringOrUnsetWhenEmpty($parameterName, $value)
894
    {
895
        if (is_string($value) && !empty($value)) {
896
            $this->addQueryParameter($parameterName, $value);
897
        } else {
898
            unset($this->queryParameters[$parameterName]);
899 33
        }
900
    }
901 33
902 1
    /**
903
     * Adds any generic query parameter.
904
     *
905 32
     * @param string $parameterName Query parameter name
906 32
     * @param string $parameterValue Parameter value
907
     */
908 32
    public function addQueryParameter($parameterName, $parameterValue)
909 32
    {
910 32
        $this->queryParameters[$parameterName] = $parameterValue;
911
    }
912
913 32
    /**
914 32
     * Sets the sort parameter.
915
     *
916
     * $sorting must include a field name (or the pseudo-field score),
917
     * followed by a space,
918
     * followed by a sort direction (asc or desc).
919
     *
920
     * Multiple fallback sortings can be separated by comma,
921
     * ie: <field name> <direction>[,<field name> <direction>]...
922
     *
923
     * @param string|bool $sorting Either a comma-separated list of sort fields and directions or FALSE to reset sorting to the default behavior (sort by score / relevance)
924
     * @see http://wiki.apache.org/solr/CommonQueryParameters#sort
925
     */
926
    public function setSorting($sorting)
927
    {
928
        if ($sorting) {
929
            if (!is_string($sorting)) {
930
                throw new \InvalidArgumentException('Sorting needs to be a string!');
931
            }
932
            $sortParameter = $this->removeRelevanceSortField($sorting);
933
            $this->queryParameters['sort'] = $sortParameter;
934
        } else {
935
            unset($this->queryParameters['sort']);
936
        }
937
    }
938
939
    /**
940
     * Removes the relevance sort field if present in the sorting field definition.
941
     *
942
     * @param string $sorting
943
     * @return string
944 2
     */
945
    protected function removeRelevanceSortField($sorting)
946 2
    {
947 2
        $sortParameter = $sorting;
948
        list($sortField) = explode(' ', $sorting);
949
        if ($sortField === 'relevance') {
950
            $sortParameter = '';
951
            return $sortParameter;
952
        }
953
954
        return $sortParameter;
955
    }
956
957 1
    /**
958
     * Enables or disables the debug parameter for the query.
959 1
     *
960 1
     * @param bool $debugMode Enables debugging when set to TRUE, deactivates debugging when set to FALSE, defaults to TRUE.
961
     */
962
    public function setDebugMode($debugMode = true)
963
    {
964
        if ($debugMode) {
965
            $this->queryParameters['debugQuery'] = 'true';
966 1
            $this->queryParameters['echoParams'] = 'all';
967 1
        } else {
968 1
            unset($this->queryParameters['debugQuery']);
969
            unset($this->queryParameters['echoParams']);
970
        }
971 1
    }
972 1
973 1
    /**
974
     * Returns the link target page id.
975
     *
976 1
     * @return int
977
     */
978
    public function getLinkTargetPageId()
979
    {
980
        return $this->linkTargetPageId;
981
    }
982 128
983
    /**
984 128
     * Activates the collapsing on the configured field, if collapsing was enabled.
985 128
     *
986
     * @return bool
987
     */
988
    protected function initializeCollapsingFromConfiguration()
989
    {
990 85
        // check collapsing
991
        if ($this->solrConfiguration->getSearchVariants()) {
992 85
            $collapseField = $this->solrConfiguration->getSearchVariantsField();
993
            $this->setVariantField($collapseField);
994
            $this->setCollapsing(true);
995
996
            return true;
997
        }
998
999
        return false;
1000 1
    }
1001
1002 1
    /**
1003
     * @return void
1004
     */
1005
    protected function initializeFaceting()
1006
    {
1007
        $faceting = Faceting::fromTypoScriptConfiguration($this->solrConfiguration);
1008
        $this->setFaceting($faceting);
1009
    }
1010
1011 2
    /**
1012
     * @return void
1013 2
     */
1014 2
    protected function initializeGrouping()
1015
    {
1016
        $grouping = Grouping::fromTypoScriptConfiguration($this->solrConfiguration);
1017
        $this->setGrouping($grouping);
1018
    }
1019
1020
    /**
1021
     * @return void
1022 1
     */
1023
    protected function initializeFilters()
1024 1
    {
1025 1
        $filters = Filters::fromTypoScriptConfiguration($this->solrConfiguration);
1026
        $this->setFilters($filters);
1027
    }
1028
}
1029