<commit_before># Hydration

If you're developing something like a REST API, there's a good chance at some point you'll want to *hydrate* some related objects
and return them in your response. For example, suppose we wanted to return a Venue with its Category hydrated:

```clojure
;; No hydration
{:name        "The Tempest"
 :category_id 120
 ...}

;; w/ hydrated :category
{:name        "The Tempest"
 :category_id 120
 :category    {:name "Dive Bar"
               ...}
 ...}
```

The code to do something like this is enormously hairy in vanilla JDBC + HoneySQL. Luckily, Toucan makes this kind of thing a piece
of cake:

```clojure
(hydrate (Venue 120) :category)
```

Toucan is clever enough to automatically figure out that Venue's `:category_id` corresponds to the `:id` of a Category, and constructs efficient queries
to fetch it. Toucan can hydrate single objects, sequences of objects, and even objects inside hydrated objects, all in an efficient way that minimizes
the number of DB calls. You can also define custom functions to hydrate different keys, and add additional mappings for automatic hydration, as
discussed below.

## Architecture

The following flowchart demonstrates how `hydrate` decides *how* to hydrate something:

```
                          hydrate <-------------+
                            |                   |
                        hydrate-many            |
                            | (for each form)   |
                        hydrate-1               | (recursively)
                            |                   |
                 keyword? --+-- vector?         |
                    |             |             |
               hydrate-kw    hydrate-vector ----+
                    |
           can-automagically-batched-hydrate?
                          |
         true ------------+----------------- false
          |                                    |
 automagically-batched-hydrate    can-fn-based-batched-hydrate?
                                              |
                            true -------------+------------- false
                             |                                 |
                  fn-based-batched-hydrate              simple-hydrate
```

The various methods of hydration are discussed in more detail below.


## Automagic Batched Hydration (via hydration-keys)

`hydrate` attempts to do a *batched hydration* where possible.
If the key being hydrated is defined as one of some model's `hydration-keys`,
`hydrate` will do a batched `db/select` if a corresponding key ending with `_id` or `-id`
is found in the objects being batch hydrated.

```clojure
(models/defmodel User :users
  models/IModel
  ;; tell Toucan to do batched hydration of the key `:user` by fetching instances of User with given `:id`s
  (hydration-keys []
    [:user]))

;; ... later, somewhere else ...

(hydrate [{:user_id 100}, {:user_id 101}] :user)

;; -> [{:user_id 100, :user {...}}
;;     {:user_id 101, :user {...}}]
;;
;; (the :user property is now hydrated by fetching the User with each :user_id)
```

Since `:user` is a hydration key for `User`, a single `db/select` will used to
fetch `Users`:

```clojure
(db/select User :id [:in #{100 101}])
```

The corresponding `Users` are then added under the key `:user`.


## Function-Based Batched Hydration (via functions marked ^:batched-hydrate)

If the key can't be hydrated auto-magically with the appropriate `:hydration-keys`,
`hydrate` will look for a function tagged with `:batched-hydrate` in its metadata, and
use that instead. If a matching function is found, it is called with a collection of objects,
e.g.

```clojure
(defn with-fields
  \"Efficiently add `Fields` to a collection of TABLES.\"
  {:batched-hydrate :fields}
  [tables]
  ...)

(let [tables (get-some-tables)]
  (hydrate tables :fields))     ; uses with-fields
```

By default, the function will be used to hydrate keys that match its name; as in the example above,
you can specify a different key to hydrate for in the metadata instead.


## Simple Hydration (via functions marked ^:hydrate)

If the key is *not* eligible for batched hydration, `hydrate` will look for a function or method
tagged with `:hydrate` in its metadata, and use that instead; if a matching function
is found, it is called on the object being hydrated and the result is `assoc`ed:

```clojure
(defn ^:hydrate dashboard [{:keys [dashboard_id]}]
  (Dashboard dashboard_id))

(let [dc (DashboardCard ...)]
  (hydrate dc :dashboard))    ; roughly equivalent to (assoc dc :dashboard (dashboard dc))
```

As with `:batched-hydrate` functions, by default, the function will be used to hydrate keys that
match its name; you can specify a different key to hydrate instead as the metadata value of `:hydrate`:
<commit_msg>fix minor errors in example code<commit_after># Hydration

