The Registry

perspective

The registry is a vector of hash-map called attribute. The registry contains all the information about data available to EQL consumers.

Most important types of attributes are roots, true columns, pseudo columns, joins and variables; each identified by a unique key.

Let’s see what we can have in the registry in details.

  • SQL snippets have been simplified for explanation purpose

  • Backticks are used as quote marks

  • For clarity, only relevant parts of the registry are included

Roots

Roots can be defined as simple as:

  • Registry

  • Query

  • SQL output

{:key :people/list
 :type :root
 :table "person"
 :output [:person/name :person/id]}
[{:people/list [:person/id :person/name]}]
SELECT `id`, `name` FROM `person`

True columns

A true column is a keyword that denote a real column in a database table.

  • Registry

[{:key :farmer/name
  :type :true-column}
 {:key :cow/color
  :type :true-column}]

Please note: For security reason, only exposed columns are available for querying. Keywords not found in the column set will be ignored. That means if you forget to include any of them in a root’s output, you won’t be ablet to use the missing ones in a query.

Walkable will scan for columns in the whole registry for true columns (in :output for instance) so you don’t have to declare them like the above at all. You do need to declare, when you want some customizations - or to tell that the column is also an ident (aka "primary key").

Idents

  • registry

  • Query

  • SQL output

{:key :person/id
  :type :true-column
  :primary-key true
  :output [:person/name]} (1)
1 Idents also have :output: like roots.
[{[:person/id 1] [:person/id :person/name]}]
SELECT `id`, `name` FROM `person` WHERE `person`.`id` = 1

Pseudo columns

In your registry you can define so-called pseudo columns that look just like normal columns from client-side view:

  • Registry

[{:key :person/id (1)
  :type :true-column
  :primary-key true
  :output [:person/yob :person/age]} (1)
 {:key :person/age
  :type :pseudo-column
  :formula [:- 2018 :person/yob]}]
1 :person/age must be mentioned in :person/id’s `:output to be accessible, just like :person/yob.

You can’t tell the difference from client-side:

  • Query for a true column

  • Query for a pseudo column

[{[:person/id 9]
  [:person/yob]}]
[{[:person/id 9]
  [:person/age]}]
  • Filter with a true column

  • Filter with a pseudo column

[{(:people/list {:filters [:= 1988 :person/yob]})
  [:person/name]}]
[{(:people/list {:filters [:= 30 :person/age]})
  [:person/name]}]

Behind the scenes, Walkable will expand the pseudo columns to whatever they are defined. You can also use pseudo columns in other pseudo columns’ definition.

Please note you can only use true columns from the same table in the definition of pseudo columns. For instance, the following doesn’t make sense:

  • Registry

[{:key :person/age
  :type :pseudo-column
  :formula [:- 2018 :pet/yob]}]

Your RDMS will throw an exception once you query for such pseudo-column anyway.

Joins

A join describes the relationship between two tables. You declare it in the registry with :join-path which is the path to traverse from the source table to the target one.

There are two patterns of join paths:

  • A column in the source table equals another column in the target table.

  • A dedicated join table is involved.

Let’s see some examples.

Pattern 1: Two equal columns from two tables

  • Data

  • Query

  • Registry

  • SQL output

  • Result

Assume table cow contains:

id color

10

white

20

brown

and table farmer has:

id name cow_id

1

jon

10

2

mary

20

and you want to get a farmer along with their cow using the query:

[{[:farmer/id 1] [:farmer/name {:farmer/cow [:cow/id :cow/color]}]}]

For the join :farmer/cow, table farmer is the source and table cow is the target.

then you must define the :join-path like this:

[{:key :farmer/cow
  :type :join
  :join-path [:farmer/cow-id :cow/id] (1)
  :output [:cow/id :cow/color]}] (2)
1 the join-path says: start with the value of column farmer.cow_id (the join column) then find the correspondent in the column cow.id.
2 don’t forget :output

Internally, Walkable will generate this query to fetch the entity whose ident is [:farmer/id 1]:

SELECT `farmer`.`name`, `farmer`.`cow_id` FROM `farmer` WHERE `farmer`.`id` = 1

the value of column farmer.cow_id will be collected (for this example it’s 10). Walkable will then build the query for the join :farmer/cow:

SELECT `cow`.`id`, `cow`.`color` FROM `cow` WHERE `cow`.`id` = 10
{[:farmer/id 1] #:farmer{:number 1,
                            :name "jon",
                            :cow #:cow{:index 10,
                                       :color "white"}}}

Pattern 2: A join involving a join table

  • Data

  • Query

  • Registry

  • SQL output

  • Result

Assume the following tables:

source table person:

id name

1

jon

2

mary

target table pet:

id name

10

kitty

11

maggie

20

ginger

join table person_pet:

person_id pet_id adoption_year

1

10

2010

1

11

2011

2

20

2010

you may query for a person and their pets along with their adoption year

[{[:person/id 1] [:person/name {:person/pets [:pet/name :person-pet/adoption-year]}]}]

then the :join-path is as simple as:

[{:key :person/pets
  :type :join
  :output [:pet/name :person-pet/adoption-year] (2)
  :join-path [:person/id :person-pet/person-id
              :person-pet/pet-id :pet/id]}]

Never forget :output!

Walkable will issue an SQL query for [:person/id 1]:

SELECT `person`.`name` FROM `person` WHERE `person`.`id` = 1

and another query for the join :person/pets:

SELECT `pet`.`name`, `person_pet`.`adoption_year`
FROM `person_pet` JOIN `pet` ON `person_pet`.`pet_id` = `pet`.`id` WHERE `person_pet`.`person_id` = 1

and our not-so-atonishing result:

{[:person/id 1] #:person{:id 1,
                            :name "jon",
                            :pets [{:pet/id 10,
                                    :pet/name "kitty"
                                    :person-pet/adoption-year 2010}
                                   {:pet/id 11,
                                    :pet/name "maggie"
                                    :person-pet/adoption-year 2011}]}}

Cardinality of roots and joins

Idents always have cardinality of :one. Roots and joins can cardinality of :many by default. You can change their cardinality like this:

;; registry
[{:key :app.auth/me
  :type :root
  ...
  :cardinality :one}
  {:key :person/spouse
  :type :join
  ...
  :cardinality :one}]