Completed
Push — master ( 9819c1...f2d6e9 )
by Rafael
06:19
created

Query::setTieParameter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
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 121
    public function __construct($keywords, $solrConfiguration = null, SiteHashService $siteHashService = null, EscapeService $escapeService = null, SolrLogManager $solrLogManager = null)
200
    {
201 121
        $keywords = (string)$keywords;
202
203 121
        $this->logger = is_null($solrLogManager) ? GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__) : $solrLogManager;
204 121
        $this->solrConfiguration = is_null($solrConfiguration) ? Util::getSolrConfiguration() : $solrConfiguration;
205 121
        $this->siteHashService = is_null($siteHashService) ? GeneralUtility::makeInstance(SiteHashService::class) : $siteHashService;
206 121
        $this->escapeService = is_null($escapeService) ? GeneralUtility::makeInstance(EscapeService::class) : $escapeService;
207 121
        $this->setKeywords($keywords);
208 121
        $this->sorting = '';
209
210 121
        $this->linkTargetPageId = $this->solrConfiguration->getSearchTargetPage();
211
212 121
        $this->initializeQuery();
213
214 121
        $this->id = ++self::$idCount;
215 121
    }
216
217
    /**
218
     * @return void
219
     */
220 120
    protected function initializeQuery()
221
    {
222
        // Filters
223 120
        $this->initializeFilters();
224
225
        // What fields to search
226 120
        $queryFields = QueryFields::fromString($this->solrConfiguration->getSearchQueryQueryFields());
227 120
        $this->setQueryFields($queryFields);
228
229
        // What fields to return from Solr
230 120
        $returnFieldsArray = $this->solrConfiguration->getSearchQueryReturnFieldsAsArray(['*', 'score']);
231 120
        $returnFields = ReturnFields::fromArray($returnFieldsArray);
232 120
        $this->setReturnFields($returnFields);
233
234
        // Configure highlighting
235 120
        $highlighting = Highlighting::fromTypoScriptConfiguration($this->solrConfiguration);
236 120
        $this->setHighlighting($highlighting);
237
238
        // Configure faceting
239 120
        $this->initializeFaceting();
240
241
        // Initialize grouping
242 120
        $this->initializeGrouping();
243
244
        // Configure collapsing
245 120
        $this->initializeCollapsingFromConfiguration();
246 120
    }
247
248
    /**
249
     * @param QueryFields $queryFields
250
     */
251 120
    public function setQueryFields(QueryFields $queryFields)
252
    {
253 120
        $this->queryFields = $queryFields;
254 120
    }
255
256
    /**
257
     * @return QueryFields
258
     */
259 83
    public function getQueryFields()
260
    {
261 83
        return $this->queryFields;
262
    }
263
264
    /**
265
     * magic implementation for clone(), makes sure that the id counter is
266
     * 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
     * @return string the string representation of the query
279
     */
280 1
    public function __toString()
281
    {
282 1
        return $this->getQueryString();
283
    }
284
285
    /**
286
     * Builds the query string which is then used for Solr's q parameters
287
     *
288
     * @return string Solr query string
289
     */
290 41
    public function getQueryString()
291
    {
292 41
        if (!$this->rawQueryString) {
293 38
            $this->buildQueryString();
294
        }
295
296 41
        return $this->queryString;
297
    }
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 4
    public function setQueryString($queryString)
308
    {
309 4
        $this->queryString = $queryString;
310 4
    }
311
312
    /**
313
     * Creates the string that is later used as the q parameter in the solr query
314
     *
315
     * @return void
316
     */
317 38
    protected function buildQueryString()
318
    {
319
        // very simple for now
320 38
        $this->queryString = $this->keywords;
321 38
    }
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
     *
327
     * @param bool $useRawQueryString TRUE to use raw queries (like Lucene Query Language) or FALSE for regular, escaped queries
328
     */
329 4
    public function useRawQueryString($useRawQueryString)
