Найти: db / txInstant, ближайший к дате, определенной в атрибуте в Datomic?

#clojure #datomic

#clojure #datomic

Вопрос:

Допустим, у меня есть следующая Datomic схема:

 ; --- e1
{:db/id                 #db/id[:db.part/db]
 :db/ident              :e1/guid
 :db/unique             :db.unique/identity
 :db/valueType          :db.type/string
 :db/cardinality        :db.cardinality/one
 :db.install/_attribute :db.part/db}
{:db/id                 #db/id[:db.part/db]
 :db/ident              :e1/createdAt
 :db/valueType          :db.type/instant
 :db/cardinality        :db.cardinality/one
 :db.install/_attribute :db.part/db}
{:db/id                 #db/id[:db.part/db]
 :db/ident              :e1/e2s
 :db/valueType          :db.type/ref
 :db/cardinality        :db.cardinality/many
 :db.install/_attribute :db.part/db}
 ..

; --- e2
{:db/id                 #db/id[:db.part/db]
 :db/ident              :e2/guid
 :db/valueType          :db.type/string
 :db/cardinality        :db.cardinality/one
 :db/unique             :db.unique/identity
 :db.install/_attribute :db.part/db}
{:db/id                 #db/id[:db.part/db]
 :db/ident              :e2/startedAt
 :db/valueType          :db.type/instant
 :db/cardinality        :db.cardinality/one
 :db.install/_attribute :db.part/db}
{:db/id                 #db/id[:db.part/db]
 :db/ident              :e2/stoppedAt
 :db/valueType          :db.type/instant
 :db/cardinality        :db.cardinality/one
 :db.install/_attribute :db.part/db}
 ..
  

Я хотел бы найти :db/txInstant последнюю транзакцию, совершенную с e1 или с любым e2, связанным с e1, где e1 :e1/createdAt или e2 :e2/startedAt или :e2/stoppedAt меньше или равно указанной дате ( #inst ). Как я могу создать такой запрос в Datomic?

Ответ №1:

Вы можете запросить базу данных «истории» для «максимального» времени транзакции, соответствующего заданным вами критериям. Вы также можете проверить, какой атрибут изменен с ?attr, если хотите.

 (d/transact conn [{:db/id        (d/tempid :db.part/user)
                   :e1/guid      (str (d/squuid))
                   :e1/createdAt #inst "2016-10-21"
                   :e1/e2s       [{:db/id        (d/tempid :db.part/user)
                                   :e2/guid      (str (d/squuid))
                                   :e2/startedAt #inst "2016-10-23"
                                   :e2/stoppedAt #inst "2016-10-25"}]}])


(d/q '[:find (max ?inst)
       :in $ ?d
       :where
       [?e1 :e1/createdAt ?create]
       [?e1 :e1/e2s ?e2]
       [?e2 :e2/startedAt ?start]
       [?e2 :e2/stoppedAt ?stop]
       [(compare ?d ?create) ?c1]
       [(compare ?d ?start) ?c2]
       [(compare ?d ?stop) ?c3]
       (not [(pos? ?c1)] [(pos? ?c2)] [(pos? ?c3)])
       [?e1 ?attr _ ?tx]
       [?tx :db/txInstant ?inst]]
     (d/history (d/db conn))
     #inst "2016-10-24")
  

2016-10-24 даст вам #inst, с которым вы проводите свои данные. 2016-10-26 ничего не даст.

Обновить:

Пожалуйста, позвольте мне изменить схему с помощью:

1) переименуйте e1 и e2 в «задача», где задача (e1) может иметь много подзадач (e2)

2) сохраните все временные метки в самой транзакции (т.е. createdAt, StartedAt, stoppedAt — все они становятся txInstant в tx).

С этим изменением запрос заключается в том, чтобы просто найти ближайший (максимальный) txInstant из e1 и / или e2 (задача и ее подзадачи) с заданным моментом (?tc).

 (q '[:find (max ?t) .
     :in $ ?e ?tc
     :where
     (or-join [?tx]
              ;; parent task created
              [?e :task/name _ ?tx]
              ;; subtask started or stopped
              (and [?e :task/subtask ?s]
                   [?s _ _ ?tx]))
     [?tx :db/txInstant ?t]
     [(compare ?tc ?t) ?x]
     [(>= ?x 0)]]
   (d/history (db conn))
   parent-task1
   #inst "2016-10-26")
  

Пример сценария:

 @(d/transact conn [{:db/id                 #db/id[:db.part/db]
                    :db/ident              :task/name
                    :db/valueType          :db.type/string
                    :db/cardinality        :db.cardinality/one
                    :db.install/_attribute :db.part/db}
                   {:db/id                 #db/id[:db.part/db]
                    :db/ident              :task/completed
                    :db/valueType          :db.type/boolean
                    :db/cardinality        :db.cardinality/one
                    :db.install/_attribute :db.part/db}
                   {:db/id                 #db/id[:db.part/db]
                    :db/ident              :task/subtask
                    :db/valueType          :db.type/ref
                    :db/cardinality        :db.cardinality/many
                    :db.install/_attribute :db.part/db}
                   {:db/id        #db/id[:db.part/tx]
                    :db/txInstant #inst "2016-01-01"}])

;; parent task created on 10-21
(let [p               (d/tempid :db.part/user -1)
      {tids :tempids} @(d/transact conn [{:db/id        #db/id [:db.part/tx]
                                          :db/txInstant #inst "2016-10-21"}
                                         [:db/add p :task/name "Parent Task"]])]
  (def parent-task1 (d/resolve-tempid (db conn) tids p)))

;; start a subtask on 10-23
(let [s               (d/tempid :db.part/user -2)
      {tids :tempids} @(d/transact conn [{:db/id        #db/id [:db.part/tx]
                                          :db/txInstant #inst "2016-10-23"}
                                         [:db/add s :task/name "subtask 1"]
                                         [:db/add parent-task1 :task/subtask  s]])]
  (def subtask1 (d/resolve-tempid (db conn) tids s)))

;; stop the subtask on 10-25
@(d/transact conn [{:db/id #db/id [:db.part/tx]
                    :db/txInstant #inst "2016-10-25"}
                   [:db/add subtask1 :task/completed true]])
  

При этом приведенный выше запрос даст следующий результат:

 2016-10-20 => nil 
2016-10-21 => 2016-10-21 
2016-10-22 => 2016-10-21
2016-10-23 => 2016-10-23
2016-10-24 => 2016-10-23 
2016-10-25 => 2016-10-25 
2016-10-26 => 2016-10-25
  

Комментарии:

1. Это почти то, что мне нужно, но я бы хотел, чтобы 2016-10-26 предоставил мне последнюю db / txInstant сущностей, а не «ничего». Можно ли настроить запрос, чтобы учесть и это?