Passed
Push — sheepy/introspection ( 000d40...b6b869 )
by Marco
02:43
created

QueryComponentFactory   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 236
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 6
Bugs 0 Features 0
Metric Value
wmc 25
eloc 73
dl 0
loc 236
ccs 71
cts 71
cp 1
rs 10
c 6
b 0
f 0

15 Methods

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