Recognise dataview results as date

@TurkeyTips , when you’re first writing up examples; any reason why you don’t use dv.current().date10 ? Or similarly dv.page("Leonardo moves").date11 ?

No need to do all those dv.pages() when referring to the current or a known file.

And if you’ve already calculated propertyValue once, then why do it again to display it later on (without even the calculation being the same?!) ?

Finally, please use await app.fileManager.processFrontMatter (), as it’s indeed an asynchronous method.

At first glance this looks great and works perfectely. Thanks a lot. However, it doesn’t update when I change the original data (i.e.: Date10:: 1460-01-01 → 1460-01-31). This seems logical to me as the command just asks to add a field to front matter, not to redo it when there are changes, right?
Is there a way to include something like an automatic update cycle? Like to redo the command whenever the file is opened or something like that?

I’m sorry, I was actually afraid to scare you off with too much infos, I’ll try to present it more understandable and would be delighted to get any suggestions to enhance the system:

The overall aim is to create a network of persons and manuscripts involved in a certain theme. I need to be able to track the development of the network over time and geographic space. We are talking of around 150 manuscripts and around 400 people. The goal is to be able to query what relations were existant at a given time and place between manuscripts, between persons, and between manuscripts and persons. I can show the relations according to place via the plug in ‘Map View’ and the social relations via the plug in ‘Excalibrain’, which works fine. Yet, to sort and query my notes according to time is a problem.
I have started to create separate notes for every action of a certain person or manuscript (birth, death, creation, ownership …) as I need to be able to refer seperately to the dates and other specifics of the actions in order to relate them to other persons and manuscripts. Every note contains fields to define the action, 4 dates as already explained to show uncertainties, conflicting information and time spans, and further fields about location, relations and so on. The structure of all notes is the same and I treat persons and manuscripts alike. In the Inlines I refer to the absolute file names and not this.file expressions (which I’d prefer), because these caused problems when I queried the different notes to get overviews, giving all results as zero.

CASE
KNOWN FACTS:
The manuscript BasUBHOIV32 was written by Regiomontanus in the last two years before his death. About his death exist conflicting sources, stating different dates. It is crucial that I show the conflicts between the dates instead of solving them. The notes also contain infos about the sources, but that’s not important now.

MY NOTES
file name: Johannes Regiomontanus death
contains properties:
action:: death
date-start-begin:: 1476-07-06
date-start-end:: = [[Johannes Regiomontanus death]].date-start-begin
date-end-begin:: = [[Johannes Regiomontanus death]].date-start-begin
date-end-end:: 1476-07-08

The result is fine:
date-start-begin:: 1476-07-06
date-start-end:: 06-07-1476
date-end-begin:: 06-07-1476
date-end-end:: 1476-07-08
The format in the queried fields changed, it seems that the query results are recognised as dates.

I entered the start-begin and the end-end dates, in this case the start-end date equals the start-begin and the end-begin equals the end-end. In the case of the action ‘death’ the last two don’t make much sense. I think I prefer to keep the fields, in order to keep the structure of all notes identical. I don’t want to enter these dates manually or leave them empty, as I need the notes to update automatically whenever I change a date.

note: @BasUBHOIV32 prodGeography
contains properties:
action:: production
date-start-begin:: = date([[Johannes Regiomontanus death]].date-end-begin) - dur(2years)
date-start-end:: = [[@BasUBHOIV32 prodGeography]].date-start-begin
date-end-begin:: = [[Johannes Regiomontanus death]].date-start-begin
date-end-end:: = [[Johannes Regiomontanus death]].date-end-end
The start-begin contains still the query which we know doesn’t work (2 years before his death). The start-end equals the start begin because I have no further info limiting the start of the production process. But I could well gain this info in the course of the project. The end-begin equals the start-begin of his death, the end-end the end-end of his death, because the definite end of the production process was some time between these two dates.

The result is as expected errors for the first two fields, dates for the last two:
date-start-begin:: = date([[Johannes Regiomontanus death]].date-end-begin) - dur(2years)
date-start-end::

= date([[Johannes Regiomontanus death]].date-end-begin) - dur(2years)

