Completed
Push — master ( 09a07f...8de6a8 )
by Simonas
01:54
created

Repository::findDocument()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
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\ElasticsearchDSL\Query\QueryStringQuery;
17
use ONGR\ElasticsearchDSL\Search;
18
use ONGR\ElasticsearchDSL\Sort\FieldSort;
19
use ONGR\ElasticsearchBundle\Result\DocumentIterator;
20
21
/**
22
 * Document repository class.
23
 */
24
class Repository
25
{
26
    /**
27
     * @var Manager
28
     */
29
    private $manager;
30
31
    /**
32
     * @var string Fully qualified class name
33
     */
34
    private $className;
35
36
    /**
37
     * @var string Elasticsearch type name
38
     */
39
    private $type;
40
41
    /**
42
     * Constructor.
43
     *
44
     * @param Manager $manager
45
     * @param string  $className
46
     */
47
    public function __construct($manager, $className)
48
    {
49
        if (!is_string($className)) {
50
            throw new \InvalidArgumentException('Class name must be a string.');
51
        }
52
53
        if (!class_exists($className)) {
54
            throw new \InvalidArgumentException(
55
                sprintf('Cannot create repository for non-existing class "%s".', $className)
56
            );
57
        }
58
59
        $this->manager = $manager;
60
        $this->className = $className;
61
        $this->type = $this->resolveType($className);
62
    }
63
64
    /**
65
     * Returns elasticsearch manager used in the repository.
66
     *
67
     * @return Manager
68
     */
69
    public function getManager()
70
    {
71
        return $this->manager;
72
    }
73
74
    /**
75
     * @return array
76
     */
77
    public function getType()
78
    {
79
        return $this->type;
80
    }
81
82
    /**
83
     * Returns a single document data by ID or null if document is not found.
84
     *
85
     * @param string $id      Document ID to find
86
     * @param string $routing Custom routing for the document
87
     *
88
     * @return object
89
     */
90
    public function find($id, $routing = null)
91
    {
92
        return $this->manager->find($this->type, $id, $routing);
93
    }
94
95
    /**
96
     * Returns documents by a set of ids
97
     *
98
     * @param array  $ids
99
     *
100
     * @return DocumentIterator The objects.
101
     */
102
    public function findByIds(array $ids)
103
    {
104
        $args = [];
105
        $manager = $this->getManager();
106
        $args['body']['docs'] = [];
107
        $args['index'] = $manager->getIndexName();
108
        $args['type'] = $this->getType();
109
110
        foreach ($ids as $id) {
111
            $args['body']['docs'][] = [
112
                '_id' => $id
113
            ];
114
        }
115
116
        $mgetResponse = $manager->getClient()->mget($args);
117
118
        $return = [];
119
120
        foreach ($mgetResponse['docs'] as $item) {
121
            if ($item['found']) {
122
                $return['hits']['hits'][] = $item;
123
            }
124
        }
125
126
        $return['hits']['total'] = count($return['hits']['hits']);
127
128
        return new DocumentIterator($return, $manager);
129
    }
130
131
    /**
132
     * Finds documents by a set of criteria.
133
     *
134
     * @param array      $criteria   Example: ['group' => ['best', 'worst'], 'job' => 'medic'].
135
     * @param array|null $orderBy    Example: ['name' => 'ASC', 'surname' => 'DESC'].
136
     * @param int|null   $limit      Example: 5.
137
     * @param int|null   $offset     Example: 30.
138
     *
139
     * @return array|DocumentIterator The objects.
140
     */
141
    public function findBy(
142
        array $criteria,
143
        array $orderBy = [],
144
        $limit = null,
145
        $offset = null
146
    ) {
147
        $search = $this->createSearch();
148
149
        if ($limit !== null) {
150
            $search->setSize($limit);
151
        }
152
        if ($offset !== null) {
153
            $search->setFrom($offset);
154
        }
155
156
        foreach ($criteria as $field => $value) {
157
            $search->addQuery(
158
                new QueryStringQuery(is_array($value) ? implode(' OR ', $value) : $value, ['default_field' => $field])
159
            );
160
        }
161
162
        foreach ($orderBy as $field => $direction) {
163
            $search->addSort(new FieldSort($field, $direction));
164
        }
165
166
        return $this->findDocuments($search);
167
    }
168
169
    /**
170
     * Finds a single document by a set of criteria.
171
     *
172
     * @param array      $criteria   Example: ['group' => ['best', 'worst'], 'job' => 'medic'].
173
     * @param array|null $orderBy    Example: ['name' => 'ASC', 'surname' => 'DESC'].
174
     *
175
     * @return object|null The object.
176
     */
177
    public function findOneBy(array $criteria, array $orderBy = [])
178
    {
179
        $result = $this->findBy($criteria, $orderBy, null, null);
180
181
        return $result->first();
182
    }
183
184
    /**
185
     * Returns search instance.
186
     *
187
     * @return Search
188
     */
189
    public function createSearch()
190
    {
191
        return new Search();
192
    }
193
194
195
    /**
196
     * Parses scroll configuration from raw response.
197
     *
198
     * @param array  $raw
199
     * @param string $scrollDuration
200
     *
201
     * @return array
202
     */
203
    private function getScrollConfiguration($raw, $scrollDuration)
204
    {
205
        $scrollConfig = [];
206
        if (isset($raw['_scroll_id'])) {
207
            $scrollConfig['_scroll_id'] = $raw['_scroll_id'];
208
            $scrollConfig['duration'] = $scrollDuration;
209
        }
210
211
        return $scrollConfig;
212
    }
213
214
    /**
215
     * Returns DocumentIterator with composed Document objects from array response.
216
     *
217
     * @param Search $search
218
     *
219
     * @return DocumentIterator
220
     */
221 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...
222
    {
223
        $results = $this->executeSearch($search);
224
225
        return new DocumentIterator(
226
            $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...
227
            $this->getManager(),
228
            $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...
229
        );
230
    }
231
232
233
    /**
234
     * Returns ArrayIterator with access to unmodified documents directly.
235
     *
236
     * @param Search $search
237
     *
238
     * @return ArrayIterator
239
     */
240 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...
241
    {
242
        $results = $this->executeSearch($search);
243
244
        return new ArrayIterator(
245
            $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...
246
            $this->getManager(),
247
            $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...
248
        );
249
    }
250
251
    /**
252
     * Returns RawIterator with access to node with all returned values included.
253
     *
254
     * @param Search $search
255
     *
256
     * @return RawIterator
257
     */
258 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...
259
    {
260
        $results = $this->executeSearch($search);
261
262
        return new RawIterator(
263
            $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...
264
            $this->getManager(),
265
            $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...
266
        );
267
    }
268
269
    /**
270
     * Executes search to the elasticsearch and returns raw response.
271
     *
272
     * @param Search $search
273
     *
274
     * @return array
275
     */
276
    private function executeSearch(Search $search)
277
    {
278
        return $this->getManager()->search([$this->getType()], $search->toArray(), $search->getQueryParams());
279
    }
280
281
    /**
282
     * Counts documents by given search.
283
     *
284
     * @param Search $search
285
     * @param array  $params
286
     * @param bool   $returnRaw If set true returns raw response gotten from client.
287
     *
288
     * @return int|array
289
     */
290
    public function count(Search $search, array $params = [], $returnRaw = false)
291
    {
292
        $body = array_merge(
293
            [
294
                'index' => $this->getManager()->getIndexName(),
295
                'type' => $this->type,
296
                'body' => $search->toArray(),
297
            ],
298
            $params
299
        );
300
301
        $results = $this
302
            ->getManager()
303
            ->getClient()->count($body);
304
305
        if ($returnRaw) {
306
            return $results;
307
        } else {
308
            return $results['count'];
309
        }
310
    }
311
312
    /**
313
     * Removes a single document data by ID.
314
     *
315
     * @param string $id      Document ID to remove
316
     * @param string $routing Custom routing for the document
317
     *
318
     * @return array
319
     *
320
     * @throws \LogicException
321
     */
322
    public function remove($id, $routing = null)
323
    {
324
        $params = [
325
            'index' => $this->getManager()->getIndexName(),
326
            'type' => $this->type,
327
            'id' => $id,
328
        ];
329
330
        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...
331
            $params['routing'] = $routing;
332
        }
333
334
        $response = $this->getManager()->getClient()->delete($params);
335
336
        return $response;
337
    }
