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:
Query that finds TODOs with schedule or deadline for today.
Query that finds the DOINGs (no matter if they have any schedule/deadline)
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.
I implemented Points 1 and 2 and works fine, but I dont’ know how to implement point 3. Any advice?? THANKS!
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!
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?
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 hope that helps.
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.
In all honesty learning queries has been pulling info from various resources. Hence I’m trying to answer forum questions where I can
My journey with learning Logseq queries has been all over the place… slow and steady I guess
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
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??
I cannot filter only by “going” property pages
I cannot remove block with #epic (initially I though it was working)
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
ok… some syntax errors on my side but the idea was fine I have taken your code lines and applied some changes:
1. Included also deadlines and not only shcedules
2. Extended the time range from today +7 days.
3. Order by DOING, shcduled and deadline
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
I also removed the "DOING" bit. The syntax of get is (get collection key default).
In this case using a default would be weird 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)
) )
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
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
#+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
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:
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
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