Dataview: reuse DQL queries?

Hi all,

I’m a big fan of Dataview’s SQL-like syntax for building queries. I would love to be able to re-use DQL queries by embedding them in multiple pages, similarly to how we can re-use DataviewJS queries using the dv.view() function.

One of the reasons dv.view() is so flexible is that we can call the function dv.current() to get a reference to the page in which dv.view() is loaded, allowing it to display information from the embedding page.

However, there doesn’t seem to be a similar mechanism for DQL.

DQL does have the this keyword, which refers to the query’s page. For example, here is a page that displays a link to itself using a DQL query:

image

Imagine I wanted to be able to reuse this query by embedding it in another page. It doesn’t work; the query continues to display a link to the embedded child page, not the title of the embedding parent page:

image

Now imagine I have a much more complex DQL query, say a report of projects assigned to the current team member’s page. I would love to be able to write that DQL query once and then embed it on the page of each person on the team, rather than have to copy-and-paste it a dozen times and maintain it in many places.

I’ve been searching and experimenting, but so far I haven’t found any way to refer to the embedding page from a DQL query. Does anyone know of a way?

If not, I’d like to suggest to @blacksmithgu the idea of adding a new keyword, similar to this, that refers to the page in which this query is embedded. Perhaps outer, or view, or parent? (Since pages can be embedded many layers deep, perhaps this keyword should refer to the topmost page doing the embedding: maybe top, or pane?)

Thanks for reading this far. :slight_smile: I’d love to hear your thoughts!

6 Likes

Hi again all,

I’ve found something of a workaround, so I’d thought I’d share if anyone comes across this topic in the future.

We can take advantage of the fact that DataviewJS can execute DQL queries and use it to embed a DQL query that can be reused across multiple pages.

To do this, we create a DataviewJS script in our Scripts directory to embed a DQL query inside a dv.execute() block. Note that we use the backtick operator to create a multi-line string so the query looks natural.

Scripts/reusable-query.js

dv.execute(`

LIST ComplexField
FROM "Folder"
WHERE ...
FLATTEN ... as ComplexField
SORT ComplexField

`);

Then, in our target page, we include the script using a DataviewJS block (in this case I use an inline block).

Notes/MyPage.md

Here is the query:

`$= dv.view("/Scripts/reusable-query");`

Because dv.view() executes in the context of the embedding page, this DQL query can now be maintained in one place and reused across many pages, which was my primary goal.

This approach has some downsides though, including that the reusable DQL has to be kept as a .js file instead of a .md file, and so it’s not a first-class citizen of the Obsidian UI. For example, I can’t update the script from within Obisidan; I have to use a text editor for that.

However, overall I’m pretty pleased with this workaround, and I hope it’s helpful to someone else too.

14 Likes

Craig - I’m trying to recreate but having a bit of trouble. Should query have forward slashes? I expected to copy file path that contains back slashes…either way seems problematic…also do I need full file path ? thanks!

Hi @Twita ,

The script file should be named something like myscript.js (the ending must be .js, not .md --that’s important), and it can be in any folder, including the root. You’ll need to use a text editor of some kind to create the script – you won’t be able to create or edit it through Obsidian.

In your embedding page, you refer to the script by its full path but not the .js extension.

So if your script was myscript.js in the root of your directory, the embed would look like this:

```dataviewjs
dv.view("/myscript");
```

If the script were in a directory called “Scripts”, it would be:

```dataviewjs
dv.view("/Scripts/myscript");
```

Regardless of whether you’re using Windows or Linux (or, I presume, Mac), I think you should probably be using forward slashes, not backslashes, because I believe that’s what Dataview expects.

(EDIT: Removed the incorrect “.js” from the dv.view() commands)

1 Like

Hey Craig - very much appreciate the response. Still not working. Maybe my writing it out to you will help me (or you, more likely) solve it… My steps:

  1. Tested a dataview table in a note - to make sure it works.
Table zColor, zShape
From ""
Where file.link = [[]]
  1. Used Visual Studio Code to create a .js file in my ‘035 Scripts’ folder called Case004a. Following is full script.

dv.execute(`

Table zColor, zShape
From “”
Where file.link = [[]]

`);