338
339
    /**
340
     * Partial document update.
341
     *
342
     * @param string $id     Document id to update.
343
     * @param array  $fields Fields array to update.
344
     * @param string $script Groovy script to update fields.
345
     * @param array  $params Additional parameters to pass to the client.
346
     *
347
     * @return array
348
     */
349
    public function update($id, array $fields = [], $script = null, array $params = [])
350
    {
351
        $body = array_filter(
352
            [
353
                'doc' => $fields,
354
                'script' => $script,
355
            ]
356
        );
357
358
        $params = array_merge(
359
            [
360
                'id' => $id,
361
                'index' => $this->getManager()->getIndexName(),
362
                'type' => $this->type,
363
                'body' => $body,
364
            ],
365
            $params
366
        );
367
368
        return $this->getManager()->getClient()->update($params);
369
    }
370
371
    /**
372
     * Resolves elasticsearch type by class name.
373
     *
374
     * @param string $className
375
     *
376
     * @return array
377
     */
378
    private function resolveType($className)
379
    {
380
        return $this->getManager()->getMetadataCollector()->getDocumentType($className);
381
    }
382
383
    /**
384
     * Returns fully qualified class name.
385
     *
386
     * @return string
387
     */
388
    public function getClassName()
389
    {
390
        return $this->className;
391
    }
392
}
393