Dataview (for inline query ‘= date([[Johannes Regiomontanus death]].date-end-begin) - dur(2years)’): No implementation found for ‘null - duration’

date-end-begin:: 06-07-1476
date-end-end:: 08-07-1476

When I query for an overview on Regiomontanus, I get a nice result:

TABLE WITHOUT ID action, date-start-begin, date-end-end, location, related-to, function
FROM "03 Analysen/Datenblätter/atomic notes"
WHERE contains(file.name, this.file.name)
SORT date-start-begin ASC

Result looks like that, dates nicely sorted:

action9 date-start-begin date-end-end location related-to function
ouevre - - Georg von Peuerbach -
birth 06-06-1436 06-06-1436 #Ort/Königsberg in Bayern - -
studies 01-01-1448 31-12-1450 Leipzig - -
studies 01-01-1450 01-01-1467 Vienna - -
entourage of a cardinal 01-01-1461 31-12-1465 Italy - -
adviser of bishop and king 01-01-1467 31-12-1471 Hungary - -
sojourn 01-01-1471 01-01-1475 Nürnberg - -
sojourn 01-01-1475 08-07-1476 Rome - -
death 06-07-1476 08-07-1476 Rome - -

But this nice result happens only because all date-start-begin fields are manually filled in. If I change i.e. the date-start-begin in the note for his sojourn in Nürnberg to refer to his birth, the sorting gets broken because the data is not recognised as date anymore.

file name: Johannes Regiomontanus sojournNuremberg
contains properties:
action:: sojourn
date-start-begin:: = [[Johannes Regiomontanus birth]].date-start-begin
date-start-end:: = [[Johannes Regiomontanus sojournNuremberg]].date-start-begin
date-end-begin:: = [[Johannes Regiomontanus sojournNuremberg]].date-start-begin
date-end-end:: = [[Johannes Regiomontanus sojournRome]].date-start-begin

Result looks like this
date-start-begin:: 06-06-1436
date-start-end:: 06-06-1436
date-end-begin:: 06-06-1436
date-end-end:: 01-01-1475

Table from above now sorts the sojourn as last date, because it doesn’t recognise it as date:

action9 date-start-begin date-end-end location related-to function
ouevre - - Georg von Peuerbach -
birth 06-06-1436 06-06-1436 #Ort/Königsberg in Bayern - -
studies 01-01-1448 31-12-1450 Leipzig - -
studies 01-01-1450 01-01-1467 Vienna - -
entourage of a cardinal 01-01-1461 31-12-1465 Italy - -
adviser of bishop and king 01-01-1467 31-12-1471 Hungary - -
sojourn 01-01-1475 08-07-1476 Rome - -
death 06-07-1476 08-07-1476 Rome - -
sojourn 06-06-1436 01-01-1475 Nürnberg - -

I’m starting to understand the pattern, but let’s drastically reduce it to a simpler(?) case to use when testing:

  • t77566 A:
    • date-start-start: 2024-01-01
    • date-start-end: [[t77566 A]].date-start-start + dur(1 year)
    • date-end-start: [[t77566 B]].date-start-end
    • date-end-end: [[t77566 B]].date-start-end + dur(1 month)
  • t77566 B:
    • date-start-start: 2024-02-01
    • date-start-end: 2024-02-29
    • date-end-start: [[t77566 B]].date-start-end + dur(1 week)
    • date-end-end: [[t77566 A]].date-end-end

It’s a whimsical example, but I think it covers most cases. I’m using t77566 as a reference to this notes number within the forum, so lets just ignore that for the rest of this answer.

In this example the start of A was/is somewhere within this year. It starts after a fixed date of 2024-01-01, and it’s potential end is a year later to the start of the start period. The end of A needs to be within a month after the ending of B’s start period.

For B we’ve fixed the start period to be within the month of February, and that the end is at least a week after the end of the start period, and before the end of A.

This an attempt to link back and forth, where I’m trying to depict that B happens within the period of entire A, and that B will take a least a week to complete, and A will take a month to complete after that again.

