Получить самый последний объект из 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, и улучшит мои парадигмы?


person benkay    schedule 28.09.2013    source источник


Ответы (2)


Меня беспокоила идея добавления дополнительных временных меток в базу данных, которая номинально понимает время как первоклассный принцип, и поэтому (после ночи размышлений о подходах, изложенных 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», а затем просмотрел временную метку транзакции с помощью этой функции для последующей сортировки.

person benkay    schedule 29.09.2013
comment
Стоит отметить, что, насколько мне известно, это дает вам момент, когда текущее значение :post/title было подтверждено для сущности, а не когда сущность в последний раз была предметом транзакции. - person spieden; 11.09.2015
comment
Что делать, если данные не помещаются в памяти? Например. вы хотите получить, скажем, 5 самых последних объектов и не заботиться о других. - person Terminus; 02.02.2019

На самом деле нет более элегантного способа сделать это, чем обход транзакций. Вот почему я предпочитаю иметь отдельный доменный атрибут для временных меток, вместо того, чтобы полагаться на временные метки транзакций из 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 ~@args))]
     (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"))
person August Lilleaas    schedule 28.09.2013