Calling dataviewjs script inside DB Folder formula column

What I’m trying to do

I set up a Notion-like database table using the Dataview and DB Folder plugins. I added a relation column to self-reference the database so that I can link items together to create a hierarchy (unidirectional links). I want items to support each other in a specific way.

I had set up multiple “Insights” databases to do this as illustrated here, but I need more flexibility in terms of the number of levels in the hierarchy.

My problem with this new single-database approach is that I can accidentally break the hierarchy by creating a circle of linked items. E.g. I could accidentally use a high-level item to support a lower-level item. I could add a dataviewjs script to each file to check for this, but I’d rather have a visual cue in the database table when I make this mistake.

Things I have tried

I’ve tried playing around with the formula column type after looking at the DB Folder documentation, but I can’t grasp the syntax and have not found great examples to give me some direction.

My approach would be to convert this dataviewjs code into JavaScript, change it so that it alerts me when it finds duplicate backlinks, and add that into a file to be called in the column. But I’m not sure where to start with the code conversion.

Any help getting started would be greatly appreciated :slight_smile:

The trick to detecting this kind of loops is simply to have a dictionary where you keep track of all the links you’ve already traversed. So when you’re ready to traverse a new link, you check against this dictionary, and if you find that it has already been looked at bail out with a warning message.

1 Like

Thank you for your idea.

I’ve modified this dataviewjs script to be:

```dataviewjs
let page = dv.current().file.path;
let pages = new Set();
let stack = [page];
while (stack.length > 0) {
    let elem = stack.pop();
    let meta = dv.page(elem);
    if (!meta) continue;

    for (let inlink of meta.file.outlinks.array()) {

        if (
        inlink.path.includes("Insights")
        ) {
	        if (
	        pages.has(inlink.path)
	        ) {
	        return "Circle";
	        }
	        pages.add(inlink.path);
	        stack.push(inlink.path);
        }
    }
}
```

That script works correctly inside of a page. But I can’t figure out how to call it from the formula column. I tried adding it to a separate file called circularLinkCheck.js inside of a folder named “js” (as suggested in the documentation), wrapping it in a function like this:

function circularLinkCheck() {

let page = dv.current().file.path;
let pages = new Set();
let stack = [page];
while (stack.length > 0) {
    let elem = stack.pop();
    let meta = dv.page(elem);
    if (!meta) continue;

    for (let inlink of meta.file.outlinks.array()) {

        if (
        inlink.path.includes("Insights")
        ) {
	        if (
	        pages.has(inlink.path)
	        ) {
	        return "Circle";
	        }
	        pages.add(inlink.path);
	        stack.push(inlink.path);
        }
    }
}

}

and then calling it from the formula column like this:

${db.js.circularLinkCheck()}

or like this:

${db.js.dataview.circularLinkCheck()}

but there is no output when I know a circular link is present.

Update on things I’ve been trying

I’m having trouble calling any scripts in the formula column so I added my experience to this discussion on a similar issue.

In looking for alternative routes I discovered that text columns will show the results of inline dataviewJS queries. However, it looks like the query will look at the column and not the file when viewing the column.

E.g. for a text column with an ID of “text_field”, this query inside of a file in the table

text_field:: `$= dv.current().file.path`

will show the path of the file when viewed inside the file, but it will show the path of the table when viewing the table:

image

In addition, the script I’d like to call results in “Promise” between angle brackets when using this inline query:

`$= dv.view('circularLinkCheck')`

Here is the script in the circularLinkCheck.js file:

let page = dv.current().file.path;
let pages = new Set();
let stack = [page];

while (stack.length > 0) {
    let elem = stack.pop();
    let meta = dv.page(elem);
    if (!meta) continue;

    for (let inlink of meta.file.outlinks.array()) {

        if (
        inlink.path.includes("Insights")
        ) {
	        if (
	        pages.has(inlink.path)
	        ) {
	        return "Circle";
	        }
	        pages.add(inlink.path);
	        stack.push(inlink.path);
        }
    }
}

The script will correctly return “Circle” when called via regular dataviewjs query but not from an inline query. So if it is possible to use that script in a text column, it will need to somehow reference a table row, and I may need to fix the “Promise” issue.

You should call this like:

`$= await dv.view('circularLinkCheck')`

That should resolve the promise before returning. Also be aware that when you do something like field:: `= this.test` you’re actually storing the query, not the query result, and it will be evaluated in whatever context you display that field in.

I’ve seen a variant like field:: `$= dv.page(“Your page name”).test `, which would still store the query, but this variant would at least be “globally” true since it refers back to the “correct” page instead of what this would happen to refer to.


I’ve not got time to look at other parts of your response, so sorry if this is not a complete answer, but I just wanted to present these two short notes related to your request.

1 Like

Thank you for your reply! I found a workaround for now - at least one that enables the overall system I’m using at it currently stands. I’ve detailed that system here.

I’m sure I’ll explore inline queries again in the future though so I appreciate your help :slight_smile: