Passed
Push — main ( ae47d0...a5bc80 )
by Simon
01:06
created

CoreIndexTrait   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 349
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 73
dl 0
loc 349
rs 9.68
c 1
b 0
f 0
wmc 34

22 Methods

Rating   Name   Duplication   Size   Complexity  
A getCopyFields() 0 3 1
A addSortField() 0 12 3
A addFilterField() 0 8 2
A addAllFulltextFields() 0 3 1
A getClient() 0 3 1
A setFilterFields() 0 8 2
A getStoredFields() 0 3 1
A getFilterFields() 0 3 1
A getFacetFields() 0 3 1
A addFacetField() 0 9 2
A addFulltextFieldsForClass() 0 11 4
A addCopyField() 0 5 1
A getSortFields() 0 3 1
A addAllFieldsByType() 0 8 2
A setCopyFields() 0 5 1
A setClient() 0 5 1
A setFacetFields() 0 8 2
A setSortFields() 0 8 2
A addAllDateFields() 0 3 1
A getFulltextFields() 0 5 1
A setStoredFields() 0 8 2
A setFulltextFields() 0 5 1
1
<?php
2
/**
3
 * Trait CoreIndexTrait|Firesphere\SearchBackend\Traits\CoreIndexTrait Used to extract methods from the
4
 * {@link \Firesphere\SearchBackend\Indexes\CoreIndex} to make the code more readable
5
 *
6
 * @package Firesphere\Elastic\Search
7
 * @author Simon `Firesphere` Erkelens; Marco `Sheepy` Hermo
8
 * @copyright Copyright (c) 2018 - now() Firesphere & Sheepy
9
 */
10
11
namespace Firesphere\SearchBackend\Traits\IndexingTraits;
12
13
use Elastic\Elasticsearch\Client as ElasticClient;
0 ignored issues
show
Bug introduced by
The type Elastic\Elasticsearch\Client was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use ReflectionException;
15
use SilverStripe\Core\Config\Config;
16
use SilverStripe\Core\Injector\Injector;
17
use SilverStripe\ORM\DataObject;
18
use SilverStripe\ORM\FieldType\DBDate;
19
use SilverStripe\ORM\FieldType\DBString;
20
use Solarium\Core\Client\Client as SolrClient;
0 ignored issues
show
Bug introduced by
The type Solarium\Core\Client\Client was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
22
/**
23
 * Trait CoreIndexTrait
24
 * Getters and Setters for the ElasticIndex and SolrIndex
25
 *
26
 * @package Firesphere\Search\Backend
27
 */
