数据库聚合:计算相关实体而不会丢失零计数结果

时间:2017-11-21 15:42:18

标签: datomic datalog

我有一个由问题组成的域模型,每个问题都与许多评论和肯定相关联。

我想制作一个Datalog query,为每个问题提取一堆内容属性,以及相关评论和肯定的数量,包括这些关系为空时(例如,某些问题没有评论或没有确认),在这种情况下,返回的计数应为0。

我看过以下Gist,其中显示了如何使用(sum ...)(or-join)以及“重量”#39;当关系为空时,变量得到零计数。

然而,当有两种关系时,我不知道如何使这项工作。我尝试了以下方法,但返回的计数不正确:

(def query '[:find (sum ?comment-weight) (sum ?affirmation-weight) ?text ?time ?source-identifier ?q
             :with ?uniqueness
             :where
             [?q :question/text ?text]
             [?q :question/time ?time]
             [?q :question/source-identifier ?source-identifier]
             (or-join [?q ?uniqueness ?comment-weight ?affirmation-weight]
               (and [?comment :comment/question ?q]
                 [?affirmation :affirmation/question ?q]
                 ['((identity ?comment) (identity ?affirmation)) ?uniqueness]
                 [(ground 1) ?comment-weight]
                 [(ground 1) ?affirmation-weight])
               (and [?comment :comment/question ?q]
                 [(identity ?comment) ?uniqueness]
                 [(ground 1) ?comment-weight]
                 [(ground 0) ?affirmation-weight])
               (and [?affirmation :affirmation/question ?q]
                 [(identity ?affirmation) ?uniqueness]
                 [(ground 1) ?affirmation-weight]
                 [(ground 0) ?comment-weight])
               (and [(identity ?q) ?uniqueness]
                 [(ground 0) ?comment-weight]
                 [(ground 0) ?affirmation-weight]))])

最初询问Clojurians Slack。

2 个答案:

答案 0 :(得分:0)

总而言之,诀窍是将每个问题,评论和确认视为一个“数据点”,它具有“权重”和“权重”。每个计数为0或1,并且唯一标识(以便Datalog正确计数,请参阅:with)。特别是,每个问题对所有计数的权重都为零。

发布的代码几乎是正确的,它只需删除(or-join ...)的第一个子句,这会导致创建“人工”代码。污染计数的数据点(评论+肯定元组)。

以下内容应该有效:

[:find (sum ?comment-weight) (sum ?affirmation-weight) ?text ?time ?source-identifier ?q
 :with ?uniqueness
 :where
 [?q :question/text ?text]
 [?q :question/time ?time]
 [?q :question/source-identifier ?source-identifier]
 (or-join [?q ?uniqueness ?comment-weight ?affirmation-weight]
   (and 
     [?comment :comment/question ?q]
     [(identity ?comment) ?uniqueness]
     [(ground 1) ?comment-weight]
     [(ground 0) ?affirmation-weight])
   (and 
     [?affirmation :affirmation/question ?q]
     [(identity ?affirmation) ?uniqueness]
     [(ground 1) ?affirmation-weight]
     [(ground 0) ?comment-weight])
   (and 
     [(identity ?q) ?uniqueness]
     [(ground 0) ?comment-weight]
     [(ground 0) ?affirmation-weight]))]

答案 1 :(得分:0)

您可以在查询中调用任意函数,因此请考虑直接使用datomic.api/datoms(或子查询或您自己的任意函数)来计算注释和肯定。

使用datomic.api/datoms

'[:find ?comment-count ?affirmation-count ?text ?time ?source-identifier ?q
  :where
  [?q :question/text ?text]
  [?q :question/time ?time]
  [?q :question/source-identifier ?source-identifier]
  [(datomic.api/datoms $ :vaet ?q :comment/question) ?comments]
  [(count ?comments) ?comment-count]
  [(datomic.api/datoms $ :vaet ?q :affirmation/question) ?affirmations]
  [(count ?affirmations) ?affirmation-count]]

或使用子查询:

'[:find ?comment-count ?affirmation-count ?text ?time ?source-identifier ?q
  :where
  [?q :question/text ?text]
  [?q :question/time ?time]
  [?q :question/source-identifier ?source-identifier]
  [(datomic.api/q [:find (count ?comment) .
                   :in $ ?q
                   :where [?comment :comment/question ?q]]
     $ ?q) ?comment-count-or-nil]
  [(clojure.core/or ?comment-count-or-nil 0) ?comment-count]
  [(datomic.api/q [:find (count ?affirmation) .
                   :in $ ?q
                   :where [?affirmation :affirmation/question ?q]]
     $ ?q) ?affirmation-count-or-nil]
  [(clojure.core/or ?affirmation-count-or-nil 0) ?affirmation-count]]

或使用自定义功能:

(defn count-for-question [db question kind]
  (let [dseq (case kind
               :comments (d/datoms db :vaet question :comment/question)
               :affirmations (d/datoms db :vaet question :affirmation/question))]
    (reduce (fn [x _] (inc x)) 0 dseq)))

'[:find ?comment-count ?affirmation-count ?text ?time ?source-identifier ?q
  :where
  [?q :question/text ?text]
  [?q :question/time ?time]
  [?q :question/source-identifier ?source-identifier]
  [(user/count-for-question $ ?q :comments) ?comment-count]
  [(user/count-for-question $ ?q :affirmations) ?affirmation-count]]