Add query input or function day-of-week

It would be great to be able to have queries depend on the day of the week, for example, on Monday to return blocks with the tag #Monday, on Tuesday to return blocks with #Tuesday, etc. This could be accomplished with a query input :day-of-week, or even better, a day-of-week function.

Actually I would like to see it possible based on date.
Eg. References to a date that is a Monday.

Like in Excel that we can ask, what day is today? And what day is in this cell? And are they the same?

So like that.

There is var-last-day-of-the-month, i am not sure but this library is used by logseq. We have to fork the library and add something like last-day-of-the-weak, first-day-of-the-weak, actually we can add anything, but rn there are only last-day-of-the-month and first-day-of-the-month

clj-time already has:

  • day-of-week
  • weekend?
  • weekday?
  • monday?sunday?
  • january?december?
  • last-day-of-month? or last-day-of-the-month?
  • first-day-of-month? or first-day-of-the-month?
  • nth-day-of-the-month?
  • and more

I wonder if this is implemented in queries though.
I just get errors of unknown function.
E.g. [(clj.core/days ?d)]
Idk if this is how to call it, but we do for example [(clojure.string/starts-with? ?c "test")]. So using the same form for this.
It’s probably something to use in a plugin.

As far as I know in queries we only have access to clojure.core and some clojure.string.
See datascript, which is the datalog implementation Logseq uses.

I too have encountered the unknown function error when trying to use functions like clojure.string/split and clojure.string/replace inside a query statement,
but I was able to use the same functions inside a pre-defined :result-transform function or :view function inside the config.edn.

Maybe the above problem can be solved by registering pre-defined functions in config.edn.

There is a difference between what can be used in a :where clause and what can be used in the :result-transform clause.
If we want to filter result data we do this in the :where clause. When we then want to change the result data in any way we use :result-transform
We don’t need to do so in the config file however. This can happen in the query itself.

For this use case we want to filter the result data based on day of the week. And for that we need some sort of time conversion logic. Unfortunately it seems to mentioned functions can’t be used in the :where part of a query.

As to split and replace, those are data transform functions and not data filter functions. Hence they are only made available in the :result-transform part.
However functions like str and subs are available in the :where. These are after all listed in the link I used.

I hope this clarifies what functions are available where.

To summarize, for the :where clause those functions listed in the link I added in my previous post.
For the :result-transform clause those functions from clojure.core and some from clojure.string. (but potentially not all of them, I’m not sure)
For the functions in :result-transform we can use them in the query itself and don’t need (but could!) to define them through the config file.

1 Like

How hard would it be to make the functions of clj-time available in queries? Unfortunately I know very little about the architecture of logseq.

I agree this would be great addition. As of now Query result shows only dates and have to open Calendar everytime to check which day it corresponds to.

I have query to show weekly task distribution but unfortunately. I am unable to get Day of the Week in Table.

