How to Copy/Export Results from an Advanced Query in Logseq

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 , inside macros{} :
    :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.
  • Usage:
    • Put the macro at a child block under the query:
      • i.e. {{copyparent}}
    • 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.
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!