Passed
Push — master ( a02f46...bb45b5 )
by Paul
04:32
created

AbstractElasticsearchRepository::getIndex()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 7
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 0
crap 12
1
<?php
2
3
declare(strict_types=1);
4
5
namespace CCT\Component\ODMElasticsearch\Repository;
6
7
use CCT\Component\ODMElasticsearch\Repository\Exception\ReverseTransformationException;
8
use CCT\Component\ODMElasticsearch\Repository\Model\DocumentSupportInterface;
9
use CCT\Component\ODMElasticsearch\Repository\Traits\AggregationTrait;
10
use CCT\Component\ODMElasticsearch\Transformer\DataTransformerInterface;
11
use Elastica\Document;
12
use Elastica\Exception\InvalidException;
13
use Elastica\Query;
14
use Elastica\Query\AbstractQuery;
15
16
abstract class AbstractElasticsearchRepository implements ObjectRepositoryInterface
17
{
18
    use AggregationTrait;
19
20
    /**
21
     * The entity class name
22
     *
23
     * @var string
24
     */
25
    protected $entityName;
26
27
    /**
28
     * @var Index
29
     */
30
    protected $index;
31
32
    /**
33
     * @var Index
34
     */
35
    protected $indexAlias;
36
37
    /**
38
     * @var boolean
39
     */
40
    protected $useIndexAlias = false;
41
42
    /**
43
     * @var Query
44
     */
45
    protected $query;
46
47
    /**
48
     * @var DataTransformerInterface
49
     */
50
    protected $dataTransformer;
51
52
    /**
53
     * @var array
54
     */
55
    protected $errors;
56
57
    /**
58
     * AbstractElasticsearchRepository constructor.
59
     * Creates the index on Elasticsearch if not existing already.
60
     *
61
     * @param string $entityName
62
     * @param IndexMapping $indexMapping
63
     * @param DataTransformerInterface $dataTransformer
64
     *
65
     * @throws \Exception
66
     */
67 2
    public function __construct(
68
        string $entityName,
69
        IndexMapping $indexMapping,
70
        DataTransformerInterface $dataTransformer
71
    ) {
72 2
        $this->entityName = $entityName;
73
74 2
        $this->index = $indexMapping->getIndex($entityName);
75
76 2
        $this->indexAlias = $indexMapping->getIndexAlias($entityName);
77
78 2
        $this->dataTransformer = $dataTransformer;
79
80 2
        $this->query = new Query();
81
82 2
        $this->errors = [];
83 2
    }
84
85
    /**
86
     * Gets the results as hydrated objects
87
     *
88
     * @return array
89
     */
90
    public function getResults(): array
91
    {
92
        $resultSet = $this->getIndex()->search($this->query);
93
94
        $objects = [];
95
96
        foreach ($resultSet->getDocuments() as $document) {
97
            $objects[] = $this->reverseTransform($document);
98
        }
99
100
        return $objects;
101
    }
102
103
    /**
104
     * Gets the scalar results for the query.
105
     *
106
     * @return array
107
     */
108
    public function getScalarResults(): array
109
    {
110
        $resultSet = $this->getIndex()->search($this->query);
111
112
        return $resultSet->getResponse()->getData();
113
    }
114
115
    /**
116
     * Get count of query
117
     *
118
     * @return int number of documents matching the query
119
     */
120
    public function getCount(): int
121
    {
122
        return $this->getIndex()->count($this->query);
123
    }
124
125
    /**
126
     * @param DocumentSupportInterface $object
127
     *
128
     * @return bool
129
     */
130
    public function save(DocumentSupportInterface $object): bool
131
    {
132
        if ($this->index->hasDocument($object->getId())) {
133
            return $this->update($object);
134
        }
135
136
        return $this->insert($object);
137
    }
138
139
    /**
140
     * Bulk insert of multiple objects
141
     *
142
     * @param array $objects
143
     *
144
     * @return bool
145
     */
146
    public function batchInsert(array $objects): bool
147
    {
148
        $documents = [];
149
        foreach ($objects as $object) {
150
            $documents[] = $this->transformToDocument($object);
151
        }
152
153
        $this->index->addDocuments($documents);
154
        $this->index->refresh();
155
156
        return true;
157
    }
158
159
    /**
160
     * Adds a limit of results to the query.
161
     *
162
     * @param int $limit
163
     *
164
     * @return void
165
     */
166
    public function limit($limit): void
167
    {
168
        $this->query->setSize($limit);
169
    }
170
171
    /**
172
     * Offset\from of results to the query.
173
     *
174
     * @param int $offset
175
     *
176
     * @return void
177
     */
178
    public function offset($offset): void
179
    {
180
        $this->query->setFrom($offset);
181
    }
182
183
    /**
184
     * Just check if the there is some result for the criteria.
185
     * The result is based on ID column.
186
     *
187
     * @return bool
188
     */
189
    public function exists(): bool
190
    {
191
        return $this->getIndex()->exists();
192
    }
193
194
    /**
195
     * Specifies an ordering for the query results.
196
     * Replaces any previously specified orderings, if any.
197
     *
198
     * @param string $sort The ordering expression.
199
     * @param string $order The ordering direction.
200
     *
201
     * @return void
202
     */
203
    public function orderBy($sort, $order = null): void
204
    {
205
        $direction = $order ?? 'asc';
206
        $this->query->setSort([$sort => $direction]);
207
    }
208
209
    /**
210
     * It clears the current Criteria state (remove the filters).
211
     *
212
     * @return void
213
     */
214
    public function clear(): void
215
    {
216
        $this->query = new Query();
217
    }
218
219
    /**
220
     * Find entity by id on elastic search
221
     *
222
     * @param $id
223
     *
224
     * @return DocumentSupportInterface|null
225
     * @throws \ReflectionException
226
     */
227
    public function findById($id): ?DocumentSupportInterface
228
    {
229
        $document = $this->getIndex()->getDocument($id);
230
231
        return $this->reverseTransform($document);
232
    }
233
234
    /**
235
     * @param DocumentSupportInterface $object
236
     *
237
     * @return bool
238
     */
239
    public function insert(DocumentSupportInterface $object): bool
240
    {
241
        $document = $this->transformToDocument($object);
242
243
        try {
244
            $this->getIndex()->addDocument($document);
245
        } catch (InvalidException $exception) {
246
            $this->log($exception->getMessage());
247
248
            return false;
249
        }
250
251
        return true;
252
    }
253
254
    /**
255
     * @param DocumentSupportInterface $object
256
     *
257
     * @return bool
258
     */
259
    public function update(DocumentSupportInterface $object): bool
260
    {
261
        $document = $this->transformToDocument($object);
262
263
        try {
264
            $this->index->updateDocument($document);
265
        } catch (InvalidException $exception) {
266
            $this->log($exception->getMessage());
267
268
            return false;
269
        }
270
271
        return true;
272
    }
273
274
    /**
275
     * @param DocumentSupportInterface $object
276
     *
277
     * @return bool
278
     */
279
    public function delete(DocumentSupportInterface $object): bool
280
    {
281
        $document = $this->transformToDocument($object);
282
283
        try {
284
            $this->index->deleteDocument($document);
285
        } catch (InvalidException $exception) {
286
            $this->log($exception->getMessage());
287
288
            return false;
289
        }
290
291
        return true;
292
    }
293
294
    /**
295
     * Creates an elastica document
296
     *
297
     * @param DocumentSupportInterface $object
298
     *
299
     * @return Document
300
     */
301
    public function transformToDocument(DocumentSupportInterface $object): Document
302
    {
303
        if (null === $object->getId()) {
304
            throw new \RuntimeException('Object must have and id function getId()');
305
        }
306
307
        $data = $this->dataTransformer->transform($object);
308
309
        if (null === $data) {
310
            throw new \RuntimeException(
311
                'Data was not converted to array. Please check a config for ' . \get_class($object)
312
            );
313
        }
314
315
        return new Document($object->getId(), $data);
316
    }
317
318
    /**
319
     * Convert elastica document to entity object
320
     *
321
     * @param Document $document
322
     *
323
     * @return DocumentSupportInterface
324
     * @throws \ReflectionException
325
     */
326
    public function reverseTransform(Document $document): DocumentSupportInterface
327
    {
328
        $entity = new \ReflectionClass($this->entityName);
329
        /** @var DocumentSupportInterface $object */
330
        $object = $entity->newInstanceWithoutConstructor();
331
332
        if (false === $object instanceof DocumentSupportInterface) {
333
            throw new ReverseTransformationException(
334
                sprintf('Entity "%s" is not an instance of "%s"', $this->entityName, DocumentSupportInterface::class)
335
            );
336
        }
337
338
        $documentData = $document->getData();
339
        if (!\is_array($documentData)) {
0 ignored issues
show
introduced by
The condition is_array($documentData) is always true.
Loading history...
340
            return null;
341
        }
342
343
        $this->dataTransformer->reverseTransform($document->getData(), $object);
344
345
        return $object;
346
    }
347
348
    /**
349
     * Get any errors logged
350
     *
351
     * @return array
352
     */
353
    public function getErrors(): array
354
    {
355
        return $this->errors;
356
    }
357
358
    /**
359
     * Log error and other messages
360
     *
361
     * @param string $message
362
     * @param string $type
363
     */
364
    public function log(string $message, string $type = 'error'): void
365
    {
366
        if ('error' === $type) {
367
            $this->errors[] = $message;
368
        }
369
    }
370
371
    /**
372
     * {@inheritdoc}
373
     * @throws \ReflectionException
374
     */
375
    public function find($id): ?DocumentSupportInterface
376
    {
377
        return $this->findById($id);
378
    }
379
380
    /**
381
     * {@inheritdoc}
382
     */
383
    public function findAll(): array
384
    {
385
        $this->clear();
386
387
        $maxSize = $this->getCount();
388
389
        $this->query->setSize($maxSize);
390
391
        $this->query->setFrom(0);
392
393
        return $this->getResults();
394
    }
395
396
    /**
397
     * {@inheritdoc}
398
     */
399
    public function findBy(AbstractQuery $query, ?array $orderBy = null, int $limit = null, int $offset = null): array
400
    {
401
        $this->clear();
402
403
        $this->query->setQuery($query);
404
405
        if (null !== $orderBy && \count($orderBy) > 0) {
406
            $sort = reset($orderBy);
407
            $direction = next($orderBy) === 'desc' ? 'desc' : 'asc';
408
            $this->orderBy($sort, $direction);
409
        }
410
411
        $this->query->setSize($limit ?? 1000);
412
413
        $this->query->setFrom($offset ?? 0);
414
415
        return $this->getResults();
416
    }
417
418
    /**
419
     * {@inheritdoc}
420
     */
421
    public function findOneBy(AbstractQuery $query): ?DocumentSupportInterface
422
    {
423
        $this->clear();
424
425
        $this->query->setQuery($query);
426
427
        $resultSet = $this->getIndex()->search($this->query);
428
429
        $document = current($resultSet->getDocuments());
430
        if (false === $document) {
431
            return null;
432
        }
433
434
        return $this->reverseTransform($document);
435
    }
436
437
    /**
438
     * {@inheritdoc}
439
     */
440
    public function getClassName(): string
441
    {
442
        return $this->entityName;
443
    }
444
445
    /**
446
     * @return Index
447
     */
448
    protected function getIndex(): Index
449
    {
450
        if (true === $this->useIndexAlias && null !== $this->indexAlias) {
451
            return $this->indexAlias;
452
        }
453
454
        return $this->index;
455
    }
456
457
    /**
458
     * @param bool $use
459
     */
460
    protected function useIndexAlias(bool $use): void
461
    {
462
        $this->useIndexAlias = $use;
463
    }
464
}
465