All Projects → tarantool-php → mapper

tarantool-php / mapper

Licence: MIT license
Map tarantool tuples to php objects.

Programming Languages

PHP
23972 projects - #3 most used programming language

Projects that are alternatives of or similar to mapper

Awesome Python Models
A curated list of awesome Python libraries, which implement models, schemas, serializers/deserializers, ODM's/ORM's, Active Records or similar patterns.
Stars: ✭ 124 (+82.35%)
Mutual labels:  schema, mapper
Flatfiles
Reads and writes CSV, fixed-length and other flat file formats with a focus on schema definition, configuration and speed.
Stars: ✭ 275 (+304.41%)
Mutual labels:  schema, mapper
typescript-to-json-schema
Generate JSON schema from your Typescript sources
Stars: ✭ 54 (-20.59%)
Mutual labels:  schema
schemaglue
Naturally breaks down your monolithic graphql schema into bits and pieces and then glue them back together.
Stars: ✭ 117 (+72.06%)
Mutual labels:  schema
schemarkdown
The core library for generate Markdown document from database schema.
Stars: ✭ 4 (-94.12%)
Mutual labels:  schema
krab
Krab is a migration and automation tool for PostgreSQL based on HCL syntax
Stars: ✭ 15 (-77.94%)
Mutual labels:  schema
docker
Docker images for tarantool database
Stars: ✭ 44 (-35.29%)
Mutual labels:  tarantool
ion-schema-kotlin
A Kotlin reference implementation of the Ion Schema Specification.
Stars: ✭ 23 (-66.18%)
Mutual labels:  schema
hasura-sdk
Hasura Schema & Metadata Typescript SDK
Stars: ✭ 21 (-69.12%)
Mutual labels:  schema
migrana
Migrana is a Datomic migration tool that gives you the control over how your Datomic database evolves.
Stars: ✭ 22 (-67.65%)
Mutual labels:  schema
mongodb-schema
Infer a probabilistic schema for a MongoDB collection.
Stars: ✭ 106 (+55.88%)
Mutual labels:  schema
volder
volder is powerful Object schema validation lets you describe your data using a simple and readable schema and transform a value to match the requirements
Stars: ✭ 106 (+55.88%)
Mutual labels:  schema
EsriRESTScraper
A Python class that scrapes ESRI Rest Endpoints and exports data to a geodatabase
Stars: ✭ 43 (-36.76%)
Mutual labels:  schema
PropMapper
Object mapper for .NET. Flat and basic, but FAST.
Stars: ✭ 30 (-55.88%)
Mutual labels:  mapper
ddal
DDAL(Distributed Data Access Layer) is a simple solution to access database shard.
Stars: ✭ 33 (-51.47%)
Mutual labels:  schema
godmt
Tool that can parse Go files into an abstract syntax tree and translate it to several programming languages.
Stars: ✭ 42 (-38.24%)
Mutual labels:  schema
avrow
Avrow is a pure Rust implementation of the avro specification https://avro.apache.org/docs/current/spec.html with Serde support.
Stars: ✭ 27 (-60.29%)
Mutual labels:  schema
proto2gql
The project has been migrated to https://github.com/EGT-Ukraine/go2gql.
Stars: ✭ 21 (-69.12%)
Mutual labels:  schema
react-aztec
🔥 Material UI based dynamic form component for React using JSON
Stars: ✭ 37 (-45.59%)
Mutual labels:  schema
schema2ldif
Schema 2 ldif : tool to convert .schema to .ldif files and mange them live into an openldap server
Stars: ✭ 14 (-79.41%)
Mutual labels:  schema

Tarantool Mapper

License Testing Latest Version Total Downloads Telegram

Requirements

You need latest php major version (8.0 or 8.1), and tarantool latest major branch (2.6, 2.8, 2.10).

Installation

The recommended way to install the library is through Composer:

$ composer require tarantool/mapper

Create mapper

Usually, you manage dependencies in your service provider. To get started you should instantiate client instance and pass it to mapper constructor. In this example we use PurePacker and StreamConnection. It means you don't need any pecl extensions. To see other implementations please check client documentation

use Tarantool\Client\Client;
use Tarantool\Mapper\Mapper;

$client = Client::fromDefaults();
$mapper = new Mapper($client);

Existing types

You can start with your current configuration. Please, note - all instances are mapped to key-value objects.

$globalSpace = $mapper->findOrFail('_space', ['name' => '_space']);
echo $globalSpace->id; // 280

$indexes = $mapper->find('_index', ['id' => $globalSpace->id]);
var_dump($indexes); // indexes on _index space
echo $indexes[0]->name;  // primary index
echo $indexes[0]->type; // tree

