a mini social media app for small communities
at main 5.7 kB view raw
1module database 2 3import time 4import db.pg 5import entity { Post, User, Like, LikeCache } 6import util 7 8// add_post adds a new post to the database, returns true if this succeeded and 9// false otherwise. 10pub fn (app &DatabaseAccess) add_post(post &Post) bool { 11 sql app.db { 12 insert post into Post 13 } or { 14 return false 15 } 16 return true 17} 18 19// get_post_by_id gets a post by its id, returns none if it does not exist. 20pub fn (app &DatabaseAccess) get_post_by_id(id int) ?Post { 21 posts := sql app.db { 22 select from Post where id == id limit 1 23 } or { [] } 24 if posts.len != 1 { 25 return none 26 } 27 return posts[0] 28} 29 30// get_post_by_author_and_timestamp gets a post by its author and timestamp, 31// returns none if it does not exist 32pub fn (app &DatabaseAccess) get_post_by_author_and_timestamp(author_id int, timestamp time.Time) ?Post { 33 posts := sql app.db { 34 select from Post where author_id == author_id && posted_at == timestamp order by posted_at desc limit 1 35 } or { [] } 36 if posts.len == 0 { 37 return none 38 } 39 return posts[0] 40} 41 42// get_posts_with_tag gets a list of the 10 most recent posts with the given tag. 43// this performs sql string operations and probably is not very efficient, use 44// sparingly. 45pub fn (app &DatabaseAccess) get_posts_with_tag(tag string, offset int) []Post { 46 posts := sql app.db { 47 select from Post where body like '%#(${tag})%' order by posted_at desc limit 10 offset offset 48 } or { [] } 49 return posts 50} 51 52// get_pinned_posts returns a list of all pinned posts. 53pub fn (app &DatabaseAccess) get_pinned_posts() []Post { 54 posts := sql app.db { 55 select from Post where pinned == true 56 } or { [] } 57 return posts 58} 59 60// get_recent_posts returns a list of the ten most recent posts. 61pub fn (app &DatabaseAccess) get_recent_posts() []Post { 62 posts := sql app.db { 63 select from Post order by posted_at desc limit 10 64 } or { [] } 65 return posts 66} 67 68// get_popular_posts returns a list of the ten most liked posts. 69// TODO: make this time-gated (i.e, top ten liked posts of the day) 70pub fn (app &DatabaseAccess) get_popular_posts() []Post { 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 eprintln('cached like ${it} does not have a post related to it (from get_popular_posts)') 75 return Post{} 76 } 77 }).filter(it.id != 0) 78 return posts 79} 80 81// get_posts_from_user returns a list of all posts from a user in descending 82// order by posting date. 83pub fn (app &DatabaseAccess) get_posts_from_user(user_id int, limit int) []Post { 84 posts := sql app.db { 85 select from Post where author_id == user_id order by posted_at desc limit limit 86 } or { [] } 87 return posts 88} 89 90// get_all_posts_from_user returns a list of all posts from a user in descending 91// order by posting date. 92pub fn (app &DatabaseAccess) get_all_posts_from_user(user_id int) []Post { 93 posts := sql app.db { 94 select from Post where author_id == user_id order by posted_at desc 95 } or { [] } 96 return posts 97} 98 99// pin_post pins the given post, returns true if this succeeds and false 100// otherwise. 101pub fn (app &DatabaseAccess) pin_post(post_id int) bool { 102 sql app.db { 103 update Post set pinned = true where id == post_id 104 } or { 105 return false 106 } 107 return true 108} 109 110// update_post updates the given post's title and body with the given title and 111// body, returns true if this succeeds and false otherwise. 112pub fn (app &DatabaseAccess) update_post(post_id int, new_title string, new_body string, new_nsfw bool) bool { 113 sql app.db { 114 update Post set body = new_body, title = new_title, nsfw = new_nsfw where id == post_id 115 } or { 116 return false 117 } 118 return true 119} 120 121// delete_post deletes the given post and all likes associated with it, returns 122// true if this succeeds and false otherwise. 123pub fn (app &DatabaseAccess) delete_post(id int) bool { 124 sql app.db { 125 delete from Post where id == id 126 delete from Like where post_id == id 127 delete from LikeCache where post_id == id 128 } or { 129 return false 130 } 131 return true 132} 133 134////// searching ////// 135 136// PostSearchResult represents a search result for a post. 137pub struct PostSearchResult { 138pub mut: 139 post Post 140 author User 141} 142 143@[inline] 144pub fn PostSearchResult.from_post(app &DatabaseAccess, post &Post) PostSearchResult { 145 return PostSearchResult{ 146 post: post 147 author: app.get_user_by_id(post.author_id) or { app.get_unknown_user() } 148 } 149} 150 151@[inline] 152pub fn PostSearchResult.from_post_list(app &DatabaseAccess, posts []Post) []PostSearchResult { 153 mut results := []PostSearchResult{ 154 cap: posts.len, 155 len: posts.len 156 } 157 for index, post in posts { 158 results[index] = PostSearchResult.from_post(app, post) 159 } 160 return results 161} 162 163// search_for_posts searches for posts matching the given query. 164// todo: levenshtein distance, query options/filters (user:beep, !excluded-text, 165// etc) 166pub fn (app &DatabaseAccess) search_for_posts(query string, limit int, offset int) []PostSearchResult { 167 queried_posts := app.db.exec_param_many_result('SELECT * FROM search_for_posts($1, $2, $3)', [query, limit.str(), offset.str()]) or { 168 eprintln('search_for_posts error in app.db.error: ${err}') 169 pg.Result{} 170 } 171 posts := queried_posts.rows.map(fn [queried_posts] (it pg.Row) Post { 172 return Post.from_row(queried_posts, it) 173 }) 174 return PostSearchResult.from_post_list(app, posts) 175} 176 177// get_post_count gets the number of posts in the database. 178pub fn (app &DatabaseAccess) get_post_count() int { 179 n := app.db.exec('SELECT COUNT(id) FROM "Post"') or { 180 eprintln('get_post_count error in app.db.error: ${err}') 181 [] 182 } 183 return if n.len == 0 { 0 } else { util.or_throw(n[0].vals[0]).int() } 184}