Completed
Push — upgrade ( f08820 )
by Simonas
14:39
created

Repository::update()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21

Duplication

Lines 21
Ratio 100 %

Importance

Changes 0
Metric Value
dl 21
loc 21
rs 9.584
c 0
b 0
f 0
cc 1
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
 * @deprecated it will be deleted in v7. Use `IndexService` service instead.
24
 *
25
 * Document repository class.
26
 */
27 View Code Duplication
class Repository
0 ignored issues
show
Duplication introduced by
This class 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...
28
{
29
    /**
30
     * @var Manager
31
     */
32
    private $manager;
33
34
    /**
35
     * @var string Fully qualified class name
36
     */
37
    private $className;
38
39
    /**
40
     * @var string Elasticsearch type name
41
     */
42
    private $type;
43
44
    /**
45
     * Constructor.
46
     *
47
     * @param Manager $manager
48
     * @param string  $className
49
     */
50
    public function __construct($manager, $className)
51
    {
52
        if (!is_string($className)) {
53
            throw new \InvalidArgumentException('Class name must be a string.');
54
        }
55
56
        if (!class_exists($className)) {
57
            throw new \InvalidArgumentException(
58
                sprintf('Cannot create repository for non-existing class "%s".', $className)
59
            );
60
        }
61
62
        $this->manager = $manager;
63
        $this->className = $className;
64
        $this->type = $this->resolveType($className);
65
    }
66
67
    /**
68
     * Returns elasticsearch manager used in the repository.
69
     *
70
     * @return Manager
71
     */
72
    public function getManager()
73
    {
74
        return $this->manager;
75
    }
76
77
    /**
78
     * @return array
79
     */
80
    public function getType()
81
    {
82
        return $this->type;
83
    }
84
85
    /**
86
     * Returns a single document data by ID or null if document is not found.
87
     *
88
     * @param string $id      Document ID to find
89
     * @param string $routing Custom routing for the document
90
     *
91
     * @return object
92
     */
93
    public function find($id, $routing = null)
94
    {
95
        return $this->manager->find($this->type, $id, $routing);
96
    }
97
98
    /**
99
     * Returns documents by a set of ids
100
     *
101
     * @param array $ids
102
     *
103
     * @return DocumentIterator The objects.
104
     */
105
    public function findByIds(array $ids)
106
    {
107
        $args = [];
108
        $manager = $this->getManager();
109
        $args['body']['docs'] = [];
110
        $args['index'] = $manager->getIndexName();
111
        $args['type'] = $this->getType();
112
113
        foreach ($ids as $id) {
114
            $args['body']['docs'][] = [
115
                '_id' => $id
116
            ];
117
        }
118
119
        $mgetResponse = $manager->getClient()->mget($args);
120
121
        $return = [
122
            'hits' => [
123
                'hits' => [],
124
                'total' => 0,
125
            ]
126
        ];
127
128
        foreach ($mgetResponse['docs'] as $item) {
129
            if ($item['found']) {
130
                $return['hits']['hits'][] = $item;
131
            }
132
        }
133
134
        $return['hits']['total'] = count($return['hits']['hits']);
135
136
        return new DocumentIterator($return, $manager);
137
    }
138
139
    /**
140
     * Finds documents by a set of criteria.
141
     *
142
     * @param array      $criteria   Example: ['group' => ['best', 'worst'], 'job' => 'medic'].
143
     * @param array|null $orderBy    Example: ['name' => 'ASC', 'surname' => 'DESC'].
144
     * @param int|null   $limit      Example: 5.
145
     * @param int|null   $offset     Example: 30.
146
     *
147
     * @return array|DocumentIterator The objects.
148
     */
149
    public function findBy(
150
        array $criteria,
151
        array $orderBy = [],
152
        $limit = null,
153
        $offset = null
154
    ) {
155
        $search = $this->createSearch();
156
157
        if ($limit !== null) {
158
            $search->setSize($limit);
159
        }
160
        if ($offset !== null) {
161
            $search->setFrom($offset);
162
        }
163
164
        foreach ($criteria as $field => $value) {
165
            if (preg_match('/^!(.+)$/', $field)) {
166
                $boolType = BoolQuery::MUST_NOT;
167
                $field = preg_replace('/^!/', '', $field);
168
            } else {
169
                $boolType = BoolQuery::MUST;
170
            }
171
172
            $search->addQuery(
173
                new QueryStringQuery(is_array($value) ? implode(' OR ', $value) : $value, ['default_field' => $field]),
174
                $boolType
175
            );
176
        }
177
178
        foreach ($orderBy as $field => $direction) {
179
            $search->addSort(new FieldSort($field, $direction));
180
        }
181
182
        return $this->findDocuments($search);
183
    }
184
185
    /**
186
     * Finds a single document by a set of criteria.
187
     *
188
     * @param array      $criteria   Example: ['group' => ['best', 'worst'], 'job' => 'medic'].
189
     * @param array|null $orderBy    Example: ['name' => 'ASC', 'surname' => 'DESC'].
190
     *
191
     * @return object|null The object.
192
     */
193
    public function findOneBy(array $criteria, array $orderBy = [])
194
    {
195
        return $this->findBy($criteria, $orderBy, 1, null)->current();
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
    public function findDocuments(Search $search)
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
    public function findArray(Search $search)
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
    public function findRaw(Search $search)
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