a mini social media app for small communities

select optimizations

Changed files
+35 -46
doc
src
+1 -1
build.maple
··· 64 64 task:run.real = { 65 65 description = 'Run beep using config.real.maple' 66 66 category = 'run' 67 - run = '${v} -d veb_livereload run ${v_main} config.real.maple' 67 + run = '${v} run ${v_main} config.real.maple' 68 68 } 69 69 70 70 task:cloc = {
+1 -1
doc/todo.md
··· 23 23 created-before:<date> 24 24 is:admin 25 25 ``` 26 - - [ ] misc:replace `SEARCH *` with `SEARCH <column>` 27 26 28 27 ## planing 29 28 ··· 65 64 discord, and other common links. we want those ones to look fancy! 66 65 - [x] post:saving (add the post to a list of saved posts that a user can view later) 67 66 - [x] site:message of the day (admins can add a welcome message displayed on index.html) 67 + - [x] misc:replace `SELECT *` with `SELECT <column>` 68 68 69 69 ## graveyard 70 70
+5 -9
src/database/like.v
··· 1 1 module database 2 2 3 3 import entity { Like, LikeCache } 4 + import util 4 5 5 6 // add_like adds a like to the database, returns true if this succeeds and false 6 7 // otherwise. ··· 18 19 // get_net_likes_for_post returns the net likes of the given post. 19 20 pub fn (app &DatabaseAccess) get_net_likes_for_post(post_id int) int { 20 21 // check cache 21 - cache := sql app.db { 22 - select from LikeCache where post_id == post_id limit 1 23 - } or { [] } 22 + cache := app.db.exec_param('SELECT likes FROM "LikeCache" WHERE post_id = $1 LIMIT 1', post_id.str()) or { [] } 24 23 25 24 mut likes := 0 26 25 27 26 if cache.len != 1 { 28 27 println('calculating net likes for post: ${post_id}') 29 28 // calculate 30 - db_likes := sql app.db { 31 - select from Like where post_id == post_id 32 - } or { [] } 33 - 29 + db_likes := app.db.exec_param('SELECT is_like FROM "Like" WHERE post_id = $1', post_id.str()) or { [] } 34 30 for like in db_likes { 35 - if like.is_like { 31 + if util.or_throw(like.vals[0]).bool() { 36 32 likes++ 37 33 } else { 38 34 likes-- ··· 51 47 return likes 52 48 } 53 49 } else { 54 - likes = cache.first().likes 50 + likes = util.or_throw(cache.first().vals[0]).int() 55 51 } 56 52 57 53 return likes
+1 -3
src/database/notification.v
··· 47 47 // get_notification_count gets the amount of notifications a user has, with a 48 48 // given limit. 49 49 pub fn (app &DatabaseAccess) get_notification_count(user_id int, limit int) int { 50 - notifications := sql app.db { 51 - select from Notification where user_id == user_id limit limit 52 - } or { [] } 50 + notifications := app.db.exec_param2('SELECT id FROM "Notification" WHERE user_id = $1 LIMIT $2', user_id.str(), limit.str()) or { [] } 53 51 return notifications.len 54 52 } 55 53
+5 -5
src/database/post.v
··· 1 1 module database 2 2 3 3 import time 4 + import db.pg 4 5 import entity { Post, User, Like, LikeCache } 6 + import util 5 7 6 8 // add_post adds a new post to the database, returns true if this succeeded and 7 9 // false otherwise. ··· 66 68 // get_popular_posts returns a list of the ten most liked posts. 67 69 // TODO: make this time-gated (i.e, top ten liked posts of the day) 68 70 pub fn (app &DatabaseAccess) get_popular_posts() []Post { 69 - cached_likes := sql app.db { 70 - select from LikeCache order by likes desc limit 10 71 - } or { [] } 72 - posts := cached_likes.map(fn [app] (it LikeCache) Post { 73 - return app.get_post_by_id(it.post_id) or { 71 + cached_likes := app.db.exec('SELECT post_id FROM "LikeCache" ORDER BY likes DESC LIMIT 10') or { [] } 72 + posts := cached_likes.map(fn [app] (it pg.Row) Post { 73 + return app.get_post_by_id(util.or_throw(it.vals[0]).int()) or { 74 74 eprintln('cached like ${it} does not have a post related to it (from get_popular_posts)') 75 75 return Post{} 76 76 }
+7 -6
src/database/saved_post.v
··· 1 1 module database 2 2 3 + import db.pg 3 4 import entity { SavedPost, Post } 5 + import util 4 6 5 7 // get_saved_posts_for gets all SavedPost objects for a given user. 6 8 pub fn (app &DatabaseAccess) get_saved_posts_for(user_id int) []SavedPost { ··· 13 15 // get_saved_posts_as_post_for gets all saved posts for a given user converted 14 16 // to Post objects. 15 17 pub fn (app &DatabaseAccess) get_saved_posts_as_post_for(user_id int) []Post { 16 - saved_posts := sql app.db { 17 - select from SavedPost where user_id == user_id && saved == true 18 - } or { [] } 19 - posts := saved_posts.map(fn [app] (it SavedPost) Post { 20 - return app.get_post_by_id(it.post_id) or { 18 + saved_posts := app.db.exec_param('SELECT id, post_id FROM "SavedPost" WHERE user_id = $1 AND saved = TRUE', user_id.str()) or { [] } 19 + posts := saved_posts.map(fn [app] (it pg.Row) Post { 20 + return app.get_post_by_id(util.or_throw(it.vals[1]).int()) or { 21 21 // if the post does not exist, we will remove it now 22 + id := util.or_throw(it.vals[0]).int() 22 23 sql app.db { 23 - delete from SavedPost where id == it.id 24 + delete from SavedPost where id == id 24 25 } or { 25 26 eprintln('get_saved_posts_as_post_for: failed to remove non-existent post from saved post: ${it}') 26 27 }
+14 -20
src/database/user.v
··· 1 1 module database 2 2 3 3 import entity { User, Notification, Like, LikeCache, Post } 4 + import util 4 5 5 6 // new_user creates a new user and returns their struct after creation. 6 7 pub fn (app &DatabaseAccess) new_user(user User) ?User { ··· 134 135 135 136 // does_user_like_post returns true if a user likes the given post. 136 137 pub fn (app &DatabaseAccess) does_user_like_post(user_id int, post_id int) bool { 137 - likes := sql app.db { 138 - select from Like where user_id == user_id && post_id == post_id 139 - } or { [] } 138 + likes := app.db.exec_param2('SELECT id, is_like FROM "Like" WHERE user_id = $1 AND post_id = $2', user_id.str(), post_id.str()) or { [] } 140 139 if likes.len > 1 { 141 140 // something is very wrong lol 142 - eprintln('a user somehow got two or more likes on the same post (user: ${user_id}, post: ${post_id})') 141 + eprintln('does_user_like_post: a user somehow got two or more likes on the same post (user: ${user_id}, post: ${post_id})') 143 142 } else if likes.len == 0 { 144 143 return false 145 144 } 146 - return likes.first().is_like 145 + return util.or_throw(likes.first().vals[1]).bool() 147 146 } 148 147 149 148 // does_user_dislike_post returns true if a user dislikes the given post. 150 149 pub fn (app &DatabaseAccess) does_user_dislike_post(user_id int, post_id int) bool { 151 - likes := sql app.db { 152 - select from Like where user_id == user_id && post_id == post_id 153 - } or { [] } 150 + likes := app.db.exec_param2('SELECT id, is_like FROM "Like" WHERE user_id = $1 AND post_id = $2', user_id.str(), post_id.str()) or { [] } 154 151 if likes.len > 1 { 155 152 // something is very wrong lol 156 - eprintln('a user somehow got two or more likes on the same post (user: ${user_id}, post: ${post_id})') 153 + eprintln('does_user_dislike_post: a user somehow got two or more likes on the same post (user: ${user_id}, post: ${post_id})') 157 154 } else if likes.len == 0 { 158 155 return false 159 156 } 160 - return !likes.first().is_like 157 + return !util.or_throw(likes.first().vals[1]).bool() 161 158 } 162 159 163 160 // does_user_like_or_dislike_post returns true if a user likes *or* dislikes the 164 161 // given post. 165 162 pub fn (app &DatabaseAccess) does_user_like_or_dislike_post(user_id int, post_id int) bool { 166 - likes := sql app.db { 167 - select from Like where user_id == user_id && post_id == post_id 168 - } or { [] } 163 + likes := app.db.exec_param2('SELECT id FROM "Like" WHERE user_id = $1 AND post_id = $2', user_id.str(), post_id.str()) or { [] } 169 164 if likes.len > 1 { 170 165 // something is very wrong lol 171 - eprintln('a user somehow got two or more likes on the same post (user: ${user_id}, post: ${post_id})') 166 + eprintln('does_user_like_or_dislike_post: a user somehow got two or more likes on the same post (user: ${user_id}, post: ${post_id})') 172 167 } 173 168 return likes.len == 1 174 169 } ··· 185 180 } 186 181 187 182 // delete posts and their likes 188 - posts_from_this_user := sql app.db { 189 - select from Post where author_id == user_id 190 - } or { [] } 183 + posts_from_this_user := app.db.exec_param('SELECT id FROM "Post" WHERE author_id = $1', user_id.str()) or { [] } 191 184 192 185 for post in posts_from_this_user { 186 + id := util.or_throw(post.vals[0]).int() 193 187 sql app.db { 194 - delete from Like where post_id == post.id 195 - delete from LikeCache where post_id == post.id 188 + delete from Like where post_id == id 189 + delete from LikeCache where post_id == id 196 190 } or { 197 - eprintln('failed to delete like cache for post during user deletion: ${post.id}') 191 + eprintln('failed to delete like cache for post during user deletion: ${id}') 198 192 } 199 193 } 200 194
+1 -1
src/entity/likes.v
··· 13 13 pub struct LikeCache { 14 14 pub mut: 15 15 id int @[primary; sql: serial] 16 - post_id int 16 + post_id int @[unique] 17 17 likes int 18 18 }