Fork me on GitHub
#datascript
<
2021-10-26
>
Ty01:10:23

I'm trying to build up a datascript database to model my domain (basic western music theory) so that I can run interesting queries on it for some UI stuff. Here's what I have currently (you'll probably hate it, I'm still pretty green).

(ns modern-guitar.db
  (:require [datascript.core :as d]))

(def conn (d/create-conn {}))

(defn interval [name value]
  {:db/id -1
   :interval/name name
   :interval/value value})

(defn append [thing]
  (d/transact! conn [thing]))

(append (interval "R" 0))
(append (interval "b2" 1))
(append (interval "2" 2))
(append (interval "b3" 3))
(append (interval "3" 4))
(append (interval "4" 5))
(append (interval "b5" 6))
(append (interval "5" 7))
(append (interval "b6" 8))
(append (interval "6" 9))
(append (interval "b7" 10))
(append (interval "7" 11))
(append (interval "O" 12))

(defn chord [name]
  {:db/id -1
   :chord/name name})

(def chord-types ["Major"
                  "Minor"
                  "7th"
                  "Major 7th"
                  "Minor 7th"])

(defn add-chord-to-db [name]
  (append (chord name)))

(doseq [name chord-types] (add-chord-to-db name))

(defn query [q]
  (d/q q @conn))

; Find all chord types by name
(def get-all-chords
  '[:find ?name
    :where
    [?e :chord/name ?name]])

(query get-all-chords)

; Find all intervals by name and value
(def get-all-intervals '[:find ?name ?value
       :where
       [?e :interval/name ?name]
       [?e :interval/value ?value]])

(query get-all-intervals)

Ty01:10:21

I'd like to take the intervals, which I need to be reified entities for many reasons, and include them as an attribute of the chord entities. So chords would having something like: { ... :chord/name "Whatever" :chord/interval <interval-id>} What's the best way to get that <interval-id> to add it to the chords as I generate them?

Ty01:10:18

Even better I guess I would want to use a schema with a cardinality of many and then have a list of entity IDs for my intervals.

{ :chord/intervals [<interval-eid-1> <interval-eid-2> ... ] }

lilactown02:10:29

assuming that interval names are unique, you can declare it so in your schema and then transact the relationships as nested maps

lilactown03:10:19

i.e.

;; schema
{:interval/name {:db/unique :db.unique/identity}
 :chord/intervals {:db/cardinality :db.cardinality/many
                   :db/valueType :db.value/ref}}

;; transact relations between some chord and some intervals
{:chord/name "Whatever"
 :chord/intervals [{:interval/name "R"} {:interval/name "b5"}]}

lilactown03:10:31

(updated example schema to also include the :chord/intervals as a relationship to many other entity)

lilactown03:10:43

internally the value of :chord/intervals on the entity will get stored as a collection of entity IDs. updates to the intervals will be reflected in queries that start from a chord and then follow to the interval entities

lilactown03:10:58

i.e.

;; find the names of all the intervals for chord "Whatever"
(d/q
 '[:find ?name ?value
   :in $ ?chord
   :where
   [?c :chord/name ?chord]
   [?c :chord/intervals ?interval]
   [?interval :interval/name ?name]
   [?interval :interval/value ?value]]
  @conn
  "Whatever")

Ty07:10:32

Wonderful, thanks a ton for the help!