$guest = $mapper->find('_user', ['name' => 'guest']);
echo $guest->id; // 0
echo $guest->type; // user

Describe entities

To get started you should describe your types and fields using meta object.

$person = $mapper->getSchema()->createSpace('person');

// add properties
$person->addProperty('id', 'unsigned');
$person->addProperty('name', 'string');
$person->addProperty('birthday', 'unsigned');
$person->addProperty('gender', 'string');

// add multiple properties
$person->addProperties([
  'telegram' => 'string',
  'vk' => 'string',
  'facebook' => 'string',
]);

// add indexes
// first index is primary
$person->createIndex([
    'type' => 'hash', // define type
    'fields' => ['id'],
]);

// create unique indexes using property or array of properties as parameter
$person->createIndex('name');

// create not unique indexes
$person->createIndex([
    'fields' => 'birthday',
    'unique' => false
]);

// if you wish - you can specify index name
$person->createIndex([
    'fields' => ['name', 'birthday'],
    'type' => 'hash',
    'name' => 'name_with_birthday',
]);

Use migrations

use Tarantool\Mapper\Mapper;
use Tarantool\Mapper\Migration;

class InitTesterSchema implements Migration
{
  public function migrate(Mapper $mapper)
  {
    $tester = $mapper->getSchema()->createSpace('tester', [
      'engine' => 'memtx', // or vinyl
      'properties' => [
        'id' => 'unsigned',
        'name' => 'string',
      ]
    ]);
    $tester->createIndex('id');
  }
}

$mapper->getBootstrap()->register(InitTesterSchema::class);
// or register instance $mapper->getBootstrap()->register(new InitTesterSchema());

$mapper->getBootstrap()->migrate();

Use fluent api

use Tarantool\Mapper\Mapper;
use Tarantool\Mapper\Migration;

class InitTesterSchema implements Migration
{
  public function migrate(Mapper $mapper)
  {
    $mapper->getSchema()->createSpace('person')
      ->addProperty('id', 'unsigned')
      ->addProperty('name', 'string')
      ->addIndex('id');
  }
}

Working with the data

Now you can store and retreive data from tarantool storage using mapper instance.

// get repository instance
$persons = $mapper->getRepository('person');

// create new entity
$dmitry = $persons->create([
  'id' => 1,
  'name' => 'Dmitry'
]);

// save
$mapper->save($dmitry);

// you can create entities using mapper wrapper.
// this way entity will be created and saved in the tarantool
$vasily = $mapper->create('person', [
  'id' => 2,
  'name' => 'Vasily'
]);

// you can retreive entites by id from repository
$helloWorld = $mapper->getRepository('post')->find(3);

// or using mapper wrapper
$helloWorld = $mapper->find('post', 3);

// updates are easy
$helloWorld->title = "Hello World!";
$mapper->save($helloWorld);

Indexes

