Search code examples
data-modelingdatomic

Datomic newbie question - modelling related facts when one fact changes


If I have a customer entity and they move from an address at time t1 that has these facts:

  • address_line_1 = "10 Downing St"
  • address_line_2 = "Westminster"
  • city = "London"

to a new address and time t2 with these facts:

  • address_line_1 = "1600 Pennsylvania Ave NW"
  • city = "Washington DC"

How do you avoid the address at t2 onwards looking like:

  • address_line_1 = "1600 Pennsylvania Ave NW"
  • address_line_2 = "Westminster"
  • city = "Washington DC"

Options I can think of:

  1. assert the fact at t2 that address_line_2 = "" to reset or blank it out.
  2. have address as it's own entity an point to a new address entity that only has the two facts: address_line_1 = "1600 Pennsylvania Ave NW" and city = "Washington DC" asserted on it.
  3. assert a new fact at t2 like "moved_house" = true to express that tehir address is different.

My thoughts:

  • Option 1 seems to rely on "knowing" what's been set previously across all time that may no longer be true so you can blank them out.
  • Option 2 seems best - but does mean more netities being defined than I was expecting.
  • Option 3 seems yuck!

Anyone else's thoughts on this would be much appreciated :)


Solution

  • You can use option 2 as component entities.

    https://support.cognitect.com/hc/en-us/articles/215581418-Component-Attributes?mobile_site=true

    Here is a sample:

    ;; schema
    ;;
    (d/transact conn [{:db/ident      :client/address
                      :db/cardinality :db.cardinality/one
                      :db/valueType   :db.type/ref
                      :db/isComponent true}
                      {:db/ident      :address/line1
                      :db/valueType   :db.type/string
                      :db/cardinality :db.cardinality/one}
                      {:db/ident      :address/country
                      :db/valueType   :db.type/string
                      :db/cardinality :db.cardinality/one}])
    
    ;; create a new client with address - 1 Main Street
    ;;
    (d/transact conn [{:db/id         (d/tempid :db.part/user -1)
                      :client/address {:address/line1   "1 Main Street"
                                       :address/country "USA"}}])
    
    ;; datomic will return you two entity id.  One is for :client/address
    ;; and another one for :address/line1 and :address/country
    ;;
    (d/q '[:find (pull ?e [*])
          :where [?e :client/address]]
        (d/db conn))
    ;; => [[{:db/id 17592186045418, :client/address {:db/id 17592186045419, :address/line1 "1 Main Street", :address/country "USA"}}]]
    
    ;; now update its client address to 9 Kings Road.
    ;;
    (d/transact conn [{:db/id          17592186045418
                      :client/address {:address/line1 "9 Kings Road"}}])
    
    ;; 17592186045418 will then have a :client/address 
    ;; pointing to a new entity with the new address
    ;;
    (d/q '[:find (pull ?e [*])
          :where [?e :client/address]]
        (d/db conn))
    ;; => [[{:db/id 17592186045418, :client/address {:db/id 17592186045421, :address/line1 "9 Kings Road"}}]]