← ガイダンス一覧へ戻る

10全体マップと「サブ埋没」判定

Zettelkasten Inbox / ブロック詳細

知識の木を丸ごと俯瞰する読み取り専用の画面。本題は「関連づけでは繋がっているのに、本文からは1度も引かれていないノート」を、本文を再帰的にたどって炙り出すこと。

抽象度 Lv.4(技術用語+システム上のやりとりまで/製品名・ライブラリ名は書かない)

1. 概要

受信箱が「これから処理する1件」を見る画面なのに対して、こちらは Tag → ABC Note → Literature Note という知識の木を、丸ごと1画面で俯瞰する読み取り専用の画面。

ただ一覧するだけではない。このブロックの本題は 「関連づけとしては繋がっているのに、本文からは1度も引かれていないノート」を探し出すことにある。 アプリではこれを「サブ埋没」と呼んでいる。

Notionのプロパティ(関連づけ)を見るだけの他のブロックと違い、 ここだけはABC Noteの本文の中身を再帰的にたどって調べる。 このアプリで最も種類の違う処理が入っている場所である。

2. 位置づけ

1. アクセス確認 → 10. 全体マップと「サブ埋没」判定 → 2番(修正モードで受信箱へ戻る)

3. 処理内容(Lv.4)

4. なぜこの位置にあるか(設計意図)

受信箱と対になる画面。受信箱は「まだ何もしていないノート」だけを見せる。 だから一度処理したノートは、受信箱からは二度と見えない。 処理したあとで「Tagが違った」「本文に書き足し忘れた」と気付いたときに、 もう一度その1件へ戻る唯一の入口が、この全体マップになっている。

「サブ埋没」という考え方が、このアプリの思想を一番よく表している。 Notionの関連づけを付ければ、見た目のうえでは繋がる。だが ABC Noteの本文で引用していなければ、その知識は使われていない。 関連づけは繋がりの宣言にすぎず、本文への引用が繋がりの実体である―― という区別を、機械的に検出する仕組みにしている。

2段階の「埋没」がある。 埋没=Tagは付いているがABC Noteに1つも繋がっていないノート。 サブ埋没=ABC Noteには繋がっているが、その本文からは引かれていないノート。 前者は関連づけを見るだけで分かるので 10-4 で出るが、後者は本文を読まないと分からないので別のAPIになっている。

3件ずつに分けている理由。1件の判定でNotionへ何十往復もしうるので、 全部まとめて投げると1回の要求が長くなりすぎる。 小分けにして進み具合を画面に出しながら少しずつ進める形にしている。

5. 詳細プロセス分解

工程主体やりとりの内容方向
10-1ブラウザ → マップAPI全体マップの要求
10-2マップAPI → Notion3つのDBを同時に全件取得
10-3マップAPI → Notion100件を超える関連だけ追加で取得
10-4マップAPI(内部)木の組み立てと13種類の集計
10-5マップAPI → ブラウザTag・ABC・Literatureの木と集計
10-6ブラウザ → サブ埋没APIABC NoteのID(1回3件まで)
10-7サブ埋没API → Notion本文を入れ子の奥まで再帰的に取得
10-8サブ埋没API(内部)ブロックのJSONからIDを探す
10-9サブ埋没API → ブラウザ本文から引かれている/いないの2配列
10-10ブラウザ(内部)修正が必要なノートを受信箱へ渡す

各行を文章にすると

10-1 全体マップの画面を開くと、木のデータを丸ごと1回で問い合わせる。

技術的には GET /api/mapCache-Control: no-store。絞り込みの指定は無い。 画面側の検索・折りたたみ・「空のTagを隠す」といった操作は すべて受け取ったデータに対してブラウザの中で行うので、 このAPIを呼ぶのは画面を開いたときの1回だけ。

10-2 Notionの3つのデータベースを、同時に全部取りに行く。

技術的には Tag DB・ABC DB・Literature DB の3つの全件取得を 同時に投げて、3つとも揃うのを待つ2番の受信箱の初期読込がまったく同じ3つを1つずつ順番に取っているのに対し、 こちらは並行。同じアプリの中で判断が分かれている。 なおLiterature DBには絞り込みが無いので、処理済みも未処理も含めた全件が対象になる。 2番が「両方空」で絞っていたのとは規模が桁違いで、ここがこの画面の重さの原因。

10-3 Literature Noteを1件ずつ見て、関連づけが多すぎて 一度に返ってこなかったものだけ、追加で取りに行く。

