Completed
Push — master ( bd2156...bea8fa )
by Simonas
11s
created

Repository::findArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
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 ONGR\ElasticsearchBundle\Result\ArrayIterator;
15
use ONGR\ElasticsearchBundle\Result\RawIterator;
16
use ONGR\ElasticsearchBundle\Result\Result;
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
 * Document repository class.
24
 */
25
class Repository
26
{
27
    /**
28
     * @var Manager
29
     */
30
    private $manager;
31
32
    /**
33
     * @var string Fully qualified class name
34
     */
35
    private $className;
36
37
    /**
38
     * @var string Elasticsearch type name
39
     */
40
    private $type;
41
42
    /**
43
     * Constructor.
44
     *
45
     * @param Manager $manager
46
     * @param string  $className
47
     */
48
    public function __construct($manager, $className)
49
    {
50
        if (!is_string($className)) {
51
            throw new \InvalidArgumentException('Class name must be a string.');
52
        }
53
54
        if (!class_exists($className)) {
55
            throw new \InvalidArgumentException(
56
                sprintf('Cannot create repository for non-existing class "%s".', $className)
57
            );
58
        }
59
60
        $this->manager = $manager;
61
        $this->className = $className;
62
        $this->type = $this->resolveType($className);
63
    }
64
65
    /**
66
     * Returns elasticsearch manager used in the repository.
67
     *
68
     * @return Manager
69
     */
70
    public function getManager()
71
    {
72
        return $this->manager;
73
    }
74
75
    /**
76
     * @return array
77
     */
78
    public function getType()
79
    {
80
        return $this->type;
81
    }
82
83
    /**
84
     * Returns a single document data by ID or null if document is not found.
85
     *
86
     * @param string $id      Document ID to find
87
     * @param string $routing Custom routing for the document
88
     *
89
     * @return object
90
     */
91
    public function find($id, $routing = null)
92
    {
93
        return $this->manager->find($this->type, $id, $routing);
94
    }
95
96
    /**
97
     * Returns documents by a set of ids
98
     *
99
     * @param array  $ids
100
     *
101
     * @return DocumentIterator The objects.
102
     */
103
    public function findByIds(array $ids)
104
    {
105
        $args = [];
106
        $manager = $this->getManager();
107
        $args['body']['docs'] = [];
108
        $args['index'] = $manager->getIndexName();
109
        $args['type'] = $this->getType();
110
111
        foreach ($ids as $id) {
112
            $args['body']['docs'][] = [
113
                '_id' => $id
114
            ];
115
        }
116
117
        $mgetResponse = $manager->getClient()->mget($args);
118
119
        $return = [];
120
121
        foreach ($mgetResponse['docs'] as $item) {
122
            if ($item['found']) {
123
                $return['hits']['hits'][] = $item;
124
            }
125
        }
126
127
        $return['hits']['total'] = count($return['hits']['hits']);
128
129
        return new DocumentIterator($return, $manager);
130
    }
131
132
    /**
133
     * Finds documents by a set of criteria.
134
     *
135
     * @param array      $criteria   Example: ['group' => ['best', 'worst'], 'job' => 'medic'].
136
     * @param array|null $orderBy    Example: ['name' => 'ASC', 'surname' => 'DESC'].
137
     * @param int|null   $limit      Example: 5.
138
     * @param int|null   $offset     Example: 30.
139
     *
140
     * @return array|DocumentIterator The objects.
141
     */
142
    public function findBy(
143
        array $criteria,
144
        array $orderBy = [],
145
        $limit = null,
146
        $offset = null
147
    ) {
148
        $search = $this->createSearch();
149
150
        if ($limit !== null) {
151
            $search->setSize($limit);
152
        }
153
        if ($offset !== null) {
154
            $search->setFrom($offset);
155
        }
156
157
        foreach ($criteria as $field => $value) {
158
            $search->addQuery(
159
                new QueryStringQuery(is_array($value) ? implode(' OR ', $value) : $value, ['default_field' => $field])
160
            );
161
        }
162
163
        foreach ($orderBy as $field => $direction) {
164
            $search->addSort(new FieldSort($field, $direction));
165
        }
166
167
        return $this->execute($search);
0 ignored issues
show
Deprecated Code introduced by
The method ONGR\ElasticsearchBundle...e\Repository::execute() has been deprecated with message: Use strict execute functions instead. e.g. executeIterator, executeRawIterator.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
168
    }
169
170
    /**
171
     * Finds a single document by a set of criteria.
172
     *
173
     * @param array      $criteria   Example: ['group' => ['best', 'worst'], 'job' => 'medic'].
174
     * @param array|null $orderBy    Example: ['name' => 'ASC', 'surname' => 'DESC'].
175
     *
176
     * @return object|null The object.
177
     */
178
    public function findOneBy(array $criteria, array $orderBy = [])
179
    {
180
        $result = $this->findBy($criteria, $orderBy, null, null);
181
182
        return $result->first();
183
    }
184
185
    /**
186
     * Returns search instance.
187
     *
188
     * @return Search
189
     */
190
    public function createSearch()
191
    {
192
        return new Search();
193
    }
194
195
    /**
196
     * Executes given search.
197
     *
198
     * @deprecated Use strict execute functions instead. e.g. executeIterator, executeRawIterator.
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 = Result::RESULTS_OBJECT)
207
    {
208
        return $this->manager->execute([$this->type], $search, $resultsType);
0 ignored issues
show
Deprecated Code introduced by
The method ONGR\ElasticsearchBundle...vice\Manager::execute() has been deprecated with message: use strict return type functions from Repository instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
209
    }
210
211
212
    /**
213
     * Parses scroll configuration from raw response.
214
     *
215
     * @param $raw
216
     * @param null $scrollDuration
217
     *
218
     * @return array
219
     */
220
    private function getScrollConfiguration($raw, $scrollDuration = null)
221
    {
222
        $scrollConfig = [];
223 View Code Duplication
        if (isset($raw['_scroll_id'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
224
            $scrollConfig['_scroll_id'] = $raw['_scroll_id'];
225
            $scrollConfig['duration'] = $scrollDuration;
226
        }
227
228
        return $scrollConfig;
229
    }
230
231
232
    /**
233
     * Returns DocumentIterator with composed Document objects from array response.
234
     *
235
     * @param Search $search
236
     *
237
     * @return DocumentIterator
238
     */
239
    public function findDocument(Search $search)
240
    {
241
        $results = $this->executeSearch($search);
242
243
        return new DocumentIterator($results, $this->getManager(), $this->getScrollConfiguration($results));
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...
244
    }
245
246
247
    /**
248
     * Returns ArrayIterator with access to unmodified documents directly.
249
     *
250
     * @param Search $search
251
     *
252
     * @return ArrayIterator
253
     */
254
    public function findArray(Search $search)
255
    {
256
        $results = $this->executeSearch($search);
257
258
        return new ArrayIterator($results, $this->getManager(), $this->getScrollConfiguration($results));
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...
259
    }
260
261
    /**
262
     * Returns RawIterator with access to node with all returned values included.
263
     *
264
     * @param Search $search
265
     *
266
     * @return RawIterator
267
     */
268
    public function findRaw(Search $search)
269
    {
270
        $results = $this->executeSearch($search);
271
272
        return new RawIterator($results, $this->getManager(), $this->getScrollConfiguration($results));
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...
273
    }
274
275
    /**
276
     * Executes search to the elasticsearch and returns raw response.
277
     *
278
     * @param Search $search
279
     *
280
     * @return array
281
     */
282
    private function executeSearch(Search $search)
283
    {
284
        return $this->getManager()->search([$this->getType()], $search->toArray(), $search->getQueryParams());
285
    }
286
287
    /**
288
     * Counts documents by given search.
289
     *
290
     * @param Search $search
291
     * @param array  $params
292
     * @param bool   $returnRaw If set true returns raw response gotten from client.
293
     *
294
     * @return int|array
295
     */
296
    public function count(Search $search, array $params = [], $returnRaw = false)
297
    {
298
        $body = array_merge(
299
            [
300
                'index' => $this->getManager()->getIndexName(),
301
                'type' => $this->type,
302
                'body' => $search->toArray(),
303
            ],
304
            $params
305
        );
306
307
        $results = $this
308
            ->getManager()
309
            ->getClient()->count($body);
310
311
        if ($returnRaw) {
312
            return $results;
313
        } else {
314
            return $results['count'];
315
        }
316
    }
317
318
    /**
319
     * Removes a single document data by ID.
320
     *
321
     * @param string $id      Document ID to remove
322
     * @param string $routing Custom routing for the document
323
     *
324
     * @return array
325
     *
326
     * @throws \LogicException
327
     */
328
    public function remove($id, $routing = null)
329
    {
330
        $params = [
331
            'index' => $this->getManager()->getIndexName(),
332
            'type' => $this->type,
333
            'id' => $id,
334
        ];
335
336
        if ($routing) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $routing of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
337
            $params['routing'] = $routing;
338
        }
339
340
        $response = $this->getManager()->getClient()->delete($params);
341
342
        return $response;
343
    }
344
345
    /**
346
     * Partial document update.
347
     *
348
     * @param string $id     Document id to update.
349
     * @param array  $fields Fields array to update.
350
     * @param string $script Groovy script to update fields.
351
     * @param array  $params Additional parameters to pass to the client.
352
     *
353
     * @return array
354
     */
355
    public function update($id, array $fields = [], $script = null, array $params = [])
356
    {
357
        $body = array_filter(
358
            [
359
                'doc' => $fields,
360
                'script' => $script,
361
            ]
362
        );
363
364
        $params = array_merge(
365
            [
366
                'id' => $id,
367
                'index' => $this->getManager()->getIndexName(),
368
                'type' => $this->type,
369
                'body' => $body,
370
            ],
371
            $params
372
        );
373
374
        return $this->getManager()->getClient()->update($params);
375
    }
376
377
    /**
378
     * Resolves elasticsearch type by class name.
379
     *
380
     * @param string $className
381
     *
382
     * @return array
383
     */
384
    private function resolveType($className)
385
    {
386
        return $this->getManager()->getMetadataCollector()->getDocumentType($className);
387
    }
388
389
    /**
390
     * Returns fully qualified class name.
391
     *
392
     * @return string
393
     */
394
    public function getClassName()
395
    {
396
        return $this->className;
397
    }
398
}
399