28
trait CoreIndexTrait
29
{
30
    /**
31
     * @var ElasticClient|SolrClient Query client
32
     */
33
    protected $client;
34
    /**
35
     * @var array Facet fields
36
     */
37
    protected $facetFields = [];
38
    /**
39
     * @var array Fulltext fields
40
     */
41
    protected $fulltextFields = [];
42
    /**
43
     * @var array Filterable fields
44
     */
45
    protected $filterFields = [];
46
    /**
47
     * @var array Sortable fields
48
     */
49
    protected $sortFields = [];
50
    /**
51
     * @var array Stored fields
52
     */
53
    protected $storedFields = [];
54
55
    /**
56
     * @var array Fields to copy to the default fields
57
     */
58
    protected $copyFields = [
59
        '_text' => [
60
            '*',
61
        ],
62
    ];
63
64
65
    /**
66
     * usedAllFields is used to determine if the addAllFields method has been called
67
     * This is to prevent a notice if there is no yml.
68
     *
69
     * @var bool
70
     */
71
    protected $usedAllFields = false;
72
73
74
    /**
75
     * Get the client
76
     *
77
     * @return ElasticClient|SolrClient
78
     */
79
    public function getClient()
80
    {
81
        return $this->client;
82
    }
83
84
    /**
85
     * Set/override the client
86
     *
87
     * @param ElasticClient|SolrClient $client
88
     * @return $this
89
     */
90
    public function setClient($client): self
91
    {
92
        $this->client = $client;
93
94
        return $this;
95
    }
96
97
    /**
98
     * Add a facet field
99
     *
100
     * @param $field
101
     * @param array $options
102
     * @return $this
103
     */
104
    public function addFacetField($field, $options): self
105
    {
106
        $this->facetFields[$field] = $options;
107
108
        if (!in_array($options['Field'], $this->getFilterFields(), true)) {
109
            $this->addFilterField($options['Field']);
110
        }
111
112
        return $this;
113
    }
114
115
    /**
116
     * Set the fields to use for faceting
117
     * @param $fields
118
     * @return $this
119
     */
120
    public function setFacetFields($fields)
121
    {
122
        $this->facetFields = $fields;
123
        foreach ($fields as $field => $option) {
124
            $this->addFulltextField($option['Field']);
0 ignored issues
show
Bug introduced by
The method addFulltextField() does not exist on Firesphere\SearchBackend...ngTraits\CoreIndexTrait. Did you maybe mean addFulltextFieldsForClass()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

124
            $this->/** @scrutinizer ignore-call */ 
125
                   addFulltextField($option['Field']);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
125
        }
126
127
        return $this;
128
    }
129
130
    /**
131
     * @return array
132
     */
133
    public function getFacetFields()
134
    {
135
        return $this->facetFields;
136
    }
137
138
    /**
139
     * Get the fulltext fields
140
     *
141
     * @return array
142
     */
143
    public function getFulltextFields(): array
144
    {
145
        return array_values(
146
            array_unique(
147
                $this->fulltextFields
148
            )
149
        );
150
    }
151
152
    /**
153
     * Set the fulltext fields
154
     *
155
     * @param array $fulltextFields
156
     * @return $this
157
     */
158
    public function setFulltextFields($fulltextFields): self
159
    {
160
        $this->fulltextFields = $fulltextFields;
161
162
        return $this;
163
    }
164
165
    /**
166
     * Add all text-type fields to the given index
167
     *
168
     * @throws ReflectionException
169
     */
170
    public function addAllFulltextFields()
171
    {
172
        $this->addAllFieldsByType(DBString::class);
173
    }
174
175
    /**
176
     * Add all date-type fields to the given index
177
     *
178
     * @throws ReflectionException
179
     */
180
    public function addAllDateFields()
181
    {
182
        $this->addAllFieldsByType(DBDate::class);
183
    }
184
185
    /**
186
     * Add all database-backed text fields as fulltext searchable fields.
187
     *
188
     * For every class included in the index, examines those classes and all parent looking for "DBText" database
189
     * fields (Varchar, Text, HTMLText, etc) and adds them all as fulltext searchable fields.
190
     *
191
     * Note, there is no check on boosting etc. That needs to be done manually.
192
     *
193
     * @param string $dbType
194
     * @throws ReflectionException
195
     */
196
    protected function addAllFieldsByType($dbType = DBString::class): void
197
    {
198
        $this->usedAllFields = true;
199
        $classes = $this->getClasses();
0 ignored issues
show
Bug introduced by
It seems like getClasses() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

199
        /** @scrutinizer ignore-call */ 
200
        $classes = $this->getClasses();
Loading history...
200
        foreach ($classes as $key => $class) {
201
            $fields = DataObject::getSchema()->databaseFields($class, true);
202
203
            $this->addFulltextFieldsForClass($fields, $dbType);
204
        }
205
    }
206
207
    /**
208
     * Add all fields of a given type to the index
209
     *
210
     * @param array $fields The fields on the DataObject
211
     * @param string $dbType Class type the reflection should extend
212
     * @throws ReflectionException
213
     */
214
    protected function addFulltextFieldsForClass(array $fields, $dbType = DBString::class): void
215
    {
216
        foreach ($fields as $field => $type) {
217
            $pos = strpos($type, '(');
218
            if ($pos !== false) {
219
                $type = substr($type, 0, $pos);
220
            }
221
            $conf = Config::inst()->get(Injector::class, $type);
222
            $ref = new ReflectionClass($conf['class']);
0 ignored issues
show
Bug introduced by
The type Firesphere\SearchBackend...gTraits\ReflectionClass was not found. Did you mean ReflectionClass? If so, make sure to prefix the type with \.
Loading history...
223
            if ($ref->isSubclassOf($dbType)) {
224
                $this->addFulltextField($field);
225
            }
226
        }
227
    }
228
229
230
    /**
231
     * Add a filterable field
232
     *
233
     * @param $filterField
234
     * @return $this
235
     */
236
    public function addFilterField($filterField): self
237
    {
238
        $key = array_search($filterField, $this->getFulltextFields(), true);
239
        if ($key === false) {
240
            $this->filterFields[] = $filterField;
241
        }
242
243
        return $this;
244
    }
245
246
    /**
247
     * Set the filter fields
248
     *
249
     * @param array $filterFields
250
     * @return $this
251
     */
252
    public function setFilterFields($filterFields): self
253
    {
254
        $this->filterFields = $filterFields;
255
        foreach ($filterFields as $filterField) {
256
            $this->addFulltextField($filterField);
257
        }
258
259
        return $this;
260
    }
261
262
    /**
263
     * Get the filter fields
264
     *
265
     * @return array
266
     */
267
    public function getFilterFields(): array
268
    {
269
        return $this->filterFields;
270
    }
271
272
    /**
273
     * Add a field to sort on
274
     *
275
     * @param $sortField
276
     * @return $this
277
     */
278
    public function addSortField($sortField): self
279
    {
280
        if (!in_array($sortField, $this->getFulltextFields(), true) &&
281
            !in_array($sortField, $this->getFilterFields(), true)
282
        ) {
283
            $this->addFulltextField($sortField);
284
            $this->sortFields[] = $sortField;
285
        }
286
287
        $this->setSortFields(array_unique($this->getSortFields()));
288
289
        return $this;
290
    }
291
292
    /**
293
     * Set/override the sortable fields
294
     *
295
     * @param array $sortFields
296
     * @return $this
297
     */
298
    public function setSortFields($sortFields): self
299
    {
300
        $this->sortFields = $sortFields;
301
        foreach ($sortFields as $sortField) {
302
            $this->addFulltextField($sortField);
303
        }
304
305
        return $this;
306
    }
307
308
    /**
309
     * Get the sortable fields
310
     *
311
     * @return array
312
     */
313
    public function getSortFields(): array
314
    {
315
        return $this->sortFields;
316
    }
317
318
    /**
319
     * Stub to be compatible with Solr. Elastic stores everything anyway
320
     * @param array $storedFields
321
     * @return $this
322
     */
323
    public function setStoredFields(array $storedFields)
324
    {
325
        $this->storedFields = $storedFields;
326
        foreach ($storedFields as $storedField) {
327
            $this->addFulltextField($storedField);
328
        }
329
330
        return $this;
331
    }
332
333
    /**
334
     * @return array
335
     */
336
    public function getStoredFields(): array
337
    {
338
        return $this->storedFields;
339
    }
340
341
342
    /**
343
     * Add a copy field
344
     *
345
     * @param string $field Name of the copyfield
346
     * @param array $options Array of all fields that should be copied to this copyfield
347
     * @return $this
348
     */
349
    public function addCopyField($field, $options): self
350
    {
351
        $this->copyFields[$field] = $options;
352
353
        return $this;
354
    }
355
356
    /**
357
     * Set the copy fields
358
     *
359
     * @param array $copyField
360
     * @return $this
361
     */
362
    public function setCopyFields($copyField): self
363
    {
364
        $this->copyFields = $copyField;
365
366
        return $this;
367
    }
368
369
    /**
370
     * Return the copy fields
371
     *
372
     * @return array
373
     */
374
    public function getCopyFields(): array
375
    {
376
        return $this->copyFields;
377
    }
378
379
}
380