How to create independent output rows (no child) for EACH task that meets these conditions?

Hello. Maybe this one would be easy for someone with some expertise.
I am starting learning datalog and need some help with this “simple” query. I need to:

  1. :white_check_mark: Query that finds TODOs with schedule or deadline for today.
  2. :white_check_mark: Query that finds the DOINGs (no matter if they have any schedule/deadline)
  3. :x: Do not show child tasks. But if one child task meets one of the previous conditions, it should have its own output.

That is (see screenshot), if this is the task list, the query should extract only the red ones and in different rows wihtout showing sub blocks.
imagen

I implemented Points 1 and 2 and works fine, but I dont’ know how to implement point 3.
Any advice?? THANKS!

#+BEGIN_QUERY
{:title "(TODO Schedules or Deadlines for Today) OR (just DOING)"
 :query [:find (pull ?block [*])
         :in $ ?today
         :where
         (or-join [?block ?today]
           (and
             (or
               [?block :block/scheduled ?d]
               [?block :block/deadline ?d])
             [(= ?d ?today)]
             [?block :block/marker "TODO"])
           [?block :block/marker "DOING"])]
 :inputs [:today]
:breadcrumb-show? true             
     :result-transform (fn [result]       
                       (sort-by (fn [b]. 
                                 (or (get b :block/scheduled) (get b :block/deadline))) result))
    :collapsed? false 

}
#+END_QUERY
3 Likes

Still not as advanced as you (just used the task queries in basic form) but tracking this in case I can learn something.

I have updated the code. I am still having some issues, but it could interest you since now it is doing the basic.

1 Like

You technically can’t.
The child blocks are part of the block you return.
What you can do is return them separately as well.
Add option :remove-block-children? false.
And then you could use result transform to add a collapsed state to the result blocks.
I use that result transform logic here:

It also has a sort.
I hope this helps you along. If not, let me know!

1 Like

These are definitively useful lines for my purpose. Thanks! I have already build something (almost) useful for my workflow. Tomorrow I will play a little bit more with the code and share it.

By the moment I have separated the taks ( :remove-block-children? false) and filtered the #epic block (usually this is evaluated during the project reviews, not really during a regular todo list of the day, so it is fine).

Now I need 3 features to complete this wonderful query!:

  • Show the task block only if it the page that contians the block has page property “status:: going”.
  • Add scheduled and deadline column in the table (this one should be easy I think).
  • Sort by DOINGs at the top and TODOs (with scheduled/deadlines) at the bottom.

I will try tomorrow and share what I have. Hopefully I build something useful for someone else too.

PD: Which is in your opinion, the best resource to learn datalog/clojure for Logseq query purposes?

I appreciate your help, thank you!

Depends on what you consider easy! You need add the values as properties. Just like the mapping with the collapsed state. Here’s a complete example for scheduled from my own graph :slight_smile: hope that helps.

#+BEGIN_QUERY
{:title [:h3 "Taken"]
 :inputs [:query-page]
 :query [:find (pull ?b [*]) ?datum
  :keys block sched
  :in $ ?page
  :where
   [?p :block/name ?page]
   [?parent :block/page ?p]
   [?parent :block/refs ?j]
   [?j :block/journal-day ?d]
   [(str ?d) ?sd]
   [(re-pattern "(\\d{4})(\\d{2})(\\d{2})") ?rx] 
   [(re-find ?rx ?sd) [_ ?yyyy ?mm ?dd]]
   [(str ?dd "-" ?mm "-" ?yyyy) ?datum]
   [?b :block/marker "TODO"]
   (not [?b :block/priority "Y"])
   (not [?b :block/priority "Z"])
   (or 
     [?b :block/scheduled ?d]
     [?b :block/deadline ?d]
   )
   (not
     [?plan :block/parent ?parent]
     [?plan :block/refs ?b]
   )
 ]
 :result-transform (fn [res] 
  (sort-by 
    (juxt 
      (fn [r] (get r :block/priority "X"))
      (fn [r] (get r :block/scheduled 99999999))
      (fn [r] (get r :block/deadline 99999999))
      (fn [r] (get r :block/content))
    )
    (map (fn [m] 
      (update (:block m) :block/properties 
        (fn [u] (assoc u :scheduled (get-in m [:sched]) ) )
      )
    ) res)
  )
 )
 :breadcrumb-show? false
}
#+END_QUERY

For sorting between Doing and Todo you can use the :block/marker in the sort.

You can use :block/page attribute to pull the page for a block [?block :block/page ?p]. Then check for the page properties of that page.
[?p :block/properties ?prop] and then a get [(get ?prop :status) ?status] and finally checking the value, [(= ?status "going")].
There’s probably also simple query short hand available, but I generally don’t use it so wouldn’t know.

