Fork me on GitHub

I see the default behaviour for adding new entity with pre-existing :db/unique is upsert. It there a convenient way to turn this behaviour down? In other words I would want to insert an entity if it doesn't exist or fail.


Isn’t it so that the upsert behaviour is when :db.unique/identity is specified


and not when :db.unique/value


(defn ensure-non-existant [db [lookup-attr lookup-val :as lookup-ref]]
  (when (ffirst
         (d/q [:find '?e :where ['?e lookup-attr '?val] :in '$ '?val]
              db lookup-val))
    (throw (ex-info "already exists"
                    {:lookup-ref lookup-ref}))))

👍 3

you could do something like that in a tx function and call it (ensure-non-existant db [:my/id-attr "42"])


yes sorry i meaned db/identity , so it's seems that querying the database prior to the transact is the only option we have here

Lennart Buit10:04:16

You can write it as a database function to guarantee atomicity


Your described use-case sounds like you want :db.unique/value instead, as that will throw an anomaly if you attempt to transact a new entity with an existing value for a :db.unique/value attribute.


That seems simpler than using transaction functions; am I missing something?


ok sorry, I get the first answer of @U11SJ6Q0K and the the one of @U0JJ68RBR now. Using :db.unique/value instead of :db.unique/identity do the job, meaning we can still make lookup ref on this field (as :db.unique/identity) but an upsert fails with an Unique conflict exception.


I've got an attribute whose potential values are enums, so I followed the best practices guide and made its valueType a ref; I'd like to constrain the possible values to the small set of enums I've set up as {:db/ident :some.enum/value}, but attribute predicates don't seem like a good fit because they get the EID instead of the keyword, which requires a DB query to link the two.


If closed-set validity is more important to you than extensibility, consider just using a keyword and checking the value with an attribute predicate


The benefit of enums as refs is that you can rename them with ident semantics and you can attach additional assertions to them. If you don’t need that, that’s ok


I'd never considered attaching additional assertions to them, that's really interesting. What kind of assertions would you put on the enum itself?


Additionally, this “best practice” was formulated before attr predicates or entity predicates existed. In those days, all constraint checking was best-effort and application-enforced. Making the enums a ref gave at least some possibility of putting a queryable metaschema into your db


re “what assertions”: well some you get for free, like the transaction that introduced the enum


you could also place enums into a hierarchy (like e.g. multimethod heirarchies) or reference them from an “enum set” (i.e. the attribute, or some other enum, references the attributes as its legal value range in a metaschema) or with a :db/doc for a human to read, etc


all of this also lets your enum set be more dynamic in the application. Using an attribute predicate would require a redeployment to add or change the schema


note that neither method protects you from some schematic change that makes some existing enum values invalid


an entity predicate will catch it and fail the transaction if you use it to verify things opportunistically, but that’s the only tool available to you. For everything else you need to manually remove or change old values, make sure everyone stops writing them, then update your attribute predicate or homespun meta-schema


When you say "metaschema", do you mean that I might have an attribute called :valid-enum-vals that contains the EIDs of the enums that are currently valid?


yes, I mean you have some attributes that describe entity-level (or higher) schema relationships


Thank you for this link, I'm gonna watch that right now


Note that it’s pretty old (2013). It’s still relevant from a data-modeling perspective, but you would probably leverage entity predicates, required attributes, and attribute predicates more now


also, you may not necessarily want to be so rigid about entity-level schemas


That was a really interesting talk, thank you 👏:skin-tone-2:


It's given me a lot to think about, and it's also gotten me curious about how the RDF world handles this problem (and whether it matches with Antonio's proposed solution)


in our app we have :enum/attribute in all enum values that point to the attribute the enum value is valid for… when we create web forms we automatically query the options from the database based on that


That's a really interesting idea as well.


How have folks approached constraining enums-as-refs?


I've contemplated an entity-predicate, but that requires programmer involvement instead of the db automatically checking it for us (as far as I understand from the docs), and I was hoping for something that happened without programmer effort (and thus couldn't accidentally be forgotten).


Is there a smooth upgrade path from dev-local to datomic cloud? I see import-cloud, but that seems to only work in the cloud->local direction.

Joe Lane18:04:35

"upgrade path"?


Well, I should rephrase. Is it easy to import data in the other direction? I imagine it’s as “simple” as streaming all the datoms in dev-local into the cloud db, but just double checking.


I have a tiny application that I’d like to use dev-local for, and one day it might need something more robust. Just weighing my options on how easy that move would be.

Joe Lane18:04:41

Is your tiny application running on the internet, or on your laptop?


Just laptop at the moment.

Joe Lane18:04:41

Ok, this is good. Once your application is bigger than your laptop, use cloud. Unfortunately it isn't as "simple" as "upload the datoms", and there are a variety of ways depending on if you need history or not, if the db already exists in c loud, etc.


If you don’t need history, what’s the recommended approach?


Ah okay, glad I asked. So dev-local is really not meant as a standalone database that I’d run on, say, a VM exposed to the internet. I figured I might be able to get away with doing that and could move to cloud later.

Joe Lane18:04:20

Correct, its for developing, locally (and/or tests in CI)


Gotcha, thanks Joe. I’ll look into cloud.