Completed
Pull Request — 5.0 (#797)
by
unknown
13:40
created

Repository::update()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 21
rs 9.3142
cc 1
eloc 11
nc 1
nop 4
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\ElasticsearchDSL\Query\FullText\QueryStringQuery;
17
use ONGR\ElasticsearchDSL\Query\Compound\BoolQuery;
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
            'hits' => [
121
                'hits' => [],
122
                'total' => 0,
123
            ]
124
        ];
125
126
        foreach ($mgetResponse['docs'] as $item) {
127
            if ($item['found']) {
128
                $return['hits']['hits'][] = $item;
129
            }
130
        }
131
132
        $return['hits']['total'] = count($return['hits']['hits']);
133
134
        return new DocumentIterator($return, $manager);
135
    }
136
137
    /**
138
     * Finds documents by a set of criteria.
139
     *
140
     * @param array      $criteria   Example: ['group' => ['best', 'worst'], 'job' => 'medic'].
141
     * @param array|null $orderBy    Example: ['name' => 'ASC', 'surname' => 'DESC'].
142
     * @param int|null   $limit      Example: 5.
143
     * @param int|null   $offset     Example: 30.
144
     *
145
     * @return array|DocumentIterator The objects.
146
     */
147
    public function findBy(
148
        array $criteria,
149
        array $orderBy = [],
150
        $limit = null,
151
        $offset = null
152
    ) {
153
        $search = $this->createSearch();
154
155
        if ($limit !== null) {
156
            $search->setSize($limit);
157
        }
158
        if ($offset !== null) {
159
            $search->setFrom($offset);
160
        }
161
162
        foreach ($criteria as $field => $value) {
163
            if (preg_match('/^!(.+)$/', $field)) {
164
                $boolType = BoolQuery::MUST_NOT;
165
                $field = preg_replace('/^!/', '', $field);
166
            } else {
167
                $boolType = BoolQuery::MUST;
168
            }
169
170
            $search->addQuery(
171
                new QueryStringQuery(is_array($value) ? implode(' OR ', $value) : $value, ['default_field' => $field]),
172
                $boolType
173
            );
174
        }
175
176
        foreach ($orderBy as $field => $direction) {
177
            $search->addSort(new FieldSort($field, $direction));
178
        }
179
180
        return $this->findDocuments($search);
181
    }
182
183
    /**
184
     * Finds a single document by a set of criteria.
185
     *
186
     * @param array      $criteria   Example: ['group' => ['best', 'worst'], 'job' => 'medic'].
187
     * @param array|null $orderBy    Example: ['name' => 'ASC', 'surname' => 'DESC'].
188
     *
189
     * @return object|null The object.
190
     */
191
    public function findOneBy(array $criteria, array $orderBy = [])
192
    {
193
        $result = $this->findBy($criteria, $orderBy, null, null);
194
195
        return $result->first();
196
    }
197
198
    /**
199
     * Returns search instance.
200
     *
201
     * @return Search
202
     */
203
    public function createSearch()
204
    {
205
        return new Search();
206
    }
207
208
    /**
209
     * Parses scroll configuration from raw response.
210
     *
211
     * @param array  $raw
212
     * @param string $scrollDuration
213
     *
214
     * @return array
215
     */
216
    public function getScrollConfiguration($raw, $scrollDuration)
217
    {
218
        $scrollConfig = [];
219
        if (isset($raw['_scroll_id'])) {
220
            $scrollConfig['_scroll_id'] = $raw['_scroll_id'];
221
            $scrollConfig['duration'] = $scrollDuration;
222
        }
223
224
        return $scrollConfig;
225
    }
226
227
    /**
228
     * Returns DocumentIterator with composed Document objects from array response.
229
     *
230
     * @param Search $search
231
     *
232
     * @return DocumentIterator
233
     */
234 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...
235
    {
236
        $results = $this->executeSearch($search);
237
238
        return new DocumentIterator(
239
            $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...
240
            $this->getManager(),
241
            $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...
242
        );
243
    }
244
245
246
    /**
247
     * Returns ArrayIterator with access to unmodified documents directly.
248
     *
249
     * @param Search $search
250
     *
251
     * @return ArrayIterator
252
     */
253 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...
254
    {
255
        $results = $this->executeSearch($search);
256
257
        return new ArrayIterator(
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
     * Returns RawIterator with access to node with all returned values included.
266
     *
267
     * @param Search $search
268
     *
269
     * @return RawIterator
270
     */
271 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...
272
    {
273
        $results = $this->executeSearch($search);
274
275
        return new RawIterator(
276
            $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...
277
            $this->getManager(),
278
            $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...
279
        );
280
    }
281
282
    /**
283
     * Executes search to the elasticsearch and returns raw response.
284
     *
285
     * @param Search $search
286
     *
287
     * @return array
288
     */
289
    private function executeSearch(Search $search)
290
    {
291
        return $this->getManager()->search([$this->getType()], $search->toArray(), $search->getUriParams());
292
    }
293
294
    /**
295
     * Counts documents by given search.
296
     *
297
     * @param Search $search
298
     * @param array  $params
299
     * @param bool   $returnRaw If set true returns raw response gotten from client.
300
     *
301
     * @return int|array
302
     */
303
    public function count(Search $search, array $params = [], $returnRaw = false)
304
    {
305
        $body = array_merge(
306
            [
307
                'index' => $this->getManager()->getIndexName(),
308
                'type' => $this->type,
309
                'body' => $search->toArray(),
310
            ],
311
            $params
312
        );
313
314
        $results = $this
315
            ->getManager()
316
            ->getClient()->count($body);
317
318
        if ($returnRaw) {
319
            return $results;
320
        } else {
321
            return $results['count'];
322
        }
323
    }
324
325
    /**
326
     * Removes a single document data by ID.
327
     *
328
     * @param string $id      Document ID to remove
329
     * @param string $routing Custom routing for the document
330
     *
331
     * @return array
332
     *
333
     * @throws \LogicException
334
     */
335
    public function remove($id, $routing = null)
336
    {
337
        $params = [
338
            'index' => $this->getManager()->getIndexName(),
339
            'type' => $this->type,
340
            'id' => $id,
341
        ];
342
343
        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...
344
            $params['routing'] = $routing;
345
        }
346
347
        $response = $this->getManager()->getClient()->delete($params);
348
349
        return $response;
350
    }
351
352
    /**
353
     * Partial document update.
354
     *
355
     * @param string $id     Document id to update.
356
     * @param array  $fields Fields array to update.
357
     * @param string $script Groovy script to update fields.
358
     * @param array  $params Additional parameters to pass to the client.
359
     *
360
     * @return array
361
     */
362
    public function update($id, array $fields = [], $script = null, array $params = [])
363
    {
364
        $body = array_filter(
365
            [
366
                'doc' => $fields,
367
                'script' => $script,
368
            ]
369
        );
370
371
        $params = array_merge(
372
            [
373
                'id' => $id,
374
                'index' => $this->getManager()->getIndexName(),
375
                'type' => $this->type,
376
                'body' => $body,
377
            ],
378
            $params
379
        );
380
381
        return $this->getManager()->getClient()->update($params);
382
    }
383
384
    /**
385
     * Resolves elasticsearch type by class name.
386
     *
387
     * @param string $className
388
     *
389
     * @return array
390
     */
391
    private function resolveType($className)
392
    {
393
        return $this->getManager()->getMetadataCollector()->getDocumentType($className);
394
    }
395
396
    /**
397
     * Returns fully qualified class name.
398
     *
399
     * @return string
400
     */
401
    public function getClassName()
402
    {
403
        return $this->className;
404
    }
405
}
406