技術的には ページ取得の応答には関連が最大25件しか入らず、 「まだ続きがある」印だけが立つ。その印が立っているものだけ、 専用の問い合わせで100件ずつ取り切る。 印が立っていなければ追加の通信は起きないので、 普通のノート(関連が数件)では往復ゼロ。 ただしこのループは1件ずつ順番に回るので、 関連の多いノートが何件もあると、その分だけ直列に時間が積み上がる。

10-4 取ってきた3つを組み合わせて木を作り、数を数える。 どこにも収まらないものは「要確認」として別枠に出す。

技術的には 組み立ての規則が3つ。 (1) ABC Noteの配置:親Tagがちょうど1つで、そのTagが実在すればその下へ。 親Tagが0個・2個以上・存在しないTagを指す、のいずれかなら「要確認」の別枠。 6番の候補絞り込みと同じ条件なので、ここに出るABC Noteは受信箱の候補には永久に出てこない。 (2) 埋没の判定:ABC Noteへの関連が1つも無いLiterature Noteを、 付いているTagごとに集める。これが「Tagは付けたが、どの知識にも組み込まれていない」ノート。 (3) 集計:13種類。Tag数、ABC Noteを持つTagの数、ABC総数、 Literature総数、Tag付き/Tagなし、Tag付きでABCあり/Tag付きでABCなし、 引用されているLiteratureの数、関連の総本数、埋没の数、埋没の関連本数、要確認のABC数。 並び順は埋没が多いTagが上――「片付いていないところ」から目に入るようにしてある。

10-5 木と集計を返す。5分間は同じものを使い回す。

技術的には 200no-store。 サーバー側の控えは5分。2番の30秒より長いのは、 この画面が「いま処理する1件」ではなく「全体の傾向」を見るものだから。 保存・削除・Tag作成が起きれば、時間に関係なく即座に捨てられる。

10-6 木が届いたら、画面はすぐに次の分析を始める。 引用されているノートを持つABC Noteを、3件ずつ順番に調べていく。

技術的には POST /api/map/sub-buried{"abcIds": [...]}1回1〜3件で、それ以外は 400。 3件の束を順番に投げ、1つ終わるごとに進み具合(何件中何件)を画面に出す。 Literature Noteが1件も繋がっていないABC Noteは、調べる意味が無いので最初から除外する。 画面を離れたり再読み込みしたりすると、途中で取り消される。

10-7 ABC Noteの本文を、上から順に、折りたたみの中まで潜って読んでいく。

技術的には ここがこのアプリで唯一の 再帰的な探索。ブロックを100件ずつ取り、 「子を持つ」印が立っているブロックがあれば、その中へ降りて同じことを繰り返す。 3番の本文取得が直下しか見ないのと正反対で、 トグルの中でも表の中でも、どこに書かれていても見つける。 暴走を防ぐ歯止めが2つ。 (1) 同じブロックは1度しか降りない(訪問済みを記録する)。 (2) 5,000ブロックで打ち切る。 さらに探しているIDが全部見つかった時点で、途中でも打ち切る―― これが効いていて、本文の冒頭でリンクを並べているABC Noteなら、 最初の100件を読んだ時点で終わる。

10-8 ブロック1つ1つについて、その中身のどこかに 対象ノートの番号が書かれていないかを探す。

技術的には やり方が大胆で、 ブロックの構造を解析せず、中の文字列という文字列を全部さらって、 32桁の16進またはハイフン付きのIDに一致するものを集める。 配列でも入れ子の物でも、再帰的に降りて文字列だけを拾う。 メンション(ページの埋め込みリンク)でも、 本文中に貼ったNotionのURLでも、どちらでも見つかるのが利点。 「メンションだけを探す」実装にすると、URLを直接貼った引用を見落とすため。 比較の前にハイフンを外して小文字に揃えるので、表記の違いに左右されない。 結果は「本文から引かれているノート」と「引かれていないノート」の2つの配列になる。

10-9 判定結果を返す。30分は同じ結果を使い回す。

技術的には 控えの鍵が2つの組み合わせになっている。 そのABC Noteの最終更新時刻と、 繋がっているLiterature NoteのIDを並べ替えて連結した文字列。 本文を直しても、関連を1つ足しても、鍵が変わって控えが無効になる。 30分という長さを許せるのは、この鍵があるから。 画面側は届いた結果を集計に反映し、「本文から引かれている◯件/サブ埋没◯件」を出す。

10-10 直したいノートが見つかったら、その場から受信箱へ飛べる。 飛んだ先では、既存のTagとABC Noteが「維持」と表示されて外せなくなる。

