Completed
Push — master ( fc247d...cdf1fb )
by Timo
14:38
created

tokenizeByQuotesAndEscapeDependingOnContext()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 29
ccs 16
cts 16
cp 1
rs 8.439
c 0
b 0
f 0
cc 6
eloc 17
nc 5
nop 1
crap 6
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\Site\SiteHashService;
28
use ApacheSolrForTypo3\Solr\FieldProcessor\PageUidToHierarchy;
29
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
30
use TYPO3\CMS\Core\Utility\GeneralUtility;
31
32
/**
33
 * A Solr search query
34
 *
35
 * @author Ingo Renner <[email protected]>
36
 */
37
class Query
38
{
39
40
    // FIXME extract link building from the query, it's not the query's domain
41
42
    const SORT_ASC = 'ASC';
43
    const SORT_DESC = 'DESC';
44
45
    const OPERATOR_AND = 'AND';
46
    const OPERATOR_OR = 'OR';
47
48
    /**
49
     * Used to identify the queries.
50
     *
51
     * @var int
52
     */
53
    protected static $idCount = 0;
54
55
    /**
56
     * @var int
57
     */
58
    protected $id;
59
60
    /**
61
     * @var TypoScriptConfiguration
62
     */
63
    protected $solrConfiguration;
64
65
    /**
66
     * @var string
67
     */
68
    protected $keywords;
69
70
    /**
71
     * @var string
72
     */
73
    protected $keywordsRaw;
74
75
    /**
76
     * @var array
77
     */
78
    protected $filters = [];
79
80
    /**
81
     * @var string
82
     */
83
    protected $sorting;
84
85
    // TODO check usage of these two variants, especially the check for $rawQueryString in getQueryString()
86
    /**
87
     * @var
88
     */
89
    protected $queryString;
90
91
    /**
92
     * @var array
93
     */
94
    protected $queryParameters = [];
95
96
    /**
97
     * @var int
98
     */
99
    protected $resultsPerPage;
100
101
    /**
102
     * @var int
103
     */
104
    protected $page;
105
106
    /**
107
     * @var int
108
     */
109
    protected $linkTargetPageId;
110
111
    /**
112
     * Holds the query fields with their associated boosts. The key represents
113
     * the field name, value represents the field's boost. These are the fields
114
     * that will actually be searched.
115
     *
116
     * Used in Solr's qf parameter
117
     *
118
     * @var array
119
     * @see http://wiki.apache.org/solr/DisMaxQParserPlugin#qf_.28Query_Fields.29
120
     */
121
    protected $queryFields = [];
122
123
    /**
124
     * List of fields that will be returned in the result documents.
125
     *
126
     * used in Solr's fl parameter
127
     *
128
     * @var array
129
     * @see http://wiki.apache.org/solr/CommonQueryParameters#fl
130
     */
131
    protected $fieldList = [];
132
133
    /**
134
     * @var array
135
     */
136
    protected $filterFields;
137
138
    /**
139
     * @var array
140
     * @deprecated since 6.1 will be removed in 7.0
141
     */
142
    protected $sortingFields = [];
143
144
    /**
145
     * @var bool
146
     */
147
    private $rawQueryString = false;
148
149
    /**
150
     * The field by which the result will be collapsed
151
     * @var string
152
     */
153
    protected $variantField = 'variantId';
154
155
    /**
156
     * @var SiteHashService
157
     */
158
    protected $siteHashService = null;
159
160
    /**
161
     * Query constructor.
162
     * @param string $keywords
163
     * @param TypoScriptConfiguration $solrConfiguration
164
     * @param SiteHashService|null $siteHashService
165
     */
166 131
    public function __construct($keywords, $solrConfiguration = null, SiteHashService $siteHashService = null)
167
    {
168 131
        $keywords = (string) $keywords;
169
170 131
        $this->solrConfiguration = is_null($solrConfiguration) ? Util::getSolrConfiguration() : $solrConfiguration;
171 131
        $this->siteHashService = is_null($siteHashService) ? GeneralUtility::makeInstance(SiteHashService::class) : $siteHashService;
172
173 131
        $this->setKeywords($keywords);
174 131
        $this->sorting = '';
175
176
        // What fields to search
177 131
        $queryFields = $this->solrConfiguration->getSearchQueryQueryFields();
178 131
        if ($queryFields != '') {
179 26
            $this->setQueryFieldsFromString($queryFields);
180
        }
181
182
        // What fields to return from Solr
183 131
        $this->fieldList = $this->solrConfiguration->getSearchQueryReturnFieldsAsArray(['*', 'score']);
184 131
        $this->linkTargetPageId = $this->solrConfiguration->getSearchTargetPage();
185
186 131
        $this->initializeQuery();
187
188 131
        $this->id = ++self::$idCount;
189 131
    }
190
191
    /**
192
     * @return void
193
     */
194 130
    protected function initializeQuery()
195
    {
196 130
        $this->initializeCollapsingFromConfiguration();
197 130
    }
198
199
    /**
200
     * Writes a message to the devLog.
201
     *
202
     * @param string $msg
203
     * @param int $severity
204
     * @param mixed $dataVar
205
     */
206
    protected function writeDevLog($msg, $severity = 0, $dataVar = false)
207
    {
208
        GeneralUtility::devLog($msg, 'solr', $severity, $dataVar);
209
    }
210
211
    /**
212
     * Takes a string of comma separated query fields and _overwrites_ the
213
     * currently set query fields. Boost can also be specified in through the
214
     * given string.
215
     *
216
     * Example: "title^5, subtitle^2, content, author^0.5"
217
     * This sets the query fields to title with  a boost of 5.0, subtitle with
218
     * a boost of 2.0, content with a default boost of 1.0 and the author field
219
     * with a boost of 0.5
220
     *
221
     * @param string $queryFields A string defining which fields to query and their associated boosts
222
     * @return void
223
     */
224 27
    public function setQueryFieldsFromString($queryFields)
225
    {
226 27
        $fields = GeneralUtility::trimExplode(',', $queryFields, true);
227
228 27
        foreach ($fields as $field) {
229 27
            $fieldNameAndBoost = explode('^', $field);
230
231 27
            $boost = 1.0;
232 27
            if (isset($fieldNameAndBoost[1])) {
233 27
                $boost = floatval($fieldNameAndBoost[1]);
234
            }
235
236 27
            $this->setQueryField($fieldNameAndBoost[0], $boost);
237
        }
238 27
    }
239
240
    /**
241
     * Sets a query field and its boost. If the field does not exist yet, it
242
     * gets added. Boost is optional, if left out a default boost of 1.0 is
243
     * applied.
244
     *
245
     * @param string $fieldName The field's name
246
     * @param float $boost Optional field boost, defaults to 1.0
247
     * @return void
248
     */
249 27
    public function setQueryField($fieldName, $boost = 1.0)
250
    {
251 27
        $this->queryFields[$fieldName] = (float)$boost;
252 27
    }
253
254
    /**
255
     * magic implementation for clone(), makes sure that the id counter is
256
     * incremented
257
     *
258
     * @return void
259
     */
260 5
    public function __clone()
261
    {
262 5
        $this->id = ++self::$idCount;
263 5
    }
264
265
    /**
266
     * returns a string representation of the query
267
     *
268
     * @return string the string representation of the query
269
     */
270 8
    public function __toString()
271
    {
272 8
        return $this->getQueryString();
273
    }
274
275
    /**
276
     * Builds the query string which is then used for Solr's q parameters
277
     *
278
     * @return string Solr query string
279
     */
280 37
    public function getQueryString()
281
    {
282 37
        if (!$this->rawQueryString) {
283 35
            $this->buildQueryString();
284
        }
285
286 37
        return $this->queryString;
287
    }
288
289
    /**
290
     * Sets the query string without any escaping.
291
     *
292
     * Be cautious with this function!
293
     * TODO remove this method as it basically just sets the q parameter / keywords
294
     *
295
     * @param string $queryString The raw query string.
296
     */
297 3
    public function setQueryString($queryString)
298
    {
299 3
        $this->queryString = $queryString;
300 3
    }
301
302
    /**
303
     * Creates the string that is later used as the q parameter in the solr query
304
     *
305
     * @return void
306
     */
307 35
    protected function buildQueryString()
308
    {
309
        // very simple for now
310 35
        $this->queryString = $this->keywords;
311 35
    }
312
313
    /**
314
     * Sets whether a raw query sting should be used, that is, whether the query
315
     * string should be escaped or not.
316
     *
317
     * @param bool $useRawQueryString TRUE to use raw queries (like Lucene Query Language) or FALSE for regular, escaped queries
318
     */
319 3
    public function useRawQueryString($useRawQueryString)
320
    {
321 3
        $this->rawQueryString = (boolean)$useRawQueryString;
322 3
    }
323
324
    /**
325
     * Returns the query's ID.
326
     *
327
     * @return int The query's ID.
328
     */
329
    public function getId()
330
    {
331
        return $this->id;
332
    }
333
334
    /**
335
     * Quote and escape search strings
336
     *
337
     * @param string $string String to escape
338
     * @return string The escaped/quoted string
339
     */
340 131
    public function escape($string)
341
    {
342
        // when we have a numeric string only, nothing needs to be done
343 131
        if (is_numeric($string)) {
344 1
            return $string;
345
        }
346
347
        // when no whitespaces are in the query we can also just escape the special characters
348 131
        if (preg_match('/\W/', $string) != 1) {
349 110
            return $this->escapeSpecialCharacters($string);
350
        }
351
352
        // when there are no quotes inside the query string we can also just escape the whole string
353 37
        $hasQuotes = strrpos($string, '"') !== false;
354 37
        if (!$hasQuotes) {
355 30
            return $this->escapeSpecialCharacters($string);
356
        }
357
358 7
        $result = $this->tokenizeByQuotesAndEscapeDependingOnContext($string);
359
360 7
        return $result;
361
    }
362
363
    /**
364
     * This method is used to escape the content in the query string surrounded by quotes
365
     * different then when it is not in a quoted context.
366
     *
367
     * @param string $string
368
     * @return string
369
     */
370 7
    protected function tokenizeByQuotesAndEscapeDependingOnContext($string)
371
    {
372 7
        $result = '';
373 7
        $quotesCount = substr_count($string, '"');
374 7
        $isEvenAmountOfQuotes = $quotesCount % 2 === 0;
375
376
        // go over all quote segments and apply escapePhrase inside a quoted
377
        // context and escapeSpecialCharacters outside the quoted context.
378 7
        $segments = explode('"', $string);
379 7
        $segmentsIndex = 0;
380 7
        foreach ($segments as $segment) {
381 7
            $isInQuote = $segmentsIndex % 2 !== 0;
382 7
            $isLastQuote = $segmentsIndex === $quotesCount;
383
384 7
            if ($isLastQuote && !$isEvenAmountOfQuotes) {
385 1
                $result .= '\"';
386
            }
387
388 7
            if ($isInQuote && !$isLastQuote) {
389 6
                $result .= $this->escapePhrase($segment);
390
            } else {
391 7
                $result .= $this->escapeSpecialCharacters($segment);
392
            }
393
394 7
            $segmentsIndex++;
395
        }
396
397 7
        return $result;
398
    }
399
400
    // pagination
401
402
    /**
403
     * Escapes a value meant to be contained in a phrase with characters with
404
     * special meanings in Lucene query syntax.
405
     *
406
     * @param string $value Unescaped - "dirty" - string
407
     * @return string Escaped - "clean" - string
408
     */
409 6
    protected function escapePhrase($value)
410
    {
411 6
        $pattern = '/("|\\\)/';
412 6
        $replace = '\\\$1';
413
414 6
        return '"' . preg_replace($pattern, $replace, $value) . '"';
415
    }
416
417
    /**
418
     * Escapes characters with special meanings in Lucene query syntax.
419
     *
420
     * @param string $value Unescaped - "dirty" - string
421
     * @return string Escaped - "clean" - string
422
     */
423 131
    protected function escapeSpecialCharacters($value)
424
    {
425
        // list taken from http://lucene.apache.org/core/4_4_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#package_description
426
        // which mentions: + - && || ! ( ) { } [ ] ^ " ~ * ? : \ /
427
        // of which we escape: ( ) { } [ ] ^ " ~ : \ /
428
        // and explicitly don't escape: + - && || ! * ?
429 131
        $pattern = '/(\\(|\\)|\\{|\\}|\\[|\\]|\\^|"|~|\:|\\\\|\\/)/';
430 131
        $replace = '\\\$1';
431
432 131
        return preg_replace($pattern, $replace, $value);
433
    }
434
435
    /**
436
     * Gets the currently showing page's number
437
     *
438
     * @return int page number currently showing
439
     */
440 1
    public function getPage()
441
    {
442 1
        return $this->page;
443
    }
444
445
    /**
446
     * Sets the page that should be shown
447
     *
448
     * @param int $page page number to show
449
     * @return void
450
     */
451 1
    public function setPage($page)
452
    {
453 1
        $this->page = max(intval($page), 0);
454 1
    }
455
456
    /**
457
     * Gets the index of the first result document we're showing
458
     *
459
     * @return int index of the currently first document showing
460
     */
461
    public function getStartIndex()
462
    {
463
        return ($this->page - 1) * $this->resultsPerPage;
464
    }
465
466
    /**
467
     * Gets the index of the last result document we're showing
468
     *
469
     * @return int index of the currently last document showing
470
     */
471
    public function getEndIndex()
472
    {
473
        return $this->page * $this->resultsPerPage;
474
    }
475
476
    // query elevation
477
478
    /**
479
     * Activates and deactivates query elevation for the current query.
480
     *
481
     * @param bool $elevation True to enable query elevation (default), FALSE to disable query elevation.
482
     * @param bool $forceElevation Optionally force elevation so that the elevated documents are always on top regardless of sorting, default to TRUE.
483
     * @param bool $markElevatedResults Mark elevated results
484
     * @return void
485
     */
486 26
    public function setQueryElevation(
487
        $elevation = true,
488
        $forceElevation = true,
489
        $markElevatedResults = true
490
    ) {
491 26
        if ($elevation) {
492 22
            $this->queryParameters['enableElevation'] = 'true';
493 22
            $this->setForceElevation($forceElevation);
494 22
            if ($markElevatedResults) {
495 22
                $this->addReturnField('isElevated:[elevated]');
496
            }
497
        } else {
498 5
            $this->queryParameters['enableElevation'] = 'false';
499 5
            unset($this->queryParameters['forceElevation']);
500 5
            $this->removeReturnField('isElevated:[elevated]');
501 5
            $this->removeReturnField('[elevated]'); // fallback
502
        }
503 26
    }
504
505
    /**
506
     * Enables or disables the forceElevation query parameter.
507
     *
508
     * @param bool $forceElevation
509
     */
510 22
    protected function setForceElevation($forceElevation)
511
    {
512 22
        if ($forceElevation) {
513 21
            $this->queryParameters['forceElevation'] = 'true';
514
        } else {
515 1
            $this->queryParameters['forceElevation'] = 'false';
516
        }
517 22
    }
518
519
    // collapsing
520
521
    /**
522
     * Check whether collapsing is active
523
     *
524
     * @return bool
525
     */
526 3
    public function getIsCollapsing()
527
    {
528 3
        return array_key_exists('collapsing', $this->filters);
529
    }
530
531
    /**
532
     * @param string $fieldName
533
     */
534 3
    public function setVariantField($fieldName)
535
    {
536 3
        $this->variantField = $fieldName;
537 3
    }
538
539
    /**
540
     * @return string
541
     */
542 1
    public function getVariantField()
543
    {
544 1
        return $this->variantField;
545
    }
546
547
    /**
548
     * @param bool $collapsing
549
     */
550 4
    public function setCollapsing($collapsing = true)
551
    {
552 4
        if ($collapsing) {
553 4
            $this->filters['collapsing'] = '{!collapse field=' . $this->variantField . '}';
554 4
            if ($this->solrConfiguration->getSearchVariantsExpand()) {
555 2
                $this->queryParameters['expand'] = 'true';
556 4
                $this->queryParameters['expand.rows'] = $this->solrConfiguration->getSearchVariantsLimit();
557
            }
558
        } else {
559 1
            unset($this->filters['collapsing']);
560 1
            unset($this->queryParameters['expand']);
561 1
            unset($this->queryParameters['expand.rows']);
562
        }
563 4
    }
564
565
    // grouping
566
567
    /**
568
     * Adds a field to the list of fields to return. Also checks whether * is
569
     * set for the fields, if so it's removed from the field list.
570
     *
571
     * @param string $fieldName Name of a field to return in the result documents
572
     */
573 23
    public function addReturnField($fieldName)
574
    {
575 23
        if (strpos($fieldName, '[') === false
576 23
            && strpos($fieldName, ']') === false
577 23
            && in_array('*', $this->fieldList)
578
        ) {
579 1
            $this->fieldList = array_diff($this->fieldList, ['*']);
580
        }
581
582 23
        $this->fieldList[] = $fieldName;
583 23
    }
584
585
    /**
586
     * Removes a field from the list of fields to return (fl parameter).
587
     *
588
     * @param string $fieldName Field to remove from the list of fields to return
589
     */
590 6
    public function removeReturnField($fieldName)
591
    {
592 6
        $key = array_search($fieldName, $this->fieldList);
593
594 6
        if ($key !== false) {
595 2
            unset($this->fieldList[$key]);
596
        }
597 6
    }
598
599
    /**
600
     * Activates and deactivates grouping for the current query.
601
     *
602
     * @param bool $grouping TRUE to enable grouping, FALSE to disable grouping
603
     * @return void
604
     */
605 2
    public function setGrouping($grouping = true)
606
    {
607 2
        if ($grouping) {
608 1
            $this->queryParameters['group'] = 'true';
609 1
            $this->queryParameters['group.format'] = 'grouped';
610 1
            $this->queryParameters['group.ngroups'] = 'true';
611
        } else {
612 1
            foreach ($this->queryParameters as $key => $value) {
613
                // remove all group.* settings
614 1
                if (GeneralUtility::isFirstPartOfStr($key, 'group')) {
615 1
                    unset($this->queryParameters[$key]);
616
                }
617
            }
618
        }
619 2
    }
620
621
    /**
622
     * Sets the number of groups to return per group field or group query
623
     *
624
     * Internally uses the rows parameter.
625
     *
626
     * @param int $numberOfGroups Number of groups per group.field or group.query
627
     */
628 1
    public function setNumberOfGroups($numberOfGroups)
629
    {
630 1
        $this->setResultsPerPage($numberOfGroups);
631 1
    }
632
633
    /**
634
     * Gets the number of groups to return per group field or group query
635
     *
636
     * Internally uses the rows parameter.
637
     *
638
     * @return int Number of groups per group.field or group.query
639
     */
640 1
    public function getNumberOfGroups()
641
    {
642 1
        return $this->getResultsPerPage();
643
    }
644
645
    /**
646
     * Returns the number of results that should be shown per page
647
     *
648
     * @return int number of results to show per page
649
     */
650 25
    public function getResultsPerPage()
651
    {
652 25
        return $this->resultsPerPage;
653
    }
654
655
    /**
656
     * Sets the number of results that should be shown per page
657
     *
658
     * @param int $resultsPerPage Number of results to show per page
659
     * @return void
660
     */
661 32
    public function setResultsPerPage($resultsPerPage)
662
    {
663 32
        $this->resultsPerPage = max(intval($resultsPerPage), 0);
664 32
    }
665
666
    /**
667
     * Adds a field that should be used for grouping.
668
     *
669
     * @param string $fieldName Name of a field for grouping
670
     */
671 1
    public function addGroupField($fieldName)
672
    {
673 1
        if (!isset($this->queryParameters['group.field'])) {
674 1
            $this->queryParameters['group.field'] = [];
675
        }
676
677 1
        $this->queryParameters['group.field'][] = $fieldName;
678 1
    }
679
680
    /**
681
     * Gets the fields set for grouping.
682
     *
683
     * @return array An array of fields set for grouping.
684
     */
685 1
    public function getGroupFields()
686
    {
687 1
        return (array) $this->getQueryParameter('group.field', []);
688
    }
689
690
    /**
691
     * Adds sorting configuration for grouping.
692
     *
693
     * @param string $sorting value of sorting configuration
694
     */
695 1
    public function addGroupSorting($sorting)
696
    {
697 1
        if (!isset($this->queryParameters['group.sort'])) {
698 1
            $this->queryParameters['group.sort'] = [];
699
        }
700 1
        $this->queryParameters['group.sort'][] = $sorting;
701 1
    }
702
703
    /**
704
     * Gets the sorting set for grouping.
705
     *
706
     * @return array An array of sorting configurations for grouping.
707
     */
708 1
    public function getGroupSortings()
709
    {
710 1
        return (array) $this->getQueryParameter('group.sort', []);
711
    }
712
713
    // faceting
714
715
    /**
716
     * Adds a query that should be used for grouping.
717
     *
718
     * @param string $query Lucene query for grouping
719
     */
720 1
    public function addGroupQuery($query)
721
    {
722 1
        if (!isset($this->queryParameters['group.query'])) {
723 1
            $this->queryParameters['group.query'] = [];
724
        }
725
726 1
        $this->queryParameters['group.query'][] = $query;
727 1
    }
728
729
    /**
730
     * Gets the queries set for grouping.
731
     *
732
     * @return array An array of queries set for grouping.
733
     */
734 1
    public function getGroupQueries()
735
    {
736 1
        return (array) $this->getQueryParameter('group.query', []);
737
    }
738
739
    /**
740
     * Sets the maximum number of results to be returned per group.
741
     *
742
     * @param int $numberOfResults Maximum number of results per group to return
743
     */
744 1
    public function setNumberOfResultsPerGroup($numberOfResults)
745
    {
746 1
        $numberOfResults = max(intval($numberOfResults), 0);
747
748 1
        $this->queryParameters['group.limit'] = $numberOfResults;
749 1
    }
750
751
    // filter
752
753
    /**
754
     * Gets the maximum number of results to be returned per group.
755
     *
756
     * @return int Maximum number of results per group to return
757
     */
758 1
    public function getNumberOfResultsPerGroup()
759
    {
760
        // default if nothing else set is 1, @see http://wiki.apache.org/solr/FieldCollapsing
761 1
        $numberOfResultsPerGroup = 1;
762
763 1
        if (!empty($this->queryParameters['group.limit'])) {
764 1
            $numberOfResultsPerGroup = $this->queryParameters['group.limit'];
765
        }
766
767 1
        return $numberOfResultsPerGroup;
768
    }
769
770
    /**
771
     * Activates and deactivates faceting for the current query.
772
     *
773
     * @param bool $faceting TRUE to enable faceting, FALSE to disable faceting
774
     * @return void
775
     */
776 31
    public function setFaceting($faceting = true)
777
    {
778 31
        if ($faceting) {
779 31
            $this->queryParameters['facet'] = 'true';
780 31
            $this->queryParameters['facet.mincount'] = $this->solrConfiguration->getSearchFacetingMinimumCount();
781 31
            $this->queryParameters['facet.limit'] = $this->solrConfiguration->getSearchFacetingFacetLimit();
782
783 31
            $this->applyConfiguredFacetSorting();
784
        } else {
785 1
            $this->removeFacetingParametersFromQuery();
786
        }
787 31
    }
788
789
    /**
790
     * Removes all facet.* or f.*.facet.* parameters from the query.
791
     *
792
     * @return void
793
     */
794 1
    protected function removeFacetingParametersFromQuery()
795
    {
796 1
        foreach ($this->queryParameters as $key => $value) {
797
            // remove all facet.* settings
798 1
            if (GeneralUtility::isFirstPartOfStr($key, 'facet')) {
799 1
                unset($this->queryParameters[$key]);
800
            }
801
802
            // remove all f.*.facet.* settings (overrides for individual fields)
803 1
            if (GeneralUtility::isFirstPartOfStr($key, 'f.') && strpos($key, '.facet.') !== false) {
804 1
                unset($this->queryParameters[$key]);
805
            }
806
        }
807 1
    }
808
809
    /**
810
     * Reads the facet sorting configuration and applies it to the queryParameters.
811
     *
812
     * @return void
813
     */
814 31
    protected function applyConfiguredFacetSorting()
815
    {
816 31
        $sorting = $this->solrConfiguration->getSearchFacetingSortBy();
817 31
        if (!GeneralUtility::inList('count,index,alpha,lex,1,0,true,false', $sorting)) {
818
            // when the sorting is not in the list of valid values we do not apply it.
819 10
            return;
820
        }
821
822
        // alpha and lex alias for index
823 21
        if ($sorting == 'alpha' || $sorting == 'lex') {
824 1
            $sorting = 'index';
825
        }
826
827 21
        $this->queryParameters['facet.sort'] = $sorting;
828 21
    }
829
830
    /**
831
     * Sets facet fields for a query.
832
     *
833
     * @param array $facetFields Array of field names
834
     */
835 1
    public function setFacetFields(array $facetFields)
836
    {
837 1
        $this->queryParameters['facet.field'] = [];
838
839 1
        foreach ($facetFields as $facetField) {
840 1
            $this->addFacetField($facetField);
841
        }
842 1
    }
843
844
    /**
845
     * Adds a single facet field.
846
     *
847
     * @param string $facetField field name
848
     */
849 2
    public function addFacetField($facetField)
850
    {
851 2
        $this->queryParameters['facet.field'][] = $facetField;
852 2
    }
853
854
    /**
855
     * Removes a filter on a field
856
     *
857
     * @param string $filterFieldName The field name the filter should be removed for
858
     * @return void
859
     */
860 1
    public function removeFilter($filterFieldName)
861
    {
862 1
        foreach ($this->filters as $key => $filterString) {
863 1
            if (GeneralUtility::isFirstPartOfStr($filterString,
864 1
                $filterFieldName . ':')
865
            ) {
866 1
                unset($this->filters[$key]);
867
            }
868
        }
869 1
    }
870
871
    /**
872
     * Removes a filter based on key of filter array
873
     *
874
     * @param string $key array key
875
     */
876 1
    public function removeFilterByKey($key)
877
    {
878 1
        unset($this->filters[$key]);
879 1
    }
880
881
    /**
882
     * Removes a filter by the filter value. The value has the following format:
883
     *
884
     * "fieldname:value"
885
     *
886
     * @param string $filterString The filter to remove, in the form of field:value
887
     */
888 1
    public function removeFilterByValue($filterString)
889
    {
890 1
        $key = array_search($filterString, $this->filters);
891 1
        if ($key === false) {
892
            // value not found, nothing to do
893
            return;
894
        }
895 1
        unset($this->filters[$key]);
896 1
    }
897
898
    /**
899
     * Gets all currently applied filters.
900
     *
901
     * @return array Array of filters
902
     */
903 30
    public function getFilters()
904
    {
905 30
        return $this->filters;
906
    }
907
908
    // sorting
909
910
    /**
911
     * Sets access restrictions for a frontend user.
912
     *
913
     * @param array $groups Array of groups a user has been assigned to
914
     */
915 29
    public function setUserAccessGroups(array $groups)
916
    {
917 29
        $groups = array_map('intval', $groups);
918 29
        $groups[] = 0; // always grant access to public documents
919 29
        $groups = array_unique($groups);
920 29
        sort($groups, SORT_NUMERIC);
921
922 29
        $accessFilter = '{!typo3access}' . implode(',', $groups);
923
924 29
        foreach ($this->filters as $key => $filter) {
925 25
            if (GeneralUtility::isFirstPartOfStr($filter, '{!typo3access}')) {
926 25
                unset($this->filters[$key]);
927
            }
928
        }
929
930 29
        $this->addFilter($accessFilter);
931 29
    }
932
933
    /**
934
     * Adds a filter parameter.
935
     *
936
     * @param string $filterString The filter to add, in the form of field:value
937
     * @return void
938
     */
939 37
    public function addFilter($filterString)
940
    {
941
        // TODO refactor to split filter field and filter value, @see Drupal
942 37
        if ($this->solrConfiguration->getLoggingQueryFilters()) {
943 1
            $this->writeDevLog('adding filter', 0, [$filterString]);
944
        }
945
946 37
        $this->filters[] = $filterString;
947 37
    }
948
949
950
    // query parameters
951
952
    /**
953
     * Limits the query to certain sites
954
     *
955
     * @param string $allowedSites Comma-separated list of domains
956
     */
957 26
    public function setSiteHashFilter($allowedSites)
958
    {
959 26
        if (trim($allowedSites) === '*') {
960 1
            return;
961
        }
962
963 25
        $allowedSites = GeneralUtility::trimExplode(',', $allowedSites);
964 25
        $filters = [];
965
966 25
        foreach ($allowedSites as $site) {
967 25
            $siteHash = $this->siteHashService->getSiteHashForDomain($site);
968 25
            $filters[] = 'siteHash:"' . $siteHash . '"';
969
        }
970
971 25
        $this->addFilter(implode(' OR ', $filters));
972 25
    }
973
974
    /**
975
     * Limits the query to certain page tree branches
976
     *
977
     * @param string $pageIds Comma-separated list of page IDs
978
     */
979
    public function setRootlineFilter($pageIds)
980
    {
981
        $pageIds = GeneralUtility::trimExplode(',', $pageIds);
982
        $filters = [];
983
984
            /** @var $processor PageUidToHierarchy */
985
        $processor = GeneralUtility::makeInstance(PageUidToHierarchy::class);
986
        $hierarchies = $processor->process($pageIds);
987
988
        foreach ($hierarchies as $hierarchy) {
989
            $lastLevel = array_pop($hierarchy);
990
            $filters[] = 'rootline:"' . $lastLevel . '"';
991
        }
992
993
        $this->addFilter(implode(' OR ', $filters));
994
    }
995
996
    /**
997
     * Adds a sort field and the sorting direction for that field
998
     *
999
     * @param string $fieldName The field name to sort by
1000
     * @param string $direction Either ApacheSolrForTypo3\Solr\Query::SORT_ASC to sort the field ascending or ApacheSolrForTypo3\Solr\Query::SORT_DESC to sort descending
1001
     * @return void
1002
     * @throws \InvalidArgumentException if the $direction parameter given is neither ApacheSolrForTypo3\Solr\Query::SORT_ASC nor ApacheSolrForTypo3\Solr\Query::SORT_DESC
1003
     * @deprecated since 6.1 will be removed in 7.0
1004
     */
1005 1
    public function addSortField($fieldName, $direction)
1006
    {
1007 1
        GeneralUtility::logDeprecatedFunction();
1008
1009 1
        $isValidSorting = $direction === self::SORT_DESC || $direction === self::SORT_ASC;
1010 1
        if (!$isValidSorting) {
1011 1
            throw new \InvalidArgumentException('Invalid sort direction "' . $direction . '"', 1235051723);
1012
        }
1013
1014 1
        $this->sortingFields[$fieldName] = $direction;
0 ignored issues
show
Deprecated Code introduced by
The property ApacheSolrForTypo3\Solr\Query::$sortingFields has been deprecated with message: since 6.1 will be removed in 7.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1015 1
    }
1016
1017
    /**
1018
     * Gets the currently set sorting fields and their sorting directions
1019
     *
1020
     * @return array An associative array with the field names as key and their sorting direction as value
1021
     * @deprecated since 6.1 will be removed in 7.0
1022
     */
1023 1
    public function getSortingFields()
1024
    {
1025 1
        GeneralUtility::logDeprecatedFunction();
1026
1027 1
        return $this->sortingFields;
0 ignored issues
show
Deprecated Code introduced by
The property ApacheSolrForTypo3\Solr\Query::$sortingFields has been deprecated with message: since 6.1 will be removed in 7.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1028
    }
1029
1030
    /**
1031
     * Gets the list of fields a query will return.
1032
     *
1033
     * @return array Array of field names the query will return
1034
     */
1035 6
    public function getFieldList()
1036
    {
1037 6
        return $this->fieldList;
1038
    }
1039
1040
    /**
1041
     * Sets the fields to return by a query.
1042
     *
1043
     * @param array|string $fieldList an array or comma-separated list of field names
1044
     * @throws \UnexpectedValueException on parameters other than comma-separated lists and arrays
1045
     */
1046 2
    public function setFieldList($fieldList = ['*', 'score'])
1047
    {
1048 2
        if (is_string($fieldList)) {
1049 1
            $fieldList = GeneralUtility::trimExplode(',', $fieldList);
1050
        }
1051
1052 2
        if (!is_array($fieldList) || empty($fieldList)) {
1053 1
            throw new \UnexpectedValueException(
1054 1
                'Field list must be a comma-separated list or array and must not be empty.',
1055 1
                1310740308
1056
            );
1057
        }
1058
1059 2
        $this->fieldList = $fieldList;
1060 2
    }
1061
1062
    /**
1063
     * Gets the query type, Solr's qt parameter.
1064
     *
1065
     * @return string Query type, qt parameter.
1066
     */
1067 1
    public function getQueryType()
1068
    {
1069 1
        return $this->queryParameters['qt'];
1070
    }
1071
1072
    /**
1073
     * Sets the query type, Solr's qt parameter.
1074
     *
1075
     * @param string|bool $queryType String query type or boolean FALSE to disable / reset the qt parameter.
1076
     * @see http://wiki.apache.org/solr/CoreQueryParameters#qt
1077
     */
1078 1
    public function setQueryType($queryType)
1079
    {
1080 1
        if ($queryType) {
1081 1
            $this->queryParameters['qt'] = $queryType;
1082
        } else {
1083 1
            unset($this->queryParameters['qt']);
1084
        }
1085 1
    }
1086
1087
    /**
1088
     * Sets the query operator to AND or OR. Unsets the query operator (actually
1089
     * sets it back to default) for FALSE.
1090
     *
1091
     * @param string|bool $operator AND or OR, FALSE to unset
1092
     */
1093 1
    public function setOperator($operator)
1094
    {
1095 1
        if (in_array($operator, [self::OPERATOR_AND, self::OPERATOR_OR])) {
1096 1
            $this->queryParameters['q.op'] = $operator;
1097
        }
1098
1099 1
        if ($operator === false) {
1100 1
            unset($this->queryParameters['q.op']);
1101
        }
1102 1
    }
1103
1104
    /**
1105
     * Gets the alternative query, Solr's q.alt parameter.
1106
     *
1107
     * @return string Alternative query, q.alt parameter.
1108
     */
1109 1
    public function getAlternativeQuery()
1110
    {
1111 1
        return $this->queryParameters['q.alt'];
1112
    }
1113
1114
    /**
1115
     * Sets an alternative query, Solr's q.alt parameter.
1116
     *
1117
     * This query supports the complete Lucene Query Language.
1118
     *
1119
     * @param mixed $alternativeQuery String alternative query or boolean FALSE to disable / reset the q.alt parameter.
1120
     * @see http://wiki.apache.org/solr/DisMaxQParserPlugin#q.alt
1121
     */
1122 26
    public function setAlternativeQuery($alternativeQuery)
1123
    {
1124 26
        if ($alternativeQuery) {
1125 26
            $this->queryParameters['q.alt'] = $alternativeQuery;
1126
        } else {
1127 1
            unset($this->queryParameters['q.alt']);
1128
        }
1129 26
    }
1130
1131
    // keywords
1132
1133
    /**
1134
     * Set the query to omit the response header
1135
     *
1136
     * @param bool $omitHeader TRUE (default) to omit response headers, FALSE to re-enable
1137
     */
1138 1
    public function setOmitHeader($omitHeader = true)
1139
    {
1140 1
        if ($omitHeader) {
1141 1
            $this->queryParameters['omitHeader'] = 'true';
1142
        } else {
1143 1
            unset($this->queryParameters['omitHeader']);
1144
        }
1145 1
    }
1146
1147
    /**
1148
     * Get the query keywords, keywords are escaped.
1149
     *
1150
     * @return string query keywords
1151
     */
1152 20
    public function getKeywords()
1153
    {
1154 20
        return $this->keywords;
1155
    }
1156
1157
    /**
1158
     * Sets the query keywords, escapes them as needed for Solr/Lucene.
1159
     *
1160
     * @param string $keywords user search terms/keywords
1161
     */
1162 131
    public function setKeywords($keywords)
1163
    {
1164 131
        $this->keywords = $this->escape($keywords);
1165 131
        $this->keywordsRaw = $keywords;
1166 131
    }
1167
1168
    /**
1169
     * Gets the cleaned keywords so that it can be used in templates f.e.
1170
     *
1171
     * @return string The cleaned keywords.
1172
     */
1173 18
    public function getKeywordsCleaned()
1174
    {
1175 18
        return $this->cleanKeywords($this->keywordsRaw);
1176
    }
1177
1178
    /**
1179
     * Helper method to escape/encode keywords for use in HTML
1180
     *
1181
     * @param string $keywords Keywords to prepare for use in HTML
1182
     * @return string Encoded keywords
1183
     */
1184 22
    public static function cleanKeywords($keywords)
1185
    {
1186 22
        $keywords = trim($keywords);
1187 22
        $keywords = GeneralUtility::removeXSS($keywords);
1188 22
        $keywords = htmlentities($keywords, ENT_QUOTES,
1189 22
            $GLOBALS['TSFE']->metaCharset);
1190
1191
        // escape triple hashes as they are used in the template engine
1192
        // TODO remove after switching to fluid templates
1193 22
        $keywords = Template::escapeMarkers($keywords);
1194
1195 22
        return $keywords;
1196
    }
1197
1198
    // relevance, matching
1199
1200
    /**
1201
     * Gets the raw, unescaped, unencoded keywords.
1202
     *
1203
     * USE WITH CAUTION!
1204
     *
1205
     * @return string raw keywords
1206
     */
1207 19
    public function getKeywordsRaw()
1208
    {
1209 19
        return $this->keywordsRaw;
1210
    }
1211
1212
    /**
1213
     * Sets the minimum match (mm) parameter
1214
     *
1215
     * @param mixed $minimumMatch Minimum match parameter as string or boolean FALSE to disable / reset the mm parameter
1216
     * @see http://wiki.apache.org/solr/DisMaxRequestHandler#mm_.28Minimum_.27Should.27_Match.29
1217
     */
1218 1
    public function setMinimumMatch($minimumMatch)
1219
    {
1220 1
        if (is_string($minimumMatch) && !empty($minimumMatch)) {
1221 1
            $this->queryParameters['mm'] = $minimumMatch;
1222
        } else {
1223 1
            unset($this->queryParameters['mm']);
1224
        }
1225 1
    }
1226
1227
    /**
1228
     * Sets the boost function (bf) parameter
1229
     *
1230
     * @param mixed $boostFunction boost function parameter as string or boolean FALSE to disable / reset the bf parameter
1231
     * @see http://wiki.apache.org/solr/DisMaxRequestHandler#bf_.28Boost_Functions.29
1232
     */
1233 1
    public function setBoostFunction($boostFunction)
1234
    {
1235 1
        if (is_string($boostFunction) && !empty($boostFunction)) {
1236 1
            $this->queryParameters['bf'] = $boostFunction;
1237
        } else {
1238 1
            unset($this->queryParameters['bf']);
1239
        }
1240 1
    }
1241
1242
    // query fields
1243
    // TODO move up to field list methods
1244
1245
    /**
1246
     * Sets the boost query (bq) parameter
1247
     *
1248
     * @param mixed $boostQuery boost query parameter as string or array to set a boost query or boolean FALSE to disable / reset the bq parameter
1249
     * @see http://wiki.apache.org/solr/DisMaxQParserPlugin#bq_.28Boost_Query.29
1250
     */
1251 1
    public function setBoostQuery($boostQuery)
1252
    {
1253 1
        if ((is_string($boostQuery) || is_array($boostQuery)) && !empty($boostQuery)) {
1254 1
            $this->queryParameters['bq'] = $boostQuery;
1255
        } else {
1256 1
            unset($this->queryParameters['bq']);
1257
        }
1258 1
    }
1259
1260
    /**
1261
     * Gets a specific query parameter by its name.
1262
     *
1263
     * @param string $parameterName The parameter to return
1264
     * @param mixed $defaultIfEmpty
1265
     * @return mixed The parameter's value or $defaultIfEmpty if not set
1266
     */
1267 10
    public function getQueryParameter($parameterName, $defaultIfEmpty = null)
1268
    {
1269 10
        $parameters = $this->getQueryParameters();
1270 10
        return isset($parameters[$parameterName]) ? $parameters[$parameterName] : $defaultIfEmpty;
1271
    }
1272
1273
    /**
1274
     * Builds an array of query parameters to use for the search query.
1275
     *
1276
     * @return array An array ready to use with query parameters
1277
     */
1278 63
    public function getQueryParameters()
1279
    {
1280 63
        $queryParameters = array_merge(
1281
            [
1282 63
                'fl' => implode(',', $this->fieldList),
1283 63
                'fq' => array_values($this->filters)
1284
            ],
1285 63
            $this->queryParameters
1286
        );
1287
1288 63
        $queryFieldString = $this->getQueryFieldsAsString();
1289 63
        if (!empty($queryFieldString)) {
1290 26
            $queryParameters['qf'] = $queryFieldString;
1291
        }
1292
1293 63
        return $queryParameters;
1294
    }
1295
1296
    // general query parameters
1297
1298
    /**
1299
     * Compiles the query fields into a string to be used in Solr's qf parameter.
1300
     *
1301
     * @return string A string of query fields with their associated boosts
1302
     */
1303 65
    public function getQueryFieldsAsString()
1304
    {
1305 65
        $queryFieldString = '';
1306
1307 65
        foreach ($this->queryFields as $fieldName => $fieldBoost) {
1308 27
            $queryFieldString .= $fieldName;
1309
1310 27
            if ($fieldBoost != 1.0) {
1311 27
                $queryFieldString .= '^' . number_format($fieldBoost, 1, '.', '');
1312
            }
1313
1314 27
            $queryFieldString .= ' ';
1315
        }
1316
1317 65
        return trim($queryFieldString);
1318
    }
1319
1320
    /**
1321
     * Enables or disables highlighting of search terms in result teasers.
1322
     *
1323
     * @param bool $highlighting Enables highlighting when set to TRUE, deactivates highlighting when set to FALSE, defaults to TRUE.
1324
     * @param int $fragmentSize Size, in characters, of fragments to consider for highlighting.
1325
     * @see http://wiki.apache.org/solr/HighlightingParameters
1326
     * @return void
1327
     */
1328 31
    public function setHighlighting($highlighting = true, $fragmentSize = 200)
1329
    {
1330 31
        if ($highlighting) {
1331 31
            $this->queryParameters['hl'] = 'true';
1332 31
            $this->queryParameters['hl.fragsize'] = (int)$fragmentSize;
1333
1334 31
            $highlightingFields = $this->solrConfiguration->getSearchResultsHighlightingFields();
1335 31
            if ($highlightingFields != '') {
1336 25
                $this->queryParameters['hl.fl'] = $highlightingFields;
1337
            }
1338
1339
            // the fast vector highlighter can only be used, when the fragmentSize is
1340
            // higher then 17 otherwise solr throws an exception
1341 31
            $useFastVectorHighlighter = ($fragmentSize >= 18);
1342 31
            $wrap = explode('|', $this->solrConfiguration->getSearchResultsHighlightingWrap());
1343
1344 31
            if ($useFastVectorHighlighter) {
1345 29
                $this->queryParameters['hl.useFastVectorHighlighter'] = 'true';
1346 29
                $this->queryParameters['hl.tag.pre'] = $wrap[0];
1347 29
                $this->queryParameters['hl.tag.post'] = $wrap[1];
1348
            }
1349
1350 31
            if (isset($wrap[0]) && isset($wrap[1])) {
1351 26
                $this->queryParameters['hl.simple.pre'] = $wrap[0];
1352 31
                $this->queryParameters['hl.simple.post'] = $wrap[1];
1353
            }
1354
        } else {
1355
            // remove all hl.* settings
1356 1
            foreach ($this->queryParameters as $key => $value) {
1357 1
                if (GeneralUtility::isFirstPartOfStr($key, 'hl')) {
1358 1
                    unset($this->queryParameters[$key]);
1359
                }
1360
            }
1361
        }
1362 31
    }
1363
1364
    // misc
1365
1366
    /**
1367
     * Enables or disables spellchecking for the query.
1368
     *
1369
     * @param bool $spellchecking Enables spellchecking when set to TRUE, deactivates spellchecking when set to FALSE, defaults to TRUE.
1370
     */
1371 22
    public function setSpellchecking($spellchecking = true)
1372
    {
1373 22
        if ($spellchecking) {
1374 22
            $this->queryParameters['spellcheck'] = 'true';
1375 22
            $this->queryParameters['spellcheck.collate'] = 'true';
1376 22
            $maxCollationTries = $this->solrConfiguration->getSearchSpellcheckingNumberOfSuggestionsToTry();
1377 22
            $this->addQueryParameter('spellcheck.maxCollationTries', $maxCollationTries);
1378
        } else {
1379 1
            unset($this->queryParameters['spellcheck']);
1380 1
            unset($this->queryParameters['spellcheck.collate']);
1381 1
            unset($this->queryParameters['spellcheck.maxCollationTries']);
1382
        }
1383 22
    }
1384
1385
    /**
1386
     * Adds any generic query parameter.
1387
     *
1388
     * @param string $parameterName Query parameter name
1389
     * @param string $parameterValue Parameter value
1390
     */
1391 30
    public function addQueryParameter($parameterName, $parameterValue)
1392
    {
1393 30
        $this->queryParameters[$parameterName] = $parameterValue;
1394 30
    }
1395
1396
    /**
1397
     * Sets the sort parameter.
1398
     *
1399
     * $sorting must include a field name (or the pseudo-field score),
1400
     * followed by a space,
1401
     * followed by a sort direction (asc or desc).
1402
     *
1403
     * Multiple fallback sortings can be separated by comma,
1404
     * ie: <field name> <direction>[,<field name> <direction>]...
1405
     *
1406
     * @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)
1407
     * @see http://wiki.apache.org/solr/CommonQueryParameters#sort
1408
     */
1409 2
    public function setSorting($sorting)
1410
    {
1411 2
        if ($sorting) {
1412 2
            if (!is_string($sorting)) {
1413
                throw new \InvalidArgumentException('Sorting needs to be a string!');
1414
            }
1415 2
            $sortParameter = $this->removeRelevanceSortField($sorting);
1416 2
            $this->queryParameters['sort'] = $sortParameter;
1417
        } else {
1418 1
            unset($this->queryParameters['sort']);
1419
        }
1420 2
    }
1421
1422
    /**
1423
     * Removes the relevance sort field if present in the sorting field definition.
1424
     *
1425
     * @param string $sorting
1426
     * @return string
1427
     */
1428 2
    protected function removeRelevanceSortField($sorting)
1429
    {
1430 2
        $sortParameter = $sorting;
1431 2
        list($sortField) = explode(' ', $sorting);
1432 2
        if ($sortField == 'relevance') {
1433 1
            $sortParameter = '';
1434 1
            return $sortParameter;
1435
        }
1436
1437 2
        return $sortParameter;
1438
    }
1439
1440
    /**
1441
     * Enables or disables the debug parameter for the query.
1442
     *
1443
     * @param bool $debugMode Enables debugging when set to TRUE, deactivates debugging when set to FALSE, defaults to TRUE.
1444
     */
1445
    public function setDebugMode($debugMode = true)
1446
    {
1447
        if ($debugMode) {
1448
            $this->queryParameters['debugQuery'] = 'true';
1449
            $this->queryParameters['echoParams'] = 'all';
1450
        } else {
1451
            unset($this->queryParameters['debugQuery']);
1452
            unset($this->queryParameters['echoParams']);
1453
        }
1454
    }
1455
1456
    /**
1457
     * Returns the link target page id.
1458
     *
1459
     * @return int
1460
     */
1461 2
    public function getLinkTargetPageId()
1462
    {
1463 2
        return $this->linkTargetPageId;
1464
    }
1465
1466
    /**
1467
     * Activates the collapsing on the configured field, if collapsing was enabled.
1468
     *
1469
     * @return bool
1470
     */
1471 130
    protected function initializeCollapsingFromConfiguration()
1472
    {
1473
        // check collapsing
1474 130
        if ($this->solrConfiguration->getSearchVariants()) {
1475 3
            $collapseField = $this->solrConfiguration->getSearchVariantsField();
1476 3
            $this->setVariantField($collapseField);
1477 3
            $this->setCollapsing(true);
1478
1479 3
            return true;
1480
        }
1481
1482 127
        return false;
1483
    }
1484
}
1485