#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 сущностей, а не «ничего». Можно ли настроить запрос, чтобы учесть и это?