Skip to content

131 configure default key options #181

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ public function up()
{
Schema::create('taggables', function (Blueprint $collection) {
//
});
}, [
'keyOptions' => [
'type' => 'padded',
],
]);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
"require": {
"php": "^8.2",
"ext-json": "*",
"composer/composer": "^2.7.0",
"laravel-freelancer-nl/arangodb-php-client": "^2.7.0",
"composer/composer": "^2.8.0",
"laravel-freelancer-nl/arangodb-php-client": "^2.8.0",
"laravel-freelancer-nl/fluentaql": "^2.0",
"laravel/framework": "^11.0",
"spatie/laravel-data": "^4.4.0",
Expand Down
13 changes: 13 additions & 0 deletions config/arangodb.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,22 @@
return [
'datetime_format' => 'Y-m-d\TH:i:s.vp',
'schema' => [
/*
* @see https://docs.arangodb.com/stable/develop/http-api/collections/#create-a-collection_body_keyOptions_allowUserKeys
*/
'keyOptions' => [
'allowUserKeys' => true,
'type' => 'traditional',
],
'key_handling' => [
'prioritize_configured_key_type' => false,
'use_traditional_over_autoincrement' => true,
],
// Key type prioritization takes place in the following order:
// 1: table config within the migration file (this always takes priority)
// 2: The id column methods such as id() and ...Increments() methods in the migration file
// 3: The configured key type above.
// The order of 2 and 3 can be swapped; in which case the configured key takes priority over column methods.
// These settings are merged, individual keyOptions can be overridden in this way.
],
];
55 changes: 55 additions & 0 deletions docs/key-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Key generator options
Tables in ArangoDB can be created using one of the available key generators. You can read up about them
[here](https://docs.arangodb.com/stable/concepts/data-structure/documents/#document-keys)
and [here](https://docs.arangodb.com/stable/develop/http-api/collections/#create-a-collection_body_keyOptions).

The following assumes you have knowledge about ArangoDB keys as can be obtained through the above links.

## Column defined key generators
Laravel has several column methods which can be used to set a primary key. If the given field is equal to:
'id', '_key' or '_id', the key generator will be set according to the mapping below.

If these column methods are not found, or not called on these fields, the configured default generator is used.
You can also ignore the column methods by setting the config value
'arangodb.schema.key_handling.prioritize_configured_key_type' to true.

By default, we map the key methods to the following ArangoDB key generators:

| Laravel column method | ArangoDB key generator |
|:----------------------|:-----------------------|
| autoIncrement() | traditional |
| id() | traditional |
| increments('id') | traditional |
| smallIncrements | traditional |
| bigIncrements | traditional |
| mediumIncrements | traditional |
| uuid(id) | uuid |
| ulid(id) | _n/a_ |

## Traditional vs autoincrement key generators
Even though ArangoDB has an autoincrement key generator we don't use it by default as it is not cluster safe.
The traditional key generator is similar to autoincrement: it is cluster safe although there may be gaps between
the _key increases.

If you want the column methods to set the generator to autoincrement you can override the default behaviour by setting
the config value 'arangodb.schema.key_handling.use_traditional_over_autoincrement' to false.
In which case any given offset in the 'from' method is also used.

## ulid
There is no ulid key generator in ArangoDB. The 'padded' generator may be used if you want
a lexigraphical sort order. You can do so by setting it in the config as the default key, and using configured keys only.
Or by setting it within the migration in the table options.

## Table option key generators
You can set the key options for the table in the migration. This overrides both the default key options and the one defined by column methods.

```
Schema::create('taggables', function (Blueprint $collection) {
//
}, [
'keyOptions' => [
'type' => 'padded',
],
]);
```

5 changes: 4 additions & 1 deletion src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ class Connection extends IlluminateConnection
use ManagesTransactions;
use RunsQueries;

protected ?ArangoClient $arangoClient = null;
/**
* @var ArangoClient|null
*/
protected $arangoClient;

/**
* The ArangoDB driver name.
Expand Down
52 changes: 31 additions & 21 deletions src/Schema/Blueprint.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
use Illuminate\Support\Fluent;
use Illuminate\Support\Traits\Macroable;
use LaravelFreelancerNL\Aranguent\Connection;
use LaravelFreelancerNL\Aranguent\Schema\Concerns\Columns;
use LaravelFreelancerNL\Aranguent\Schema\Concerns\Indexes;
use LaravelFreelancerNL\Aranguent\Schema\Concerns\Tables;
use LaravelFreelancerNL\Aranguent\Schema\Concerns\ColumnCommands;
use LaravelFreelancerNL\Aranguent\Schema\Concerns\IndexCommands;
use LaravelFreelancerNL\Aranguent\Schema\Concerns\TableCommands;

/**
* Class Blueprint.
Expand All @@ -28,9 +28,9 @@
class Blueprint
{
use Macroable;
use Tables;
use Columns;
use Indexes;
use TableCommands;
use ColumnCommands;
use IndexCommands;

/**
* The connection that is used by the blueprint.
Expand Down Expand Up @@ -282,28 +282,16 @@ public function __call($method, $args = [])
}
}

$autoIncrementMethods = ['increments', 'autoIncrement'];
if (in_array($method, $autoIncrementMethods)) {
$this->setKeyGenerator('autoincrement');
}

if ($method === 'uuid') {
$this->setKeyGenerator('uuid');
$keyMethods = ['autoIncrement', 'bigIncrements', 'increments', 'mediumIncrements', 'tinyIncrements', 'uuid'];
if (in_array($method, $keyMethods)) {
$this->handleKeyCommands($method, $args);
}

$this->ignoreMethod($method);

return $this;
}

protected function setKeyGenerator(string $generator = 'traditional'): void
{
$column = end($this->columns);
if ($column === '_key' || $column === 'id') {
$this->keyGenerator = $generator;
}
}

protected function ignoreMethod(string $method)
{
$info = [];
Expand All @@ -318,4 +306,26 @@ public function renameIdField(mixed $fields)
return $value === 'id' ? '_key' : $value;
}, $fields);
}

/**
* @param mixed[] $options
* @return mixed[]
*/
protected function setKeyOptions($tableOptions)
{
$configuredKeyOptions = config('arangodb.schema.keyOptions');

$columnOptions = [];
$columnOptions['type'] = $this->keyGenerator;

$mergedKeyOptions = (config('arangodb.schema.key_handling.prioritize_configured_key_type'))
? array_merge($columnOptions, $configuredKeyOptions, $tableOptions)
: array_merge($configuredKeyOptions, $columnOptions, $tableOptions);

if ($mergedKeyOptions['type'] === 'autoincrement' && $this->incrementOffset !== 0) {
$mergedKeyOptions['offset'] = $this->incrementOffset;
}

return $mergedKeyOptions;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use Illuminate\Support\Fluent;

trait Columns
trait ColumnCommands
{
/**
* Indicate that the given attributes should be renamed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use ArangoClient\Exceptions\ArangoException;
use Illuminate\Support\Fluent;

trait Indexes
trait IndexCommands
{
use HandlesIndexNaming;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use Illuminate\Support\Fluent;

trait Tables
trait TableCommands
{
/**
* Indicate that the table needs to be created.
Expand All @@ -17,35 +17,55 @@ trait Tables
public function create($options = [])
{
$parameters = [];
$parameters['options'] = array_merge(
[
'keyOptions' => config('arangodb.schema.keyOptions'),
],
$options,
);
$parameters['options'] = $options;
$parameters['explanation'] = "Create '{$this->table}' table.";
$parameters['handler'] = 'table';

return $this->addCommand('create', $parameters);
}

public function executeCreateCommand($command)
/**
* @param string $command
* @param mixed[] $args
* @return void
*/
public function handleKeyCommands($command, $args)
{
if ($this->connection->pretending()) {
$this->connection->logQuery('/* ' . $command->explanation . " */\n", []);
$acceptedKeyFields = ['id', '_id', '_key'];

$columns = ($command === 'autoIncrement') ? end($this->columns) : $args;
$columns = (is_array($columns)) ? $columns : [$columns];

if (count($columns) !== 1 || ! in_array($columns[0], $acceptedKeyFields)) {
return;
}
$options = $command->options;

if ($this->keyGenerator !== 'traditional') {
$options['keyOptions']['type'] = $this->keyGenerator;
if ($command === 'uuid') {
$this->keyGenerator = 'uuid';

return;
}

if ($this->keyGenerator === 'autoincrement' && $this->incrementOffset !== 0) {
$options['keyOptions']['offset'] = $this->incrementOffset;
if (config('arangodb.schema.key_handling.use_traditional_over_autoincrement') === false) {
$this->keyGenerator = 'autoincrement';

return;
}

$this->keyGenerator = 'traditional';
}

public function executeCreateCommand($command)
{
if ($this->connection->pretending()) {
$this->connection->logQuery('/* ' . $command->explanation . " */\n", []);

return;
}

$options = $command->options;
$options['keyOptions'] = $this->setKeyOptions($options['keyOptions'] ?? []);

if (!$this->schemaManager->hasCollection($this->table)) {
$this->schemaManager->createCollection($this->table, $options);
}
Expand Down
18 changes: 18 additions & 0 deletions tests/Console/ShowModelCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,21 @@
->expectsOutputToContain('computed')
->assertSuccessful();
});

test('model:show \\TestSetup\\Models\\Child', function () {
$this->artisan('model:show', ['model' => '\\TestSetup\\Models\\Child'])
->expectsOutputToContain('arangodb')
->expectsOutputToContain('children')
->expectsOutputToContain('traditional')
->doesntExpectOutput('computed')
->assertSuccessful();
});

test('model:show \\TestSetup\\Models\\House', function () {
$this->artisan('model:show', ['model' => '\\TestSetup\\Models\\House'])
->expectsOutputToContain('arangodb')
->expectsOutputToContain('houses')
->expectsOutputToContain('traditional')
->doesntExpectOutput('computed')
->assertSuccessful();
});
Loading
Loading