Passed
Push — feature/streamline-schema-inde... ( 97bf6c...e143ba )
by Bas
03:31
created

Indexes::spatialIndex()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 3
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace LaravelFreelancerNL\Aranguent\Schema\Concerns;
4
5
use ArangoClient\Exceptions\ArangoException;
6
use Illuminate\Support\Fluent;
7
8
trait Indexes
9
{
10
    /**
11
     * Add a new index command to the blueprint.
12
     *
13
     * @param  string|array<string>|null  $columns
14
     */
15 28
    protected function indexCommand(
16
        string $type,
17
        array|null|string $columns = null,
18
        string $name = null,
19
        array $indexOptions = []
20
    ): Fluent {
21 28
        if ($columns === null) {
22 15
            $columns = end($this->columns);
23
        }
24
25 28
        if (is_string($columns)) {
26 20
            $columns = [$columns];
27
        }
28
29 28
        $columns = $this->renameIdField($columns);
0 ignored issues
show
Bug introduced by
It seems like renameIdField() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

29
        /** @scrutinizer ignore-call */ 
30
        $columns = $this->renameIdField($columns);
Loading history...
30
31 28
        $indexOptions['name'] = $name ?: $this->createIndexName($type, $columns, $indexOptions);
32
33 28
        return $this->addCommand('index', compact('type', 'columns', 'indexOptions'));
0 ignored issues
show
Bug introduced by
It seems like addCommand() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

33
        return $this->/** @scrutinizer ignore-call */ addCommand('index', compact('type', 'columns', 'indexOptions'));
Loading history...
34
    }
35
36
    /**
37
     * Specify an index for the table.
38
     *
39
     * @param array|null|string $columns
40
     * @param null|string $name
41
     * @param null|string $algorithm
42
     * @param array<mixed> $indexOptions
43
     * @return Fluent
44
     */
45 6
    public function index($columns = null, $name = null, $algorithm = null, $indexOptions = [])
46
    {
47 6
        $type = $this->mapIndexAlgorithm($algorithm);
48
49 6
        return $this->indexCommand($type, $columns, $name, $indexOptions);
50
    }
51
52
    /**
53
     * Specify a spatial index for the table.
54
     *
55
     * @param string|array $columns
56
     * @param null|string $name
57
     * @param array<mixed> $indexOptions
58
     * @return Fluent
59
     */
60 2
    public function spatialIndex($columns, $name = null, $indexOptions = [])
61
    {
62 2
        return $this->indexCommand('geo', $columns, $name, $indexOptions);
63
    }
64
65
    /**
66
     *  Specify a spatial index for the table.
67
     *
68
     * @param  array<mixed>|null|string  $columns
69
     * @param  null|string  $name
70
     * @param  array<mixed>  $indexOptions
71
     * @return Fluent
72
     */
73 1
    public function geoIndex($columns, $name = null, $indexOptions = [])
74
    {
75 1
        return $this->spatialIndex($columns, $name, $indexOptions);
76
    }
77
78
    /**
79
     *  Specify a inverted index for the table.
80
     *
81
     * @param  array<mixed>|null|string  $columns
82
     * @param  null|string  $name
83
     * @param  array<mixed>  $indexOptions
84
     * @return Fluent
85
     */
86 13
    public function invertedIndex($columns = null, $name = null, $indexOptions = [])
87
    {
88 13
        return $this->indexCommand('inverted', $columns, $name, $indexOptions);
89
    }
90
91 1
    public function persistentIndex(array $columns = null, string $name = null, array $indexOptions = []): Fluent
92
    {
93 1
        return $this->indexCommand('persistent', $columns, $name, $indexOptions);
94
    }
95
96
    /**
97
     * @param array<string>|null|string $columns
98
     * @param string|null $name
99
     * @param array<mixed> $indexOptions
100
     * @return Fluent
101
     */
102 15
    public function primary($columns = null, string $name = null, array $indexOptions = []): Fluent
103
    {
104 15
        $indexOptions['unique'] = true;
105
106 15
        return $this->indexCommand('persistent', $columns, $name, $indexOptions);
107
    }
108
109
    /**
110
     * Create a TTL index for the table.
111
     *
112
     * @param string $columns
113
     * @param int $expireAfter
114
     * @param null $name
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $name is correct as it would always require null to be passed?
Loading history...
115
     * @param array $indexOptions
116
     * @return Fluent
117
     */
118 2
    public function ttlIndex($columns, $expireAfter, $name = null, $indexOptions = []): Fluent
119
    {
120 2
        $indexOptions['expireAfter'] = $expireAfter;
121 2
        return $this->indexCommand('ttl', $columns, $name, $indexOptions);
122
    }
123
124
    /**
125
     * Specify a unique index for the table.
126
     *
127
     * @param  string|array  $columns
128
     * @param  string  $name
129
     * @param  string|null  $algorithm
130
     */
131 14
    public function unique($columns = null, $name = null, $algorithm = null): Fluent
132
    {
133 14
        $type = $this->mapIndexAlgorithm($algorithm);
134
135 14
        $indexOptions = [];
136 14
        $indexOptions['unique'] = true;
137
138 14
        return $this->indexCommand($type, $columns, $name, $indexOptions);
139
    }
140
141
    /**
142
     * @throws ArangoException
143
     */
144 28
    public function executeIndexCommand(Fluent $command)
145
    {
146 28
        if ($this->connection->pretending()) {
147
            $this->connection->logQuery('/* ' . $command->explanation . " */\n", []);
148
149
            return;
150
        }
151
152
        // Skip persistent indexes set solely on id - ArangoDB already sets a unique index on _key.
153
        if (
154 28
            $command->type === 'persistent'
0 ignored issues
show
introduced by
The condition $command->type === 'persistent' is always false.
Loading history...
155 28
            && count($command->columns) === 1
156 28
            && $command->columns[0] === '_key'
157
        ) {
158 1
            return;
159
        }
160
161 27
        $options = [
162 27
            'type' => $command->type,
163 27
            'fields' => $command->columns,
164 27
            'unique' => $command->unique,
165 27
            'options' => $command->indexOptions,
166 27
        ];
167
168 27
        if (isset($command->indexOptions) && is_array($command->indexOptions)) {
0 ignored issues
show
introduced by
The condition is_array($command->indexOptions) is always false.
Loading history...
169 27
            $options = array_merge($options, $command->indexOptions);
170
        }
171
172 27
        $this->schemaManager->createIndex($this->table, $options);
173
    }
174
175
    /**
176
     * Indicate that the given index should be dropped.
177
     */
178 9
    public function dropIndex(string $name): Fluent
179
    {
180 9
        $parameters = [];
181 9
        $parameters['name'] = 'dropIndex';
182 9
        $parameters['index'] = $name;
183 9
        $parameters['explanation'] = "Drop the '" . $name . "' index on the {$this->table} table.";
184 9
        $parameters['handler'] = 'collection';
185
186 9
        return $this->addCommand('dropIndex', $parameters);
187
    }
188
189
    /**
190
     * Indicate that the given index should be dropped.
191
     */
192
    public function dropPersistentIndex(string $name): Fluent
193
    {
194
        return $this->dropIndex($name);
195
    }
196
197
    /**
198
     * Indicate that the given index should be dropped.
199
     */
200 1
    public function dropPrimary(string $name): Fluent
201
    {
202 1
        return $this->dropIndex($name);
203
    }
204
205
    /**
206
     * Indicate that the given index should be dropped.
207
     */
208 1
    public function dropUnique(string $name): Fluent
209
    {
210 1
        return $this->dropIndex($name);
211
    }
212
213
    /**
214
     * Indicate that the given index should be dropped.
215
     */
216 2
    public function dropSpatialIndex(string $name): Fluent
217
    {
218 2
        return $this->dropIndex($name);
219
    }
220
221
    /**
222
     * Indicate that the given index should be dropped.
223
     */
224 1
    public function dropInvertedIndex(string $name): Fluent
225
    {
226 1
        return $this->dropIndex($name);
227
    }
228
229
230
231
    /**
232
     * Drop the index by first getting all the indexes on the table; then selecting the matching one
233
     * by name.
234
     */
235 9
    public function executeDropIndexCommand(Fluent $command)
236
    {
237 9
        if ($this->connection->pretending()) {
238
            $this->connection->logQuery('/* ' . $command->explanation . " */\n", []); // @phpstan-ignore-line
239
240
            return;
241
        }
242 9
        $indexes = $this->schemaManager->getIndexes($this->table);
243 9
        $arrayIndex = array_search($command->index, array_column($indexes, 'name'), true);
244 9
        $indexId = $indexes[$arrayIndex]->id;
245
246 9
        $this->schemaManager->deleteIndex($indexId);
247
    }
248
249
    /**
250
     * @param  string|null  $algorithm
251
     * @return mixed|string
252
     */
253 20
    protected function mapIndexAlgorithm($algorithm): mixed
254
    {
255 20
        $algorithm = strtoupper($algorithm);
0 ignored issues
show
Bug introduced by
It seems like $algorithm can also be of type null; however, parameter $string of strtoupper() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

255
        $algorithm = strtoupper(/** @scrutinizer ignore-type */ $algorithm);
Loading history...
256
257 20
        $algorithmConversion = [
258 20
            'HASH' => 'hash',
259 20
            'BTREE' => 'persistent',
260 20
            'RTREE' => 'geo',
261 20
            'TTL' => 'ttl',
262 20
        ];
263
264 20
        return (isset($algorithmConversion[$algorithm])) ? $algorithmConversion[$algorithm] : 'persistent';
265
    }
266
267
    /**
268
     * Create a default index name for the table.
269
     *
270
     * @param  string  $type
271
     */
272 28
    public function createIndexName($type, array $columns, array $options = []): string
273
    {
274 28
        $nameParts = [];
275 28
        $nameParts[] = $this->prefix . $this->table;
276 28
        $nameParts = array_merge($nameParts, $columns);
277 28
        $nameParts[] = $type;
278 28
        $nameParts = array_merge($nameParts, array_keys($options));
279 28
        array_filter($nameParts);
280
281 28
        $index = strtolower(implode('_', $nameParts));
282 28
        $index = preg_replace("/\[\*+\]+/", '_array', $index);
283
284 28
        return preg_replace('/[^A-Za-z0-9]+/', '_', $index);
285
    }
286
}
287