Backend Lab 1-3 Event-based System 把 Replay 變成 View
view 並不會存資料,可以把 view 看成是一種「查資料的方式」。
把 replay 的 query 存成一個 view:
1 | CREATE VIEW order_status_view AS |
以後要查 order status 只需要:
1 | SELECT * FROM order_state_view |
view 並不會存資料,可以把 view 看成是一種「查資料的方式」。
把 replay 的 query 存成一個 view:
1 | CREATE VIEW order_status_view AS |
以後要查 order status 只需要:
1 | SELECT * FROM order_state_view |
要測試一些效能差異前需要 insert 大量資料的方式:
1 | INSERT INTO events (id, type, payload, created_at) |
order_created 、payment_confirmed 跟 order_completed ,就能計算出現在 order 的狀態是 completed。events table schematype:發生了什麼事payload:細節(json)created_at1 | CREATE TABLE events ( |
1 | INSERT INTO events (id, type, payload) |
用計算的方式算出想要的東西,例如訂單狀態。
1 | -- 算出所有 order 是否已付款 |
1 | INSERT INTO events (id, type, payload, created_at) |
Heptabase 更新很快,在 NixOS 上安裝最新版的 Heptabase 最好下載 AppImage 後自己寫 nix 安裝。
假設資料夾結構有:
1 | home-common.nix |
下載的 AppImage 放在 root directory。
增加 heptabase.nix:
1 | { |
在 hosts/pc/ 底下增加 overlay.nix:
1 | self: super: { |
在 configuration.nix 增加 overlays:
1 | { |
Home Manager 安裝 heptabase:
1 | in |
在已經有 NixOS 設定的情況下,裝新的電腦。
/etc/nixos/configuration.nix,裝好 vim、wget 跟 git/etc/nixos/etc/nixos 到 step 3 clone 的 repohardware-configuration.nix 到對應位置1 | # System ADR-N Title |
有以下情況之一就值得寫:
tsvector 資料 type 來存被處理過、可搜尋的文字內容,通常將原始文字經過拆詞、正規化在加上位置資訊變成可以高效比對的搜尋 index。tsvector 來搜尋,不是去找原始資料tsvector 欄位需要額外裝 extension,例如 [zhparser](https://github.com/amutu/zhparser) 。1 | FROM postgres:17.0-bookworm |
1 | CREATE EXTENSION IF NOT EXISTS zhparser; |
1 | # 建立一個叫 zh 的 text search configuration,並指定 parser 用 zhparser |
tsvector 欄位 1 | ALTER TABLE documents ADD COLUMN fts tsvector; |
1 | UPDATE documents |
coalesce(title, '') 表示如果 title 是 NULL 就當成空字串,避免 to_tsvector(NULL) 直接變成 NULL setweight(to_tsvector('zh', coalesce(title, '')), 'A') 用 zh config 切欄位 title 的中文、產生 tsvector、權重設為 A(最高)setweight(to_tsvector('zh', coalesce(content, '')), 'B') 跟上面差在權重是 B(比 A 低)setweight(to_tsvector('english', coalesce(title, '')), 'A') 就是切英文|| 是用來合併 tsvector 的ORDER BY ts_rank(fts, q) DESC ,權重高的結果會自動排前面1 | CREATE INDEX documents_fts_idx |
GIN = Generalized Inverted Index1 | SELECT uuid, title, |
ts_headline 會從 content 內擷取「命中關鍵字附近」的一小段文字fts @@ ( ... ) 是全文搜尋的比對 operator,意思是「這筆文件的索引內容是否符合搜尋條件?」plainto_tsquery('zh', 'Docker 中文搜尋') 用 zh config 把輸入轉成 tsquery plainto_tsquery('english', 'Docker 中文搜尋') 同一段輸入用英文規則再解析一次plainto_tsquery 用 || 連接表示 ORts_rank(fts, tsquery) 是 PostgreSQL 算「相關度分數」,會考量命中幾次、權重跟詞出現的位置。ORDER BY … DESC 排序就能讓相關度高的結果排前面。tsquery 在這裡要再寫一次,因為 WHERE 跟 ORDER BY 是同一層,不能 reuse expressionOpenWebUI 設定:

在對話把 Heptabase MCP 打開的時候,第一次會 redirect 到 Heptabase 做 authentication,但回到 OpenWebUI 會出現這個 error:
1 | OAuth callback failed: invalid_request: 'client_id' and 'client_secret' parameters must not be provided twice |
看了下 OpenWebUI 的 code,把 backend/open_webui/utils/oauth.py line 739、742~748 註解起來就能動了~

跑自己編的 OpenWebUI 的方式:
1 | bash ./run.sh |
這個 script 會 build docker image 並且把 container 跑起來。
目前(2025-12-21)gpt 5.2 model 實驗起來,可以在 Heptabase Journal 加內容、建立新卡片、使用關鍵字提問,但 whiteboard 相關的互動好像不 work。
Open WebUI v0.6.43 可以正常接 Heptabase 的 MCP 了!(2025-12-28 updated)
去了六個點、找到了四個 cache。一開始是最靠近圓山捷運站的、上次我一眼就看到寶盒但在辦活動、又有工作人員在旁邊就沒拿,這次也有人在旁邊發傳單,暫時先不輕舉妄動XD
接下來就是悠哉的在圓山散步,在昨日世界發現了個蠻棒的平台,平台上有人在拍照,我在附近一直晃來晃去找 cache,最後被蚊子咬得滿頭包……

接著晃到明日世界,到座標點附近很快就找到啦~這顆非常明顯~經過摩天輪進到自行車道,依著提示也很快找到 Good Old Days 。

看地圖在中山橋底下、有個需要攀爬的 cache,不曉得需要爬多高,還是好奇的先去看看了。看一看覺得,嗯,我還是找人一起來在底下幫我掩護(?)好了,我實在有點無法自己那麼顯眼的在那邊爬…… 😅
最後走到福壽宮,雖說困難度是 4,但依照提示的方式找還是蠻快就找到的~

樹德公園是這天最後一個點,走到座標位置看了下提示,四處張望之後心想「一定是這裡!」旁邊有個阿北背對我坐在長椅上,我只好靠近藏寶點、輕手輕腳開手電筒照著照的看半天,繞了一圈又一圈就是沒看到,東看看看西看看,看到阿北都回家了還是沒看到。
照往例,找不到就會翻前面獵友的 log,看著看著覺得一開始認為的藏寶點可能不對。但今天已經找到幾個 cache,有點累了,在想要放棄時,心想「再找最後一次好了!」依照線索看看附近,手電筒照阿照。欸嘿!這裡怎麼有個可疑的小東西咧~輕輕一拉,找到啦!
好吧提示說得沒有錯,是我找錯地方 . _ .
俗話說,獵友的 log 要小心看(俗話並沒有說過這句話)
