Matt's Roof Garden

Powered by 🌱Roam Garden

If anyone has any idea how to make the output of a datalog query look nicer I’d be very interested

John C

I've been playing around with roamAlphaApi, and separately with roam/js. I've got a technique for searching creating a roam/js script which watches for a block with id="foo", then overrides the contents of that block with some the result of some arbitrary javascript function. It would be pretty easy to put that together with roamAlpaAPI to run arbitrary queries.

Here's the example I hacked up: Create a hiccup block as follows

hiccup [:p {:id "wordcount"}]

and a roam/js block as follows:


let countallwords=function countAllWords() {
var elem = document.getElementById('wordcount');
  if (elem!=undefined) {
    if (elem.innerHTML.length==0) {
        elem.innerHTML="...calculating wordcount...";
        let wcpromise = new Promise(function(resolve, reject) {
            let wc=window.roamAlphaAPI.q('[:find ?e :where [?e :block/string]]').map(n=>window.roamAlphaAPI.pull('[*]', n[0])[':block/string']).join(' ').match(/([^\u0000-\u007F]|\w)+/g).length;
             resolve(wc);
        });
        wcpromise.then(function setPara(wc) {
        elem.innerHTML="<B>Word Count:"+wc+"</B>";
        })
    }
}
};
 window.setInterval(countallwords, 1000);

That counts all words in your database. The query is pretty crap, should use inline pulls. But you could plug your own query in there and then use js to format the output.

I think the above could be turned into a widget framework for roam. My javascript is pretty bad so it needs work. Window.setInterval needs cleaning up.

I had a play with this. It's a little fiddly but it works, and you could use this as a starting point. Note this is running roamAPI queries so the format is slightly different.

Create five blocks as follows

A hiccup block

:hiccup [:div {:id "query"}]

Nested under the hiccup block, a roam API query in a code block. Code type must be Clojure.


[:find (pull ?e [[:block/string][:block/uid]]) 
:in $ ?title 
:where 
[?f :node/title ?title]  
 [?e :block/refs ?f]
]

Also nested under the hiccup block, an arg for the query. The query is finding all blocks that link to a page (you can use anything, this is just an example), so I'm using the title of a page I know is in my DB

Coda

A roam JS block (not nested under the hiccup)

{{``roam/js}}

The roam JS code: (nested under the roam/js block)


let formatQuery=function formatQuery() {
let elem = document.getElementById('query');
let elemblockid = elem.parentElement.id.slice(-9);
let subblockstrings=window.roamAlphaAPI.q("[:find (pull ?c [[:block/string]]) :in $ ?a :where [?e :block/uid ?a] [?e :block/children ?c]]", elemblockid);
let queryText = subblockstrings[0][0]['string'];
//assumes querytext is /clojure
queryText= queryText.slice(11,-3).replace(/\n/g,'').replace(/\r/g,'')
let argtext=subblockstrings[1][0]['string'];
let results= window.roamAlphaAPI.q(queryText, argtext);
elem.innerHTML='';
results.forEach(r=>{
    let a=r[0];
    let markup="<p>"+a['string']+ " " + a['uid']+"</p>";
    elem.innerHTML=elem.innerHTML+markup;
});
};
if (window.myinterval)  
     window.clearInterval(intervalId);
window.myinterval=window.setInterval(formatQuery, 1000);

So when run: the code grab the id of the hiccup, searches roam via API for it's children blocks, assumes the first child is a roamAPI query and the second is an arg for that query, runs the query, then generates a para tag for each result in the query and dumps into the original hiccup container.

It updates dynamically whenever you change the query or arg. It's crappy because the output is hardcoded, but that's fixable.🙂