Completed
Pull Request — master (#513)
by Mantas
03:31
created

Repository::execute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
rs 9.4286
cc 1
eloc 5
nc 1
nop 2
1
<?php
2
3
/*
4
 * This file is part of the ONGR package.
5
 *
6
 * (c) NFQ Technologies UAB <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ONGR\ElasticsearchBundle\Service;
13
14
use Elasticsearch\Common\Exceptions\Missing404Exception;
15
use ONGR\ElasticsearchBundle\Result\AbstractResultsIterator;
16
use ONGR\ElasticsearchBundle\Result\RawIterator;
17
use ONGR\ElasticsearchDSL\Query\QueryStringQuery;
18
use ONGR\ElasticsearchDSL\Search;
19
use ONGR\ElasticsearchDSL\Sort\FieldSort;
20
use ONGR\ElasticsearchBundle\Result\DocumentIterator;
21
22
/**
23
 * Repository class.
24
 */
25
class Repository
26
{
27
    const RESULTS_ARRAY = 'array';
28
    const RESULTS_OBJECT = 'object';
29
    const RESULTS_RAW = 'raw';
30
    const RESULTS_RAW_ITERATOR = 'raw_iterator';
31
32
    /**
33
     * @var Manager
34
     */
35
    private $manager;
36
37
    /**
38
     * @var string Fully qualified class name
39
     */
40
    private $className;
41
42
    /**
43
     * @var string Elasticsearch type name
44
     */
45
    private $type;
46
47
    /**
48
     * Constructor.
49
     *
50
     * @param Manager $manager
51
     * @param string  $className
52
     */
53
    public function __construct($manager, $className)
54
    {
55
        if (!is_string($className)) {
56
            throw new \InvalidArgumentException('Class name must be a string.');
57
        }
58
59
        if (!class_exists($className)) {
60
            throw new \InvalidArgumentException(
61
                sprintf('Cannot create repository for non-existing class "%s".', $className)
62
            );
63
        }
64
65
        $this->manager = $manager;
66
        $this->type = $this->resolveType($className);
67
    }
68
69
    /**
70
     * Returns elasticsearch manager used in the repository.
71
     *
72
     * @return Manager
73
     */
74
    public function getManager()
75
    {
76
        return $this->manager;
77
    }
78
79
    /**
80
     * @return array
81
     */
82
    public function getType()
83
    {
84
        return $this->type;
85
    }
86
87
    /**
88
     * Returns a single document data by ID or null if document is not found.
89
     *
90
     * @param string $id         Document Id to find.
91
     * @param string $resultType Result type returned.
92
     *
93
     * @return object|null
94
     *
95
     * @throws \LogicException
96
     */
97
    public function find($id, $resultType = self::RESULTS_OBJECT)
98
    {
99
        $params = [
100
            'index' => $this->getManager()->getIndexName(),
101
            'type' => $this->type,
102
            'id' => $id,
103
        ];
104
105
        try {
106
            $result = $this->getManager()->getClient()->get($params);
107
        } catch (Missing404Exception $e) {
108
            return null;
109
        }
110
111
        if ($resultType === self::RESULTS_OBJECT) {
112
            return $this->getManager()->getConverter()->convertToDocument($result, $this);
0 ignored issues
show
Documentation introduced by
$result is of type callable, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
113
        }
114
115
        return $this->parseResult($result, $resultType, '');
0 ignored issues
show
Documentation introduced by
$result is of type callable, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
116
    }
117
118
    /**
119
     * Finds entities by a set of criteria.
120
     *
121
     * @param array      $criteria   Example: ['group' => ['best', 'worst'], 'job' => 'medic'].
122
     * @param array|null $orderBy    Example: ['name' => 'ASC', 'surname' => 'DESC'].
123
     * @param int|null   $limit      Example: 5.
124
     * @param int|null   $offset     Example: 30.
125
     * @param string     $resultType Result type returned.
126
     *
127
     * @return array|DocumentIterator The objects.
128
     */
129
    public function findBy(
130
        array $criteria,
131
        array $orderBy = [],
132
        $limit = null,
133
        $offset = null,
134
        $resultType = self::RESULTS_OBJECT
135
    ) {
136
        $search = $this->createSearch();
137
138
        if ($limit !== null) {
139
            $search->setSize($limit);
140
        }
141
        if ($offset !== null) {
142
            $search->setFrom($offset);
143
        }
144
145
        foreach ($criteria as $field => $value) {
146
            $search->addQuery(
147
                new QueryStringQuery(is_array($value) ? implode(' OR ', $value) : $value, ['default_field' => $field])
148
            );
149
        }
150
151
        foreach ($orderBy as $field => $direction) {
152
            $search->addSort(new FieldSort($field, $direction));
153
        }
154
155
        return $this->execute($search, $resultType);
156
    }
157
158
    /**
159
     * Finds only one entity by a set of criteria.
160
     *
161
     * @param array      $criteria   Example: ['group' => ['best', 'worst'], 'job' => 'medic'].
162
     * @param array|null $orderBy    Example: ['name' => 'ASC', 'surname' => 'DESC'].
163
     * @param string     $resultType Result type returned.
164
     *
165
     * @throws \Exception
166
     *
167
     * @return object|null The object.
168
     */
169
    public function findOneBy(array $criteria, array $orderBy = [], $resultType = self::RESULTS_OBJECT)
170
    {
171
        $result = $this->findBy($criteria, $orderBy, null, null, $resultType);
172
173
        switch ($resultType) {
174
            case self::RESULTS_OBJECT:
175
            case self::RESULTS_RAW_ITERATOR:
176
                return $result->first();
177
            case self::RESULTS_ARRAY:
178
                return array_shift($result);
179
            case self::RESULTS_RAW:
180
                return array_shift($result['hits']['hits']);
181
            default:
182
                throw new \Exception('Wrong results type selected');
183
        }
184
    }
185
186
    /**
187
     * Returns search instance.
188
     *
189
     * @return Search
190
     */
191
    public function createSearch()
192
    {
193
        return new Search();
194
    }
195
196
    /**
197
     * Executes given search.
198
     *
199
     * @param Search $search
200
     * @param string $resultsType
201
     *
202
     * @return DocumentIterator|RawIterator|array
203
     *
204
     * @throws \Exception
205
     */
206
    public function execute(Search $search, $resultsType = self::RESULTS_OBJECT)
207
    {
208
        $results = $this
209
            ->getManager()
210
            ->search([$this->type], $search->toArray(), $search->getQueryParams());
211
212
        return $this->parseResult($results, $resultsType, $search->getScroll());
0 ignored issues
show
Documentation introduced by
$results is of type callable, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
213
    }
214
215
    /**
216
     * Counts documents by given search.
217
     *
218
     * @param Search $search
219
     * @param array  $params
220
     * @param bool   $returnRaw If set true returns raw response gotten from client.
221
     *
222
     * @return int|array
223
     */
224
    public function count(Search $search, array $params = [], $returnRaw = false)
225
    {
226
        $body = array_merge(
227
            [
228
                'index' => $this->getManager()->getIndexName(),
229
                'type' => $this->type,
230
                'body' => $search->toArray(),
231
            ],
232
            $params
233
        );
234
235
        $results = $this
236
            ->getManager()
237
            ->getClient()->count($body);
238
239
        if ($returnRaw) {
240
            return $results;
241
        } else {
242
            return $results['count'];
243
        }
244
    }
245
246
    /**
247
     * Delete by query.
248
     *
249
     * @param Search $search
250
     *
251
     * @return array
252
     */
253
    public function deleteByQuery(Search $search)
254
    {
255
        $params = [
256
            'index' => $this->getManager()->getIndexName(),
257
            'type' => $this->type,
258
            'body' => $search->toArray(),
259
        ];
260
261
        return $this
262
            ->getManager()
263
            ->getClient()
264
            ->deleteByQuery($params);
265
    }
266
267
    /**
268
     * Fetches next set of results.
269
     *
270
     * @param string $scrollId
271
     * @param string $scrollDuration
272
     * @param string $resultsType
273
     *
274
     * @return AbstractResultsIterator
275
     *
276
     * @throws \Exception
277
     */
278
    public function scroll(
279
        $scrollId,
280
        $scrollDuration = '5m',
281
        $resultsType = self::RESULTS_OBJECT
282
    ) {
283
        $results = $this->getManager()->getClient()->scroll(['scroll_id' => $scrollId, 'scroll' => $scrollDuration]);
284
285
        return $this->parseResult($results, $resultsType, $scrollDuration);
0 ignored issues
show
Documentation introduced by
$results is of type callable, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
286
    }
287
288
    /**
289
     * Removes a single document data by ID.
290
     *
291
     * @param string $id Document ID to remove.
292
     *
293
     * @return array
294
     *
295
     * @throws \LogicException
296
     */
297
    public function remove($id)
298
    {
299
        $params = [
300
            'index' => $this->getManager()->getIndexName(),
301
            'type' => $this->type,
302
            'id' => $id,
303
        ];
304
305
        $response = $this->getManager()->getClient()->delete($params);
306
307
        return $response;
308
    }
309
310
    /**
311
     * Partial document update.
312
     *
313
     * @param string $id     Document id to update.
314
     * @param array  $fields Fields array to update.
315
     * @param string $script Groovy script to update fields.
316
     * @param array  $params Additional parameters to pass to the client.
317
     *
318
     * @return array
319
     */
320
    public function update($id, array $fields = [], $script = null, array $params = [])
321
    {
322
        $body = array_filter(
323
            [
324
                'doc' => $fields,
325
                'script' => $script,
326
            ]
327
        );
328
329
        $params = array_merge(
330
            [
331
                'id' => $id,
332
                'index' => $this->getManager()->getIndexName(),
333
                'type' => $this->type,
334
                'body' => $body,
335
            ],
336
            $params
337
        );
338
339
        return $this->getManager()->getClient()->update($params);
340
    }
341
342
    /**
343
     * Resolves elasticsearch type by class name.
344
     *
345
     * @param string $className
346
     *
347
     * @return array
348
     */
349
    private function resolveType($className)
350
    {
351
        return $this->getManager()->getMetadataCollector()->getDocumentType($className);
352
    }
353
354
    /**
355
     * Parses raw result.
356
     *
357
     * @param array  $raw
358
     * @param string $resultsType
359
     * @param string $scrollDuration
360
     *
361
     * @return DocumentIterator|RawIterator|array
362
     *
363
     * @throws \Exception
364
     */
365
    private function parseResult($raw, $resultsType, $scrollDuration = null)
366
    {
367
        $scrollConfig = [];
368
        if (isset($raw['_scroll_id'])) {
369
            $scrollConfig['_scroll_id'] = $raw['_scroll_id'];
370
            $scrollConfig['duration'] = $scrollDuration;
371
        }
372
373
        switch ($resultsType) {
374
            case self::RESULTS_OBJECT:
375
                return new DocumentIterator($raw, $this, $scrollConfig);
376
            case self::RESULTS_ARRAY:
377
                return $this->convertToNormalizedArray($raw);
378
            case self::RESULTS_RAW:
379
                return $raw;
380
            case self::RESULTS_RAW_ITERATOR:
381
                return new RawIterator($raw, $this, $scrollConfig);
382
            default:
383
                throw new \Exception('Wrong results type selected');
384
        }
385
    }
386
387
    /**
388
     * Normalizes response array.
389
     *
390
     * @param array $data
391
     *
392
     * @return array
393
     */
394
    private function convertToNormalizedArray($data)
395
    {
396
        if (array_key_exists('_source', $data)) {
397
            return $data['_source'];
398
        }
399
400
        $output = [];
401
402
        if (isset($data['hits']['hits'][0]['_source'])) {
403
            foreach ($data['hits']['hits'] as $item) {
404
                $output[] = $item['_source'];
405
            }
406
        } elseif (isset($data['hits']['hits'][0]['fields'])) {
407
            foreach ($data['hits']['hits'] as $item) {
408
                $output[] = array_map('reset', $item['fields']);
409
            }
410
        }
411
412
        return $output;
413
    }
414
415
    /**
416
     * Returns fully qualified class name.
417
     *
418
     * @return string
419
     */
420
    public function getClassName()
421
    {
422
        return $this->className;
423
    }
424
}
425