Как суммировать значения, включая объекты с отсутствующими атрибутами, в Datalog/DataScript/Datomic

Я изучаю Datalog/DataScript/Datomic. Для этого я настроил простую базу данных реестра на DataScript. На данный момент он в основном состоит из набора учетных записей и списка записей с атрибутами :entry.record/account и :entry.record/amount. Теперь я пытаюсь получить баланс всех счетов, суммируя все :entry.record/amount для каждого счета. Этот запрос дает мне баланс для всех счетов, которые имеют записи в бухгалтерской книге:

  (d/q '[:find ?account ?account-name (sum ?amount)
     :with ?record
     :in $
     :where [?account :account/name ?account-name]
            [?record :entry.record/account ?account]
            [?record :entry.record/amount ?amount]]
   @conn)

Но у меня есть несколько учетных записей, в которых до сих пор нет зарегистрированных записей, и они не отображаются здесь. Я хочу сделать запрос, который включает их, перечисленные со значением 0. Я играл с or-join и missing?, чтобы включить эти учетные записи в запрос, но я понятия не имею, как получить сумму до 0 для учетных записей. Например, этот запрос:

  (d/q '[:find ?account ?account-name (sum ?amount)
     :with ?record
     :in $
     :where [?account :account/name ?account-name]
     (or-join [?record]
              (and [?record :entry.record/account ?account]
                   [?record :entry.record/amount ?amount])
              [(missing? $ ?record :entry.record/account)])]
   @conn)

Выдает исключение с сообщением Query for unknown vars: [?amount], так как вторая часть or-join не может присвоить значение ?amount.


person Peluko    schedule 13.06.2021    source источник


Ответы (1)


Datalog от Datomic определенно неудобен для такого рода агрегации; моя рекомендация действительно состоит в том, чтобы использовать or-join, чтобы выдать нулевое количество:

[:find ?account ?account-name (sum ?amount)
 :with ?sum-term
 :in $
 :where [?account :account/name ?account-name]
 (or-join [?account ?amount ?sum-term]
   (and
     [?sum-term :entry.record/account ?account]
     [?sum-term :entry.record/amount ?amount])
   (and
     [(identity ?account) ?sum-term]
     [(ground 0) ?amount]))]

См. также: Datomic aggregations: связанные с подсчетом объекты без потери результатов с нулевым счетом

person Valentin Waeselynck    schedule 14.06.2021
comment
Это работает отлично, но это несколько сложно. Насколько я понимаю, вы используете identity для привязки к free-var ?sum-term. Я проверил это, также используя (group 0) ?sum-term, и он дает те же результаты. Есть ли проблема с использованием group вместо identity? - person Peluko; 14.06.2021
comment
@Peluko ​​Я полагаю, вы имели в виду (ground 0) ?sum-term? Кажется, это работает нормально, но лично я нахожу его менее регулярным семантически, что может затруднить чтение кода. - person Valentin Waeselynck; 15.06.2021