datomic

braai engineer 2025-10-13T16:37:44.959379Z

How to query a composite tuple containing a date (`:db.type/instant`) using d/index-range, such that the returned index range is inclusive of that date, e.g. if I store #inst "2025-10-13", I want to pass a tuple-end value to d/index-range that will include all values up to & including that value? Do I need to increment the date and filter out any values that might be 1ms after midnight? Is there a better way to do it?

⭐ 1
favila 2025-10-13T16:39:41.057199Z

what does "date" mean with an instant?

braai engineer 2025-10-13T16:40:43.565529Z

by date I mean that I convert a LocalDate to java.util.Date because Datomic only supports storing :db.type/instant

favila 2025-10-13T16:41:47.760789Z

... that's not a good idea

favila 2025-10-13T16:42:51.793049Z

localdate is just a Y-M-D; it has to both add precision that doesn't exist and project it into the unix epoch chronology, both of which are easy to do incorrectly and incrementally accumulate errors

braai engineer 2025-10-13T16:43:14.962139Z

storing accounting entries which have a posting date (not datetime, currently), but they could have time associated with them how should I be storing dates and datetimes, if not using instant?

favila 2025-10-13T16:43:45.842279Z

something that doesn't add extra precision or a unix epoch chronology

braai engineer 2025-10-13T16:44:19.958259Z

should I separate out year / month / day / hour / minute / seconds / ms as Datomic attrs?

favila 2025-10-13T16:44:29.778039Z

things that work fine: string in ISO, Epoch days, a long (20251019), a tuple of these, etc

favila 2025-10-13T16:45:08.265929Z

anything that lexically sorts the same way and has a straightforward round-tripping encoding

braai engineer 2025-10-13T16:45:13.785489Z

wouldn't that string be much costlier to store than an Instant?

favila 2025-10-13T16:45:39.196519Z

probably, but it's also human readable; all of these have tradeoffs

favila 2025-10-13T16:46:00.406479Z

the most compact is Epoch Day (for LocalDate), but it's not very readable

favila 2025-10-13T16:47:35.372039Z

anyway, to your original question: index-range termination when you want the termination condition to be "inclusive"

favila 2025-10-13T16:48:16.202759Z

you can either bump the end value by exactly one quantum, or you can leave it nil and use take-while

👍 1
braai engineer 2025-10-13T17:50:31.498609Z

I mentioned LocalDate, but I convert all dates to UTC before storage. Is :db.type/instant still a bad idea? I don't see why it's a bad idea if I want to support posting dates with datetime or just date (zero time) in future.

favila 2025-10-13T17:51:03.548939Z

How will you tell the difference in the future?

favila 2025-10-13T17:59:07.807249Z

Keep in mind that a Date or Instant is a long counting milliseconds since unix epoch, meant to represent a moment in time and be converted into locally-reckoned values as perceived in the past according to complex rules. If your time is not that (which LocalDate and LocalDateTime are not--they are ymd hms values that don't represent a moment on the unix epoch timeline), then encoding them as instants is fraught with peril. If you have a sidechannel where you keep the "real" type of the time (e.g. on the attribute), and completely control all encoding, decoding and display of these times in and out of java.util.Date perfectly forever, then you can get away with it. If you mess up, you get at best confused users and at worst irrecoverably corrupt data

favila 2025-10-13T17:59:46.729959Z

however I have never seen it done successfully, and I have cleaned up multiple messes related to this problem in my career, in both datomic and sql databases

👍 1
favila 2025-10-13T17:59:58.010959Z

so my advice is Just Don't

favila 2025-10-13T18:00:41.013349Z

if it's not a moment in time, don't use a type with those semantics or store it that way at rest

👏 1
braai engineer 2025-10-13T18:39:21.143839Z

Why don't heterogeneous :db/tupleTypes https://docs.datomic.com/schema/schema-reference.html#tuples :db.type/short? I want to store [entity-ref some-boolean year month day hour minute seconds] but now I have to use Long for most components, which is costly to store.

favila 2025-10-13T19:05:46.614989Z

what is :db.type/short? I don't think that exists even outside tuples

favila 2025-10-13T19:06:18.049079Z

clojure doesn't store numbers at any precision less than long

favila 2025-10-13T19:06:27.196929Z

and they are typically boxed

favila 2025-10-13T19:07:13.362569Z

datomic stores data with https://github.com/Datomic/fressian, which has a variable-length integer encoding

👍 1
ghadi 2025-10-13T19:09:26.080659Z

You can bitpack a long, too

favila 2025-10-13T19:24:52.257569Z

xml schema has a bunch of https://www.w3.org/TR/xmlschema11-2/#built-in-primitive-datatypes which at some point in my career I wanted to represent in datomic and javascript in a single attribute in a precision-preserving way (to map to another xml format 1:1 instead of across multiple attributes) with temporal sorting (there was a defined way to sort datetimes with mixed precisions). Turns out you can do this in 52 bits if you make some compromises. https://gist.github.com/favila/d635c5aeb3a391f424d6 Note the abuse of the unix epoch.

favila 2025-10-13T19:27:13.031649Z

How will you tell the difference between something that was originally a date only and something that was a date+time? (or is this not a thing in your schema?)

braai engineer 2025-10-13T18:43:52.695019Z

What is the underlying storage for :db.type/boolean? Is it a bit, or a Long that stores 1 or 0? The docs don't specify the cost.

favila 2025-10-13T19:05:14.146519Z

In jvm, memory cost of primitive bool is typically sizeof int; but in clojure it's going to almost always be boxed

braai engineer 2025-10-13T19:14:57.840229Z

But what is it Datomic, though? Is there an optimized storage format for boolean, esp. as part of a heterogeneous tuple?

favila 2025-10-13T19:15:23.380349Z

yes, as one byte in fressian https://github.com/Datomic/fressian/wiki/Bytecodes

🙏 1
favila 2025-10-13T19:15:28.965019Z

F5 or F6