I'm realizing that I might have some out-of-date ideas about tempids.
I see in https://docs.datomic.com/transactions/partitions.html#making-temporary-ids that the style of tempid returned by d/tempid is obsolete, and there is now a new mechanism for partition assignment. I also see that in the docs around transaction data (https://docs.datomic.com/transactions/transaction-data-reference.html#tempids), user-provided tempids are expected to always be strings. Are there no remaining reasons to use d/tempid in new code?
That makes sense. But I'm a little surprised that the datomic.db.DbId tempids are treated differently than string tempids by most of the Datomic API (`d/pull`, d/datoms, d/q, d/entity, etc.). Passing the old datomic.db.DbId tempids gets you an empty result, while passing a string throws.
I guess the reasoning here is that passing tempids to functions that read from a database is undefined and so there is no need for consistency. But I find it a pretty common occurrence when implementing upsert operations to have an identifier that may be either a tempid or an entity id, and it feels a little strange to wrap everything in (when-not (string? eid) ...). Am I missing a better approach here?
> missing a better approach here? I'm missing the problem statement > I find it a pretty common occurrence when implementing upsert operations to have an identifier that may be either a tempid or an entity id ok, so what's the problem? you are sending in a tempid or an entity id to the function that produces transaction data, right?
Yes. The problem statement is: unless I'm certain a given ID will never be a string tempid, I have to put a guard around anywhere I pass it to the Datomic API to avoid an exception. It would be nice if the Datomic API treated anything that is not resolvable (e.g.: string tempid, datomic.db.DbId tempid, non-resolvable integer entity ID, non-resolvable lookup ref) equivalently. It's surprising behavior to me that these functions throw with some tempids and not others.
none of those functions work with tempids
only transact has anything about tempids
what exactly is an example of something you are doing with prior to calling an "upsert" with d/transact?
> none of those functions work with tempids
Maybe not by design, but in practice, they return the correct answer if you try to use them with a d/tempid-generated ID to get some attribute value from a db (the correct answer being that there is no such value).
One example is a helper tx function for resetting a cardinality-many attr to match a provided input set. To implement this, you need to get the attribute's current value to compare with the input set to determine which values to add and retract. So you end up with code like:
(let [desired-vals ...
current-vals (:track/artists (d/entity db eid))
to-add (set/difference desired-vals current-vals)
to-retract (set/difference current-vals desired-vals]
...)
If eid is the result of calling d/tempid, current-vals will be nil, which is correct--there are no extant values that you need to retract because this is a new entity. If eid is a string tempid, d/entity throws this exception: Execution error (Exceptions$IllegalArgumentExceptionInfo) at datomic.error/arg (error.clj:79). :db.error/not-a-keyword Cannot interpret as a keyword: foo, no leading : .I see this forum post regarding the same behavior: https://forum.datomic.com/t/datomic-db-dbid-tempids-vs-string-tempids-as-arguments-to-queries/2185.
sorry I didn't read past > not by design
this is all UB
the string error is trying to coerce a string to a keyword so it can find an eid from an ident kw
the other d/tempid ret returning nil through d/entity is going through I have no idea what path, but this is all wrong
I want to correct something I said earlier: tempids are only valid within a single call to d/transact, and calling d/resolve-tempid on stuff returning from that call to transact
> That makes sense. But I'm a little surprised that the datomic.db.DbId tempids are treated differently than string tempids by most of the Datomic API (`d/pull`, d/datoms, d/q, d/entity, etc.). Passing the old datomic.db.DbId tempids gets you an empty result, while passing a string throws.
tempids have no meaning outside of a single invocation of transact
I am surprised by the strong verbiage in the docs @enn