As you can see in Installation guide, we need to provide floor-plan/compile-floor-plan a map describing the floor-plan. You've got some idea about how such a floor-plan looks like as you glanced the example in Overview section. Now let's walk through each key of the floor-plan map in details.


  • SQL snippets have been simplified for explanation purpose
  • Backticks are used as quote marks
  • For clarity, only relevant parts of the floor-plan are included

1 :idents

Idents are the root of all queries. From an SQL dbms perspective, you must start your graph query from somewhere, and it's a table.

1.1 Keyword idents

Keyword idents can be defined as simple as:

SQL output
{:idents {:people/all "person"}}
`[{:people/all [:person/id :person/name]}]
SELECT `id`, `name` FROM `person`

1.2 Vector idents

These are idents whose key implies some condition. Instead of providing just the table, you provide the column (as a namespaced keyword) whose value match the ident argument found in the ident key.

For example, the following vector ident will require a floor-plan like:

Vector ident
SQL output
;; dispatch-key: `:person/by-id`
;; ident arguments: `1`
[:person/by-id 1]
{:idents {:person/by-id :person/id}}
[{[:person/by-id 1] [:person/id :person/name]}]
SELECT `id`, `name` FROM `person` WHERE `person`.`id` = 1

2 :joins

Each entry in :joins describes the "path" from the source table (of the source entity) to the target table (and optionally the join table).

Let's see some examples.

Example 1: Join column living in source table

SQL output

Assume table house contains:

id color
10 white
20 brown

and table farmer has:

id name house_id
1 jon 10
2 mary 20

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

[{[:farmer/by-id 1] [:farmer/name {:farmer/house [:house/id :house/color]}]}]

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

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

{:joins {:farmer/house [:farmer/house-id :house/id]}}

the above join path says: start with the value of column farmer.house_id (the join column) then find the correspondent in the column

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

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

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

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

Example 2: A join involving a join table

SQL output

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/by-id 1] [:person/name {:person/pets [:pet/name :person-pet/adoption-year]}]}]

then the :joins part of our floor-plan is as simple as:

{:joins {:person/pets [:person/id :person-pet/person-id
                       :person-pet/pet-id :pet/id]}}

Walkable will issue an SQL query for [:person/by-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/by-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}]}}

Example 3: Join column living in target table

No big deal. This is no more difficult than example 1.


Assume table farmer contains:

id name
1 jon
2 mary

and table house has:

id name owner_id
10 white 1
20 brown 2

The floor-plan for this example can be a good exercise for the reader of this documentation. (Sorry, actually I'm just too lazy to type it here :D )

3 :reversed-joins

A handy way to avoid typing a join whose path is just reversed version of another.


The floor-plan for such a join is straightforward:

;; floor-plan
{:joins          {:farmer/house [:farmer/house-id :house/id]}
 :reversed-joins {:house/owner :farmer/house}}

so you can go both ways:

  • find the house of a given farmer
[{[:farmer/by-id 1] [:farmer/name {:farmer/house [:house/id :house/color]}]}]
  • find the owner of a given house
[{[:house/by-id 10] [:house/id :house/color {:house/owner [:farmer/name]}]}]

Also, another reason to use :reversed-joins is that it helps with semantics.

4 :true-columns

A set of available columns must be provided at compile time so Walkable can pre-compute part of SQL query strings.

;; floor-plan
{:true-columns #{:farmer/name :house/color}}

Walkable will automatically include columns found in :joins paths so you don't have to.

Please note: keywords not found in the column set will be ignored. That means if you forget to include any of them, you can't use the missing one in a query's property or filter.

The rationale for having a pre-defined set of columns is that your query resolver doesn't have to limit itself to an SQL database as a single source of data. If Walkable can't match a keyword to a column, an ident or a join, it will be passed down to the next plugin in the Pathom plugin chain.

On the other hand, you don't have to include every single column in your database if you know you will never use some of them in a query's property or filter.

5 :cardinality

Idents and joins can have cardinality of either :many (which is default) or :one. You declare that by their dispatch keys:

;; floor-plan
{:cardinality {:person/by-id :one
               ;; you can skip all `:many`!
               :people/all   :many}}

6 :extra-conditions

Please see documentation for Filters

7 :emitter

Please see documentation for Emitters

8 :pseudo-columns

Please see documentation for Pseudo-columns

9 :pagination-fallbacks

Please see documentation for Pagination

10 :aggregators

Please see documentation for Aggregators

11 :variable-getters, :variable-getter-graphs and :public-variables

Please see documentation for Variables

12 :required-columns

results matching ""

    No results matching ""