Hi there,
I would like to know if there’s a way to copy or export the results generated by an advanced query in Logseq. Any help or suggestions would be greatly appreciated!
Plugin “logseq-cuvee” seems only works for queries, not advanced query
I don’t have a solution for the generated results. But if you want to copy a raw list of contents (for returned blocks) or names (for returned pages), I can prepare some custom code.
Thank you so much! I really appreciate your offer for custom code. In fact, I need to copy the results of the following advanced query:
#+BEGIN_QUERY
{:title "🔙 All Backlinks in Current Page"
:inputs [:query-page]
:query [
:find (pull ?refpage [:block/name]) ; only care about block name
:in $ ?current
:where
[?b :block/refs ?refpage] ; blocks with references
(not [?refpage :block/journal-day]) ; not a journal
[?p :block/name ?current] ; get current page
[?b :block/page ?p] ; block must be on current page
]
:view (fn [res]
[:ul (for [row res]
[:li [:a {:href (str "#/page/" (:block/name row))} (:block/name row)]]
)])
}
#+END_QUERY
- Preparation:
- Add a macro inside file
config.edn
, insidemacros{}
:
:copyparent "<button class='kit eval' data-kit='copyquery'>► copy query results of parent block </button>"
- The code below requires having kits inside file
custom.js
. - Inside page
CopyQuery
in Logseq, put the following code in a javascript code-block:const LS = logseq.api const Module = logseq.Module const Kits = Module.Kits const Msg = Module.Msg function advancedQuery(content, queryWord){ const queryStart = content.indexOf("[", queryWord + 5) if (queryStart < 0) return var queryEnd = queryStart + 1 var n = 1 while (n > 0) { const close = content.indexOf("]", queryEnd) if (close < 0) return const open = content.indexOf("[", queryEnd) if (close < open || open < 0) { queryEnd = close + 1 n -= 1 continue } queryEnd = open + 1 n += 1 } return content.slice(queryStart, queryEnd) } function simpleQuery(content, queryWord){ const queryStart = queryWord + 6 const queryEnd = content.indexOf("}}", queryStart) return content.slice(queryStart, queryEnd) } function getBlocks(content, queryWord){ if (queryWord < 0) return if (content[queryWord - 1] === ":") { const query = advancedQuery(content, queryWord) if (query) { const name = query.includes("?current") && '"' + LS.get_current_page().name + '"' return LS.datascript_query(query, name).flat() } } else if (content.slice(queryWord - 2, queryWord) === "{{") { const query = simpleQuery(content, queryWord) if (query) return LS.custom_query(query) } } logseq.kits.copyquery = Kits.addClickEventToButton.bind(null, function onCopyParentClicked(e){ const child = e.target.closest("div.ls-block") const parent = child.parentElement.closest("div.ls-block") const blockId = parent.getAttribute("blockid") const block = logseq.api.get_block(blockId) const content = block.content.replace(/\n?.*[:][:].*\n?/g, "\n").trim() const queryWord = content.indexOf("query") const res = getBlocks(content, queryWord) if (!res) return Msg.ofStatus("Missing query", "warning") const out = [] res.forEach( (r)=>{ const name = r.name if (name) out.push(name) else out.push(r.content) }) navigator.clipboard.writeText(out.join("\n")) Msg.ofStatus("Copied " + res.length, "success") })
- This code makes plenty of assumptions, so:
- it will work with the specific query
- it will work with some other queries
- it will not work with many other queries
- To make it work for a specific query, provide it here to see what can be done.
- This code makes plenty of assumptions, so:
- Add a macro inside file
- Usage:
- Put the macro at a child block under the query:
- i.e.
{{copyparent}}
- i.e.
- Press the button. This should automatically:
- run the query of the parent block
- copy everything returned as text separated by new lines
- show a success message with the number of copied items
- Paste somewhere the content of the clipboard.
- Put the macro at a child block under the query:
1 Like
Thank you so much for your help, it worked! You really saved the day.
I don’t have much of a coding background and I never knew that Logseq could be extended in this way. I’m thrilled and I definitely want to continue exploring more. I really appreciate your guidance.
I used GPT to modify the copyquery
code to make the results appear as links, which better suits my needs. Here’s the modified code:
const LS = logseq.api;
const Module = logseq.Module;
const Kits = Module.Kits;
const Msg = Module.Msg;
function advancedQuery(content, queryWord) {
const queryStart = content.indexOf("[", queryWord + 5);
if (queryStart < 0) return;
var queryEnd = queryStart + 1;
var n = 1;
while (n > 0) {
const close = content.indexOf("]", queryEnd);
if (close < 0) return;
const open = content.indexOf("[", queryEnd);
if (close < open || open < 0) {
queryEnd = close + 1;
n -= 1;
continue;
}
queryEnd = open + 1;
n += 1;
}
return content.slice(queryStart, queryEnd);
}
function simpleQuery(content, queryWord) {
const queryStart = queryWord + 6;
const queryEnd = content.indexOf("}}", queryStart);
return content.slice(queryStart, queryEnd);
}
function getBlocks(content, queryWord) {
if (queryWord < 0) return;
if (content[queryWord - 1] === ":") {
const query = advancedQuery(content, queryWord);
if (query) {
const name = query.includes("?current") ? `"${LS.get_current_page().name}"` : null;
return LS.datascript_query(query, name).flat();
}
} else if (content.slice(queryWord - 2, queryWord) === "{{") {
const query = simpleQuery(content, queryWord);
if (query) return LS.custom_query(query);
}
}
logseq.kits.copyquery = Kits.addClickEventToButton.bind(null, async function onCopyParentClicked(e) {
const child = e.target.closest("div.ls-block");
const parent = child.parentElement.closest("div.ls-block");
const blockId = parent.getAttribute("blockid");
const block = await logseq.api.get_block(blockId);
const content = block.content.replace(/\n?.*[:][:].*\n?/g, "\n").trim();
const queryWord = content.indexOf("query");
const res = getBlocks(content, queryWord);
if (!res) return Msg.ofStatus("Missing query", "warning");
const out = [];
res.forEach((r) => {
const name = r.name;
if (name) out.push(`[[${name}]]`);
else out.push(r.content);
});
navigator.clipboard.writeText(out.join("\n"));
Msg.ofStatus("Copied " + res.length, "success");
});
Thanks again for all your help!