Datomic: как сделать запрос по любому количеству баз данных внутри запроса?

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

(defn pull-entities-at-change-points [entity-id]
  (->>
    (d/q
      '[:find ?tx (pull ?dbs ?client [*])
        :in $ [?dbs ...] ?client
        :where
        [?client ?attr-id ?value ?tx true]
        [(datomic.api/ident $ ?attr-id) ?attr]
        [(contains? #{:client/attr1 :client/attr2 :client/attr3} ?attr)]
        [(datomic.api/tx->t ?tx) ?t]
        [?tx :db/txInstant ?inst]]
      (d/history (d/db db/conn))
      (map #(d/as-of (d/db db/conn) %) [1009 1018])
      entity-id)
    (sort-by first)))

Я пытаюсь найти все транзакции, в которых изменились определенные атрибуты объекта :client, а затем извлекаю объект в том виде, в каком он существовал в те моменты времени. Строка: (map #(d/as-of (d/db db/conn) %) [1009 1018]) — это моя попытка создать последовательность экземпляров базы данных в двух конкретных транзакциях, когда я знаю, что атрибуты клиента изменились. В идеале я хотел бы сделать все это в одном запросе, но я не уверен, что это возможно.

Надеюсь, это имеет смысл, но дайте мне знать, если вам нужна дополнительная информация.


person Stephen Hopper    schedule 17.10.2015    source источник


Ответы (1)


Я бы разделил вызовы pull на отдельные вызовы API вместо того, чтобы использовать их в запросе. Я бы ограничил сам запрос получением интересующих транзакций. Одним из примеров решения для приближения к этому может быть:

(defn pull-entities-at-change-points
  [db eid]
  (let 
    [hdb (d/history db)
     txs (d/q '[:find [?tx ...]
                :in $ [?attr ...] ?eid
                :where
                [?eid ?attr _ ?tx true]]
              hdb
              [:person/firstName :person/friends]
              eid)
      as-of-dbs (map #(d/as-of db %) txs)
     pull-w-t (fn [as-of-db]
                [(d/as-of-t as-of-db)
                 (d/pull as-of-db '[*] eid)])]
    (map pull-w-t as-of-dbs)))

Эта функция для базы данных, которую я создал с игрушечной схемой, будет возвращать такие результаты, как:

([1010
  {:db/id 17592186045418
   :person/firstName "Gerry"
   :person/friends [{:db/id 17592186045419} {:db/id 17592186045420}]}]
 [1001
  {:db/id 17592186045418
   :person/firstName "Jerry"
   :person/friends [{:db/id 17592186045419} {:db/id 17592186045420}]}])

Несколько моментов, которые я прокомментирую:

  • приведенная выше функция принимает значение базы данных вместо получения баз данных из окружающего/глобального конн.
  • мы отображаем тягу для каждого из различных моментов времени t.
  • с использованием API извлечения в качестве записи точка, а не запрос, подходит для случаев, когда у нас есть объект и другая информация под рукой, и нам просто нужны атрибуты или обход ссылок.
  • стремление сделать все в одном большом запросе на самом деле не существует в Datomic, поскольку соответствующие сегменты будут реализованы в кеше однорангового узла. Вы не экономите на пути туда и обратно, используя один запрос.
  • форма привязки коллекции предпочтительнее, чем contains и использует кэширование запросов.
person Ben Kamphaus    schedule 21.10.2015
comment
Спасибо за ответ. Это очень полезно! - person Stephen Hopper; 21.10.2015