Fork me on GitHub
#datomic
<
2021-08-28
>
pkova11:08:21

if I want to upsert an entity with a tuple key that contains a db.type/ref, it seems like I can't use a lookup ref in the transaction, otherwise I get Invalid tuple value

pkova11:08:28

is this how it works or am I doing something wrong?

lassemaatta16:08:29

are there any good guides (or best practices) on how to structure an application around datomic? I'm taking my first steps in learning datomic, and at the moment I'm trying to wrap my head around entities and how they should interact with business logic

Ivar Refsdal19:08:36

Business logic with pure data sounds good to me, but I'm no expert. "... Datomic is able to query any data strucutre [sic] that consists of a list of lists." @U0MKRS1FX

;; This is what an actual Datomic DB actually looks like.
(def my-mock-db [
  [123 :name "Test"]
  [123 :email ""]
  [456 :name "Foo"]
  [456 :email "Hello"]])

;; A "normal" query, for good measure.
(d/q '[:find ?person
       :in $ ?name
       :where [?person :name ?name]]
     my-mock-db "Test")
;; HashSet [[123]]

(d/q '[:find ?eid :in $ ?eid :where [?eid]] my-mock-db 123)
;; HashSet [[123]]

(d/q '[:find ?eid :in $ ?eid :where [?eid]] my-mock-db 999)
;; HashSet [[]]
via https://augustl.com/blog/2013/find_by_id_in_datomic/

🥳 2
❤️ 2
👍 3
favila22:08:22

Are you using on-prem? If so I recommend against using d/entity (entity maps) for anything load-bearing. It’s fine and convenient for tests or repl use but elsewhere it is an unpredictable source of io, and it’s very easy to lose track of what the data dependencies are over time (that’s it’s power and it’s liability). Also people seem continually surprised by its equality semantics

favila22:08:41

Use pull expressions and project your data into maps that are the same shape as your data (or as near as you can manage)

favila22:08:55

Keep keyworded attributes the same “type” in your app and your database, so they obey the same attr predicate. (Doubly so if you use spec)

favila22:08:13

For extra expressive power, you can have functions declare the pull expression shape of the data they expect (transitively) and use this info to compose them into larger expressions

favila22:08:44

Also remember that entities are not maps. Expect and embrace the asymmetry between reads and writes. Try to frame writes as datafied commands in their own right that produce (or are) tx data with db/ensure, tx fns, etc to provide integrity

favila22:08:46

For querying, try encapsulating “business logic” query concepts as named rules so they can be recomposed into larger queries (rules can be polymorphic! Just define the same rule name multiple times)

favila22:08:20

Do be aware of performance though. Datomic doesn’t do clause reordering, which is really unfortunate and dangerous for rules

favila22:08:37

Also keep query concerns to “find the matching entities” and pass in pull expressions for “what I want from those entities” as a parameter (or just use pull-many)

favila22:08:47

(By analogy, in sql terms you want to separate the “select” from the rest of the query)

lassemaatta03:08:02

Thanks, great ideas 👍

augustl10:08:11

I'm a bit torn here. Since the whole db is represented as an immutable value, I kind of also like the idea of just passing the db around and have various functions extract what they need directly from the db. This avoids having to create a separate mapping view of what's in the db, as well as having to know "outside" what needs to be pre-fetched from the db into maps, etc. And for tests, it's trivial to set up a db that contains what you need using with

❤️ 1
augustl10:08:44

I'm not convinced that "hiding" the db adds all that much value, really

lassemaatta10:08:41

what about leveraging spec/schema to generatively test functions? I would imagine that it's not trivial to generate data for functions which expect entities?

augustl10:08:31

that's a good point! I've only used generative tests for parts of my code that doesn't know about datomic dbs, and I've also not really used spec/schema either

lassemaatta10:08:52

(also, one argument (in favor of entities) I've heard is that it's a lot easier to accidentally write a really slow query that makes your prod environment slow to a crawl as opposed to pay a smaller performance penalty all the time when traversing between entities)

augustl10:08:46

I suppose that the code I'm talking about that reads in a datomic db, is the part of my code that returns a plain map that I use for rendering GUIs etc 🙂

augustl10:08:10

I tend to allow all that code that sits "between" the db and the rendering to access the full db, and that creating plain maps first and then processing plain maps to generate the GUI data is not something I tend to do a lot

augustl10:08:55

should be noted, my context is datascript + frontend, not actually datomic per se