How to insert "block backlink" with dataview

I need help with insert “blocks backlink”.

When I study a Book I take annotatios on Zotero, then export to Obsidian, and for every cite I have a format that includes:

Block ID
Tags
The text higlighting
Page
One comentary about it.

like this:

In raw look like this:

When I finish of taking notes, I create a note for every tags that meaning a Unique Term.

What I’m trying to do

I want to go to the Unique Term note and export ALL the block that contain a tag with the UT word.

For example in the note “La ilustración” I want to import ALL the block with the tag “La ilustración” in a table like this:

Things I have tried

I tried it with the backlink panel but is too slow import one by one, I want to import ALL the backlink at once.

Somebody know how to do this with Dataview?

It would help if you could paste the raw markdown of an example note into a code block. The pictures are helpful, but I’m having difficulty seeing the details because of your theme. It would also make it much easier to copy for testing. I’m not sure I want to hand type your note to try out Dataview queries.

1 Like

The raw markdown of one annotation:

> [!zotero-yellow] Idea principal <br> N. 12 Highl... <br> HRP6FFTP 
🏷 -  [[Kant/Martin Lutero|Martin Lutero]], [[Kant/Descartes|Descartes]], [[Kant/Spinoza|Spinoza]], [[Kant/La Ilustración|La Ilustración]]
✒ - El pensamiento -de raíces luteranas- de servirse cada cual de la propia razón como criterio último de la verdad, había sido desarrollado largamente por Descartes y por Spinoza. 
🔎 - ([p. VIII](zotero://open-pdf/library/items/QLJFN93B?page=VIII&annotation=HRP6FFTP)) 
✏ - De raíces luteranas y desarrollada por Desc. y por Spin.
Razón <i>propia</i> como <i>criterio de verdad</i>. 
^HRP6FFTP

This is how look in zotero

And this is the template for export to Obsidian by Zotero Integration:

Zotero Integration TEMPLATE
{#- infer latest annotation Date -#}
{%- macro maxAnnotationsDate() -%}
   {%- set tempDate = "" -%}
	{%- for a in annotations -%}
		{%- set testDate = a.date | format("YYYY-MM-DD#HH:mm:ss") -%}
		{%- if testDate > tempDate or tempDate == ""-%}
			{%- set tempDate = testDate -%}
		{%- endif -%}
	{%- endfor -%}
	{{tempDate}}
{%- endmacro -%}


{%- set colorCategoryToMeaning = {
"yellow": "Idea principal",
"red": "Definición (es)",
"green": "Idea secundaria o de contexto",
"blue": "Dato historico",
"purple": "Parrafo",
"magenta": "Refencia a personalidades",
"orange": "Obras y producciones",
"gray": "Preguntas y referencias"
}-%}


{# lookup Zotero colors in annotations with Category #}
{%- macro getMeaning(colorCategory) -%}
	{%- if colorCategory-%}
	 {{- colorCategoryToMeaning[colorCategory] -}}
	{%- else -%}
		{{- colorCategoryToMeaning["yellow"] -}}
	{%- endif -%}
{%- endmacro -%}

{#- handle space characters in zotero tags -#}
{%- macro printTags(rawTags) -%}
	{%- if rawTags.length > 0 -%}
	{% set comma = joiner() %}
		{%- for tag in rawTags -%}
			{{- comma() }} [[Kant/{{ tag.tag }}|{{ tag.tag }}]]
		{%- endfor %}
	{%- endif %}
{%- endmacro -%}

{%- set inline_fields = {
"abtract": abstractNote,
"Zotero": pdfZoteroLink,
"Local": pdfLink,
"Bibliografía": '"' ~ bibliography ~ '"'
}
-%}

{%- set frontmatter_fields = {
"title": '"' ~ (title | replace ('"','') or caseTitle | replace ('"','')) ~ '"',
"authors": '[' ~ authors | replace (";", ", ") ~ ']',
"editors": '[' ~ editors | replace (";", ", ") ~ ']',
"directors": '[' ~ directors | replace (";", ", ") ~ ']',
"translators": '[' ~ translators | replace (";", ", ") ~ ']',
"podcasters": '[' ~ podcasters | replace (";", ", ") ~ ']',
"scriptwriters": '[' ~ scriptwriters | replace (";", ", ") ~ ']',
"Tags": allTags | replace (" ", "_") | replace (",_", " "), 
"first-entry": minAnnotationsDate,
"last-entry": maxAnnotationsDate,
"year": date | format("YYYY"),
"date": date | format("YYYY-MM-DD"),
"citekey": citekey,
"pages": numPages,
"running-time": runningTime,
"type": type,
"class": itemType,
"language": language,
"url": url,
"isbn": ISBN}
-%}

{# generate field safely -#}
{%- macro generateField(prefix, delimiter, f, p) -%}
{%- if p and p != "[undefined]"-%}
{{prefix}}{{f}}{{delimiter}}{{p}}
{% endif %}
{%- endmacro -%}

{#- generate fields based on Zotero properties -#}
{%- macro generateFields(prefix, delimiter, fields) -%}
{%- for field, property in fields -%}
{%- if property.length > 0 -%}
{{- generateField(prefix, delimiter, field, property) -}}
{%- endif -%}
{%- endfor -%}
{%- endmacro -%}

---
aliases: ["{{title}}"{%- if authors and date-%}, "
{%- for author in authors -%}
{{author}}
{%- endfor -%}
{{" ("+date | format("YYYY") +") "}}{{title}}{{caseTitle}}"{%- endif -%}]
{{generateFields("",": ",frontmatter_fields) -}}
---

> [!info]- Metadata
{{generateFields("",": ",inline_fields) -}}
> > [!NOTE]- Abstract
{% if abstractNote.length > 0 %}
> > {{abstractNote}}
{% else -%}
> > No se encontró abstract disponible.
{% endif -%}
{% if relations.length > 0 -%}
> > [!note]- Referencias:  
> >
> > | Nota | Zotero Link |
> > | --- | --- |
{%- for r in relations %}
> > | [[{{r.title}}]] | [PDF]({{r.desktopURI}}) |
{%- endfor -%}
{%- else -%}
>
> > [!note]- Referencias:  
> > No se encontraron referencias.
{%- endif -%}
{{ "" }}

**<center>Anotaciones.</center>**

{%- set newAnnotations = annotations | filterby("date", "dateafter", lastImportDate) -%}
{% if newAnnotations.length > 0 %}
*Fecha importación nuevas anotaciones*: {{importDate | format("YY-MM-DD | HH:mm")}}
{%- set noteIndex = 1 -%}
{% for annotation in newAnnotations %}
 {% if annotation.type == "note" and annotation.color == "#a28ae5"%} 
> [!zotero-{{annotation.colorCategory | lower}}] {{getMeaning(annotation.colorCategory | lower)}} n. {{ noteIndex }} {%- set noteIndex = noteIndex + 1 %} <br> N. {{ loop.index }} {{annotation.type|capitalize}} <br> {{ annotation.id }}
{%- else %}
> [!zotero-{{annotation.colorCategory | lower}}] {{ getMeaning(annotation.colorCategory | lower) }} <br> N. {{ loop.index }} {{annotation.type|capitalize | truncate(5)}} <br> {{ annotation.id }}
{%- endif %} 
{%- if annotation.tags.length > 0 %} 
🏷 - {{printTags(annotation.tags)}}
{%- endif %}
{%- if annotation.annotatedText.length > 0 %}
✒ - {{annotation.annotatedText}} 
{%- endif %}
{%- if annotation.imageRelativePath %}
> ![[{{annotation.imageRelativePath}}|300]]
{%- endif %}
{%- if annotation.desktopURI %} 
🔎 - ([p. {{annotation.pageLabel}}]({{annotation.desktopURI}}))
{%- endif %}
{%- if annotation.comment %} 
✏ - {{annotation.comment | replace ("<b>", "") | replace ("</b>", "") | replace ("- ", ">- ")}}
{%- endif -%}
{{" "}}
^{{ annotation.id }}
{% endfor %}
{# {% endfor %} #}
{%- endif -%}
1 Like

Your choice of note format doesn’t lead to an easy solution with a standard dataview query. There are no standard tags, lists, or YAML properties. The only way I could think of creating the note with your existing structure required me to write a dataviewjs query, which seems to solve your request, but it’s big.

See how this works for you…

```dataviewjs
let link_query = await dv.query("LIST WITHOUT ID file.path FROM [[]] SORT file.name");

let linked_notes = link_query.value.values;
let rows = []; // ouput table contents 

for (let note_path of linked_notes){
	// Load text of notes linking to this file
	const contents = await dv.io.load(note_path);

	// Zotero block regular expression
	const regex = /^(> \[!zotero-yellow\][^🏷]+?)🏷 - ([^✒]+?)✒ - ([^🔎]+?)🔎 - ([^✏]+?)✏ - ([^^]+?)\^(.+?\n)/ 
	
	// Look for regular expression matches to zotero blocks and populate rows with fields
	for (const block of contents.match(new RegExp(regex, 'mg')) || []) {
		const match = block.match(new RegExp(regex, 's'));
		rows.push({
			tags: match[2],
			highlight: match[3],
			page: match[4],
			comment: match[5],
			blkid: match[6]});
	}
}
// generate table of results
dv.table([
	"Text higlighting",
	"Page",
	"Comment",
	"Tags",
	"block id"],
	rows.map(c => [
		c.highlight,
		c.page,
		c.comment,
		c.tags,
		c.blkid]));
```
1 Like

Love you! This is the closest solution to my problem.

The format of the table is correct, except that some annotations don’t import in the table format but instead appear in the callout format. I want to select only the annotations with a specific “tag.”

I think the format of my annotations can be improved for Dataview by using “Inline Fields” with “::”. For example, my tags currently appear as “:label: -” and I need to change them to “:label: ::”.

Thanks a lot for your help! I don’t know programming (I’m a philosopher, I know that I know nothing), but ChatGPT really helps.

The solution I provided uses regular expressions, which can be tricky to get correct. Also, I tried doing it with one giant expression, which increases the odds that it wont work correctly. The search term I created requires a very specific format of your note for it to work. Even moving one space in the wrong location will break it. I assumed that since you are using a tool to generate the notes, the format won’t change too much, so I thought you might be OK. Because I only had the one example note, I wasn’t able to see all the different use cases. If you have example notes that are working incorrectly, you could share those and we could adjust the code to be more robust.

However, this just goes to show why you would benefit from using Dataview specific structures, like “Inline Fields” with “::”. Does your note use the syntax “:label:” for :label: ? When I copied your code, i just got emojis, so that could also be causing issues.

One thing to consider: inline notes do weird things with ( ), and maybe with other characters. The parentheses are used to hide parts of an inline field. I noticed that you use parentheses in your template. You might need to change that.

Before I wrote the code I first submitted to you, I first tried modifying your note to use inline fields, but it looked like your note structure would need to change significantly, so I stopped moving down that path, however, I think your whole Obsidian experience will benefit from learning to incorporate them into your regular work flow.

It sounds like you’re enjoying learning new things about Obsidian. Best of luck! :smile:

1 Like

I updated the script to try and be more robust, as well as report any formatting errors in Zotero blocks that it wasn’t expecting.

Additionally, the code now verifies that the included notes must have a link to the current note in the “tags” column.

NOTE: This code requires there be a blank line between Zotero blocks. If you have one block followed by another without a blank line between it, it will join the two Zotero blocks.

Let me know if there are any other issues…

```dataviewjs
let link_query = await dv.query("LIST WITHOUT ID file.path FROM [[]] SORT file.name");

let linked_notes = link_query.value.values;
let rows = []; // ouput table contents 

for (let note_path of linked_notes){
	// Load text of notes linking to this file
	const contents = await dv.io.load(note_path);

	// Zotero block regular expression
	const regex_zotero = /(^> \[!zotero-yellow\][\S\s]+?)(\n\s*?\n|\n#)/ 
	// Subsection matches
	const regex_fields = /🏷 - ([^✒]+?)✒ - ([^🔎]+?)🔎 - ([^✏]+?)✏ - ([^^]+?)\^(.+?\n)/ 
	
	// Look for regular expression matches to zotero blocks and populate rows with fields
	for (const block of contents.match(new RegExp(regex_zotero, 'mg')) || []) {
		const zotero = block.match(new RegExp(regex_zotero, 's'))[0];
		const match = zotero.match(new RegExp(regex_fields, 's'));
		if(match){
			const fields = {
							tags:      match[1],
							highlight: match[2],
							page:      match[3],
							comment:   match[4],
							blkid:     match[5]};
			// only include Zotero blocks that inclue links to this note
			if ( fields.tags.includes(dv.current().file.name) ){
				rows.push(fields);
			}
		} else { // One of the Zotero blocks is missing something expected in the regular expression...
				dv.paragraph("> [!bug] Script Bug\n> Unexpected formmatting in Zotero block in [[" + 
				note_path + "]].  The text doesn't match the expected format for the regular expression. The following is the block for reference.\n ```\n" + 
				zotero + "\n```");
		}
	}
}
// generate table of results
dv.table([
	"Text higlighting",
	"Page",
	"Comment",
	"Tags",
	"block id"],
	rows.map(c => [
		c.highlight,
		c.page,
		c.comment,
		c.tags,
		c.blkid]));
```

If this script causes other Zotero note types to throw errors, simply remove the following section of code.

		} else { // One of the Zotero blocks is missing something expected in the regular expression...
				dv.paragraph("> [!bug] Script Bug\n> Unexpected formmatting in Zotero block in [[" + 
				note_path + "]].  The text doesn't match the expected format for the regular expression. The following is the block for reference.\n ```\n" + 
				zotero + "\n```");