Completed
Branch dev (276354)
by Raffael
15:43
created

Elasticsearch   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 237
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 22
lcom 1
dl 0
loc 237
rs 10
c 0
b 0
f 0
cbo 7

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
B setOptions() 0 23 5
D search() 0 42 9
A getIndex() 0 4 1
A getEsClient() 0 10 2
B executeQuery() 0 60 4
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * balloon
7
 *
8
 * @copyright   Copryright (c) 2012-2018 gyselroth GmbH (https://gyselroth.com)
9
 * @license     GPL-3.0 https://opensource.org/licenses/GPL-3.0
10
 */
11
12
namespace Balloon\App\Elasticsearch;
13
14
use Balloon\Filesystem;
15
use Balloon\Filesystem\Node\NodeInterface;
16
use Balloon\Server;
17
use Balloon\Server\User;
18
use Elasticsearch\Client;
19
use Elasticsearch\ClientBuilder;
20
use Generator;
21
use InvalidArgumentException;
22
use Micro\Http\Router;
23
use Psr\Log\LoggerInterface;
24
25
class Elasticsearch
26
{
27
    /**
28
     * ES server.
29
     *
30
     * @var array
31
     */
32
    protected $es_server = ['http://localhost:9200'];
33
34
    /**
35
     * ES index.
36
     *
37
     * @var string
38
     */
39
    protected $es_index = 'balloon';
40
41
    /**
42
     * ES client.
43
     *
44
     * @var Elasticsearch
45
     */
46
    protected $client;
47
48
    /**
49
     * User.
50
     *
51
     * @var User
52
     */
53
    protected $user;
54
55
    /**
56
     * Filesystem.
57
     *
58
     * @var Filesystem
59
     */
60
    protected $fs;
61
62
    /**
63
     * Logger.
64
     *
65
     * @var LoggerInterface
66
     */
67
    protected $logger;
68
69
    /*
70
     * Constructor
71
     *
72
     * @param Router
73
     */
74
    public function __construct(Server $server, LoggerInterface $logger, ?Iterable $config = null)
75
    {
76
        $this->setOptions($config);
77
        $this->logger = $logger;
78
        $this->user = $server->getIdentity();
79
        $this->fs = $server->getFilesystem();
80
    }
81
82
    /**
83
     * Set options.
84
     *
85
     * @param iterable $config
86
     *
87
     * @return Elasticsearch
88
     */
89
    public function setOptions(?Iterable $config = null): self
90
    {
91
        if (null === $config) {
92
            return $this;
93
        }
94
95
        foreach ($config as $option => $value) {
96
            switch ($option) {
97
                case 'server':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
98
                    $this->es_server = (array) $value;
99
100
                break;
101
                case 'index':
102
                    $this->es_index = (string) $value;
103
104
                break;
105
                default:
106
                    throw new InvalidArgumentException('invalid option '.$option.' given');
107
            }
108
        }
109
110
        return $this;
111
    }
112
113
    /**
114
     * Search.
115
     *
116
     * @param array $query
117
     * @param int   $skip
118
     * @param int   $limit
119
     * @param int   $total
120
     *
121
     * @return Generator
122
     */
123
    public function search(array $query, int $deleted = NodeInterface::DELETED_INCLUDE, ?int $skip = null, ?int $limit = null, ?int &$total = null): Generator
124
    {
125
        $result = $this->executeQuery($query, $skip, $limit);
126
127
        if (isset($result['error'])) {
128
            throw new Exception\InvalidQuery('failed search index, query failed');
129
        }
130
131
        $this->logger->debug('elasticsearch query executed with ['.$result['hits']['total'].'] hits', [
132
            'category' => get_class($this),
133
            'params' => [
134
                'took' => $result['took'],
135
                'timed_out' => $result['timed_out'],
136
                '_shards' => $result['_shards'],
137
                'max_score' => $result['hits']['max_score'],
138
                'hits' => $result['hits']['total'],
139
            ],
140
        ]);
141
142
        $total = $result['hits']['total'];
143
144
        $nodes = [];
145
        foreach ($result['hits']['hits'] as $node) {
146
            if ('storage' === $node['_type']) {
147
                $nodes[] = $node['_id'];
148
            } elseif ('fs' === $node['_type']) {
149
                if (isset($node['_source']['metadata']['ref'])) {
150
                    foreach ($node['_source']['metadata']['ref'] as $blob) {
151
                        $nodes[] = $blob['id'];
152
                    }
153
                }
154
155
                if (isset($node['_source']['metadata']['share_ref'])) {
156
                    foreach ($node['_source']['metadata']['share_ref'] as $blob) {
157
                        $nodes[] = $blob['id'];
158
                    }
159
                }
160
            }
161
        }
162
163
        return $this->fs->findNodesById($nodes, null, $deleted);
164
    }
165
166
    /**
167
     * Get index name.
168
     *
169
     * @return string
170
     */
171
    public function getIndex(): string
172
    {
173
        return $this->es_index;
174
    }
175
176
    /**
177
     * Get es client.
178
     *
179
     * @return Elasticsearch
180
     */
181
    public function getEsClient(): Client
182
    {
183
        if ($this->client instanceof Client) {
184
            return $this->client;
185
        }
186
187
        return $this->client = ClientBuilder::create()
188
            ->setHosts($this->es_server)
189
            ->build();
190
    }
191
192
    /**
193
     * Search.
194
     *
195
     * @param array $query
196
     * @param int   $skip
197
     * @param int   $limit
198
     *
199
     * @return array
200
     */
201
    protected function executeQuery(array $query, ?int $skip = null, ?int $limit = null): array
202
    {
203
        $shares = $this->user->getShares(true);
204
        $bool = $query['body']['query'];
205
206
        $filter1 = [];
207
        $filter1['bool']['should'][]['term']['owner'] = (string) $this->user->getId();
208
        $filter1['bool']['should'][]['term']['metadata.ref.owner'] = (string) $this->user->getId();
209
210
        $share_filter = [
211
            'bool' => [
212
                'should' => [],
213
            ],
214
        ];
215
216
        foreach ($shares as $share) {
217
            $share_filter['bool']['should'][]['term']['metadata.share_ref.share'] = $share;
218
            $share_filter['bool']['should'][]['term']['reference'] = $share;
219
            $share_filter['bool']['should'][]['term']['shared'] = $share;
220
            $share_filter['bool']['minimum_should_match'] = 1;
221
        }
222
223
        if (count($share_filter['bool']['should']) >= 1) {
224
            $rights_filter = [];
225
            $rights_filter['bool']['should'][] = $share_filter;
226
            $rights_filter['bool']['should'][] = $filter1;
227
            $rights_filter['bool']['minimum_should_match'] = 1;
228
        } else {
229
            $rights_filter = $filter1;
230
        }
231
232
        $query['body']['query']['bool'] = [
233
            'must' => [
234
                $bool,
235
                $rights_filter,
236
            ],
237
        ];
238
239
        $query['_source'] = ['metadata.*', '_id', 'owner'];
240
        $query['index'] = $this->es_index;
241
        $query['from'] = $skip;
242
        $query['size'] = $limit;
243
244
        $this->logger->debug('prepared elasticsearch query', [
245
            'category' => get_class($this),
246
            'params' => $query,
247
        ]);
248
249
        $result = $this->getEsClient()->search($query);
250
251
        if (null === $result) {
252
            $this->logger->error('failed search elastic, query returned NULL', [
253
                'category' => get_class($this),
254
            ]);
255
256
            throw new Exception\InvalidQuery('general search error occured');
257
        }
258
259
        return $result;
260
    }
261
}
262