datomic

teodorlu 2026-03-31T09:26:12.756279Z

Hi! This return-map query returns a very weird result. Details in 🧵.

[:find [?email ...]
 :keys user/email
 :where [_ :user/email ?email]]

teodorlu 2026-03-31T09:26:49.633329Z

I expect either a seq of maps or an exception. Instead, I got:

[[:user/email "foo@example.com"]]
, which looks like a map entry.

teodorlu 2026-03-31T09:27:17.348699Z

Produced with [com.datomic/peer "1.0.6726"]. Let me know if you want more details.

teodorlu 2026-03-31T11:21:41.859919Z

I was also surprised that find-scalar queries like

[:find ?email .
 :keys user/email
 :where [_ :user/email ?email]]
throws with a message of "Count of :keys/:strs/:syms must match count of :find" , not that find-scalar queries cannot be used with return maps.

2026-03-31T12:16:26.441699Z

It seems that using d/with on a db with d/as-of applied does not work. I guess as-of simply adds a datoms-filter, which in turn filters out the newly asserted datoms from with . Is there some way of making this work? My app is dependent on being able to use as-of , which then breaks any use of with . Demonstration in 🧵

2026-03-31T12:16:39.804759Z

(def conn ,,,)

@(d/transact conn [{:db/ident :test/id
                    :db/valueType :db.type/string
                    :db/cardinality :db.cardinality/one
                    :db/unique :db.unique/identity}

                   {:db/ident :test/value
                    :db/valueType :db.type/string
                    :db/cardinality :db.cardinality/one}])

(def nil-db (d/db conn))
(def nil-t (d/basis-t nil-db))

@(d/transact conn [{:test/id "id" :test/value "foo"}])

(def foo-db (d/db conn))
(def foo-t (d/basis-t foo-db))

@(d/transact conn [{:test/id "id" :test/value "bar"}])

(def bar-db (d/db conn))
(def bar-t (d/basis-t bar-db))

(:test/value (d/entity bar-db [:test/id "id"])) ;; => "bar"
(:test/value (d/entity foo-db [:test/id "id"])) ;; => "foo"
(:test/value (d/entity nil-db [:test/id "id"])) ;; => nil

(def as-of-foo-db (d/as-of (d/db conn) foo-t))
(:test/value (d/entity as-of-foo-db [:test/id "id"])) ;; => "foo"

(def with-baz-db (:db-after (d/with (d/db conn) [{:test/id "id" :test/value "baz"}])))
(:test/value (d/entity with-baz-db [:test/id "id"])) ;; => "baz"

(def with-baz-as-of-foo-db (:db-after (d/with as-of-foo-db [{:test/id "id" :test/value "baz"}])))

(:test/value (d/entity with-baz-as-of-foo-db [:test/id "id"]))
;; => "foo", expecting "baz"

favila 2026-03-31T12:52:06.292559Z

In your mind, why does the db given to d/with need to be filtered?

2026-03-31T14:07:05.896309Z

That's not what's going on here. I'm using as-of much further out to reproduce an earlier result. "What did this look like back in time?" However, since some function downstream is now using with , this pattern breaks. My code is no longer able to answer questions about databases back in time.

favila 2026-03-31T14:52:33.766329Z

I'm trying to figure out your overall intention re: d/with with filtered dbs. D/with (like d/transact) ignores filters, because the consistency properties transactions guarantee (eg unique values, implicit retractions) are about the unfiltered db value and tx log. Eg what if you add a unique value at t=5, as-of to 3, then add the same value to a different entity? D/as-of is not a fork, just a filter. https://docs.datomic.com/reference/filters.html#as-of-not-branch

2026-03-31T14:54:47.733049Z

Here's my overall intention: I would like for a function f of db to be able to also answer questions about the database as a value back in time. What was (f db) yesterday, or the day before? This has been working wonderfully with Datomic, but breaks the moment f at any point uses with to come up with an answer.

favila 2026-03-31T14:55:52.427499Z

What questions are you asking that require with?

2026-03-31T15:02:51.178879Z

That would be a big detour of an explanation, and not very relevant to the question at hand, since at the end we would still be at a place where d/with could have been useful, but ends up being a foot gun if you also would like to use as-of to answer questions about previous versions of the database.

2026-03-31T15:17:37.962949Z

Here's what I gather so far: Since with and as-of does not compose, and fails silently / in interesting ways, one should probably not rely on both of these Datomic features. In my case I ended up with an infinite loop, a couple kill -9 and a few lost hours of debugging - others might experience different outcomes.

favila 2026-03-31T15:32:07.231069Z

