Fork me on GitHub
#datascript
<
2020-08-20
>
Francesco Pischedda07:08:50

Hi everyone! I am learning datascript and I am a bit stuck at recursion; for my test I have a simple comments tree, each comment has a parent (except the root) and I would like to get, with one query, the root comments with their children, like ["author" "text" [["author1" "reply1" ["sub-author" "sub-reply" []] {"author2" "reply2" []]] Test setup:

(def schema
    {:comment/id {:db/unique :db.unique/identity}
     :comment/parent {:db/valueType :db.type/ref
                      :db/cardinality :db.cardinality/one}})


  (defn add-mock-data! []
    (let [comment1 (random-uuid)
          comment2 (random-uuid)
          comment3 (random-uuid)]
      (d/transact! db
        [{:comment/id comment1
          :comment/author "reviewer"
          :comment/created-at "2020-08-17T08:41:12Z"
          :comment/text "Comment by reviewer"}
         {:comment/id comment2
          :comment/parent {:comment/id comment1}
          :comment/author "author"
          :comment/created-at "2020-08-18T08:41:12Z"
          :comment/text  "Author's reply"}
         {:comment/id comment3
          :comment/parent {:comment/id comment2}
          :comment/author "reviewer"
          :comment/created-at "2020-08-18T09:11:02Z"
          :comment/text "Reviewer's reply"}
         ])))
Query:
(d/q '[:find ?author ?text (pull ?cc [:comment/author :comment/text])
         :where
         [?c :comment/id ?id]
         [?c :comment/author ?author]
         [?c :comment/text ?text]
         [?cc :comment/parent ?cp]
         [?cp :comment/id ?id]
         ]
    @db)
Result:
0. [ "reviewer" "Comment by reviewer" { :comment/author "author", :comment/text "Author's reply" } ]
  1. [ "author" "Author's reply" { :comment/author "reviewer", :comment/text "Reviewer's reply" } ]
What is missing so far is getting only root comment but I think it is trivial, but what I can't really get is how to generate, recursively, the list of replies 😞 ideas?

Francesco Pischedda12:08:16

to start, I think that I need to get a vector/list of children instead now I am getting [comment child] for each comment; I have tried to simplify the query to make it easier to reason about it:

(d/q '[:find ?author ?text ?cc
         :in $ %
         :where
         [?c :comment/author ?author]
         [?c :comment/text ?text]
         (children ?c ?cc)
         ]
    @db '[[(children ?c ?cc)
           [?cc :comment/parent ?c]]])

Francesco Pischedda17:08:07

quick update, as mentioned, starting with root comments is easy, filter by comments with no :coment/parent, here is the new query:

(d/q '[:find ?author ?text ?cc
         :in $ %
         :where
         [?c :comment/author ?author]
         [?c :comment/text ?text]
         [(missing? $ ?c :comment/parent)]
         (children ?c ?cc)
         ]
    @db '[[(children ?c ?cc)
           [?cc :comment/parent ?c]
           (children ?cc ?ccc)]
          [(children ?c ?cc)
           [?cc :comment/parent ?c]]])
still struggling to find a way to get a list of children, hoping I just need to refine my search keywords 🙂