Completed
Push — master ( c0af29...92d223 )
by Simonas
16:22 queued 09:39
created

Repository::findDocuments()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 10
Ratio 100 %

Importance

Changes 0
Metric Value
dl 10
loc 10
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 6
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 array  $raw
216
     * @param string $scrollDuration
217
     *
218
     * @return array
219
     */
220
    private function getScrollConfiguration($raw, $scrollDuration)
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
     * @deprecated Miss type in the function name, use findDocuments() instead. Will remove in 3.0
236
     *
237
     * @param Search $search
238
     * @return DocumentIterator
239
     */
240
    public function findDocument(Search $search)
241
    {
242
        return $this->findDocuments($search);
243
    }
244
245
246
    /**
247
     * Returns DocumentIterator with composed Document objects from array response.
248
     *
249
     * @param Search $search
250
     *
251
     * @return DocumentIterator
252
     */
253 View Code Duplication
    public function findDocuments(Search $search)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
254
    {
255
        $results = $this->executeSearch($search);
256
257
        return new DocumentIterator(
258
            $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
            $this->getManager(),
260
            $this->getScrollConfiguration($results, $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...
261
        );
262
    }
263
264
265
    /**
266
     * Returns ArrayIterator with access to unmodified documents directly.
267
     *
268
     * @param Search $search
269
     *
270
     * @return ArrayIterator
271
     */
272 View Code Duplication
    public function findArray(Search $search)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
273
    {
274
        $results = $this->executeSearch($search);
275
276
        return new ArrayIterator(
277
            $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...
278
            $this->getManager(),
279
            $this->getScrollConfiguration($results, $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...
280
        );
281
    }
282
283
    /**
284
     * Returns RawIterator with access to node with all returned values included.
285
     *
286
     * @param Search $search
287
     *
288
     * @return RawIterator
289
     */
290 View Code Duplication
    public function findRaw(Search $search)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
291
    {
292
        $results = $this->executeSearch($search);
293
294
        return new RawIterator(
295
            $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...
296
            $this->getManager(),
297
            $this->getScrollConfiguration($results, $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...
298
        );
299
    }
300
301
    /**
302
     * Executes search to the elasticsearch and returns raw response.
303
     *
304
     * @param Search $search
305
     *
306
     * @return array
307
     */
308
    private function executeSearch(Search $search)
309
    {
310
        return $this->getManager()->search([$this->getType()], $search->toArray(), $search->getQueryParams());
311
    }
312
313
    /**
314
     * Counts documents by given search.
315
     *
316
     * @param Search $search
317
     * @param array  $params
318
     * @param bool   $returnRaw If set true returns raw response gotten from client.
319
     *
320
     * @return int|array
321
     */
322
    public function count(Search $search, array $params = [], $returnRaw = false)
323
    {
324
        $body = array_merge(
325
            [
326
                'index' => $this->getManager()->getIndexName(),
327
                'type' => $this->type,
328
                'body' => $search->toArray(),
329
            ],
330
            $params
331
        );
332
333
        $results = $this
334
            ->getManager()
335
            ->getClient()->count($body);
336
337
        if ($returnRaw) {
338
            return $results;
339
        } else {
340
            return $results['count'];
341
        }
342
    }
343
344
    /**
345
     * Removes a single document data by ID.
346
     *
347
     * @param string $id      Document ID to remove
348
     * @param string $routing Custom routing for the document
349
     *
350
     * @return array
351
     *
352
     * @throws \LogicException
353
     */
354
    public function remove($id, $routing = null)
355
    {
356
        $params = [
357
            'index' => $this->getManager()->getIndexName(),
358
            'type' => $this->type,
359
            'id' => $id,
360
        ];
361
362
        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...
363
            $params['routing'] = $routing;
364
        }
365
366
        $response = $this->getManager()->getClient()->delete($params);
367
368
        return $response;
369
    }
370
371
    /**
372
     * Partial document update.
373
     *
374
     * @param string $id     Document id to update.
375
     * @param array  $fields Fields array to update.
376
     * @param string $script Groovy script to update fields.
377
     * @param array  $params Additional parameters to pass to the client.
378
     *
379
     * @return array
380
     */
381
    public function update($id, array $fields = [], $script = null, array $params = [])
382
    {
383
        $body = array_filter(
384
            [
385
                'doc' => $fields,
386
                'script' => $script,
387
            ]
388
        );
389
390
        $params = array_merge(
391
            [
392
                'id' => $id,
393
                'index' => $this->getManager()->getIndexName(),
394
                'type' => $this->type,
395
                'body' => $body,
396
            ],
397
            $params
398
        );
399
400
        return $this->getManager()->getClient()->update($params);
401
    }
402
403
    /**
404
     * Resolves elasticsearch type by class name.
405
     *
406
     * @param string $className
407
     *
408
     * @return array
409
     */
410
    private function resolveType($className)
411
    {
412
        return $this->getManager()->getMetadataCollector()->getDocumentType($className);
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