Code Duplication    Length = 376-379 lines in 2 locations

Service/IndexService.php 1 location

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

Service/Repository.php 1 location

@@ 27-405 (lines=379) @@
24
 *
25
 * Document repository class.
26
 */
27
class Repository
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,
240
            $this->getManager(),
241
            $this->getScrollConfiguration($results, $search->getScroll())
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,
259
            $this->getManager(),
260
            $this->getScrollConfiguration($results, $search->getScroll())
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,
277
            $this->getManager(),
278
            $this->getScrollConfiguration($results, $search->getScroll())
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) {
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