Подтвердить что ты не робот

Извлечение самой последней сущности из Datomic

Меня интересуют сущности и их временные метки. По сути, я хочу отсортированный по времени список сущностей.

С этой целью я создал следующие функции:

(defn return-posts
  "grabs all posts from Datomic"
  []
  (d/q '[:find ?title ?body ?slug
         :where
         [?e :post/title ?title]
         [?e :post/slug ?slug]
         [?e :post/body ?body]] (d/db connection)))

(defn get-postid-from-slug
  [slug]
  (d/q '[:find ?e
         :in $ ?slug
         :where [?e :post/slug ?slug]] (d/db connection) slug))

(defn get-post-timestamp
  "given an entid, returns the most recent timestamp"
  [entid]
  (->
   (d/q '[:find ?ts
          :in $ ?e
          :where
          [?e _ _ _]
          [?e :db/txInstant ?ts]] (d/db connection) entid)
   (sort)
   (reverse)
   (first)))

Я считаю, что это хак, основанный на невежестве.

Будет ли кто-нибудь более хорошо разбираться в идиоматическом использовании Datomic, и обновить мои парадигмы?

4b9b3361

Ответ 1

Меня беспокоила идея добавления дополнительных временных меток в базу данных, которая номинально понимает время как первоклассный принцип и так (после ночи размышления о подходах, изложенных Ulrik Sandberg) развила следующую функцию:

(defn return-posts
  "grabs all posts from Datomic"
  [uri]
  (d/q '[:find ?title ?body ?slug ?ts
         :where
         [?e :post/title ?title ?tx]
         [?e :post/slug ?slug]
         [?e :post/body ?body]
         [?tx :db/txInstant ?ts]] (d/db (d/connect uri))))

Идиоматично в Datalog, чтобы опустить привязку к самому идентификатору транзакции, поскольку нам, как правило, все равно. В этой ситуации мы очень определенно заботимся и, по словам Августа Лилеаса, хотим "пересечь транзакцию" (бывают ситуации, когда нам нужно время создания сообщения, но для этого приложения достаточно времени транзакций для упорядочения объектов).

Заметным недостатком этого подхода является то, что недавно отредактированные записи будут вбиты в список. Для этого мне нужно будет что-то сделать позже, чтобы получить "первое появление" в Datomic для истории сообщений в блоге.

Подводя итог: Я связал идентификатор объекта транзакции с идентификатором объекта "post", а затем просмотрел временную метку транзакции с помощью этой функции для последующей сортировки.

Ответ 2

На самом деле нет более элегантного способа сделать это, чем совершать транзакции. Вот почему я предпочитаю иметь отдельный атрибут домена для временных меток, вместо того, чтобы полагаться на временные метки транзакций из Datomic. Один пример, когда это необходимо, - это слияние: пусть у вас есть вики, и вы хотите объединить две страницы вики. В этом случае вы, вероятно, хотите сами контролировать метку времени, а не использовать метку времени из транзакции.

Мне нравятся атрибуты :created-at и :changed-at. Когда я выполняю транзакции с новыми объектами:

[[:db/add tempid :post/slug "..."]
 [:db/add tempid :post/title "A title"]
 [:db/add tempid :created-at (java.util.Date.)]
 [:db/add tempid :changed-at (java.util.Date.)]]

Затем для обновлений:

[[:db/add post-eid :post/title "An updated title"]
 [:db/add post-eid :changed-at (java.util.Date.)]]

Таким образом, все, что мне нужно сделать, это прочитать атрибут: created-at объекта, который будет готов и ждать в индексе.

(defmacro find-one-entity
  "Returns entity when query matches, otherwise nil"
  [q db & args]
  `(when-let [eid# (ffirst (d/q ~q ~db [email protected]))]
     (d/entity ~db eid#)))

(defn find-post-by-slug
  [db slug]
  (find-one-entity
    '[:find ?e
      :in $ ?slug
      :where
      [?e :post/slug ?slug]]
    db
    slug))

;; Get timestamp
(:created-at (find-post-by-slug db "my-post-slug"))