技術的には /?note=◯◯ というURLで受信箱を開くと、 2番が修正モードで動き、そのノートの現在の関連を全部たどって画面に渡す。 受信箱側は既存のTag・ABCを「維持」の印付きで押せない状態にする。 保存すると 8番が「開いた時点と変わっていないか」と「1つも外していないか」を確かめ、 成功したら受信箱に留まらず全体マップへ戻るこの往復が、2つの画面をつなぐ唯一の道。

ワークフロー図

← 図は横にスクロールできます

ブラウザ
マップAPI
サブ埋没API
Notion API
10-1 GET /api/map
10-2 3つのDBを同時に全件取得
10-3 100件超の関連だけ追加で取得
10-4 木を組み、13種類の集計を出す
10-5 200(5分の控えつき)
10-6 POST /api/map/sub-buried(3件ずつ)
10-7 本文を入れ子の奥まで再帰的にたどる
10-8 JSONを文字列として見てIDを探す
10-9 本文から引かれている/いないの2配列
10-10 修正が必要なノートを /?note= で開く

6. この分解から見えること

  • 「関連づけ」と「本文からの引用」を別のものとして数えている。 これがこのアプリの知識観そのもの。 プロパティで繋いだだけでは知識になっていない、本文で使って初めて知識になる―― という考え方が、13種類の集計と2段階の「埋没」という形で実装されている。
  • この画面がいちばん重い。Literature DBを絞り込みなしで全件取り、 そのあとABC Noteの本文を全部たどる。 2番が「未処理だけ」で済ませているのに対し、ここは全部が対象。 ノートが増えるほど、この画面だけが不釣り合いに遅くなる。
  • ブロックのJSONを文字列として検索するのが、上手くもあり危うくもある。 構造を解析しないので、メンションでもURLでも脚注でも見つかる。 一方でたまたま32桁の16進が書かれているだけでも一致してしまう。 実際にはIDと一致する必要があるので誤検出はほぼ起きないが、原理的な穴は残る。
  • 「要確認」の別枠が、6番との橋渡しになっている。 親Tagが1つでないABC Noteは、受信箱の候補に永久に出ない。 その存在に気付ける唯一の場所がここで、 2つの画面が「親Tagはちょうど1つ」という同じ規則を共有している。
  • 修正の入口が「増やす専用」になっている。 全体マップから直しに行っても、既存のTagやABCは外せない。 俯瞰して問題に気付いても、この経路では繋がりを切れない。 切りたければNotion側で直接やることになる。
  • 進み具合を画面に出しているのは、この画面だけ。 3件ずつ処理して「◯件中◯件」を更新する。 時間がかかることを設計側が織り込んでいる証拠でもある。

7. 失敗時の挙動

  • 木の取得に失敗:Notionのコードがそのまま返り、 画面全体が「全体マップを読み込めませんでした」+再読み込みボタンになる。
  • 件数が0または4件以上:400 + 「ABC Noteは1回につき1〜3件を指定してください。」
  • 画面が古く、ABC DBに無いIDが混ざっている:400 + 「ABC DBにないノートが含まれています。画面を更新してください。」
  • サブ埋没の判定が途中で失敗: 木の表示は残ったまま、分析だけが止まる。 「サブ埋没の分析を完了できませんでした。」と出て、 そこまでに判定できたぶんの結果は画面に残る。 全体が使えなくなるわけではない。
  • 5,000ブロックで打ち切られた場合:失敗にはならない。 見つからなかったノートが「サブ埋没」として報告される―― つまり実際には引用されているのに、埋没と誤って判定されうる。 打ち切ったことは画面に出ない。
  • どこまで進んでから失敗すると何が残るか: 読み取りのみなので、Notion側には何も残らない。

8. 未確認事項

  • 5,000ブロックの打ち切りに実際に届いたことがあるか。届いた場合、誤判定が画面に出ない点をどう扱うか
  • Literature DBが数千件になったとき、この画面が実用に耐えるか(絞り込みも打ち切りも無い)
  • 3つのDBを同時に取るこの画面と、順番に取る2番とで、判断が分かれている理由
  • 「サブ埋没」という言葉の定義が、利用者側で共有されているか(画面上の説明は簡潔)
  • 全体マップから既存の繋がりを切る操作を、今後入れる予定があるか
  • 13種類の集計のうち、実際に画面で使われていないものがあるか(型としては全部定義されている)

※ 2026年8月14〜15日時点の zettelkasten-inbox のプログラムを読んで作成しました。過去の設計資料は参照していません。

※ 番号は全体図・この解説・シーケンス図で共通です。工程番号は「ブロック番号-連番」で書いています。

全体図へ戻る / ブロック目次へ

akuramochi1.com — personal tools for learning and thinking.