Query to find the last immediate child of a block in today's journal

Hi, I am sure this is possible and maybe it isn’t even complicated. I have a “section” (Block) in the daily journal template called, say, " ## Today’s Notes" and I need to query for the last immediate child of this parent block.

Can it be done?

So I did a quick test.
Can you add this to one of your journal pages to verify it gets the note you want?
If so we can work on specific input or how you otherwise want it to work.

#+BEGIN_QUERY
{:title "last note"
 :inputs [:query-page]
 :query [:find (pull ?b [*])
  :in $ ?page
  :where
   [?p :block/name ?page]
   [?par :block/page ?p]
   [?par :block/content "## Today’s Notes"]
   [?b :block/parent ?par]
   (not [?l :block/left ?b])
 ]
}
#+END_QUERY

I get nothing from the query placed on my today’s journal page :frowning: . However I want to place this query in the Contents Page in the Sidebar and target, from there, the Today’s Journal Page (even if it’s not the one opened in the center workspace). I would assume that :inputs [:today] should be used. Have no idea about where your query fails …

What can I do to help troubleshoot this?

My Daily Journal looks like this:

- Theme of the Day
- Morning Routines
- Morning Reflections
- Activities
- Journal
  - Entry 1
  ...
  -Entry N
- Evening Reflections
- Evening Routine

I want to target, from the Contents Page, the “Entry N” (last block of Journal Parent-Block) :frowning:

Here I exemplified in the right sidebar with an actual “Entry N” block and a Simple Query, but the Complex Query should target the Parent Block (whatever name … here in the picture is “Jurnal”) and find dynamically the Entry N each time it’s ran.

And your parent block has the “## Today’s Notes” content?
That is critical!

If you wish for that, let me adjust the query for it. Also changed it a little bit to use “includes” instead of a straight up “equals to”.
Adjust the ## Today’s Notes to the actual header name and see if that produces output.

#+BEGIN_QUERY
{:title "last note"
 :inputs [:today]
 :query [:find (pull ?b [*])
  :in $ ?today
  :where
   [?p :block/journal-day ?today]
   [?par :block/page ?p]
   [?par :block/content ?c]
   [(clojure.string/includes? ?c "## Today’s Notes")]
   [?b :block/parent ?par]
   (not [?l :block/left ?b])
 ]
}
#+END_QUERY

Seems to work …

Testing …

Ok, it actually does what I needed …

My Use Case is that I need to build a Logseq-based (no external apps) Quick Capture Workflow where I go from information to note in as few steps as possible. Now my Journal Pages get lengthy as they are created by a Daily Template and I often am not on the Journal Page anyways in the working center pane so I wanted a right sidebar system to get new notes into the Journal section of my Daily Journal Page as quickly as possible. My previous solution was based on a Parent-Block called Quick-Capture which was doing the same but I would have new entries in reverse order because Out-Denting each new note would place it just under that block. Hence my need to target the last Note in that particular section.

So your solution works wonders for me. I still have to tweak it somehow because my last note can be large and have a lot o busy formatting in there and it looks ugly in the right sidebar.

Made a quick video for the simple demo, with last notes very basic/simple:
ConEmu_17ARK5yoCs

(Sorry if it feels excruciatingly slow… while recording it’s not slow at all :))) )

1 Like

Found a “Bug” :slight_smile:
If the Last Note has Child Notes the Query will return nothing …

:rofl: that is definitely a bug!
A child gets :block/left with the parent id. A second child would then refer to the first child instead. left is more of a block before me instead of previous sibling block. Good to know!
So we have to add that it isn’t the child of the block.
Here’s the updated query.

#+BEGIN_QUERY
{:title "last note"
 :inputs [:today]
 :query [:find (pull ?b [*])
  :in $ ?today
  :where
   [?p :block/journal-day ?today]
   [?par :block/page ?p]
   [?par :block/content ?c]
   [(clojure.string/includes? ?c "## Today’s Notes")]
   [?b :block/parent ?par]
   (not 
     [?l :block/left ?b]
     (not [?l :block/parent ?b])
   )
 ]
}
#+END_QUERY

That is… Actually a surprisingly similar use case for me :astonished: I might want to start using this query myself :joy:
I already use a custom start page as my landing page instead of the journal. Still it was cumbersome to have a bunch of notes in the way, so I moved my notes section to the sidebar. But this hinders my ability to quick capture. (No sidebar on my phone…)
So instead showing only the last note would solve this. Which I feel I should make empty for even better access.

