Best method for .WHERE in dataviewjs queries?

Two questions, which are probably javascript things and not necessarily dataviewjs things:

First question – query syntax

I want to list my timeline items where the metadata begins with certain digits (so I can filter out timeline-items from different years). Somehow I cannot find this anywhere.

For example, I would like to see only items from the 19th century.

start-date:: 1894
start-date:: 1895-01-11
start-date:: 1904

Here is my current faulty query:

var myCurrentPage =  dv.pages('"/timeline_items"') .where(b => b['start-date'] == "189*")

I am trying a wildcard, but it is treating it literally (not unexpected, I guess).

This also doesn’t work:

var myCurrentPage =  dv.pages('"/timeline_items"') .where(b => b['start-date'] == '189*')

I tried .includes, which gave an error.

Second question – variable name

Is the ‘b’ here arbitrary? It seems it doesn’t matter what letter I use. I am just wondering if that is correct.

 .where(b => b['start-date'] == '189*')

Let’s start with the easy one, Yes, it’s arbitrary, but you can help making it a little meaningful by choosing according to what it get to work with. I tend to go with stuff like dv.pages().where( p => ...) to denote that I’m working within the page context. And l for list items, and t for task item if my query has done something to change the context.

A contrived example:

```dataviewjs
const pages = dv.pages()
  .where(p => p.file.name == "something")  // Now we're in the _page_ context
  .file  // Pick out just the file, so we're in the _file_ context
  .where( f => f.link == "something else")
  .lists // Pick the lists out of the valid files, so _lists_ contexts
  .where( l => l.text == "Finally... ")
```

Here I could’ve used the same or different letters/names in each case, but I opted to use a single letter denoting the context as I’ve selected it down the chaining. Hope this make sense.

This is a lot easier asked, then answered. Reason being that your examples are not just strings, but a mix of strings and dates. Whenever Dataview (or dataviewjs) sees something in the format of YYYY-MM it converts that string into a date. See Data Types - Dataview

So when you are using just years, they’re not considered dates, but they remain dates.

Example showing string vs date

Load the following into a note, and study it, and see if you can fathom the difference.

start-date:: 1894
start-date:: 1895-01-11
start-date:: 1904
start-date:: 1904-01

## Display the values
```dataviewjs
function asString(date) {
  if ( typeof(date) == "number" ) {
    return date.toString()
  } else if ( date instanceof DateTime ) {
    return date.toFormat("yyyy-MM-dd")
  } else {
    return date.toString() // and hope for the best
  } 
}

const startDates = dv.current().start_date
startDates.forEach(d => {
  dv.paragraph(`${ d } -- typeof: ${ typeof(d) }, DateTime?: - ${ d instanceof DateTime }, asString: ${ asString(d) }`)
})
```

The output should be something like this:

Notice how the second and the last, are showing as complete date- and timestamps, since they’re interpreted as dates. They’ve also got “true”, and typeof: object, instead of “false” and typeof: number.

So in order to be able to work with these, we need to change the fields into common ground, and that is what the asString() function does. It converts any dates back into “YYYY-MM-DD” again, and tries to make a string out of anything else. (This function could surely be made nicer and better, and account potentially other variants to ensure that we always get as much as possible out of a string in the “YYYY-MM-DD” format as possible)

Another issue you’re facing is that you’ve named your field start-date. This is legal, but unfortunate, as you need to change how to reference. Note how in my example I changed this into start_date, which made it possible to do dv.current().start_date. If using your variant, I would have to use dv.current()["start-date"] (as I did in the next segment).

And finally, use startsWith() to match

When we’ve assured our field is just text, we can use startsWith to check the start of the text, or match() to do regex matches, and all the other string matching functions of javascript. So to do the check in your question of something starting with 189, we can do the following:

```dataviewjs
function asString(date) {
  if ( typeof(date) == "number" ) {
    return date?.toString()
  } else if ( date instanceof DateTime ) {
    return date?.toFormat("yyyy-MM-dd")
  } else {
    return date?.toString() // and hope for the best
  } 
}

const oldStuff = dv.pages('"/timeline_items"')
  .where( p => asString(p["start-date"]).startsWith('189') )
  .map( p => [ p.file.link, asString(p["start-date"]) ] )

dv.list(oldStuff)
```

And hopefully, that should work in your setting, as a similar query worked for mine to produce this list:
image

(Not exactly the same base data as in previous example, but the datestrings where the same)

An alternative approach

Doing all this stuff to ensure string handling is a little cumbersome, and might be a little confusing. Depending on your other needs, whether you need to actually use it as date sometime, it might be worth considering to enforce it as a string in all cases from the start.

This can be done doing: start_date:: "2023-02-01" in your files. Then dataview wouldn’t treat as a date at all, and your query could be the much simpler variant of:

```dataviewjs
const oldStuff = dv.pages('"/timeline_items"')
  .where( p => p.start_date.startsWith('189') )
  .map( p => [p.file.link, p.start_date] )

dv.list(oldStuff)
```

And if you wanted some of them parsed as dates, you could still do date(p.start_date) (if the year and month are present so it’s a valid date)

1 Like

Wow! Wow! WOW!

Thanks very much Holroy.

Yes, that is exactly what I’m looking for. I will review the asString and the typeof. They are new for me.

I understand that my naming convention (hyphen instead of underscore) is not standard and thus brings additional issues.

I will read more and digest, but already I implemented your notes into my query and it works as I had hoped.

I would add (for anyone else who is new to dataviewjs) that I didn’t realize until now that .sort has to come before .map. If it comes after .map, the results won’t be sorted.

Big Thanks Again!

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.