The additional links from the official docs provide some good starting points imo.
https://docs.logseq.com/#/page/advanced%20queries/block/additional%20links
My website (linked there) also provides some more resource links, as well as references and examples :slight_smile:

In all honesty learning queries has been pulling info from various resources. Hence I’m trying to answer forum questions where I can :stuck_out_tongue:
My journey with learning Logseq queries has been all over the place… slow and steady I guess :slight_smile:

1 Like

A lot of useful hints. I am still doing some tests so before writing back to ask anything, I will first process all your information and finish the tests by myself.

I will share something ASAP. And thank you once again :slight_smile:

1 Like

Ok, definetively getting crazy… I have tried a lot of things. I will just share the most sensible one in my opinion. I have tried to include some of your recommendations. But I get several erros. I included some comments in the query just to identify which parts are not working.

What am I doing wrong?? :expressionless:

  1. I cannot filter only by “going” property pages
  2. I cannot remove block with #epic (initially I though it was working)
  3. So far, I am not sharing anything related with the result transformation… First I would like ot solve this.
#+BEGIN_QUERY
{:title "time-TODOs and DOINGs from GOING and not-epics "
:query [:find (pull ?block [*])
       :in $ ?today
       :where

; THIS DOES NOT WORK...! IT SHOULD REDUCE THE SCOPE ONLY TO PAGES WITH status:: going 
        [?block :block/page ?p] ; page of the block
         [?p :block/properties ?prop] ; Get properties
         (get ?prop :status ?status) ; Get the value 
         [(= ?status "going")] ; Check value

; THIS BLOCKS WORKS FINE. It joins the todo (with deadlne/schedule ocnditions) and the DOINGs
      (or-join [?block ?today]
         (and
           (or
             [?block :block/scheduled ?d]
             [?block :block/deadline ?d]
           ) 
           [(= ?d ?today)]
           [?block :block/marker "TODO"]
           
           )  
          
         [?block :block/marker "DOING"]

          )


      ; THIS DOES NOT WORK! IT SHOULD REMOVE tasks (blocks) with #epic tag
      [?block :block/page ?p]
     [?block  :block/refs ?t]
     (not [?t :block/name "epic"])

]
        
:inputs [:today]
:remove-block-children? false ; this separates each task (block)
:breadcrumb-show? true      
   :result-transform (fn [result]
      (->> result
         (map (fn [r] (assoc r :block/properties {"scheduled" (get r :block/scheduled)})))
      )
   )       
  :collapsed? false
}
#+END_QUERY

Thank you! :pray:

Things I changed:

  • corrected the syntax of (get ?prop :status ?status) back to [(get ?prop :status) ?status] as per my comment :slight_smile:
  • expanded the not statement (not [?t :block/name "epic"]) to include the ref.
(not 
  [?block :block/refs ?t]
  [?t :block/name "epic"]
)

Full query (without result-transform):

#+BEGIN_QUERY
{:title "time-TODOs and DOINGs from GOING and not-epics "
 :query [:find (pull ?block [*])
  :in $ ?today
  :where
   [?block :block/page ?p] ; page of the block
   [?p :block/properties ?prop] ; Get properties
   [(get ?prop :status) ?status] ; Get the value 
   [(= ?status "going")] ; Check value
   (or-join [?block ?today]
     (and
       (or
         [?block :block/scheduled ?d]
         [?block :block/deadline ?d]
       ) 
       [(= ?d ?today)]
       [?block :block/marker "TODO"]
     )  
     [?block :block/marker "DOING"]
   )
   [?block :block/page ?p]
   (not 
     [?block :block/refs ?t]
     [?t :block/name "epic"]
   )
]        
 :inputs [:today]
 :remove-block-children? false ; this separates each task (block)
 :breadcrumb-show? true      
 :collapsed? false
}
#+END_QUERY

Result in my test graph:

1 Like

ok… some syntax errors on my side but the idea was fine :slight_smile: I have taken your code lines and applied some changes:
:white_check_mark:1. Included also deadlines and not only shcedules
:white_check_mark:2. Extended the time range from today +7 days.
:white_check_mark:3. Order by DOING, shcduled and deadline

  1. :x: Result transform: This the final one that I cannot solve. I want to include columns of schedule, deadline and marker (todo, doing…). I have tried to implemente your previous recommendation and also another one I found. But I am not able to add more than one column. Where is the bug on my code??

ATTEMPT 1:

:result-transform (fn [result] (sort-by  ; order by doings and then by to-do (and by schedule/deadline)
                  (juxt 
                           (fn [r] (get r :block/marker "DOING") ) 
                           (fn [r] (get r :block/scheduled) ) 
                           (fn [r] (get r :block/deadline) ) 
           ) 

; NOT WORKING...
(->> result
       (map (fn [r] (assoc r :block/properties {:marker (get r :block/marker)} )))
       (map (fn [u] (assoc u :block/properties {"scheduled" (get u :block/scheduled)} )))
        )
)
)