$note = $mapper->getSchema()->createSpace('note');
$note->addProperty('slug', 'string');
$note->addProperty('title', 'string',
$note->addProperty('status', 'string');

$note->addIndex('slug');
$note->addIndex([
  'fields' => 'status',
  'unique' => false
]);

// find using repository
$mapper->getRepository('note')->find(['status' => 'active']);
// find using shortcut
$mapper->find('note', ['status' => 'active']);

// find first
$mapper->getRepository('note')->findOne(['slug' => 'my-secret-note']);

// composite indexes can be used partial
$person = $mapper->getSchema()->createSpace('person');
$person->addProperty('id', 'unsigned');
$person->addProperty('client', 'unsigned');
$person->addProperty('sector', 'unsigned');
$person->addProperty('name', 'unsigned');

$person->addIndex('id');
$person->addIndex([
  'fields' => ['client', 'sector'],
  'unique' => false
]);

// using index parts
$mapper->find('person', ['client' => 2]);
$mapper->find('person', ['client' => 2, 'sector' => 27]);

Array properties

You can store arrays as property without any serialization to string.

$pattern = $mapper->getSchema()->createSpace('shift_pattern');
$pattern->addProperty('id', 'unsigned');
$pattern->addProperty('title', 'string');
$pattern->addProperty('pattern', '*');

$pattern->addIndex('id');

$mapper->create('shift_pattern', [
  'id' => 1,
  'title' => '5 days week',
  'pattern' => [
    ['work' => true],
    ['work' => true],
    ['work' => true],
    ['work' => true],
    ['work' => true],
    ['work' => false],
    ['work' => false],
  ]
]);

$mapper->get('shift_pattern', 1)->pattern[5]; // read element with index 5 from pattern array

Sequence plugin

If you want you can use sequence plugin that generates next value based on sequence space. Or you can implement id generator using any other source, for example with raft protocol.

$mapper->getSchema()->createSpace('post', [
    'id' => 'unsigned',
    'title' => 'string',
    'body' => 'string',
  ])
  ->addIndex('id');

$mapper->getPlugin(Tarantool\Mapper\Plugin\Sequence::class);

$entity = $mapper->create('post', [
  'title' => 'Autoincrement implemented',
  'body' => 'You can use Sequence plugin to track and fill your entity id'
]);

echo $entity->id; // will be set when you create an instance

User-defined classes plugin

If you want you can specify classes to use for repository and entity instances. Entity and repository class implementation are ommited, but you should just extend base classes.

$userClasses = $mapper->getPlugin(Tarantool\Mapper\Plugin\UserClasses::class);
$userClasses->mapEntity('person', Application\Entity\Person::class);
$userClasses->mapRepository('person', Application\Repository\Person::class);

$nekufa = $mapper->create('person', [
  'email' => '[email protected]'
]);

get_class($nekufa); // Application\Entity\Person;

$mapper->getSchema()->getSpace('person')->getRepository(); // will be instance of Application\Repository\Person

Annotation plugin

You can describe your entities using dobclock. Mapper will create space, format and indexes for you.

namespace Entity;

use Tarantool\Mapper\Entity;

class Person extends Entity
{
    /**
     * @var integer
     */
    public $id;

    /**
     * @var string
     */
    public $name;
}

class Post extends Entity
{
    /**
     * @var integer
     */
    public $id;

    /**
     * @var string
     */
    public $slug;

    /**
     * @var string
     */
    public $title;

    /**
     * @var string
     */
    public $body;

    /**
     * @var Person
     */
    public $author;
    
    /**
     * @var integer
     * @required
     */
    public $salary;
}

If you want to index fields, extend repository and define indexes property

namespace Repository;

use Tarantool\Mapper\Repository;

class Post extends Repository
{
    public $engine = 'memtx'; // or vinyl

    public $indexes = [
        // if your index is unique, you can set property collection
        ['id'],
        // extended definition unique index with one field
        [
          'fields' => ['slug'],
          'unique' => true,
        ],
        // extended definition (similar to Space::addIndex params)
        // [
        //  'fields' => ['year', 'month', 'day'],
        //  'unique' => true
        // ],
    ];
}

Register plugin and all your classes:

$mapper->getPlugin(Tarantool\Mapper\Plugin\Sequence::class); // just not to fill id manually
$mapper->getPlugin(Tarantool\Mapper\Plugin\Annotation::class)
  ->register(Entity\Person::class)
  ->register(Entity\Post::class)
  ->register(Repository\Person::class)
  ->migrate(); // sync database schema with code

$nekufa = $mapper->create('person', ['name' => 'dmitry']);

$post = $mapper->create('post', [
  'author' => $nekufa,
  'slug' => 'hello-world',
  'title' => 'Hello world',
  'body' => 'Now you can use mapper better way'
]);

// in addition you can simple get related entity
$post->getAuthor() == $nekufa; // true

// or related collection
$nekufa->getPostCollection() == [$post]; // true

Performance

Mapper overhead depends on amount of rows and operation type. Table contains overhead in milliseconds per entity. In some cases, overhead can't be calculated due float precision.

Operation Counter Client time Mapper time Total time Client RPS Mapper RPS Total RPS
create one 1 0.000 0.001 0.001 1,248.305 1,248.305
single read 1 0.000 0.001 0.001 1,700.853 1,700.853
mass read 1 0.000 0.000 0.000 9,986.438 9,986.438
create one 10 0.000 0.002 0.002 5,094.503 5,094.503
single read 10 0.000 0.002 0.002 6,356.932 6,356.932
mass read 10 0.000 0.000 0.000 82,565.039 82,565.039
create one 100 0.000 0.015 0.015 6,862.408 6,862.408
single read 100 0.000 0.011 0.011 8,781.308 8,781.308
mass read 100 0.000 0.000 0.000 373,158.719 373,158.719
create one 1000 0.002 0.127 0.129 500,000.000 7,879.048 7,756.815
single read 1000 0.001 0.106 0.107 1,000,000.000 9,445.361 9,356.981
mass read 1000 0.001 0.001 0.002 1,000,000.000 1,096,350.573 522,980.549
create one 10000 0.078 1.366 1.444 128,205.128 7,321.169 6,925.678
single read 10000 0.010 1.056 1.066 1,000,000.000 9,468.137 9,379.332
mass read 10000 0.007 0.012 0.019 1,428,571.429 800,640.283 513,083.538

Perfomance test was made on (intel i5-4670K), Ubuntu 22.04 LTS using PHP 8.1.5 For example, when single select will produce 10 000 entites, you will get about 12s overhead.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].