So first main question: ** Is this kind of resembling a scenario of yours? Am I using the links similar to what you would do? **


Assuming I’ve understand your use case let us discuss two distinct alternatives:

Using app.fileManager.processFrontMatter to fix the dates

In an earlier post I suggested to use this method to calculate stuff like B’s date-end-end to a given date by evaluating the expression needed, and storing it into a fixed version of that field.

This could have worked if we only had internal calculation of the dates. That you’ve stated that we don’t do, and my examples tries to examplify that aspect. The trouble here is that when note A’s dates depend on the values in B, it’ll not get notified of these changes unless note A is open and recalculates its values in the event that we change the values of B.

In other words, if note A is closed and not changed, and is having fixed values (where some are related to B), and you change these values of B, then A will not be updated, and the overall query would fail since it’s using the wrong values of A. That’s a bummer

Using some expression for a field value

You’ve already tried using an inline query, and also shown that it fails. Still we need to devise some way to actually use a query to set the value. I don’t think going the route of deconstructing the query like attempted earlier here is a viable way forward. I’m leaning more like explicitly setting the field to be the query value when it can’t be fixed.

I need to test this out, but I’m contemplating on actually using stuff like:

date-start-start:: 2024-01-01

date-start-end:: [[t77566 A]].date-start-start + dur(1 year)

- start-start type: `= typeof(this.date-start-start) `
- start-end type: `= typeof(this.date-start-end)`

This would allow us do stuff like choice(typeof(date-start-start) = "date"), true, false) where we need to figure out something for the true and false bits. An initial attempt which doesn’t evaluate the end variant is as follows:

- `= choice(typeof(this.date-start-start) = "date", "D:" + this.date-start-start, "E:"  + date(this.date-start-start))`
-  `= choice(typeof(this.date-start-end) = "date", "D:" + this.date-start-end, "E:"  + date(this.date-start-end))`

This when combined with the previous section reports:
image

So we can clearly see that it works in the meaning it properly prefixes the fixed date with D:, and the query variant with E:. Sadly, it doesn’t evaluate the query variant, and I do need to ponder a little on how to that using a standard query.

If we switched to dataviewjs, this would be easy to solve using the definition as above, and using dv.tryEvaluate() like @TurkeyTips has given some examples of. This would then also mean you’d have to rework your queries to be dataviewjs, and I don’t like that (if you don’t like to do that).

So the second question (this one for myself(?) :slight_smile: ) then becomes: How can we evaluate a query consistently within a standard Dataview query?. And that I’ll have to ponder a little on.

In the mean time, please answer my first question if my whimsical test setup kind of make sense as it stands (with exception of logical inconsistencies). Also please add to if there is a date calculation you’re using which I’m not examplifying.

A final third question for you @Gerda : How do you propose to handle the case of circular references? Like if A’s end date depends on B’s end date which again could rely on the starting of A’s end date?

After thinking a little more, I’ve found this is even more complex than expected, and the “simple” tryEvaluate() will not suffice. Consider this example within the same file, C (and it could get a lot worse if including other files):

date-start-start:: 2024-03-01
date-start-end:: [[C]].date-start-start + dur(1 week)
date-end-start:: [[C]].date-start-end 
date-end-end:: [[C]]-date-end-start + dur(2 days)

This note depicts an event starting in the first week of March, where the end is within two days after the start of the event, to find the full list of dates we need to do the following:

  • start-start is a given: 2024-03-01
  • start-end: start-start + 1 week » 2024-03-01 + 1 week » 2024-03-08
  • end-start: start-end » start-start + 1 week » 2024-03-01 + 1 week » 2024-03-08
  • end-end: end-start + 2 days » start-end + 2 days » (start-start + 1 week) + 2 days » (2024-03-01 + 1 week ) + 2 days » 2024-03-08 + 2 days » 2024-03-10

Here “»” denotes one expansion further down the hierarchy. With some clever caching, we can make it a little simpler as we could store known fields so we don’t need to do stuff multiple times. But we’d still need to evaluate it several rounds in order to get to the more complex values.


Luckily, I think I’ve found a way to do this, if we can shift the format to something like:

date-start-start:: 2024-04-10
date-start-end:: [[C]], "start-start", "1 week"
date-end-start:: [[C]], "start-end"
date-end-end:: [[C]], "end-start", "2 days"

Using this syntax we could identify the field as either an date or as an array. If a date we’re happy and use that date directly. If an array we can call a recursive function to find and calculate the date, and at the same time detect any loops, and store intermediate values.


Is this going to be slightly hairy? Oh yes. How will it look in your notes? Hopefully not so bad, given the syntax above an event could add something like:

`= await dv.view("_js/findDates")`

Which would output something similar to:

Start: 2024-03-01 through 2024-03-08
End: 2024-03-08 through 2024-03-10

The query variant could look something like:

```dataviewjs
await dv.view("_js/findDates", {
   query: `
TABLE WITHOUT ID action, date-start-begin, date-end-end, location, related-to, function
FROM "03 Analysen/Datenblätter/atomic notes"
WHERE contains(file.name, this.file.name)
`, 
  dateColumns: [1, 2],
  sortColumns: [1]
})

And the output would be something beautiful like your original examples… At least that is the hope if stuff works out. I’m still hoping we could use a query like the one above, and don’t need to translate every query into dataviewjs stuff.

So in that code I was working off of OPs original example where he’s pulling date10 not from the current page but from a “Test” page. And the reason I used pages rather than page is just cause it’s easier to handle for identical file names, the [0] index would be the first depth-first file rather than first breadth-first file, that’s just my preference though from having hundreds of random scrap files at root level lol (lots of stuff like “test.md”).

I’m confused what you mean by “propertyValue” being calculated again? Those are two separate blocks for two different queries, unless I’m missing something? Oh and yeah you’re totally right, I completely forgot async, thank you.

@Gerda Yeah, like @holroy said, for the frontmatter to change you’d have to rerun that js block by reopening that page. I’m not sure of any way around this since that is just how the blocks work.

I suppose in theory you could make a plugin with event listeners to re-execute blocks whenever their target file is changed, but now we’re talking a lot more effort. That or maybe an “update” command the user runs that goes through all inlinks of current page and re-execute the blocks.

Did the “tryEvaluate” method work when sorting by date in the table?

How can we evaluate a query consistently within a standard Dataview query?

Yeah that’s what I’ve been asking myself too.

I looked through a source code briefly and it seems like any time an inline query gets a value from a field it gets treated as an index, and for our case (the field holds a query) the object resulting from that indexing is of type string, and to the engine that’s the end of the story.

Though I do wonder if a recursive parseField and executeInline call on that string (assuming it is a query) would make it work. Just an idea though.

I don’t see any way to avoid a dataviewjs other than making a plugin, I got some fun ideas I can try when I get out of work. The dv.view example is cool though, is the idea to parse that with dv.query or build up a custom query from the pieces (ie. [[C]], “start-start”, “1 week”)?

Building custom queries as we tag along.

Unless you want to implement a full parser it’s the only way I can see being able to traverse down the chain until you’ll find a date (or detect a loop).

The recursion will otherwise be a pain to dealt with, as you’ll bound to end up with a string (or null) + duration sooner or later. By splitting it up into the three parts we’ve always got control on the bits and pieces. At least in theory…

Just to be quick: your modell fits well to my scenarios.
Circular references: I haven’t really thought about it hoping to be able to solve that with the different end-start and end-end data. I have to ponder if a case like that is really possible in my scenarios.

No, the tryEvaluate method does calculate the right date and works in itself, but the sorting doesn’t work. The overview query just puts this result at the end of the table.

Oh, and strangely enough reopening the page doesn’t change the front matter. In fact, not even restarting Obsidian changes the front matter. Only if I open the block and force it to rerun the front matter gets changed.

I did the query in dataviewjs and wrote a funct to evaluate the field to be a date, the sorting works.

```dataviewjs

function getAsDate(object) {
	if(typeof object == "object") {
		return object;
	}
	else if(typeof object == "string" && object.includes("$=")) {
		return dv.date(eval(object.slice(3, -1).trim()))
	}
	else if(typeof object == "string" && object.includes("=")) {
		return dv.date(dv.tryEvaluate(object.slice(2, -1).trim()))
	}
	return null
}