ATTEMPT 2

:result-transform (fn [result] (sort-by  ; order by doings and then by to-do (and by schedule/deadline)
                (juxt 
                         (fn [r] (get r :block/marker "DOING") ) 
                         (fn [r] (get r :block/scheduled) ) 
                         (fn [r] (get r :block/deadline) ) 
         )
 ; NOT WORKING...
(map (fn [m] 
      (update (:block m) :block/properties 
        (fn [u] (assoc u :scheduled (get-in m [:sched]) ) )
      )
    ) result)
 )
)

I don’t know about attempt 1 honestly, either what ->> result is suppose to mean or why it’s not working.
Attempt 2 doesn’t work as it relies on retrieving the date to use in the :find. (see my complete example earlier in this thread)

However, we can combine both to get something that works :slight_smile:
I also removed the "DOING" bit. The syntax of get is (get collection key default).
In this case using a default would be weird :smiley: all your rows have a :block/marker anyway. And maybe in another situation it would lead to undesired results.

 :result-transform (fn [result] (sort-by  ; order by doings and then by to-do (and by schedule/deadline)
  (juxt 
    (fn [r] (get r :block/marker) )
    (fn [r] (get r :block/scheduled) ) 
    (fn [r] (get r :block/deadline) ) 
  )
  (map (fn [u] (assoc u :block/properties {"scheduled" (get u :block/scheduled)} ) ) result)
 ) )

uhm… the columns are not appearing on my side…

Only the schedule is appearing :thinking:
image

This is my full query for this test after your last recommendation:

#+BEGIN_QUERY
{:title [:h3 "▶️🗓️ DOING or PLAN next days"]
:query [:find (pull ?b [*])
 :in $ ?start ?next
 :where

(or-join [?b ?start ?next]
       (and
       
(or ; search for both shcedule or deadlines
           [?b :block/scheduled ?d]
           [?b :block/deadline ?d]
)
[(>= ?d ?start)] ; search within next 7 days
         [(<= ?d ?next)]
         [?b :block/marker "TODO"]
)
       [?b :block/marker "DOING"] ; include doings no matter the deadline or schedule
)
[?b :block/page ?p] ; remove epic tasks
(not 
 [?b :block/refs ?t]
 [?t :block/name "epic"]
)
]
 :inputs [:today :7d-after]
 :remove-block-children? false 
 :breadcrumb-show? true
 :collapsed? false
  :result-transform (fn [result] (sort-by  ; order by doings and then by to-do (and by schedule/deadline)
  (juxt 
    (fn [r] (get r :block/marker) )
    (fn [r] (get r :block/scheduled) ) 
    (fn [r] (get r :block/deadline) ) 
  )
  (map (fn [u] (assoc u :block/properties {"scheduled" (get u :block/scheduled)} ) ) result)
 ) )
}                                     
#+END_QUER

:woman_facepalming:t4: that’s my fault I’m sorry. I just got it to work, not including all required columns.
I was trying to find a good example and… I can’t find it :sweat:
I’ll get back to you…

Here’s an example… It’s a different query, so it uses the find keys. It’s not on my website yet, but there are comment on it already.
I’ve manually added marker in for you. Hopefully I didn’t make a mistake :joy:

#+BEGIN_QUERY
{:title [:h4 "🎯 Deze week"]
 :query [:find (pull ?b [*]) ?sched ?dead ?m ; return the block and the sched and dead variables
  :keys block sched dead mark ; give the find values a key for use in result-transform
  :in $ ?day ?today
  :where
   ; Add the criteria for which `?b` you want to find here. I've added all tasks as an example.
   [?b :block/marker ?m]
   (not [(contains? #{"DONE" "CANCELED"} ?m)] )
   [(get-else $ ?b :block/scheduled "-") ?sched] ; get the attribute or "-"
   [(get-else $ ?b :block/deadline "-") ?dead] ; get the attribute or "-"
 ]
 :result-transform (fn [res] 
   (sort-by ; Any sort field here.
     (juxt 
       (fn [r] (get r :block/scheduled 99999999))
       (fn [r] (get r :block/priority "X"))
       (fn [r] (get r :block/deadline 99999999))
       (fn [r] (get r :block/content))
     )
     (map (fn [m] ; make a new map based on the query result
       (update (:block m) :block/properties ; update the block properties
         (fn [u] (assoc u :scheduled (get-in m [:sched]) :deadline (get-in m [:dead]) :marker (get-in m [:mark]) ) ) ; associate the sched and dead values set in the where clause
       )
     ) res)
   )
 )
 :breadcrumb-show? false
 :inputs [20230604 :today]
}
#+END_QUERY
1 Like

Not working on my side… I am trying to understand the relation between the where clause and the result, but maybe I am not getting something… This is the result after getting the variables in the where cluase and adding the transformation result:

image

There is the full query:

#+BEGIN_QUERY
{:title [:h3 "test transformation (add columns)"]
:query [:find (pull ?b [*])
 :in $ ?start ?next
 :where

[?b :block/marker ?m]
[(get-else $ ?b :block/scheduled "-") ?sched] ; get the attribute or "-"
[(get-else $ ?b :block/deadline "-") ?dead] ; get the attribute or "-"

(or-join [?b ?start ?next]
       (and
       
(or ; search for both shcedule or deadlines
           [?b :block/scheduled ?d]
           [?b :block/deadline ?d]
)
[(>= ?d ?start)] ; search within next 7 days
         [(<= ?d ?next)]
         [?b :block/marker "TODO"]
)
       [?b :block/marker "DOING"] ; include doings no matter the deadline or schedule
)
[?b :block/page ?p] ; remove epic tasks
(not 
 [?b :block/refs ?t]
 [?t :block/name "epic"]
)


]
 :inputs [:today :7d-after]
 :remove-block-children? false 
 :breadcrumb-show? true
 :collapsed? false

:result-transform (fn [res] 
   (sort-by ; Any sort field here.
     (juxt 
       (fn [r] (get r :block/scheduled 99999999))
       (fn [r] (get r :block/priority "X"))
       (fn [r] (get r :block/deadline 99999999))
       (fn [r] (get r :block/content))
     )
     (map (fn [m] ; make a new map based on the query result
       (update (:block m) :block/properties ; update the block properties
         (fn [u] (assoc u :scheduled (get-in m [:sched]) :deadline (get-in m [:dead]) :marker (get-in m [:mark]) ) ) ; associate the sched and dead values set in the where clause
       )
     ) res)
   )
 )
}                                     
#+END_QUERY

You need to change the find clause and add the key clause as seen in my example.

Yep! too much lines… sorry. NOW IS WORKING!! :partying_face: Thank you so much. It would have been almost impossible to build this without your help :slight_smile:

Your tasks management query posts and this query will be quite useful for me to use as example for future queries.

1 Like

Here is my final query just in case is useful for someone.

What does this query do:

  • Search for TO-DOs with schedule/deadline for the next 7 days.
  • Search for DOINGs (does not matter if they don’t have schedule or deadline)
  • Remove #epic parent tasks (because this task is not really doable. is just for organization/tracking)
  • Show results separately on independent rows (ignoring nested structure of tasks)
  • Organize result by first DOING -and then TODO schedule/deadline.
  • add extra columns for table view (marker, schedule and deadline)
#+BEGIN_QUERY
{:title [:h3 "▶️🗓️ DOING or PLANNED for next 7 days"]
:query [:find (pull ?b [*]) ?sched ?dead ?m ; return the block and the sched and dead variables
:keys block sched dead mark ; give the find values a key for use in result-transform
 :in $ ?start ?next
 :where

; Add the criteria for which `?b` you want to find here. 
; here scheduled and deadline dates
   [?b :block/marker ?m]
   [(get-else $ ?b :block/scheduled "-") ?sched]
   [(get-else $ ?b :block/deadline "-") ?dead]

; now we find the TO-DO for the next 7 days and also merge them with the DOINGs. DOING dates does not matter. It will appear even if it does not have any time property.
(or-join [?b ?start ?next]
       (and
       
(or ; search for both shcedule or deadlines
           [?b :block/scheduled ?d]
           [?b :block/deadline ?d]
)
[(>= ?d ?start)] ; search within next 7 days
         [(<= ?d ?next)]
         [?b :block/marker "TODO"]
)
       [?b :block/marker "DOING"] ; include doings no matter the deadline or schedule
)
[?b :block/page ?p] ; remove epic tasks
(not 
 [?b :block/refs ?t]
 [?t :block/name "epic"]
)
]
 :inputs [:today :7d-after]
 :remove-block-children? false ; show blocks separately (they already met previous conditions)
 :breadcrumb-show? true
 :collapsed? false
 :result-transform (fn [res] 
   (sort-by ; show DOINGS first. Then order by schduled and deadline
     (juxt 
       (fn [r] (get r :block/marker "DOING") )
       (fn [r] (get r :block/scheduled 99999999))
       (fn [r] (get r :block/deadline 99999999))
       (fn [r] (get r :block/content))
     )
     (map (fn [m] ; make a new map based on the query result
       (update (:block m) :block/properties ; update the block properties
         (fn [u] (assoc u :scheduled (get-in m [:sched]) :deadline (get-in m [:dead]) :marker (get-in m [:mark]) ) ) ; associate the sched and dead values set in the where clause
       )
     ) res)
   )
 )
}                                     
#+END_QUERY
2 Likes

No worries. It is quite complicated stuff.

You are very welcome! :smiley: Glad it is working.