330
    {
331 4
        $this->rawQueryString = (boolean)$useRawQueryString;
332 4
    }
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
344
    /**
345
     * Gets the currently showing page's number
346
     *
347
     * @return int page number currently showing
348
     */
349 1
    public function getPage()
350
    {
351 1
        return $this->page;
352
    }
353
354
    /**
355
     * Sets the page that should be shown
356
     *
357
     * @param int $page page number to show
358
     * @return void
359
     */
360 2
    public function setPage($page)
361
    {
362 2
        $this->page = max(intval($page), 0);
363 2
    }
364
365
    /**
366
     * Gets the index of the first result document we're showing
367
     *
368
     * @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 34
    public function setQueryElevation($elevation = true, $forceElevation = true, $markElevatedResults = true)
396
    {
397 34
        if ($elevation) {
398 30
            $this->queryParameters['enableElevation'] = 'true';
399 30
            $this->setForceElevation($forceElevation);
400 30
            if ($markElevatedResults) {
401 30
                $this->getReturnFields()->add('isElevated:[elevated]');
402
            }
403
        } else {
404 5
            $this->queryParameters['enableElevation'] = 'false';
405 5
            unset($this->queryParameters['forceElevation']);
406 5
            $this->getReturnFields()->remove('isElevated:[elevated]');
407 5
            $this->getReturnFields()->remove('[elevated]'); // fallback
408
        }
409 34
    }
410
411
    /**
412
     * Enables or disables the forceElevation query parameter.
413
     *
414
     * @param bool $forceElevation
415
     */
416 30
    protected function setForceElevation($forceElevation)
417
    {
418 30
        if ($forceElevation) {
419 29
            $this->queryParameters['forceElevation'] = 'true';
420
        } else {
421 1
            $this->queryParameters['forceElevation'] = 'false';
422
        }
423 30
    }
424
425
    // collapsing
426
427
    /**
428
     * Check whether collapsing is active
429
     *
430
     * @return bool
431
     */
432 3
    public function getIsCollapsing()
433
    {
434 3
        return $this->getFilters()->hasWithName('collapsing');
435
    }
436
437
    /**
438
     * @param string $fieldName
439
     */
440 4
    public function setVariantField($fieldName)
441
    {
442 4
        $this->variantField = $fieldName;
443 4
    }
444
445
    /**
446
     * @return string
447
     */
448 1
    public function getVariantField()
449
    {
450 1
        return $this->variantField;
451
    }
452
453
    /**
454
     * @param bool $collapsing
455
     */
456 6
    public function setCollapsing($collapsing = true)
457
    {
458 6
        if ($collapsing) {
459 6
            $this->getFilters()->add('{!collapse field=' . $this->variantField . '}', 'collapsing');
460 6
            if ($this->solrConfiguration->getSearchVariantsExpand()) {
461 2
                $this->queryParameters['expand'] = 'true';
462 6
                $this->queryParameters['expand.rows'] = $this->solrConfiguration->getSearchVariantsLimit();
463
            }
464
        } else {
465 1
            $this->getFilters()->removeByName('collapsing');
466 1
            unset($this->queryParameters['expand']);
467 1
            unset($this->queryParameters['expand.rows']);
468
        }
469 6
    }
470
471
    // grouping
472
473
    /**
474
     * 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 120
    public function setGrouping(Grouping $grouping)
480
    {
481 120
        $this->grouping = $grouping;
482 120
    }
483
484
    /**
485
     * @return Grouping
486
     */
487 86
    public function getGrouping()
488
    {
489 86
        return $this->grouping;
490
    }
491
492
    /**
493
     * Returns the number of results that should be shown per page
494
     *
495
     * @return int number of results to show per page
496
     */
497 33
    public function getResultsPerPage()
498
    {
499 33
        if ($this->getGrouping() instanceof Grouping && $this->getGrouping()->getIsEnabled()) {
500 1
            return $this->getGrouping()->getNumberOfGroups();
501
        }
502
503 33
        return $this->resultsPerPage;
504
    }
