datomic

Oleh K. 2025-12-24T16:17:28.510249Z

What is the best way of doing paginated queries for chat messages for Datomic Cloud in 2025?

Oleh K. 2025-12-30T08:42:44.437119Z

Interesting approach, thank you for sharing!

2025-12-29T16:41:17.076079Z

you can always use the linked list pattern, something like this:

(def schema
  [{:db/ident       :conv/id
    :db/valueType   :db.type/string
    :db/cardinality :db.cardinality/one
    :db/unique      :db.unique/identity}

   {:db/ident       :conv/head ;;newest message in conv
    :db/valueType   :db.type/ref
    :db/cardinality :db.cardinality/one}
   
   {:db/ident       :msg/text
    :db/valueType   :db.type/string
    :db/cardinality :db.cardinality/one}

   {:db/ident       :msg/from
    :db/valueType   :db.type/string
    :db/cardinality :db.cardinality/one}

   {:db/ident       :msg/prev
    :db/valueType   :db.type/ref
    :db/cardinality :db.cardinality/one}])


(d/transact conn {:tx-data schema})

(d/transact conn
  {:tx-data
   [
    {:db/id "m1"
     :msg/text "(first msg)"
     :msg/from "Alice"}

    {:db/id "m2"
     :msg/text "(second msg)"
     :msg/from "Bob"
     :msg/prev "m1"}

    {:db/id "m3"
     :msg/text "(last msg)"
     :msg/from "Alice"
     :msg/prev "m2"}

    {:conv/id "conv1"
     :conv/head "m3"}]})


(let [db (d/db conn)]
  (->>
    (d/pull db
      [:conv/id
       {:conv/head
        [:msg/text
         :msg/from
         ;;recursion limit
         {:msg/prev 10}]}]
      [:conv/id "conv1"])
    :conv/head
    (iterate :msg/prev)
    (take-while some?)
    (map :msg/text)))
;; ("(last msg)" "(second msg)" "(first msg)")
this will get you ordered messages and you can use recursion limit + message id to do pagination. (edit: performance will be worse but it might not matter)

Oleh K. 2025-12-24T16:19:30.270059Z

Query has where clauses that restrict messages by account, user etc.

Oleh K. 2025-12-24T16:22:02.665849Z

The main thing I’m asking seems to be: do I still need to sort on the server side? is there any difference between “q” and “qseq”?

Joe Lane 2025-12-24T17:20:48.773969Z

Are chat messages inside of some container like “conversation”? Is the design intended to be chat messages between 1 user and some AI backend (eg many messages are scoped to 1 person and 1 conversation) or is this a message platform like slack or discord (many messages from many users all within the same channel).

👀 1
Oleh K. 2025-12-24T17:33:11.651829Z

Yes, messages are wrapped in a conversation. Messages are only between two users.

Oleh K. 2025-12-24T17:42:32.049529Z

Maybe I’ve provided a bad example. In case of messages I can index them incrementally and then just fetch ranges, right? But I cannot do the same for fetching the conversations. So what would be the best approach for conversations?

Joe Lane 2025-12-24T20:41:28.932519Z

I think your choices are to sort the results of q or qseq (sorting will fully realize, so, not super valuable here) or leverage sorted indexes, possibly with tuples and index-pull using key-based pagination

1
Oleh K. 2025-12-24T20:52:06.568729Z

Thank you

Joe Lane 2025-12-24T20:53:45.393359Z

Index pull is the only api currently which supports reverse index traversal so you don’t need to sort yourself. That being said, if N is small, sorting is microseconds usually so it’s no big deal

Oleh K. 2025-12-24T20:57:58.173299Z

I'm not sure I understood the thing about the reverse index traversal. Is there a way to get results in natural order using “q” or “qseq”?

Joe Lane 2025-12-24T21:00:20.002039Z

Datomic query is set based, so the order is not deterministic

Oleh K. 2025-12-24T21:06:37.418449Z

Okay, thank you Joe. Merry Christmas!

🥳 1