dv.table(["File", "date-start-begin"], dv.pages('"03 Analysen/Datenblätter/atomic notes"')
	.where(p => p.file.name.includes(dv.current().file.name))
    .sort(p => getAsDate( p["date-start-begin"]) )
    .map(p => [p.file.link, p["date-start-begin"]]))

```

Pasted image 20240302075039

Where the 3 query chain I used is:

FileA:

date-start-begin:: 1480-07-06

FileB:

date-start-begin :: `= [[FileA]].date-start-begin + dur(1 year)`

FileC:

date-start-begin:: `$= dv.tryEvaluate((dv.page("FileB")["date-start-begin"]).slice(2, -1).trim()).minus(dv.duration("10 year"))`

That’s the best I got, I’m sure there are better ways to avoid having to recompute the chain every time, but anything I know of would be more complex and hacky than just slicing the thing lol

Also, if you want it to be more consistent and just dataview inlinejs for everything, swap out “tryEvaluate” for “eval” to execute the js line (you also have to change slice from 2 to 3):

FileA:

date-start-begin:: 1480-07-06

FileB:

date-start-begin:: `$= dv.page("FileA")["date-start-begin"].plus(dv.duration("1 year"))`

FileC:

date-start-begin:: `$= eval((dv.page("FileB")["date-start-begin"]).slice(3, -1).trim()).minus(dv.duration("10 year"))`

This is only half correct. The table looks OK, but it is not really correct. Since some of the date fields are actual date fields, but some fields are in fact queries which the table displays as date field during the rendering process. This is the very tricky part of the queries related to this.

Try this in a file of its own:

date-start-start:: 2024-04-10  
date-start-end:: `= [[t77566 C]].date-start-start + dur(1 week)`  
date-end-start:: `= [[t77566 C]].date-start-end `  
date-end-end:: `= [[t77566 C]].date-end-start + dur(2 days) `  


## Naive approach

```dataview
TABLE WITHOUT ID file.name
  , date-start-start, typeof(date-start-start) as Tss
  , date-start-end, typeof(date-start-end) as Tse
  , date-end-start, typeof(date-end-start) as Tes
  , date-end-end, typeof(date-end-end) as Tee
