4. Query model

4.1 ACTOR statement

4.1.1 Single actor

Queries to ActorDB always need to be directed to an actor. For this reason ActorDB uses the "ACTOR" command.

Basic query, sent as a single string to the database:

ACTOR actortype(actorid) create;
SELECT * FROM sometable;

actorid - id that you have set for actor.

actortype - type of actor. You can have multiple types of actors with completely different schemas.

create (flag) - if actorid does not exist, create and actor with this id. If you don't wish to create it, ommit this flag.

sometable - a table defined in actortype schema.

You can update multiple types of actors in a single query.

ACTOR actortype1(actorid1) create;
INSERT INTO table1 VALUES (1,'some text');
ACTOR actortype2(actorid2) create;
INSERT INTO tableX VALUES (3,'something else');

This is a transaction over two actors. They will both be updated or none of them will.


4.1.2 Multiple actors

ACTOR actortype(actorid1,actorid2,actorid3) create;
INSERT INTO sometable VALUES (1,'txt');

Insert will execute on all three actors of type "actortype" inside a transaction. If an actor does not exist, it will be created.


4.1.3 All actors of a type

ACTOR type1(*);
INSERT INTO sometable VALUES (1,'txt');

This query will run on all actors of type1. If there are many actors of type1 calling this is not recommended.

You can also limit by actor name. This will return all actors whose name starts with 'some_prefix':

ACTOR type1(* where id like 'some_prefix%');
INSERT INTO sometable VALUES (1,'txt');

4.1.4 Loop over actors

When your list of actors over which you wish to run a query is stored in another actor.

                    
ACTOR type1(actor);
{{ACTORS}}SELECT * FROM table2;
ACTOR type1(for X.txt in ACTORS) create;
INSERT INTO sometable VALUES (1,'{{X.val}}');

What is going on line by line:

  1. 1. Select a single actor of type1. It must exist because there is no create flag.
  2. 2. Run a select over table2 and store the result in a variable ACTORS
  3. 3. For every row in ACTORS, read the .txt column which contains the name of an actor. These actors are of type "type1". If they do not exist, they will be created.
  4. 4. For every actor in ACTORS, insert a row into "sometable". Use another column from current row of ACTORS.

4.2 Flags

ActorDB supports two flags in the actor statement. Flags must be seperated by spaces or commas.

  1. create - create an actor with this query if it does not exist.
  2. safe - require a majority of nodes to verify that what has been read, is safely replicated accross the cluster.

4.3 Variables

As mentioned in 4.1.4 ActorDB uses variables to pass data around when doing queries over multiple actors.

There are also special variables available:

ActorDB will replace this with a unique integer id. is always increasing globally across all servers every time it is used. Calls to one server are guaranteed to always have a bigger id, calls to different servers are not, but are guaranteed to be unique.

ACTOR type1(actor);
INSERT INTO table VALUES ({{uniqid}}, 'sometext');

{{uniqid.X}} will also insert a unique integer value, but can be referenced in multiple places:

ACTOR type1({{uniqid.myid}}) create;
INSERT INTO table VALUES (1,'actor created with id {{uniqid.myid}}');

{{curactor}} when executing statements over multiple actors, {{curactor}} will always reference the current executing actor. It will only work in queries over multiple actors.

ACTOR type1(actor1,actor2,actor3);
INSERT INTO table VALUES (1,'my id is {{curactor}}')

{{RESULT}} when executing a read query across multiple actors, you have to add the result to this variable:

ACTOR type1(actor1,actor2,actor3);
{{RESULT}}SELECT * FROM tablex;

Result set will contain an additional column with every row named "actor", which will contain the name of actor to which result row belongs to. Do not use {{RESULT}} in queries to a single actor.

4.3.1 Appending columns to result

Say you have a forum thread actor. Every post in the thread contains the user id of the user that wrote the message. But when reading the thread you need more than just the user id, you need his username:

ACTOR thread(1);
{{RESULT}}SELECT * FROM thread;
ACTOR user(for X.userid in RESULT);
{{INFO}}SELECT * FROM userinfo WHERE id=1;
{{X.username=INFO.name}}

Explanation line by line:

  1. 1. Switch to thread actor with id 1
  2. 2. Read all posts in thread table and store in "RESULTS" variable
  3. 3. Loop over all posts by userid column and switch to user with that id.
  4. 4. Read from his userinfo table
  5. 5. Take name from "INFO" and store into username column of result. This column does not need to exist in thread table. It will be added.

4.4 PRAGMA statement

PRAGMA commands do specific operations or queries over one or many actors.

SQLite's PRAGMA commands that are allowed: freelist_count, page_count, table_info, index_list, index_xinfo.

PRAGMA list

Will list all actors of a certain type.

ACTOR thread(*);
PRAGMA list;    

You can also add limit and offset.

ACTOR thread(*);
PRAGMA list LIMIT 100 OFFSET 100;    

Or limit the list by actor id.

ACTOR thread(* where id like 'actorname%');
PRAGMA list LIMIT 100 OFFSET 100;    

PRAGMA delete

Will delete all actors listed in "ACTOR" statement.

ACTOR thread(thread1,thread2);
PRAGMA delete;

PRAGMA exists

Will return true or false if actor exists. Works only on a single actor.

ACTOR thread(thread1);
PRAGMA exists;

PRAGMA count

Will count number of existing actors of a type.

ACTOR thread(*);
PRAGMA count;

PRAGMA copy

Will create a copy of an actor with a different name. Actor listed in "ACTOR" statement is new actor, actor listed in PRAGMA statement is actor that will be copied over. The destination name of actor should not exist. If it does it will either be overwritten or operation will fail, because that actor is actively executing requests.

ACTOR thread(threadCopyTo);
PRAGMA copy=threadCopyFrom;
<< Previous     (3. How it works) (5. Key/Value store)     Next >>