Compare commits
10 Commits
3a1d508c65
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab36028ccf | ||
|
|
c50baca85a | ||
|
|
23a033acc3 | ||
|
|
570afd621d | ||
|
|
af7e549fc7 | ||
|
|
12f56ff11f | ||
|
|
caca000c7f | ||
|
|
2007e1f7b5 | ||
|
|
663e8c8d62 | ||
|
|
2dd65b7b48 |
34
.github/workflows/deploy.yml
vendored
34
.github/workflows/deploy.yml
vendored
@@ -1,34 +0,0 @@
|
||||
name: Deploy Hugo Site
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v3
|
||||
with:
|
||||
hugo-version: "latest"
|
||||
extended: true
|
||||
|
||||
- name: Build site
|
||||
run: hugo build --minify
|
||||
|
||||
- name: Deploy via rsync
|
||||
uses: appleboy/scp-action@v0.1.7
|
||||
with:
|
||||
host: ${{ secrets.SSH_HOST }}
|
||||
username: ${{ secrets.SSH_USER }}
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
port: ${{ secrets.SSH_PORT }}
|
||||
source: "public/*"
|
||||
target: "/var/www/portfolio"
|
||||
strip_components: 1
|
||||
12
.woodpecker.yml
Normal file
12
.woodpecker.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
# Test
|
||||
when:
|
||||
event: push
|
||||
branch: master
|
||||
|
||||
steps:
|
||||
build:
|
||||
image: hugomods/hugo:latest
|
||||
commands:
|
||||
- hugo --minify --destination /site/public
|
||||
volumes:
|
||||
- /home/saurav/site:/site # writes directly to where Caddy serves from
|
||||
BIN
assets/favicon.png
Normal file
BIN
assets/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
@@ -1,11 +0,0 @@
|
||||
---
|
||||
title: "Sample Note"
|
||||
date: 2025-08-20
|
||||
# summary: "This is a sample note to demonstrate the notes section"
|
||||
tags: ["example"]
|
||||
draft: false
|
||||
---
|
||||
|
||||
This is a sample note. Notes are meant to be short, quick thoughts or TILs (Today I Learned).
|
||||
|
||||
Delete this file and add your own notes here!
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
title: "The Art of the Digital Garden"
|
||||
date: 2026-04-01T08:55:00+05:45
|
||||
draft: false
|
||||
tags: ["philosophy", "gruvbox", "hugo"]
|
||||
description: "Exploring the concept of a digital garden and how a refined Gruvbox theme enhances the experience."
|
||||
---
|
||||
|
||||
Welcome to my digital garden. This is a place where I share my thoughts, projects, and lessons learned along the way. Unlike a traditional blog, a digital garden is always growing and evolving.
|
||||
|
||||
### Why Gruvbox?
|
||||
|
||||
Gruvbox is more than just a color scheme; it's a philosophy of contrast and harmony. By using the "Hard" contrast variant, we achieve a focus that is both easy on the eyes and aesthetically pleasing.
|
||||
|
||||
```javascript
|
||||
const theme = "Gruvbox Dark Hard";
|
||||
console.log(`Current theme: ${theme}`);
|
||||
```
|
||||
|
||||
### The Beauty of Hugo
|
||||
|
||||
Hugo allows for lightning-fast build times and complete control over the structure of our content. Paired with a minimal theme like PaperMod, it provides the perfect canvas for technical writing.
|
||||
|
||||
> "A garden is never finished."
|
||||
|
||||
I hope you enjoy exploring these notes and posts as much as I enjoy writing them.
|
||||
105
content/posts/polymorphic-table-psql.md
Normal file
105
content/posts/polymorphic-table-psql.md
Normal file
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: "The Likes Table Problem: Why We Went Polymorphic."
|
||||
date: 2026-04-01T21:50:00+05:45
|
||||
draft: false
|
||||
tags: ["architecture", "backend", "PSQL"]
|
||||
description: "Concept of polymorphic table in an SQL database"
|
||||
---
|
||||
|
||||
A few days ago, I was working on adding a **Community** section to an application. The idea was simple, users should be able to:
|
||||
|
||||
- Create posts
|
||||
- Leave comments
|
||||
- Like posts
|
||||
- Like comments
|
||||
|
||||
We also had a separate News section. The new requirement was users should be able to like news articles as well.
|
||||
|
||||
Building model for `posts` and `comments` was pretty straight forward. The real challenge was to model the`likes`table.
|
||||
|
||||
## The Problem: How Do We Store Likes?
|
||||
|
||||
We’re using PostgreSQL, so enforcing relationships with foreign keys is easy and clean.
|
||||
|
||||
If only **one** thing could be liked (say, News), the schema would be simple, we would have a `news_like` table which could look something like this:
|
||||
|
||||
| user_id | news_id | timestamp |
|
||||
| --- | --- | --- |
|
||||
| (foreign key) | (foreign key) | 1775059642 |
|
||||
|
||||
But we didn’t have one entity. We had three. `posts`, `comments` and `news`.
|
||||
|
||||
We had to decide:
|
||||
|
||||
> Do we create three separate like tables? Or do we design one flexible solution?
|
||||
|
||||
|
||||
## Option 1: Three Separate Tables
|
||||
|
||||
We could create three tables: `post_likes` , `comment_likes` and `news_likes` . Each table would have proper foreign key relationships. This approach would be a clean relational way of doing things. It:
|
||||
|
||||
- Keeps strong relational integrity
|
||||
- Makes joins easy
|
||||
- Keeps structure explicit
|
||||
|
||||
This is the most “pure relational” approach. But it felt repetitive. The schema grows horizontally.
|
||||
|
||||
And if tomorrow we add something else that can be liked, we’d need yet another table.
|
||||
|
||||
It works, but it doesn’t scale elegantly.
|
||||
|
||||
## Option 2: A Polymorphic Table (What We Chose)
|
||||
|
||||
Instead of multiple tables, we created a **single polymorphic likes table**.
|
||||
|
||||
### What is a Polymorphic Table?
|
||||
|
||||
A polymorphic table can reference multiple types of resources using a shared structure.
|
||||
|
||||
We designed our `likes` table to look something like this:
|
||||
|
||||
| user_id | resource_id | resource_type | timestamp |
|
||||
| --- | --- | --- | --- |
|
||||
| (foreign key) | (uuid) | (POST / COMMENT / NEWS) | |
|
||||
|
||||
Here’s how it works:
|
||||
|
||||
- `user_id`: who liked, foreign key to `users` table.
|
||||
- `resource_id` : the UUID of the item, just the uuid, no foreign key relation.
|
||||
- `resource_type` : what type of item it is
|
||||
- `timestamp` : timestamp
|
||||
|
||||
Instead of a strict foreign key to one table, we store:
|
||||
|
||||
- The ID
|
||||
- The type of resource
|
||||
|
||||
Together, they uniquely identify what was liked.
|
||||
|
||||
With this approach, we had one clean and centralized table to store all kinds of likes. It’s much easier to expand and flexible. Since “like” is a feature common to many parts of the system, this design keeps it generic and reusable.
|
||||
|
||||
But it does has a major downside. We lose direct foreign key enforcement on `resource_id`. Because PostgreSQL can’t enforce a foreign key that dynamically points to multiple tables, referential integrity must be handled at the application level. We cannot write a simple join query to join from `comments` table or `posts` table.
|
||||
|
||||
For example, to fetch likes for a resource:
|
||||
|
||||
```tsx
|
||||
SELECT COUNT(*)
|
||||
FROM likes
|
||||
WHERE resource_id = 'some-uuid'
|
||||
AND resource_type = 'POST';
|
||||
```
|
||||
|
||||
Now, if we need resource details plus likes, we may need separate queries or application-side logic.
|
||||
|
||||
For our use case though, that trade-off was acceptable. We don’t perform heavy cross-entity joins on likes, so the downside was minimal.
|
||||
|
||||
## Why This Design Felt Right
|
||||
|
||||
The key insight was this:
|
||||
|
||||
> “Like” is not tightly coupled to Posts, Comments, or News. It’s a behavior shared across resources.
|
||||
>
|
||||
|
||||
By modeling it polymorphically, we treated “like” as a reusable system capability rather than a feature embedded in each entity.
|
||||
|
||||
And as our application grows, this decision will likely save us refactoring time.
|
||||
29
hugo.yaml
29
hugo.yaml
@@ -32,6 +32,7 @@ params:
|
||||
DateFormat: "January 2, 2006"
|
||||
defaultTheme: dark # dark, light
|
||||
disableThemeToggle: true
|
||||
googleAnalytics: "G-V0CXG8ZEG2"
|
||||
|
||||
ShowReadingTime: true
|
||||
ShowShareButtons: true
|
||||
@@ -51,11 +52,11 @@ params:
|
||||
assets:
|
||||
# disableHLJS: true # to disable highlight.js
|
||||
# disableFingerprinting: true
|
||||
favicon: "<link / abs url>"
|
||||
favicon16x16: "<link / abs url>"
|
||||
favicon32x32: "<link / abs url>"
|
||||
apple_touch_icon: "<link / abs url>"
|
||||
safari_pinned_tab: "<link / abs url>"
|
||||
favicon: ./favicon.png
|
||||
favicon16x16: ./favicon.png
|
||||
favicon32x32: ./favicon.png
|
||||
apple_touch_icon: ./favicon.png
|
||||
safari_pinned_tab: ./favicon.png
|
||||
|
||||
label:
|
||||
text: "SauravDhakal"
|
||||
@@ -81,17 +82,23 @@ params:
|
||||
Content: >
|
||||
I’m a software engineer who enjoys building thoughtful systems and learning how things really work.
|
||||
<br /><br />
|
||||
This is my digital garden - notes, projects, and lessons along the way.
|
||||
Here I write about things I learn, find interesting, or want to remember. I'm a self-hosting and cloud enthusiast, actively experimenting with both.
|
||||
<br /><br />
|
||||
Checkout my [CV](files/CV.pdf) for my works and projects.
|
||||
In my free time, I'm either reading or tinkering with something.
|
||||
<br /><br />
|
||||
Checkout my [CV](CV.pdf) for my works and projects.
|
||||
|
||||
#demo
|
||||
|
||||
socialIcons:
|
||||
- name: github
|
||||
url: "https://github.com/sauravdhakal12"
|
||||
- name: gitea
|
||||
url: "https://gitea.sauravdhakal.com.np/explore"
|
||||
- name: linkedin
|
||||
url: "https://www.linkedin.com/in/saurav-dhakal-9a8b27220/"
|
||||
- name: x
|
||||
url: "https://x.com/s0x1495"
|
||||
- name: dev
|
||||
url: "https://dev.to/sauravdhakal12/"
|
||||
|
||||
analytics:
|
||||
google:
|
||||
@@ -150,3 +157,7 @@ markup:
|
||||
# lineNos: true
|
||||
# style: monokai
|
||||
googleAnalytics: "G-V0CXG8ZEG2"
|
||||
|
||||
services:
|
||||
googleAnalytics:
|
||||
id: "G-V0CXG8ZEG2"
|
||||
|
||||
BIN
static/CV.pdf
Normal file
BIN
static/CV.pdf
Normal file
Binary file not shown.
BIN
static/favicon.png
Normal file
BIN
static/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
Reference in New Issue
Block a user