I'm still not sure what you expected it to do? Did you expect as-of to be a branch?

2026-03-31T15:34:03.718249Z

Yes, that's probably a good estimation of my mental model. But that's also not what happened in practice. I have feature A, which was implemented using with . And I have feature B which was implemented using as-of . And when these intersected, program go boom.

favila 2026-03-31T15:34:59.287629Z

The boom was because…?

2026-03-31T15:36:34.700139Z

The boom was due to a loop expecting the db to change (using with) such that it would eventually exit the loop. When the db was as-of 'ed (entering the loop from a rarely used branch of code), with did nothing, and the loop kept on going.

favila 2026-03-31T15:37:21.882249Z

The db-after also had as-of set?

2026-03-31T15:37:37.161019Z

Yes, the :db-after keeps the filter.

favila 2026-03-31T15:38:07.629749Z

Ok well that's something we could consider changing, but it's still not going to give you the behavior you want

2026-03-31T15:39:26.088509Z

I agree. That would just fail in a different manner. So the problem boils down to one of mental model / expectations about as-of being a branch. I don't think just removing the filter from the :db-after from d/with would do anyone any favors.

2026-03-31T15:40:19.313959Z

What would have saved me a couple hours, at least, was an exception when the "nonsensical" operation of using with where all the datoms are filtered out happened.

favila 2026-03-31T15:40:32.586029Z

To be clear the d/with didn't “do nothing”, it did add a transaction

favila 2026-03-31T15:40:50.025209Z

But to the db basis-t not the as-of t

favila 2026-03-31T15:41:05.087399Z

Datomic doesn't let you change the past

2026-03-31T15:41:40.349339Z

Yes, I understand - but in the context of "database as a value", then nothing changed.

favila 2026-03-31T15:42:07.042149Z

The database value includes everything up to the basis-t

2026-03-31T15:42:33.183609Z

Only if you don't consider the filter a part of the database value.

favila 2026-03-31T15:42:43.419949Z

No, even so

favila 2026-03-31T15:43:09.958019Z

Filters influence what datoms are visible, not what are present

2026-03-31T15:43:54.267699Z

Yes, I'm not confused as to those details, but from a usage perspective, that is just a matter of semantics / implementation details. Datoms that are not visible to me, might as well not be present.

favila 2026-03-31T15:47:24.519499Z

Arbitrary filtered dbs can't be a sound basis for a transaction because there are no consistency guarantees for the visible datom set

favila 2026-03-31T15:48:47.000489Z

If this were some other filter, such as history or since, I don't think you would have been surprised. But as of by itself is superficially similar to a branch (changing basis-t), thus confusion

💯 1
favila 2026-03-31T15:49:14.674009Z

But it's only similar from a query perspective, not accumulation

2026-03-31T15:50:56.202239Z

Yes, true, I can see how the implementation of as-of and the implementation of with collide. I guess what I really needed in this case was a branch. Consider my report a vote for such a feature (albeit unlikely), and perhaps one for an exception when using with on an as-of 'ed database.

👍 1
César Olea 2026-03-31T16:00:07.716939Z

Is the ddb-local storage protocol in 1.0.7556 broken?

(require '[datomic.api :as d])

;; System properties set for local dev:
(System/setProperty "aws.region" "us-east-1")
(System/setProperty "aws.accessKeyId" "dummy")
(System/setProperty "aws.secretAccessKey" "dummy")

(d/create-database "datomic:")
The above works in 1.0.7491 but in 7556 throws:
java.net.URISyntaxException: Expected scheme-specific part at index 10: localhost:
  at java.net.URI$Parser.fail (URI.java:2995)
  at java.net.URI$Parser.failExpecting (URI.java:3001)
  at java.net.URI$Parser.parse (URI.java:3201)
  at java.net.URI.<init> (URI.java:732)
  at software.amazon.awssdk.services.dynamodb.endpoints.internal.AwsEndpointProviderUtils.lambda$endpointBuiltIn$1 (AwsEndpointProviderUtils.java:66)
  ...
  at datomic.ddb$fn__19928$fn__19929.invoke (ddb.clj:21)
  at datomic.kv_dynamo.KVDynamo.get (kv_dynamo.clj:68)
According to Claude: The stack trace shows the endpoint string localhost: (extracted from the ddb-local URI) being passed to URI.create() without an http:// scheme prefix. AWS SDK v2 requires a full URI; SDK v1 accepted bare host:port.

jaret 2026-03-31T23:29:06.860829Z

That definitely looks broken. I re-created on my end. Will investigate further.

🤝 1