Completed
Push — 1.2 ( b13b4a )
by Simonas
10s
created

Repository::findByIds()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 28
rs 8.5806
cc 4
eloc 16
nc 6
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\RawIterator;
15
use ONGR\ElasticsearchBundle\Result\Result;
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->execute($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
     * Executes given search.
196
     *
197
     * @param Search $search
198
     * @param string $resultsType
199
     *
200
     * @return DocumentIterator|RawIterator|array
201
     *
202
     * @throws \Exception
203
     */
204
    public function execute(Search $search, $resultsType = Result::RESULTS_OBJECT)
205
    {
206
        return $this->manager->execute([$this->type], $search, $resultsType);
207
    }
208
209
    /**
210
     * Counts documents by given search.
211
     *
212
     * @param Search $search
213
     * @param array  $params
214
     * @param bool   $returnRaw If set true returns raw response gotten from client.
215
     *
216
     * @return int|array
217
     */
218
    public function count(Search $search, array $params = [], $returnRaw = false)
219
    {
220
        $body = array_merge(
221
            [
222
                'index' => $this->getManager()->getIndexName(),
223
                'type' => $this->type,
224
                'body' => $search->toArray(),
225
            ],
226
            $params
227
        );
228
229
        $results = $this
230
            ->getManager()
231
            ->getClient()->count($body);
232
233
        if ($returnRaw) {
234
            return $results;
235
        } else {
236
            return $results['count'];
237
        }
238
    }
239
240
    /**
241
     * Removes a single document data by ID.
242
     *
243
     * @param string $id      Document ID to remove
244
     * @param string $routing Custom routing for the document
245
     *
246
     * @return array
247
     *
248
     * @throws \LogicException
249
     */
250
    public function remove($id, $routing = null)
251
    {
252
        $params = [
253
            'index' => $this->getManager()->getIndexName(),
254
            'type' => $this->type,
255
            'id' => $id,
256
        ];
257
258
        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...
259
            $params['routing'] = $routing;
260
        }
261
262
        $response = $this->getManager()->getClient()->delete($params);
263
264
        return $response;
265
    }
266
267
    /**
268
     * Partial document update.
269
     *
270
     * @param string $id     Document id to update.
271
     * @param array  $fields Fields array to update.
272
     * @param string $script Groovy script to update fields.
273
     * @param array  $params Additional parameters to pass to the client.
274
     *
275
     * @return array
276
     */
277
    public function update($id, array $fields = [], $script = null, array $params = [])
278
    {
279
        $body = array_filter(
280
            [
281
                'doc' => $fields,
282
                'script' => $script,
283
            ]
284
        );
285
286
        $params = array_merge(
287
            [
288
                'id' => $id,
289
                'index' => $this->getManager()->getIndexName(),
290
                'type' => $this->type,
291
                'body' => $body,
292
            ],
293
            $params
294
        );
295
296
        return $this->getManager()->getClient()->update($params);
297
    }
298
299
    /**
300
     * Resolves elasticsearch type by class name.
301
     *
302
     * @param string $className
303
     *
304
     * @return array
305
     */
306
    private function resolveType($className)
307
    {
308
        return $this->getManager()->getMetadataCollector()->getDocumentType($className);
309
    }
310
311
    /**
312
     * Returns fully qualified class name.
313
     *
314
     * @return string
315
     */
316
    public function getClassName()
317
    {
318
        return $this->className;
319
    }
320
}
321