WHERE file = this.file
```

In my case this displays as:

In all cases but the last a date is rendered, but only the first column is actually a date. The second and third column are just inline queries which dataview is able to render directly. And the fourth date column shows the really tricky part, since it shows a combination of a date 2024-04-17 concatenated with 2 døgn (aka 2 days in Norwegian).

What’s actually output in the last column should have been a double evaluation, since it is set to `= [[t77566 C]].date-end-start + dur(2 days)`. However when dataview evaluates the first round, it ends up with displaying a query for a date and the duration, which can’t be evaluated any further, but it can be rendered as the date and the duration.

I had a longer round this morning, and every attempt I made came to an halt when needing to do the double (or more evaluation) since then we’ve got a query and an evaluated duration of sorts. The only way to get a value out of that would be to parse the query in the first place, and I don’t it’s worth it.

So now I’m going to attempt with the array variant, and see if we can’t get that to work somewhat consistently.

It took some time, but I got it working, and the ending table looks like this:

The primary sort is descending on type-sb column (2)to shift all the strange files to the end, then on the start-begin date column descending, and finally on the end-finish column ascending just to make a difference.

Files used to create this table

WorkingExample77566.zip (6.0 KB)

The dateRanges.js in the archive is expected to be in _js/dateRanges.js, and all the rest of the files have been in the ForumStuff/f77/f77566 folder. If you want them in other places, do remember to update queries accordingly.

One vital change to the original setup is that I’m using the field set of start-begin, start-finish, end-begin and end-finish for a few reasons:

  1. I needed to work with two sets of this as I also explored the option of using inline queries and they had the original field names (and are still present in the test files)
  2. I kind of liked this variant a little better, as it helps me focus on that these variables are defining date ranges for when something started or ended. So begin and finish I felt better conveyed this range thingy
  3. I was confused by using start and end in various combination, to such a degree that I wanted different names

Nothing in the logic depends on these names, except for the doLocal() function, and it could probably be done better to either avoid them, or have them presented to the script.

Some comments on the test files themselves

The A, B and C files have been defined as talked about earlier in this thread. At the end I duplicated the C files with a change of adding 1, 2, or 3 days to the end-finish date, so I could have something to sort on. The D file is defining all the fields in a circle…

The javascript is the engine, and the Full query test is just a random query to display all files having the start-begin field (and a folder requirement). Note that the logic shouldn’t depend on which columns actually are dates or not, as they’re recognised either by being dates, or being an array consisting of 2 or 3 elements, where the two first needs to be a link, and a string.

The variable format

There are three valid formats, all shown using this example from the C3 file:

start-begin:: 2024-04-10  
start-finish:: [[t77566 C1]], "start-begin", "1 week"  
end-begin:: [[t77566 C1]], "start-finish"  
end-finish:: [[t77566 C1]], "end-begin", "3 days"  

And they are:

  • A pure date field, the best of the best fields… :slight_smile:
  • Or an array of 2 elements, where the first is a link to a note, and the second element is a field name
  • Or an array of 3 elements, where the last added element is a positive or negative duration

The script incantations

The script has two variants to be called where the first is used for the local note, to display the calculated dates (this is the doLocal() function):

`$= await dv.view("_js/dateRanges")`

Currently with no parameters, and the end result is something like:

The first part is the definition, and the last two lines is the output of the script. Note that form for describing the dates also accommodates file renaming as these links are proper links which are handled by Obsidian when renaming and so on. And they appear in backlinks too.

The other variant is the query variant, and it could look like this:

```dataviewjs
await dv.view("_js/dateRanges", {
"query": `
  TABLE WITHOUT ID link(file.link, substring(file.name, 6)) as event
    , start-begin, typeof(start-begin) as type-sb
    , start-finish, typeof(start-finish) as type-sf
    , end-begin, typeof(end-begin) as type-eb
    , end-finish, typeof(end-finish) as type-ef
 
  FROM "ForumStuff/f77/f77566"
  WHERE start-begin `,
"displayTypeInNextColumn": true,
"sort": [ "2 desc", "1 desc", "7 asc"] // 0 is first column
})
```

The query itself could be any query that lists the field value of our variables, and the script will loop through all columns and rows detecting which cell is actually a date variable to be expanded.

This example query also lists the type of the preceding column, and has an optional parameter of "displayTypeInNextColumn": true which means that if set and the previous column was a calculated date, it’ll change the next column to be the type of what it calculated. In most case you can just leave out this line altogether, but it was very useful when debugging, and if you do set it to false instead of true, you can see which fields have been calculated since the type of those fields should now be array.

Finally a note on the "sort": array. It can be left out, and the array is in whatever order it was originally not respecting any columns sorted on the date variables. If you specify it, you need to follow the syntax as indicated above, which means:

  • “2 desc” – The primary sorting is on column 2 (where 0 is the first column), and it’s descending. In my test query this means that all the “date” values, comes before the “array” value of that pesky “D” note. :slight_smile:
  • “1 desc” – The secondary sorting is on column 1 and it’s also descending. In my test query this is the start-begin date column, and we list the newest values first
  • “7 asc” – The last sorting priority (if the two first are equal) it the end-finish column, and it’s sorted ascending. In my query that means the oldest dates are first

You can have as many or few sorting columns as you want, but if present they need to be in the format as shown above. That is in an array, where each sort column is specified with a 0-based column number, a space and either desc or asc added to the text.

The logic of the script

I can’t really go through the entire script. Look through it and ask about particularities if needed.

The overall sequential structure though is as follows:

  • Starting comment – An attempt at explaining what this is, and how to use it
  • Main logic – Here we set various parameters based on the input given to dv.view(), and choose whether we should do the local variant, doLocal or the query based variant, doQuery
  • doLocal – This sets up a local loop on the four variables, and builds an array of the calculated values of these dates. If some variables are misbehaving a warning is printed on the console. At the end it builds up a paragraph using the calculated date values.
  • doQuery – This function executes the query, and loops through each cell mutating the cell value if it is identified as a date variable. After this mutation/processing of all the cells, it sorts the values according to any sort keys, and presents the table to the user
  • Various helper functions:
    • evaluateArray – This is the main function for doing the calculation, and is called from both of the above functions.
      • It starts off by pushing a (hopefully) unique key to a stack, so we can keep track of whether we’ve tried to calculate this field already or not. This to avoid never-ending loops of circular definitions (like in the D note).
      • We then lookup the field, and store it in tmp, and calculate the newDuration to add if that is present in the array variable
      • Now we check whether tmp is an actual date, in which case we happily return it up the chain after adding the duration to it’s value
      • If tmp on the other hand is an array, we call our self recursively to further evaluate the fields and duration. We return the value from this call after adding the duration as before
      • If tmp is neither a date nor an array we throw an exception which will be listed in the console, but the originating definition of this date variable is left untouched as shown in the last line of the example table above
    • sortValues – This is just a simple function which loops on every value of the sortKeys array. If a key is non-conforming to the syntax, we bail out and leave the entire array in its original sorting. If the key is recognized, we sort on key after key, before finally returning the fully sorted table back to the caller.

In conclusion

All in all, this took far more time than I anticipated, but it do work it seems. It do require a change of your variable definitions (and possibly a rename of the variables either in the script, or in your vault). The definition change is sadly needed, as otherwise you’d need to implement a parser, and then the script would be a lot larger.

I also kind of like that now all the logic is gathered in that one script, and you can use that both within the local notes to display the calculated values (and even calculate durations of the start and/or end ranges if you so want), and as queries looking at larger group of events, persons and/or manuscripts. I also find it rather neat that it don’t need to know which columns are actually date variables or not, it just happily processes the values presented to it in the table.

The only thing I’m not fully satisfied with is that it doesn’t cache already found values. This could/should be added to the evaluateArray() through the use of a global stack based on the queryKey, but I didn’t have the energy to do so. If one would like to do so, we need to set that value before returning the value back out of the function, and do set it for just the field (and not the included duration). And then one could also add a check in the start of the function to see if there is a queryKey value in the global stack already, and if so just return it instead of doing the calculation all over again.

Phew, I think that is the end of the work on this (at least for today). I sincerely hope this is useful to you, and that you’re able to implement it into your workflow and that your workflow will benefit from it!

Wow, this is overwhelming, I’m actually speechless! I didn’t even hope that someone wld spend so much effort for my problem. This is just wonderful. I will use this for a 3-year-research project. I’ve already tried out a lot of existing digital humanities tools which I could use for the project, but they are all to rigid for my purpose and taste. I’m convinced now, I’ll handle this with Obsidian/Dataview/js. So, to me your effort was extremly helpful. Thank’s ever so much for that.

I’ve set up yr files on my laptop exactely as you’ve sent them and it works perfectely. I also think I know how to adapt yr work to my system. I don’t fully understand the script yet, but I’ll plough through for sure. I’ll have to see how far I get with the cache problem. But for now, I think I have everything I need to start remodeling my system. Yet, it might well be that I have to come back with a question or two which could arise in the process.

Depending on whether you’d want to switch names or not, and depending on how large your current data set is, you might want to consider to do a few complimentary script to identify notes with the older inline query style of date variables, and/or to identify notes with the older names.

Lastly, it could possibly also be useful to have a query identifying all of your circular references, if you are likely to produce those kind of references.

Do you see any issues with creating these queries on your own?

And even more importantly, the script as it stands does only return a table, and not the values itself. Do you think it would be useful to have an option to make it return the values themselves so that you could do further sorting/filtering on the values? (I’m kind of thinking it shouldn’t be too hard to extend the script to do such a thing)

Thanks so much for your help! I tried this and it seemed to work at first glance. But I think I’ll go ahead now with holroy’s solution below. I really appreciate the effort you spent on my problem. I’ve learned a lot. Thanks again!