#+BEGIN_QUERY
{:title [:h3 "🧭️ Weekly Distribution - Disable Page View"]
 :query [:find ?d (count ?b)
       :keys type number
       :in $ ?start ?end
       :where   
       (task ?b #{"LATER" "TODO" "DOING"})
       (or [?b :block/scheduled ?d] [?b :block/deadline ?d])
       [(> ?d ?start)]
       [(< ?d ?end)]
       ]
 :inputs [:-1w :+7d]
:collapsed? false
 :group-by ?d
 :view (fn [rows]
       [:table
        [:thead [:tr [:th "Day"] [:th "Count"]]]
        [:tbody
         (let [sorted-rows (sort-by :type rows)]
           (for [r sorted-rows]
             [:tr
              [:td (get-in r [:type])]
              [:td (get-in r [:number])]]))]])
}
#+END_QUERY
1 Like

For whatever it’s worth, here is a query that contains a (very) long rule which:

  • expects two dates in the form YYYYMMDD
  • returns true only if they are on the same day of the week
#+BEGIN_QUERY
{
 :title [:b "sameWeekDay"]
 :inputs [:today]
 :rules [
  [(sameWeekDay ?date-a ?date-b)

   [(mod ?date-a 100) ?monthday-a]
   [(mod ?date-a 10000) ?mod-a]
   [(quot ?mod-a 100) ?month-a]
   [(- ?month-a 8) ?month8-a]
   [(quot ?month8-a 6) ?month6-a]
   [(* ?month6-a 12) ?month12-a]
   [(- ?month-a ?month12-a) ?monthnum-a]
   [(inc ?monthnum-a) ?monthinc-a]
   [(* 13 ?monthinc-a) ?month13-a]
   [(quot ?month13-a 5) ?month5-a]
   [(quot ?date-a 10000) ?year-a]
   [(+ ?year-a ?month6-a) ?year6-a]
   [(mod ?year6-a 100) ?yearnum-a]
   [(quot ?yearnum-a 4) ?year4-a]
   [(quot ?year6-a 100) ?century-a]
   [(quot ?century-a 4) ?century4-a]
   [(* 5 ?century-a) ?century5-a]
   [(+ ?monthday-a ?month5-a ?yearnum-a ?year4-a ?century4-a ?century5-a) ?sum-a]
   [(mod ?sum-a 7) ?d-a]

   [(mod ?date-b 100) ?monthday-b]
   [(mod ?date-b 10000) ?mod-b]
   [(quot ?mod-b 100) ?month-b]
   [(- ?month-b 8) ?month8-b]
   [(quot ?month8-b 6) ?month6-b]
   [(* ?month6-b 12) ?month12-b]
   [(- ?month-b ?month12-b) ?monthnum-b]
   [(inc ?monthnum-b) ?monthinc-b]
   [(* 13 ?monthinc-b) ?month13-b]
   [(quot ?month13-b 5) ?month5-b]
   [(quot ?date-b 10000) ?year-b]
   [(+ ?year-b ?month6-b) ?year6-b]
   [(mod ?year6-b 100) ?yearnum-b]
   [(quot ?yearnum-b 4) ?year4-b]
   [(quot ?year6-b 100) ?century-b]
   [(quot ?century-b 4) ?century4-b]
   [(* 5 ?century-b) ?century5-b]
   [(+ ?monthday-b ?month5-b ?yearnum-b ?year4-b ?century4-b ?century5-b) ?sum-b]
   [(mod ?sum-b 7) ?d-b]

   [(= ?d-a ?d-b)]
  ]
 ]
 :query [
  :find ?today
  :in $ ?today %
  :where
   (sameWeekDay ?today 20230806)
 ]
}
#+END_QUERY

This query should show the Day of the Week:

#+BEGIN_QUERY
{:title [:h3 "🧭️ Weekly Distribution - Disable Page View"]
 :query [:find ?d (count ?b)
       :keys date number
       :in $ ?start ?end
       :where   
       (task ?b #{"LATER" "TODO" "DOING"})
       (or [?b :block/scheduled ?d] [?b :block/deadline ?d])
       [(> ?d ?start)]
       [(< ?d ?end)]
       ]
 :inputs [:-1w :+7d]
 :collapsed? false
 :group-by ?d
 :view (fn [rows]
       [:table
        [:thead [:tr [:th "Day"] [:th "Count"]]]
        [:tbody
         (let [sorted-rows (sort-by :date rows)
           days {
            0 "Sat"
            1 "Sun"
            2 "Mon"
            3 "Tue"
            4 "Wed"
            5 "Thu"
            6 "Fri"
           }
           weekDay (fn [date]
             (def month (quot (mod date 10000) 100))
             (def month6 (quot (- month 8) 6))
             (def year6 (+ (quot date 10000) month6))
             (def yearnum (mod year6 100))
             (def century (quot year6 100))
             (def d (mod (+ (mod date 100) (quot (* 13 (inc (- month (* month6 12)))) 5) yearnum (quot yearnum 4) (quot century 4) (* 5 century)) 7))
             (get days d)
            )
          ]
           (for [r sorted-rows]
             [:tr
              [:td (str (get-in r [:date]) " " (weekDay (get-in r [:date])))]
              [:td (get-in r [:number])]]))]])
}
#+END_QUERY
3 Likes

Thanks a Lot @mentaloid . It worked perfectly fine.

Although i didn’t completely understand the code but it seems weekDay Function Generates Index from date and then Maps the day via days Map.

It is working fine as shown in screenshot below.

Is there a way to register either

into the config.edn so that it can be used without copy-paste (so I can track it/change it all in one place)? Would it work as a macro if it’s inside the where part of a query?

The weekday seems to use a view for most of the logic.

So if the query returns the same type result (what’s used in :find and :keys), we can use a custom view as defined in config.edn for the same.

Find the line: ;; Pre-defined :view function to use with advanced queries
You should see an example underneath.

Syntax follows:

:<name of view>
<View logic>

The view logic would be everything after the :view part of the query.

To use it:
:view :<name of view>
In your advanced query.

If you need further help let me know!

1 Like

Thanks for the suggestion @Siferiax, I mostly wanted to have a getWeekDay function to use within :where to only find certain days. I’m not sure if the view supports filtering, but even if it does, I suppose that’s a little bit of a hack since why pass through the query if I filter it out of the view?

I wanted the function to return short day names, so I could display a block if it has a property preview-on-day-of-week with value as a string for each day of the week, so that I could find blocks that have this property with value matching (getWeekDay :today), which I implemented via a query, where I stole @mentaloid’s weekday implementation and got the string value from the day of week numeric key.

{:title "🔁 PERIODIC"
 :rules [
         [(getWeekDay ?date-a)
         [(mod ?date-a 100) ?monthday-a]
         [(mod ?date-a 10000) ?mod-a]
         [(quot ?mod-a 100) ?month-a]
         [(- ?month-a 8) ?month8-a]
         [(quot ?month8-a 6) ?month6-a]
         [(* ?month6-a 12) ?month12-a]
         [(- ?month-a ?month12-a) ?monthnum-a]
         [(inc ?monthnum-a) ?monthinc-a]
         [(* 13 ?monthinc-a) ?month13-a]
         [(quot ?month13-a 5) ?month5-a]
         [(quot ?date-a 10000) ?year-a]
         [(+ ?year-a ?month6-a) ?year6-a]
         [(mod ?year6-a 100) ?yearnum-a]
         [(quot ?yearnum-a 4) ?year4-a]
         [(quot ?year6-a 100) ?century-a]
         [(quot ?century-a 4) ?century4-a]
         [(* 5 ?century-a) ?century5-a]
         [(+ ?monthday-a ?month5-a ?yearnum-a ?year4-a ?century4-a ?century5-a) ?sum-a]
         [(mod ?sum-a 7) ?d]
         [(get {
                0 "Sat"
                1 "Sun"
                2 "Mon"
                3 "Tue"
                4 "Wed"
                5 "Thu"
                6 "Fri"
                } ?d)]]
        ]
:query [:find (pull ?h [*])
        :in $ ?query-pg
        :where
        (has-property ?h :preview-on-day-of-week)
        [(property ?h :preview-on-day-of-week) ?day]
        [?query-pg :block/journal-date ?query-date]
        [(getWeekDay ?query-date) ?query-day]
        (= ?query-day ?day)
        ]
:inputs [:query-page]
:group-by-page? false
:collapsed? false}

which does not work as I expect, but I’m able to get this to work for literals, like so

:query [:find (pull ?h [*])
        :where
        (has-property ?h :preview-on-day-of-week)
        [property ?h :preview-on-day-of-week "Sun"]
        ]

So I expect I’m reading the property in ?day correctly, but I’m not sure how I debug the function that returns the string for the day of the date input, as having the target day of week be specified by the journal date from where the query is called instead of using a literal.


but maybe I should backtrack.
My use case where I want to see something that appears on a weekly schedule: it’s my workout routine. I schedule a routine each day of the week that I modify every 4-6 months because of the seasons. I initially set this to be a task as it’s something that’s not done that I wish to do, but as it’s recurring and it’s a description, it really fills up my general todo list queries.

While I’d like to figure out how to write these queries without feeling like it’s all trial and error, I also want to learn how else I might be able to accomplish this otherwise, I think my use case question is,

How might I have tasks annotated in detail without having long list (in screen space, not in number) query results?

I considered,

  1. a query-verbosity property of a whole number, which is difficult to modify over time
  2. enforcing a policy of task children must all be tasks and task details must be a reference.
  3. default task query results as collapsed.

Numbers 2. and 3. seem within the logseq philosophy

The syntax [(property ?h :preview-on-day-of-week) ?day] is not valid. This short hand is only able to filter on a literal as you experience. It is not a get value.
The correct syntax uses the advanced syntax.

[?h :block/properties ?prop]
[(get ?prop :preview-on-day-of-week) ?day]

I would split the task “do workout” from the description of what the workout is exactly.
For example you would write out the workout you wish to do in a block. Then copy the block reference. And then make a task as:
TODO Do [workout x](((block-id)))
This makes a link out of workout x that points to the block with the description in it. Then you can schedule the task as you please.

Hope that helps / gives some ideas.

1 Like

I appreciate the input, thanks for the clarification with property for filtering against literals.


I’ll try it out this way, separating a task from the details.

1 Like

Hi @mentaloid @Siferiax
Do you have a query to find the TODO for the NEXT week?
thanks in advance

One way is to:

  • check the next 7 dates for the desired first day of the week
  • add 7 to the found date
  • use this range to filter the TODOs
1 Like