505
506
    /**
507
     * Sets the number of results that should be shown per page
508
     *
509
     * @param int $resultsPerPage Number of results to show per page
510
     * @return void
511
     */
512 39
    public function setResultsPerPage($resultsPerPage)
513
    {
514 39
        $this->resultsPerPage = max(intval($resultsPerPage), 0);
515 39
    }
516
517
    // faceting
518
519
    /**
520
     * 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 120
    public function setFaceting(Faceting $faceting)
526
    {
527 120
        $this->faceting = $faceting;
528 120
    }
529
530
    /**
531
     * @return Faceting
532
     */
533 80
    public function getFaceting()
534
    {
535 80
        return $this->faceting;
536
    }
537
538
539
    /**
540
     * Gets all currently applied filters.
541
     *
542
     * @return Filters Array of filters
543
     */
544 95
    public function getFilters()
545
    {
546 95
        return $this->filters;
547
    }
548
549
    /**
550
     * Sets the filters to use.
551
     *
552
     * @param Filters $filters
553
     */
554 121
    public function setFilters(Filters $filters)
555
    {
556 121
        $this->filters = $filters;
557 121
    }
558
559
    // sorting
560
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 38
    public function setUserAccessGroups(array $groups)
567
    {
568 38
        $groups = array_map('intval', $groups);
569 38
        $groups[] = 0; // always grant access to public documents
570 38
        $groups = array_unique($groups);
571 38
        sort($groups, SORT_NUMERIC);
572
573 38
        $accessFilter = '{!typo3access}' . implode(',', $groups);
574 38
        $this->getFilters()->removeByPrefix('{!typo3access}');
575 38
        $this->getFilters()->add($accessFilter);
576 38
    }
577
578
    // query parameters
579
580
    /**
581
     * Limits the query to certain sites
582
     *
583
     * @param string $allowedSites Comma-separated list of domains
584
     */
585 34
    public function setSiteHashFilter($allowedSites)
586
    {
587 34
        if (trim($allowedSites) === '*') {
588 1
            return;
589
        }
590
591 33
        $allowedSites = GeneralUtility::trimExplode(',', $allowedSites);
592 33
        $filters = [];
593
594 33
        foreach ($allowedSites as $site) {
595 33
            $siteHash = $this->siteHashService->getSiteHashForDomain($site);
596 33
            $filters[] = 'siteHash:"' . $siteHash . '"';
597
        }
598
599 33
        $this->getFilters()->add(implode(' OR ', $filters));
600 33
    }
601
602
    /**
603
     * Limits the query to certain page tree branches
604
     *
605
     * @param string $pageIds Comma-separated list of page IDs
606
     */
607
    public function setRootlineFilter($pageIds)
608
    {
609
        $pageIds = GeneralUtility::trimExplode(',', $pageIds);
610
        $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
            $filters[] = 'rootline:"' . $lastLevel . '"';
619
        }
620
621
        $this->getFilters()->add(implode(' OR ', $filters));
622
    }
623
624
    /**
625
     * @param ReturnFields $returnFields
626
     */
627 120
    public function setReturnFields(ReturnFields $returnFields)
628
    {
629 120
        $this->returnFields = $returnFields;
630 120
    }
631
632
    /**
633
     * @return ReturnFields
634
     */
635 85
    public function getReturnFields()
636
    {
637 85
        return $this->returnFields;
638
    }
639
640
    /**
641
     * Gets the query type, Solr's qt parameter.
642
     *
643
     * @return string Query type, qt parameter.
644
     */
645 1
    public function getQueryType()
646
    {
647 1
        return $this->queryParameters['qt'];
648
    }
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 2
    public function setQueryType($queryType)
657
    {
658 2
        $this->setQueryParameterWhenStringOrUnsetWhenEmpty('qt', $queryType);
659 2
    }
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 1
    public function setOperator($operator)
