在 Day 2 很讓我驚豔的是有人在 Jekyll Theme 中實作了 Backlink,所以今天就來研究他怎麼做到的。你可以在這個專案的 _includes/backlinks.html
或是透過該檔案在 Github 的頁面讀取原始碼。
透過原始碼,我們可以發現它的結構大概是如以下虛擬碼所示:
let all_nodes = site.notes + site.posts + site.pages
calculateLinkCount()
func calculateLinkCount()
for all_nodes as node
if node.link !== current_node.link
if node.content contains current_node.title
count++
return count
end
發現其實他只是透過遍歷這個網站所有節點 (包括文章、筆記、頁面) 的內文,確認是否含有與該篇文章相同標題的文字來判斷。非常簡單的做法,但因為他會在遍歷所有文章時,執行這段程式,所以要完成所有節點的雙向連結的複雜度是 O(n^2),在日後數量增加後,效能上可能會開始趨緩。
另外找了一個專案 Gatsby Garden,與 Jekyll Garden 相似,但是是實作在 Gastby.js 上,在專案的 gatsby-node.js
或是透過 該檔案在 Github 的頁面讀取原始碼。
透過原始碼,我們可以發現它的結構大概是如以下虛擬碼所示:
let all_nodes = site.notes + site.posts + site.pages
let all_nodes_by_title = {}
let referred_by = {}
let refers_to = {}
for all_nodes as node
let outgoingLinks = findReferences(node.content)
allNotesByTitle[node.title] = node
refers_to[node.title] = outgoingLinks
for outgoingLinks as link_title
if referred_by[link_title]
referred_by[link_title] = []
referredBy[link_title].push(node)
在這個實作則是先遍歷所有節點,並透過 findReferences()
搜尋內文中是否有 [[title]]
的特徵,將其加入預先宣告好的陣列作為索引供之後使用。如此複雜度大約為 O(n),會比上面的做法再好。
我也好奇我慣用的 Obsidian 是如何實作這方面的機制,但因為他不是開源專案,所以難以得知了。目前透過這個研究,我們大概知道未來要實作 Bi-Directional Link (雙向連結) 或是 Back Link (反向連結) 的功能時,大致可以怎麼做了。