Passed
Push — main ( 7c5697...a50605 )
by Simon
01:03
created

CoreIndexTrait::getFulltextFields()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 5
rs 10
c 1
b 0
f 0
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 ReflectionClass;
15
use ReflectionException;
16
use SilverStripe\Core\Config\Config;
17
use SilverStripe\Core\Injector\Injector;
18
use SilverStripe\ORM\DataObject;
19
use SilverStripe\ORM\FieldType\DBDate;
20
use SilverStripe\ORM\FieldType\DBString;
21
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...
22
23
/**
24
 * Trait CoreIndexTrait
25
 * Getters and Setters for the ElasticIndex and SolrIndex
26
 *
27
 * @package Firesphere\Search\Backend
28
 */
29
trait CoreIndexTrait
30
{
31
    /**
32
     * @var ElasticClient|SolrClient Query client
33
     */
34
    protected $client;
35
    /**
36
     * @var string[] Classes to index
37
     */
38
    protected $class;
39
    /**
40
     * @var array Facet fields
41
     */
42
    protected $facetFields = [];
43
    /**
44
     * @var array Fulltext fields
45
     */
46
    protected $fulltextFields = [];
47
    /**
48
     * @var array Filterable fields
49
     */
50
    protected $filterFields = [];
51
    /**
52
     * @var array Sortable fields
53
     */
54
    protected $sortFields = [];
55
    /**
56
     * @var array Stored fields
57
     */
58
    protected $storedFields = [];
59
60
    /**
61
     * @var array Fields to copy to the default fields
62
     */
63
    protected $copyFields = [
64
        '_text' => [
65
            '*',
66
        ],
67
    ];
68
69
70
    /**
71
     * usedAllFields is used to determine if the addAllFields method has been called
72
     * This is to prevent a notice if there is no yml.
73
     *
74
     * @var bool
75
     */
76
    protected $usedAllFields = false;
77
78
    /**
79
     * Add a class to index or query
80
     * $options is not used anymore, added for backward compatibility
81
     *
82
     * @param $class
83
     * @param array $options unused
84
     * @return self
85
     */
86
    public function addClass($class, $options = []): self
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed. ( Ignorable by Annotation )

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

86
    public function addClass($class, /** @scrutinizer ignore-unused */ $options = []): self

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
87
    {
88
        $this->class[] = $class;
89
90
        return $this;
91
    }
92
93
    /**
94
     * Get classes
95
     *
96
     * @return array
97
     */
98
    public function getClasses(): array
99
    {
100
        return $this->class;
101
    }
102
103
    /**
104
     * Set the classes
105
     *
106
     * @param array $class
107
     * @return $this
108
     */
109
    public function setClasses($class): self
110
    {
111
        $this->class = $class;
112
113
        return $this;
114
    }
115
116
    /**
117
     * Get the client
118
     *
119
     * @return ElasticClient|SolrClient
120
     */
121
    public function getClient()
122
    {
123
        return $this->client;
124
    }
125
126
    /**
127
     * Set/override the client
128
     *
129
     * @param ElasticClient|SolrClient $client
130
     * @return $this
131
     */
132
    public function setClient($client): self
133
    {
134
        $this->client = $client;
135
136
        return $this;
137
    }
138
139
    /**
140
     * Add a facet field
141
     *
142
     * @param $field
143
     * @param array $options
144
     * @return $this
145
     */
146
    public function addFacetField($field, $options): self
147
    {
148
        $this->facetFields[$field] = $options;
149
150
        if (!in_array($options['Field'], $this->getFilterFields(), true)) {
151
            $this->addFilterField($options['Field']);
152
        }
153
154
        return $this;
155
    }
156
157
    /**
158
     * Set the fields to use for faceting
159
     * @param $fields
160
     * @return $this
161
     */
162
    public function setFacetFields($fields)
163
    {
164
        $this->facetFields = $fields;
165
        foreach ($fields as $field => $option) {
166
            $this->addFulltextField($option['Field']);
167
        }
168
169
        return $this;
170
    }
171
172
    /**
173
     * @return array
174
     */
175
    public function getFacetFields()
176
    {
177
        return $this->facetFields;
178
    }
179
180
    /**
181
     * Add a single Fulltext field
182
     *
183
     * @param string $fulltextField
184
     * @param array $options
185
     * @return $this
186
     */
187
    abstract public function addFulltextField($fulltextField, $options = []): self;
188
189
    /**
190
     * Get the fulltext fields
191
     *
192
     * @return array
193
     */
194
    public function getFulltextFields(): array
195
    {
196
        return array_values(
197
            array_unique(
198
                $this->fulltextFields
199
            )
200
        );
201
    }
202
203
    /**
204
     * Set the fulltext fields
205
     *
206
     * @param array $fulltextFields
207
     * @return $this
208
     */
209
    public function setFulltextFields($fulltextFields): self
210
    {
211
        $this->fulltextFields = $fulltextFields;
212
213
        return $this;
214
    }
215
216
    /**
217
     * Add all text-type fields to the given index
218
     *
219
     * @throws ReflectionException
220
     */
221
    public function addAllFulltextFields()
222
    {
223
        $this->addAllFieldsByType(DBString::class);
224
    }
225
226
    /**
227
     * Add all date-type fields to the given index
228
     *
229
     * @throws ReflectionException
230
     */
231
    public function addAllDateFields()
232
    {
233
        $this->addAllFieldsByType(DBDate::class);
234
    }
235
236
    /**
237
     * Add all database-backed text fields as fulltext searchable fields.
238
     *
239
     * For every class included in the index, examines those classes and all parent looking for "DBText" database
240
     * fields (Varchar, Text, HTMLText, etc) and adds them all as fulltext searchable fields.
241
     *
242
     * Note, there is no check on boosting etc. That needs to be done manually.
243
     *
244
     * @param string $dbType
245
     * @throws ReflectionException
246
     */
247
    protected function addAllFieldsByType($dbType = DBString::class): void
248
    {
249
        $this->usedAllFields = true;
250
        $classes = $this->getClasses();
251
        foreach ($classes as $key => $class) {
252
            $fields = DataObject::getSchema()->databaseFields($class, true);
253
254
            $this->addFulltextFieldsForClass($fields, $dbType);
255
        }
256
    }
257
258
    /**
259
     * Add all fields of a given type to the index
260
     *
261
     * @param array $fields The fields on the DataObject
262
     * @param string $dbType Class type the reflection should extend
263
     * @throws ReflectionException
264
     */
265
    protected function addFulltextFieldsForClass(array $fields, $dbType = DBString::class): void
266
    {
267
        foreach ($fields as $field => $type) {
268
            $pos = strpos($type, '(');
269
            if ($pos !== false) {
270
                $type = substr($type, 0, $pos);
271
            }
272
            $conf = Config::inst()->get(Injector::class, $type);
273
            $ref = new ReflectionClass($conf['class']);
274
            if ($ref->isSubclassOf($dbType)) {
275
                $this->addFulltextField($field);
276
            }
277
        }
278
    }
279
280
281
    /**
282
     * Add a filterable field
283
     *
284
     * @param $filterField
285
     * @return $this
286
     */
287
    public function addFilterField($filterField): self
288
    {
289
        $key = array_search($filterField, $this->getFulltextFields(), true);
290
        if ($key === false) {
291
            $this->filterFields[] = $filterField;
292
        }
293
294
        return $this;
295
    }
296
297
    /**
298
     * Set the filter fields
299
     *
300
     * @param array $filterFields
301
     * @return $this
302
     */
303
    public function setFilterFields($filterFields): self
304
    {
305
        $this->filterFields = $filterFields;
306
        foreach ($filterFields as $filterField) {
307
            $this->addFulltextField($filterField);
308
        }
309
310
        return $this;
311
    }
312
313
    /**
314
     * Get the filter fields
315
     *
316
     * @return array
317
     */
318
    public function getFilterFields(): array
319
    {
320
        return $this->filterFields;
321
    }
322
323
    /**
324
     * Add a field to sort on
325
     *
326
     * @param $sortField
327
     * @return $this
328
     */
329
    public function addSortField($sortField): self
330
    {
331
        if (!in_array($sortField, $this->getFulltextFields(), true) &&
332
            !in_array($sortField, $this->getFilterFields(), true)
333
        ) {
334
            $this->addFulltextField($sortField);
335
            $this->sortFields[] = $sortField;
336
        }
337
338
        $this->setSortFields(array_unique($this->getSortFields()));
339
340
        return $this;
341
    }
342
343
    /**
344
     * Set/override the sortable fields
345
     *
346
     * @param array $sortFields
347
     * @return $this
348
     */
349
    public function setSortFields($sortFields): self
350
    {
351
        $this->sortFields = $sortFields;
352
        foreach ($sortFields as $sortField) {
353
            $this->addFulltextField($sortField);
354
        }
355
356
        return $this;
357
    }
358
359
    /**
360
     * Get the sortable fields
361
     *
362
     * @return array
363
     */
364
    public function getSortFields(): array
365
    {
366
        return $this->sortFields;
367
    }
368
369
    /**
370
     * Stub to be compatible with Solr. Elastic stores everything anyway
371
     * @param array $storedFields
372
     * @return $this
373
     */
374
    public function setStoredFields(array $storedFields)
375
    {
376
        $this->storedFields = $storedFields;
377
        foreach ($storedFields as $storedField) {
378
            $this->addFulltextField($storedField);
379
        }
380
381
        return $this;
382
    }
383
384
    /**
385
     * @return array
386
     */
387
    public function getStoredFields(): array
388
    {
389
        return $this->storedFields;
390
    }
391
392
393
    /**
394
     * Add a copy field
395
     *
396
     * @param string $field Name of the copyfield
397
     * @param array $options Array of all fields that should be copied to this copyfield
398
     * @return $this
399
     */
400
    public function addCopyField($field, $options): self
401
    {
402
        $this->copyFields[$field] = $options;
403
404
        return $this;
405
    }
406
407
    /**
408
     * Set the copy fields
409
     *
410
     * @param array $copyField
411
     * @return $this
412
     */
413
    public function setCopyFields($copyField): self
414
    {
415
        $this->copyFields = $copyField;
416
417
        return $this;
418
    }
419
420
    /**
421
     * Return the copy fields
422
     *
423
     * @return array
424
     */
425
    public function getCopyFields(): array
426
    {
427
        return $this->copyFields;
428
    }
429
430
}
431