668
    {
669 1
        if (in_array($operator, [self::OPERATOR_AND, self::OPERATOR_OR])) {
670 1
            $this->queryParameters['q.op'] = $operator;
671
        }
672
673 1
        if ($operator === false) {
674 1
            unset($this->queryParameters['q.op']);
675
        }
676 1
    }
677
678
    /**
679
     * Gets the alternative query, Solr's q.alt parameter.
680
     *
681
     * @return string Alternative query, q.alt parameter.
682
     */
683 1
    public function getAlternativeQuery()
684
    {
685 1
        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
     */
696 34
    public function setAlternativeQuery($alternativeQuery)
697
    {
698 34
        $this->setQueryParameterWhenStringOrUnsetWhenEmpty('q.alt', $alternativeQuery);
699 34
    }
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
     */
708 1
    public function setOmitHeader($omitHeader = true)
709
    {
710 1
        $omitHeader = ($omitHeader === true) ? 'true' : $omitHeader;
711 1
        $this->setQueryParameterWhenStringOrUnsetWhenEmpty('omitHeader', $omitHeader);
712 1
    }
713
714
    /**
715
     * Get the query keywords, keywords are escaped.
716
     *
717
     * @return string query keywords
718
     */
719 33
    public function getKeywords()
720
    {
721 33
        return $this->keywords;
722
    }
723
724
    /**
725
     * Sets the query keywords, escapes them as needed for Solr/Lucene.
726
     *
727
     * @param string $keywords user search terms/keywords
728
     */
729 121
    public function setKeywords($keywords)
730
    {
731 121
        $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 121
        $this->keywordsRaw = $keywords;
733 121
    }
734
735
    /**
736
     * Gets the cleaned keywords so that it can be used in templates f.e.
737
     *
738
     * @return string The cleaned keywords.
739
     */
740 26
    public function getKeywordsCleaned()
741
    {
742 26
        return $this->cleanKeywords($this->keywordsRaw);
743
    }
744
745
    /**
746
     * Helper method to escape/encode keywords for use in HTML
747
     *
748
     * @param string $keywords Keywords to prepare for use in HTML
749
     * @return string Encoded keywords
750
     */
751 26
    public static function cleanKeywords($keywords)
752
    {
753 26
        $keywords = trim($keywords);
754 26
        $keywords = htmlspecialchars($keywords);
755 26
        return $keywords;
756
    }
757
758
    // relevance, matching
759
760
    /**
761
     * Gets the raw, unescaped, unencoded keywords.
762
     *
763
     * USE WITH CAUTION!
764
     *
765
     * @return string raw keywords
766
     */
767
    public function getKeywordsRaw()
768
    {
769
        return $this->keywordsRaw;
770
    }
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 1
    public function setMinimumMatch($minimumMatch)
779
    {
780 1
        $this->setQueryParameterWhenStringOrUnsetWhenEmpty('mm', $minimumMatch);
781 1
    }
782
783
    /**
784
     * 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 1
    public function setBoostFunction($boostFunction)
790
    {
791 1
        $this->setQueryParameterWhenStringOrUnsetWhenEmpty('bf', $boostFunction);
792 1
    }
793
794
    // query fields
795
    // TODO move up to field list methods
796
797
    /**
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 1
    public function setBoostQuery($boostQuery)
804
    {
805 1
        if (is_array($boostQuery)) {
806
            $this->queryParameters['bq'] = $boostQuery;
807
            return;
808
        }
809 1
        $this->setQueryParameterWhenStringOrUnsetWhenEmpty('bq', $boostQuery);
810 1
    }
811
812
    /**
813
     * Set the tie breaker (tie) parameter
814
     *
815
     * @param mixed $tieParameter tie breaker parameter as string or boolean FALSE to disable / reset the tie parameter
816
     * @return void
817
     */
818
    public function setTieParameter($tieParameter)
819 7
    {
820
        $this->setQueryParameterWhenStringOrUnsetWhenEmpty('tie', $tieParameter);
821 7
    }
822 7
823
    /**
824
     * Gets a specific query parameter by its name.
825
     *
826
     * @param string $parameterName The parameter to return
827
     * @param mixed $defaultIfEmpty
828
     * @return mixed The parameter's value or $defaultIfEmpty if not set
829
     */
830 80
    public function getQueryParameter($parameterName, $defaultIfEmpty = null)
831
    {
832 80
        $parameters = $this->getQueryParameters();
833 80
        return isset($parameters[$parameterName]) ? $parameters[$parameterName] : $defaultIfEmpty;
834 80
    }
835 80
836 80
    /**
837 80
     * Builds an array of query parameters to use for the search query.
838 80
     *
839
     * @return array An array ready to use with query parameters
840 80
     */
841
    public function getQueryParameters()
842
    {
843
        $queryParameters = $this->getReturnFields()->build();
844
        $queryParameters = array_merge($queryParameters, $this->getFilters()->build());
845
        $queryParameters = array_merge($queryParameters, $this->queryParameters);
846
        $queryParameters = array_merge($queryParameters, $this->getQueryFields()->build());
847
        $queryParameters = array_merge($queryParameters, $this->getHighlighting()->build());
848
        $queryParameters = array_merge($queryParameters, $this->getFaceting()->build());
849
        $queryParameters = array_merge($queryParameters, $this->getGrouping()->build());
850
851
        return $queryParameters;
852 120
    }
853
854 120
    // general query parameters
855 120
856
    /**
857
     * Enables or disables highlighting of search terms in result teasers.
858
     *
859
     * @param Highlighting $highlighting
860 80
     * @see http://wiki.apache.org/solr/HighlightingParameters
861
     * @return void
862 80
     */
863
    public function setHighlighting(Highlighting $highlighting)
864
    {
865
        $this->highlighting = $highlighting;
866
    }
867
868
    /**
869
     * @return Highlighting
870
     */
871
    public function getHighlighting()
872 30
    {
873
        return $this->highlighting;
874 30
    }
875 30
876 30
    // misc
877 30
878 30
    /**
879
     * Enables or disables spellchecking for the query.
880 1
     *
881 1
     * @param bool $spellchecking Enables spellchecking when set to TRUE, deactivates spellchecking when set to FALSE, defaults to TRUE.
882 1
     */
883
    public function setSpellchecking($spellchecking = true)
884 30
    {
885
        if ($spellchecking) {
886
            $this->queryParameters['spellcheck'] = 'true';
887
            $this->queryParameters['spellcheck.collate'] = 'true';
888
            $maxCollationTries = $this->solrConfiguration->getSearchSpellcheckingNumberOfSuggestionsToTry();
889
            $this->addQueryParameter('spellcheck.maxCollationTries', $maxCollationTries);
890
        } else {
891
            unset($this->queryParameters['spellcheck']);
892
            unset($this->queryParameters['spellcheck.collate']);
893 40
            unset($this->queryParameters['spellcheck.maxCollationTries']);
894
        }
895 40
    }
896 40
897
    /**
898 6
     * This method can be used to set a query parameter when the value is a string and not empty or unset it
899
     * in any other case. Extracted to avoid duplicate code.
900 40
     *
901
     * @param string $parameterName
902
     * @param mixed $value
903
     */
904
    private function setQueryParameterWhenStringOrUnsetWhenEmpty($parameterName, $value)
905
    {
906
        if (is_string($value) && !empty($value)) {
907
            $this->addQueryParameter($parameterName, $value);
908 42
        } else {
909
            unset($this->queryParameters[$parameterName]);
910 42
        }
911 42
    }
912
913
    /**
914
     * Adds any generic query parameter.
915
     *
916
     * @param string $parameterName Query parameter name
917
     * @param string $parameterValue Parameter value
918
     */
919
    public function addQueryParameter($parameterName, $parameterValue)
920
    {
921
        $this->queryParameters[$parameterName] = $parameterValue;
922
    }
923
924
    /**
925
     * Sets the sort parameter.
926 2
     *
927
     * $sorting must include a field name (or the pseudo-field score),
928 2
     * followed by a space,
929 2
     * followed by a sort direction (asc or desc).
930
     *
931
     * Multiple fallback sortings can be separated by comma,
932 2
     * ie: <field name> <direction>[,<field name> <direction>]...
933 2
     *
934
     * @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)
935 1
     * @see http://wiki.apache.org/solr/CommonQueryParameters#sort
936
     */
937 2
    public function setSorting($sorting)
938
    {
939
        if ($sorting) {
940
            if (!is_string($sorting)) {
941
                throw new \InvalidArgumentException('Sorting needs to be a string!');
942
            }
943
            $sortParameter = $this->removeRelevanceSortField($sorting);
944
            $this->queryParameters['sort'] = $sortParameter;
945 2
        } else {
946
            unset($this->queryParameters['sort']);
947 2
        }
948 2
    }
949 2
950 1
    /**
951 1
     * Removes the relevance sort field if present in the sorting field definition.
952
     *
953
     * @param string $sorting
954 2
     * @return string
955
     */
956
    protected function removeRelevanceSortField($sorting)
957
    {
958
        $sortParameter = $sorting;
959
        list($sortField) = explode(' ', $sorting);
960
        if ($sortField === 'relevance') {
961
            $sortParameter = '';
962 29
            return $sortParameter;
963
        }
964 29
965 29
        return $sortParameter;
966 29
    }
967
968 1
    /**
969 1
     * Enables or disables the debug parameter for the query.
970
     *
971 29
     * @param bool $debugMode Enables debugging when set to TRUE, deactivates debugging when set to FALSE, defaults to TRUE.
972
     */
973
    public function setDebugMode($debugMode = true)
974
    {
975
        if ($debugMode) {
976
            $this->queryParameters['debugQuery'] = 'true';
977
            $this->queryParameters['echoParams'] = 'all';
978 2
        } else {
979
            unset($this->queryParameters['debugQuery']);
980 2
            unset($this->queryParameters['echoParams']);
981
        }
982
    }
983
984
    /**
985
     * Returns the link target page id.
986
     *
987
     * @return int
988 120
     */
989
    public function getLinkTargetPageId()
990
    {
991 120
        return $this->linkTargetPageId;
992 4
    }
993 4
994 4
    /**
995
     * Activates the collapsing on the configured field, if collapsing was enabled.
996 4
     *
997
     * @return bool
998
     */
999 116
    protected function initializeCollapsingFromConfiguration()
1000
    {
1001
        // check collapsing
1002
        if ($this->solrConfiguration->getSearchVariants()) {
1003
            $collapseField = $this->solrConfiguration->getSearchVariantsField();
1004
            $this->setVariantField($collapseField);
1005 120
            $this->setCollapsing(true);
1006
1007 120
            return true;
1008 120
        }
1009 120
1010
        return false;
1011
    }
1012
1013
    /**
1014 120
     * @return void
1015
     */
1016 120
    protected function initializeFaceting()
1017 120
    {
1018 120
        $faceting = Faceting::fromTypoScriptConfiguration($this->solrConfiguration);
1019
        $this->setFaceting($faceting);
1020
    }
1021
1022
    /**
1023 121
     * @return void
1024
     */
1025 121
    protected function initializeGrouping()
1026 121
    {
1027 121
        $grouping = Grouping::fromTypoScriptConfiguration($this->solrConfiguration);
1028
        $this->setGrouping($grouping);
1029
    }
1030
1031
    /**
1032
     * @return void
1033
     */
1034
    protected function initializeFilters()
1035
    {
1036
        $filters = Filters::fromTypoScriptConfiguration($this->solrConfiguration);
1037
        $this->setFilters($filters);
1038
    }
1039
}
1040