Yes, let’s talk about that :smiley:

I was banging my head around not wanting to script my way away of this. It should be fairly easy to add a dash just before the next header section with a script but I still want native-means only.

My previous solution was to write new notes below a short-titled (“Quick Capture”) Block that was sitting at the beginning of the Journal section. And it was quite easy to build the simple query as to quickly capture notes. I actually wrote about this workflow on Discord and uploaded a gif recording of it.

What I didn’t like is the fact that new notes would out-dent just below that block, in reverse order. Not a biggie, My first-level notes are always about different things and if I need to develop a note into a full fledged note I would Zoom In on it with shortcut so it opens inside the Center Workspace and continue from there.

With this latest quest of mine, I somehow wish that the last child in the Jurnal section will always be an empty bullet. But How to achieve that with Logseq means? I believe that that requires Javascript.

I started using Custom Commands for all my Quick Capture notes as Custom Commands can create “Singe Bullet Templates” with properties and so on. But, unfortunately, they can’t create two bullets with one Custom Command. For now I don’t have a solution yet… maybe the power of community? :slight_smile:

Ok, This post is more about a Quick Capture Workflow than a continuation of the Query Discussion, but I decided of thinking aloud on it.
So for the sake of quicker capturing I could trade a few steps at the end of the capture just to ease my next Quick Capture. I am using the query to get my last note but that should always be an empty bullet (having a large last note opened in the sidebar will just have me scroll to the end of it to be able to click for new bullet).

To get the last note to be an empty bullet I would have to develop a habit of:

  1. use:
  2. click on the result of the query:
    image
  3. I write my quick capture note straight in the last bullet (which is empty). If needed, I Zoom In on the bullet being written to continue working in the Main Area:
    image
  4. Once Zoomed-In on the Block I continue creating my note, adding bullets if needed, and lastly will add an empty bullet (hitting enter one more time) and Out-Denting that bullet from its parent (Shift+Tab), which will prepare the Empty Bullet needed for the next Quick Capture:
  5. Now, in my “Journal” section, I have this:

Now bear in mind, my interest is to get from idea to writing it in as few steps as possible in Logseq, so at step 3 above I am already typing mu idea. All other steps are a logical progression, with the addition of hitting an extra Enter and Shift-Tab at the end, to leave my Journal prepared for the next Quick Capture…

I think I have a solution for actually skipping the query in the page, because this is a two-step procedure: first click the query result, it opens a new “card” in the right sidebar, then click-to-edit the empty block … I want a one-step quick capture solution so I thought of running the Query from a Macro to which to pass the searched term -in m case ### [[Jurnal]] (but could also be a property of that block - quick-capture:: true) and the date (/today, /yesterday, etc) and the output of the macro would be the embed of the extracted block-id.

I tested without query (manually adding the block-id) and it works:

 :macros {
 
 "get-embed" "{{embed ((64f02fde-f8a4-4c6b-bdfe-3e5acdc9ef36))}}"
 
 }

image

But even if this works, how do we tell the page to reload the macro without us always triggering it by going into the block and exiting from it …

You do realize you can type directly in the query result right? Here’s a screen recording to show.

:man_facepalming: I was using the table view all along … never crossed my mind for that :face_with_diagonal_mouth:

Nevertheless, the embed is much more compact:

image

Edit: even with :breadcrumb-show? false

image

But I must confess… it’s darn close and thanks for reminding me to not use table view all the time …

:group-by-page? false :wink:

1 Like

That’s awesome!! :partying_face:
(upper block = query, lower block = embed) ↷
image

I marked your above query as the solution but I’ll paste here the one I ended up with for brevity:

#+BEGIN_QUERY
{:title "🪣Quick Capture"
 :inputs [:today]
 :query [:find (pull ?b [*])
  :in $ ?today
  :where
   [?p :block/journal-day ?today]
   [?par :block/page ?p]
   [?par :block/content ?c]
   [(clojure.string/includes? ?c "### 📝[[Jurnal]]")]
   [?b :block/parent ?par]
   (not 
     [?l :block/left ?b]
     (not [?l :block/parent ?b])
   )
 ]
:breadcrumb-show? false
:group-by-page? false
}
#+END_QUERY
1 Like