> For the complete documentation index, see [llms.txt](https://sweet-tooth.gitbook.io/specmonstah/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sweet-tooth.gitbook.io/specmonstah/tutorial/08-visiting-functions.md).

# 08: visiting functions

You now have most of the pieces you need to generate and insert fixture data into a test database. Now you just need to... actually insert the data! To insert data you must *visit* each ent in the ent db with a *visiting function*, and that's what you'll learn to do in this section and the next.

Earlier I wrote,

> Visiting ent nodes is kind of like mapping: when you call `map` on a seq, you apply a mapping function to each element, creating a new seq from the mapping function's return values. When you visit ents, you apply a visiting function to each ent. The visiting function's return value is stored as an attribute on the ent (remember that ents are implemented as graph nodes, and nodes can have attributes).

You've actually already seen a visiting function at work. In the last couple sections you learned how to use spec to generate a value for each ent. The generated value was stored under the `:spec-gen` attribute; that's because the `sg/ent-db-spec-gen` you called actually applies a visiting function to the ent db it generates. Let's create our own visiting function so you can see how this works:

```scheme
(ns reifyhealth.specmonstah-tutorial.08
  (:require [reifyhealth.specmonstah.core :as sm]))

(def schema
  {:user  {:prefix :u}
   :topic {:prefix    :t
           :relations {:owner-id [:user :id]}}
   :post  {:prefix    :p
           :relations {:topic-id [:topic :id]}}})

(defn announce
  [db {:keys [ent-name]}]
  (str "announcing... " ent-name "!"))

(defn ex-01
  []
  (-> (sm/add-ents {:schema schema} {:post [[1]]})
      (sm/visit-ents :announce announce)
      (get-in [:data :attrs])))

(ex-01)
;; =>
{:post  {:type :ent-type},
 :p0    {:type                 :ent,
         :index                0,
         :ent-type             :post,
         :query-term           [1],
         :loom.attr/edge-attrs {:t0 {:relation-attrs #{:topic-id}}},
         :announce             "announcing... :p0!"},
 :topic {:type :ent-type},
 :t0    {:type                 :ent,
         :index                0,
         :ent-type             :topic,
         :query-term           [:_],
         :loom.attr/edge-attrs {:u0 {:relation-attrs #{:owner-id}}},
         :announce             "announcing... :t0!"},
 :user  {:type :ent-type},
 :u0    {:type       :ent,
         :index      0,
         :ent-type   :user,
         :query-term [:_],
         :announce   "announcing... :u0!"}}
```

`(ex-01)` creates an ent db, applies a visiting function, and then looks up the `:attrs` key in the graph associated with the ent db's `:data` key. The `:attrs` key is where loom stores each node's attributes. We can see that each ent (`:u0`, `:t0`, `:tl0`) has an attribute with the key `:announce` and the value of `"announcing... :ent-name!"`. Let's walk through this. You call the function `ex-01`, whose body is:

```
(-> (sm/add-ents {:schema schema} {:todo [[1]]})
    (sm/visit-ents :announce announce)
    (get-in [:data :attrs]))
```

`sm/add-ents` builds the ent db and passes it to `sm/visit-ents`. `sm/visit-ents` takes three arguments: the ent db, a *visit key* (`:announce`), and a visiting function. Then, internally, `sm/visits-ents` iterates overs each ent in the ent db, passing the ent's name to the visiting function along with the db and visit key, using the return value to assign an attribute to the ent.

So in the above example, the ents are `:u0`, `:t0`, and `:p0`, and `sm/visit-ents` iterates over them in that order. For `:u0`, it passes the following arguments to the visiting function `announce`:

1. the ent db
2. a map that includes the key `:ent-name` whose value is `:u0`

The function `announce` uses the `:ent-name` passed in the second argument to return the string `"announcing... :u0!"`, and that gets associated with the `:announce` key under the ent's attributes.

So, it's great and all that the `announce` function returns its little string. But how would we use visiting functions to actually insert records into a database? The answer is that the map passed as the second argument to visiting functions contains a lot more information than just the `:ent-name` of the current ent. It also includes the `:ent-type` (`:user`, `:topic`, `:post`) and, when you're using the clojure.spec visiting function, the `:spec-gen` value. The next section will show you how to combine those values to insert data in a database.

Before you continue to the next section, though, try implementing your own visiting function. Can you write a visiting function that calls `swap` on atom, inserting a vector of `[:ent-type :ent-name]` for every ent?


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://sweet-tooth.gitbook.io/specmonstah/tutorial/08-visiting-functions.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
