Fork me on GitHub
#datalog
<
2020-12-18
>
David Bieber04:12:19

Hello! I'm learning datalog and thought I would share what I'm struggling with in case you can help me along. I'm working on a query now. The goal is to find all "blocks" with >100 descendent blocks. Here's what I have so far (not working yet).

let ancestorrule=`[ 
   [ (ancestor ?child ?parent) 
        [?parent :block/children ?child] ] 
   [ (ancestor ?child ?a) 
        [?parent :block/children ?child ] 
        (ancestor ?parent ?a) ] ] ]`;
window.roamAlphaAPI.q(`[ 
  :find 
      ?parent
  :in $ ?count % 
  :where 
    [?parent :block/string]
    [?block :block/string]
    (ancestor ?block ?parent)
	[(count ?block) ?block_count]
	[> ?block_count ?count]
  ]`, 100, ancestorrule);
I'm going to keep poking at it and trying to debug, but lmk if you know how to fix it and get it working. The current error is No protocol method ICounted.-count defined for type number: 4. My understanding is that the way I've written it, I'm trying to count each block (which doesn't make sense) -- whereas what I really want is to count the total number of blocks satisfying the other criteria. Probably I need to figure out subqueries or something like that. (And clearly this isn't an active channel -- so feel free to ignore or redirect me :))

David Bieber04:12:18

I've gotten it working using some non-datalog in the middle of the query :man-shrugging:

let ancestorrule=`[ 
   [ (ancestor ?child ?parent) 
        [?parent :block/children ?child] ]
   [ (ancestor ?child ?a) 
        [?parent :block/children ?child ] 
        (ancestor ?parent ?a) ] ] ]`;
let results = window.roamAlphaAPI.q(`
[:find ?ancestor (count ?block)
  :in $ ?count % 
  :where 
    [?ancestor :block/string]
    [?block :block/string]
    (ancestor ?block ?ancestor)]
`, 100, ancestorrule).filter((data, index) => {return data[1] > 100});
window.roamAlphaAPI.q(`
[:find ?text ?block ?childcount
:in $ [[?block ?childcount]]
:where
[?block :block/string ?text]
]
`, results);
I wonder if I could do it all in datalog though.

quoll16:12:42

Responding to the original question… Your error message gives you a hint as to where to look. It says that it’s looking for the `ICounted` protocol (specifically, the `-count` function on that protocol), on the number 4. That function is typically called when you call `count` on something, and looking at your query I see that you do. You’re trying to call `count` on the `?block` var. However, that’s a var that gets bound via a rule, and represents a binding for a resource. This is not a “countable” thing. If you’re trying to find out how many things get bound to `?block` then that would need an aggregate function, which `count` is not doing here. Also, it looks like the var `?block` is getting bound to the number 4. That’s totally unexpected, because it’s an entity. I think Datascript might allow arbitrary things in the entity position, but that’s weird if the data has done that.

quoll16:12:06

Your second post is much better. Now you’re getting the aggregate that you needed, via the :find clause.

quoll16:12:42

I’ve fallen behind on the implementations a bit, but to my knowledge: • aggregates are only possible in the :find clause, which is the final step before a query returns. • There is no syntax for subqueries.

quoll16:12:28

To my knowledge, the best you can do is have the results of one query passed in as an argument for the next query… which effectively makes that next query a subquery for you. You’re doing that, but with a filter in the middle. You could avoid the filter and just do the comparison in the query itself:

let ancestorrule=`[ 
   [ (ancestor ?child ?parent) 
        [?parent :block/children ?child] ]
   [ (ancestor ?child ?a) 
        [?parent :block/children ?child ] 
        (ancestor ?parent ?a) ] ] ]`;
.filter((data, index) => {return data[1] > 100});
window.roamAlphaAPI.q(`
[:find ?text ?block ?childcount
:in $ ?limit [[?block ?childcount]]
:where
[?block :block/string ?text]
[(< ?childcount ?limit)]
]
`, 100, window.roamAlphaAPI.q(`
         [:find ?ancestor (count ?block)
          :in $ ?count % 
          :where 
            [?ancestor :block/string]
            [?block :block/string]
            (ancestor ?block ?ancestor)]
        `, 100, ancestorrule));

quoll16:12:46

But that’s the closest I know how to do 🙂