*Note - I tried with and without ```dataview intro/outro

  1. Included the following in an obsidian note:
dv.view("/035 Scripts/Case004a.js");

*Note: I tried with and without the ‘.js’ at the end

  1. The error (when excluding .js) is:

Evaluation Error: SyntaxError: missing ) after argument list
at new Function ()
at DataviewInlineApi.view (plugin:dataview:19602:24)
at async DataviewJSRenderer.render (plugin:dataview:19705:13)

OK - if you’ve read this far, I appreciate your patience. Perhaps I’m being an idiot and missing something silly. Of course, any ideas are greatly appreciated…thanks, Craig.

1 Like

Just a question: why would you want to embed your DQL queries? Just in order to avoid having to type often used queries anew each time you want to make use of it?

If so, why don’t you just use templates?

Hi @Twita ,

I think you’re really close! I’m not entirely sure what we’re doing differently, so I set up a little mini-vault with two files in it to see if I could re-create your error. It seems to be working for me, so I’m including a screenshot of my setup as well as a zip of the vault (if the forum lets me) so that maybe you can look at it and see where the differences are.

test-vault.zip (642 Bytes)

P.S. I noticed an issue with your js file – the quotes on your From statement are curly quotes, not regular quotes. That won’t work with Dataview. That might just be a copy-paste issue into the forum, but I thought I’d mention it in case your computer is getting too helpful. :slight_smile:

2 Likes

thanks so much - will look into this once I figure out why ALL of my templates now don’t work with update…much appreciated !

Hi @alltagsverstand , great question.

In my use case, I have a team of people that are assigned to different projects. When I’m meeting with a particular person, it’s helpful for me to have a query on their contact page that shows all the projects assigned to them, the next action for each project, and so forth. The query is somewhat complex, using FLATTEN and GROUP BY to display the projects in a way that makes sense for our org.

And as we adapt our processes and workflows to new requirements, I need to make adjustments to the query to show the information I want to have at hand during a meeting. If there was a copy of the query on each contact page, I would have to remember to update each copy of the query for every contact page every time I need to make a change to the query. That’s very tedious and error-prone for someone with a brain like mine. :slight_smile:

Instead, I want to maintain this complex query in one place. That way, when I make changes to it, every contact page automatically shows the updated query without any need to edit the contact pages. I hope that helps!

Craig

P.S. You asked why I can’t use templates. If I include the complex query in a template, I get it on each new page, but I still have the problem of having to edit each page every time I want to update the query.

3 Likes

Here’s another option that lets your reusable query live in a Markdown file, which should only contain your DQL query with no extra JavaScript wrapper and no code block wrapper.

dv.execute(await dv.io.load("Scripts/reusable-query.md"))
6 Likes

This is nice. How do I get this to work with DataviewJS?

Hi @ThurMeijer! If you mean how to use my workaround above with DataviewJS, then I suggest you don’t! :smiley:

Instead, use Dataview’s existing dv.view() mechanism, which is designed for JS script reuse.

I was thinking about the execute / load suggestion @orand mentioned, because I really like the option to be able to see it as a md-file.
Is that also possible with the dv.view-option?

No, I don’t think you can execute md files with dv.view() – it’s very particular on what it will load.

And I think dv.execute() is specific to DQL queries, so I don’t think it will work for JavaScript.

That’s about the extent of my knowledge. Good luck!

You should be able to just replace execute with executeJs

dv.executeJs(await dv.io.load("Scripts/reusable-query.md"))

reusable-query.md should contain raw JavaScript that’s not wrapped in a code block.

2 Likes

Hi,

I’m the new guy around here, and I’ve got some tips and questions related to this topic. Which might be off target completely, but here goes.

First of all, using the Plaintext plugin, and adding js as an extension, one is able to edit javascript within Obsidian, albeit without some of the bells and whistles of a more dedicated code editor, like Visual Code 2. Only downfall so far, is that to create the file initially you either need to create an ordinary note within Obsidian, and rename it from .md to .js outside of Obsidian. Or you’ll create the file outside of Obsidian.

Secondly, I found a view such files with syntax highlighter, see my own answer in my own thread: Raw embed of javascript into a code block? - #3 by holroy :smiley:

Lastly, is there a particular reason you’re using dv.execute() instead of dv.query()? The latter seems to have an option to pass a file, pretending to be this, so that it can mimic being on the original note even more than what dv.execute() seems to do. It would also allow for even more post-processing, and handling after query execution if one would want that. See Dataview: Function to get length of unique values OR using nested queries - #2 by holroy, for an example where I post process a DQL query to show summary of the summaries.

Hi @holroy !

Welcome! Thanks for your note and your ideas. Your use of the plaintext, button, and templater plugins is very clever!

Yep. it’s because I want to embed the query results directly on the page. I don’t need to do any post-processing on the results.

1 Like

wow this is super cool. This trick can also be used to re-use a JavaScript file like below:

dv.executeJs(await dv.io.load("Scripts/dailyTaskDistribution/dailyTaskDistribution.js"))
1 Like

Wonderful work here. I stumbled across this completely accidentally today and it solved a problem I knew that I had before I began looking for a solution.

I’m developing a template that shows movies watched, books read each month. Across many years, there are many files I need to create. The problem with normal templating is should I make a change, I need to update many files.

This solves that perfectly, One .js file linked to all the others.

2 Likes

This is so useful. Thanks, @Craig.

  1. If I want multiple queries, is it best to put them in one js file or separate js files?

2. Does anyone know how to use this with the DQL query in a callout?

Two minutes after asking that question, the solution came to mind: put the DVJS in a callout in the placeholder. Doh!

> [!INFO] Quote  
> ```dataviewjs
>dv.view("/_inbox/t1");
>```