Passed
Push — sheepy/elevation-configuration ( bdeab0...bcf7bc )
by Marco
18:34
created

QueryComponentFactory::setIndex()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 5
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
4
namespace Firesphere\SolrSearch\Factories;
5
6
use Firesphere\SolrSearch\Indexes\BaseIndex;
7
use Firesphere\SolrSearch\Queries\BaseQuery;
8
use Firesphere\SolrSearch\Services\SolrCoreService;
9
use Firesphere\SolrSearch\Traits\QueryComponentBoostTrait;
10
use Firesphere\SolrSearch\Traits\QueryComponentFacetTrait;
11
use Firesphere\SolrSearch\Traits\QueryComponentFilterTrait;
12
use Solarium\Core\Query\Helper;
13
use Solarium\QueryType\Select\Query\Query;
14
15
/**
16
 * Class QueryComponentFactory
17
 * @package Firesphere\SolrSearch\Factories
18
 */
19
class QueryComponentFactory
20
{
21
    use QueryComponentFilterTrait;
1 ignored issue
show
Bug introduced by
The trait Firesphere\SolrSearch\Tr...eryComponentFilterTrait requires the property $ID which is not provided by Firesphere\SolrSearch\Fa...s\QueryComponentFactory.
Loading history...
22
    use QueryComponentBoostTrait;
23
    use QueryComponentFacetTrait;
24
25
    public const DEFAULT_FIELDS = [
26
        SolrCoreService::ID_FIELD,
27
        SolrCoreService::CLASS_ID_FIELD,
28
        SolrCoreService::CLASSNAME
29
    ];
30
31
    protected static $builds = [
32
        'Terms',
33
        'ViewFilter',
34
        'ClassFilter',
35
        'Filters',
36
        'Excludes',
37
        'Facets',
38
        'FacetQuery',
39
        'Spellcheck'
40
    ];
41
    /**
42
     * @var BaseQuery
43
     */
44
    protected $query;
45
    /**
46
     * @var Helper
47
     */
48
    protected $helper;
49
    /**
50
     * @var array
51
     */
52
    protected $queryArray = [];
53
    /**
54
     * @var BaseIndex
55
     */
56
    protected $index;
57
58
    /**
59
     * Build the full query
60
     * @return Query
61
     */
62
    public function buildQuery()
63
    {
64
        foreach (static::$builds as $build) {
65
            $method = sprintf('build%s', $build);
66
            $this->$method();
67
        }
68
        // Set the start
69
        $this->clientQuery->setStart($this->query->getStart());
70
        // Double the rows in case something has been deleted, but not from Solr
71
        $this->clientQuery->setRows($this->query->getRows() * 2);
72
        // Add highlighting before adding boosting
73
        $this->clientQuery->getHighlighting()->setFields($this->query->getHighlight());
74
        // Add boosting
75
        $this->buildBoosts();
76
77
        // Filter out the fields we want to see if they're set
78
        $fields = $this->query->getFields();
79
        if (count($fields)) {
80
            // We _ALWAYS_ need the ClassName for getting the DataObjects back
81
            $fields = array_merge(static::DEFAULT_FIELDS, $fields);
82
            $this->clientQuery->setFields($fields);
83
        }
84
85
        return $this->clientQuery;
86
    }
87
88
    /**
89
     * @return BaseQuery
90
     */
91
    public function getQuery(): BaseQuery
92
    {
93
        return $this->query;
94
    }
95
96
    /**
97
     * @param BaseQuery $query
98
     * @return QueryComponentFactory
99
     */
100
    public function setQuery(BaseQuery $query): self
101
    {
102
        $this->query = $query;
103
104
        return $this;
105
    }
106
107
    /**
108
     * @return array
109
     */
110
    public function getQueryArray(): array
111
    {
112
        return array_merge($this->queryArray, $this->boostTerms);
113
    }
114
115
    /**
116
     * @param array $queryArray
117
     * @return QueryComponentFactory
118
     */
119
    public function setQueryArray(array $queryArray): QueryComponentFactory
120
    {
121
        $this->queryArray = $queryArray;
122
123
        return $this;
124
    }
125
126
    /**
127
     * @return Query
128
     */
129
    public function getClientQuery(): Query
130
    {
131
        return $this->clientQuery;
132
    }
133
134
    /**
135
     * @param Query $clientQuery
136
     * @return QueryComponentFactory
137
     */
138
    public function setClientQuery(Query $clientQuery): QueryComponentFactory
139
    {
140
        $this->clientQuery = $clientQuery;
141
142
        return $this;
143
    }
144
145
    /**
146
     * @return Helper
147
     */
148
    public function getHelper(): Helper
149
    {
150
        return $this->helper;
151
    }
152
153
    /**
154
     * @param Helper $helper
155
     * @return QueryComponentFactory
156
     */
157
    public function setHelper(Helper $helper): QueryComponentFactory
158
    {
159
        $this->helper = $helper;
160
161
        return $this;
162
    }
163
164
    /**
165
     * @return BaseIndex
166
     */
167
    public function getIndex(): BaseIndex
168
    {
169
        return $this->index;
170
    }
171
172
    /**
173
     * @param BaseIndex $index
174
     * @return QueryComponentFactory
175
     */
176
    public function setIndex(BaseIndex $index): QueryComponentFactory
177
    {
178
        $this->index = $index;
179
180
        return $this;
181
    }
182
183
    /**
184
     * @return void
185
     */
186
    protected function buildTerms(): void
187
    {
188
        $terms = $this->query->getTerms();
189
190
        $boostTerms = $this->getBoostTerms();
191
192
        foreach ($terms as $search) {
193
            $term = $search['text'];
194
            $term = $this->escapeSearch($term, $this->helper);
195
            $postfix = $this->isFuzzy($search);
196
            // We can add the same term multiple times with different boosts
197
            // Not ideal, but it might happen, so let's add the term itself only once
198
            if (!in_array($term, $this->queryArray, true)) {
199
                $this->queryArray[] = $term . $postfix;
200
            }
201
            // If boosting is set, add the fields to boost
202
            if ($search['boost'] > 1) {
203
                $boostTerms = $this->buildQueryBoost($search, $term, $boostTerms);
204
            }
205
        }
206
        // Clean up the boost terms, remove doubles
207
        $this->setBoostTerms(array_values(array_unique($boostTerms)));
208
    }
209
210
    /**
211
     * @param string $searchTerm
212
     * @param Helper $helper
213
     * @return string
214
     */
215
    public function escapeSearch($searchTerm, Helper $helper): string
216
    {
217
        $term = [];
218
        // Escape special characters where needed. Except for quoted parts, those should be phrased
219
        preg_match_all('/"[^"]*"|\S+/', $searchTerm, $parts);
220
        foreach ($parts[0] as $part) {
221
            // As we split the parts, everything with two quotes is a phrase
222
            if (substr_count($part, '"') === 2) {
223
                $term[] = $helper->escapePhrase($part);
224
            } else {
225
                $term[] = $helper->escapeTerm($part);
226
            }
227
        }
228
229
        return implode(' ', $term);
230
    }
231
232
    /**
233
     * If the search is fuzzy, add fuzzyness
234
     *
235
     * @param $search
236
     * @return string
237
     */
238
    protected function isFuzzy($search): string
239
    {
240
        // When doing fuzzy search, postfix, otherwise, don't
241
        if ($search['fuzzy']) {
242
            return '~' . (is_numeric($search['fuzzy']) ? $search['fuzzy'] : '');
243
        }
244
245
        return '';
246
    }
247
248
    /**
249
     * Add spellcheck elements
250
     */
251
    protected function buildSpellcheck(): void
252
    {
253
        // Assuming the first term is the term entered
254
        $queryString = implode(' ', $this->queryArray);
255
        // Arbitrarily limit to 5 if the config isn't set
256
        $count = BaseIndex::config()->get('spellcheckCount') ?: 5;
257
        $spellcheck = $this->clientQuery->getSpellcheck();
258
        $spellcheck->setQuery($queryString);
259
        $spellcheck->setCount($count);
260
        $spellcheck->setBuild(true);
261
        $spellcheck->setCollate(true);
262
        $spellcheck->setExtendedResults(true);
263
        $spellcheck->setCollateExtendedResults('true');
264
    }
265
}
266