If you're developing something like a REST API, there's a good chance at some point you'll want to *hydrate* some related objects
and return them in your response. For example, suppose we wanted to return a Venue with its Category hydrated:

```clojure
;; No hydration
{:name        "The Tempest"
 :category_id 120
 ...}

;; w/ hydrated :category
{:name        "The Tempest"
 :category_id 120
 :category    {:name "Dive Bar"
               ...}
 ...}
```

The code to do something like this is enormously hairy in vanilla JDBC + HoneySQL. Luckily, Toucan makes this kind of thing a piece
of cake:

```clojure
(hydrate (Venue 120) :category)
```

Toucan is clever enough to automatically figure out that Venue's `:category_id` corresponds to the `:id` of a Category, and constructs efficient queries
to fetch it. Toucan can hydrate single objects, sequences of objects, and even objects inside hydrated objects, all in an efficient way that minimizes
the number of DB calls. You can also define custom functions to hydrate different keys, and add additional mappings for automatic hydration, as
discussed below.

## Architecture

The following flowchart demonstrates how `hydrate` decides *how* to hydrate something:

```
                          hydrate <-------------+
                            |                   |
                        hydrate-many            |
                            | (for each form)   |
                        hydrate-1               | (recursively)
                            |                   |
                 keyword? --+-- vector?         |
                    |             |             |
               hydrate-kw    hydrate-vector ----+
                    |
           can-automagically-batched-hydrate?
                          |
         true ------------+----------------- false
          |                                    |
 automagically-batched-hydrate    can-fn-based-batched-hydrate?
                                              |
                            true -------------+------------- false
                             |                                 |
                  fn-based-batched-hydrate              simple-hydrate
```

The various methods of hydration are discussed in more detail below.


## Automagic Batched Hydration (via hydration-keys)

`hydrate` attempts to do a *batched hydration* where possible.
If the key being hydrated is defined as one of some model's `hydration-keys`,
`hydrate` will do a batched `db/select` if a corresponding key ending with `_id` or `-id`
is found in the objects being batch hydrated.

```clojure
(models/defmodel User :users
  models/IModel
  ;; tell Toucan to do batched hydration of the key `:user` by fetching instances of User with given `:id`s
  (hydration-keys []
    [:user]))

;; ... later, somewhere else ...

(hydrate [{:user_id 100}, {:user_id 101}] :user)

;; -> [{:user_id 100, :user {...}}
;;     {:user_id 101, :user {...}}]
;;
;; (the :user property is now hydrated by fetching the User with each :user_id)
```

Since `:user` is a hydration key for `User`, a single `db/select` will used to
fetch `Users`:

```clojure
(db/select User :id [:in #{100 101}])
```

The corresponding `Users` are then added under the key `:user`.


## Function-Based Batched Hydration (via functions marked ^:batched-hydrate)

If the key can't be hydrated auto-magically with the appropriate `:hydration-keys`,
`hydrate` will look for a function tagged with `:batched-hydrate` in its metadata, and
use that instead. If a matching function is found, it is called with a collection of objects,
e.g.

```clojure
(defn with-fields
  "Efficiently add `Fields` to a collection of `tables`."
  {:batched-hydrate :fields}
  [tables]
  ...)

(let [tables (get-some-tables)]
  (hydrate tables :fields))     ; uses with-fields
```

By default, the function will be used to hydrate keys that match its name; as in the example above,
you can specify a different key to hydrate for in the metadata instead.


## Simple Hydration (via functions marked ^:hydrate)

If the key is *not* eligible for batched hydration, `hydrate` will look for a function or method
tagged with `:hydrate` in its metadata, and use that instead; if a matching function
is found, it is called on the object being hydrated and the result is `assoc`ed:

```clojure
(defn ^:hydrate dashboard [{:keys [dashboard_id]}]
  (Dashboard dashboard_id))

(let [dc (DashboardCard ...)]
  (hydrate dc :dashboard))    ; roughly equivalent to (assoc dc :dashboard (dashboard dc))
```

As with `:batched-hydrate` functions, by default, the function will be used to hydrate keys that
match its name; you can specify a different key to hydrate instead as the metadata value of `:hydrate`:

{'fraction_non_alphanumeric': 0.10537297861241524, 'fraction_numerical': 0.00584246218049035, 'mean_word_length': 2.273907103825137, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5728094', 'n_tokens_mistral': 2834, 'n_tokens_neox': 2542, 'n_words': 1111}