Fork me on GitHub
Michael Stokley19:12:54

when writing a ref attribute, how can we indicate the type? or is that deliberately omitted because datomic (and perhaps clojure) doesn't make use of types, only attributes? in some codebases, i see idents out there called "schema namespaces", and the attr would indicate it is associated with that schema (as would the refed entity, perhaps). or is it best to have the attr namespaced to match the refed entity?

Michael Stokley19:12:52

i wonder if that last option - matching the attr namespace to the namespace of the ref'ed entity - is always suitable. for example, you might have a :person/child attr ref to another :person entity. then the namespaces wouldn't match.


entities are untyped (they’re just an ID to join facts together), so refs are also untyped


e.g. what makes an entity a “person”?


You could layer a type system on top. a common approach is to add additional attributes to the ref attribute itself that indicates (human or machine-readably) the range (such as type) may have


a more recent feature is to use a pair of entity specs with db/ensure:


spec the referent, spec the referred, and add an entity predicate to the referent that asserts that the referred conforms; then add a :db/ensure when you transact changes to the referrer

Raymond Ko21:12:32

Is there a canonical direction in modeling parent-child relationships in datomic schemas? For example, consider a schema which represents books and their respective chapters. I see two ways. 1. have a :book/id , :chapter/id and :book/chapter where book -> chapter. In order to delete the chapter you have retract the chapter entity and one of the db.cardinality/many attributes in the book entity (just realized refs solve this problem, ignore this). 2. have a relationship where chapter -> book. like :chapter/book. This is a db.cardinality/oneand has the benefit of only needing to retract the entity to delete. My main issue of this is that it seems reversed and for more complicated cases, it is not always clear it is like :child/parent especially when there is domain specific terminology, Is there a standard convention like :chapter/book-parentor :chapter/parent-bookto denote attributes of this type?


One difference is that you can use :db/isComponent on :book/chapter but probably not on :chapter/book.

👍 6
Lennart Buit22:12:32

Was just about to say, but don’t you get a many-to-many relation from that

Lennart Buit22:12:56

I meant to say, if you make :book/chapter a ref-many component to a chapter, then pulling`:book/_chapter` on a capter entity also gives a set of books I think. Anyhow — Not particularly important for this discussion I’m confusing myself, ignore


I think you might be right though. The entity API might return a set of "one" book.

Raymond Ko22:12:43

Thanks, I did not now about :db/isComponentand considering my own project nests further with other types and datascript supports this, it seems like there is only one way to go if I want easy deletions.


@U963A21SL @UDF11HLKC isComponent also makes the “reverse” direction cardinality-one

👍 3

(even if there are multiple referents!)


i agree that isComponent is the best solution here. if in the future you find that components might not fit your data model, you could consider writing transaction functions that handle data cleanup from a business logic perspective.. "when this book is removed from the library, retract chapters and user highlights about those chapters and any reservations for the book". i use a combination of tx fns and a "retraction api" that cleans up the messy parts of the graph, like related unique tuple constraints that no longer make sense when parts of their value are retracted (such as book+chapter+critic+rating -> nil+nil+critic+rating)


i've often been tempted to add "reverse component references" between entities just for the sake of data cleanup, but after playing with the idea it felt wrong. components are great.. just saying that they might only get you so far before a tx fn becomes a better option 🙂

Lennart Buit23:12:45

Ah yeah @U09R86PA4, I started doubting indeed ^^, testing it in the repl confirms you are right. Thanks!