datascript

Ty 2021-10-26T01:06:23.008400Z

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)

Ty 2021-10-26T07:01:32.010300Z

Wonderful, thanks a ton for the help!

Ty 2021-10-26T01:08:21.008500Z

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?

Ty 2021-10-26T01:29:18.008700Z

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> ... ] }

lilactown 2021-10-26T02:59:29.008900Z

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

lilactown 2021-10-26T03:01:19.009100Z

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"}]}

lilactown 2021-10-26T03:03:31.009400Z

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

lilactown 2021-10-26T03:04:43.009600Z

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

lilactown 2021-10-26T03:06:58.009800Z

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")