a mini social media app for small communities
1module database 2 3import time 4import entity { Post, User, Like, LikeCache } 5 6// add_post adds a new post to the database, returns true if this succeeded and 7// false otherwise. 8pub fn (app &DatabaseAccess) add_post(post &Post) bool { 9 sql app.db { 10 insert post into Post 11 } or { 12 return false 13 } 14 return true 15} 16 17// get_post_by_id gets a post by its id, returns none if it does not exist. 18pub fn (app &DatabaseAccess) get_post_by_id(id int) ?Post { 19 posts := sql app.db { 20 select from Post where id == id limit 1 21 } or { [] } 22 if posts.len != 1 { 23 return none 24 } 25 return posts[0] 26} 27 28// get_post_by_author_and_timestamp gets a post by its author and timestamp, 29// returns none if it does not exist 30pub fn (app &DatabaseAccess) get_post_by_author_and_timestamp(author_id int, timestamp time.Time) ?Post { 31 posts := sql app.db { 32 select from Post where author_id == author_id && posted_at == timestamp order by posted_at desc limit 1 33 } or { [] } 34 if posts.len == 0 { 35 return none 36 } 37 return posts[0] 38} 39 40// get_posts_with_tag gets a list of the 10 most recent posts with the given tag. 41// this performs sql string operations and probably is not very efficient, use 42// sparingly. 43pub fn (app &DatabaseAccess) get_posts_with_tag(tag string, offset int) []Post { 44 posts := sql app.db { 45 select from Post where body like '%#(${tag})%' order by posted_at desc limit 10 offset offset 46 } or { [] } 47 return posts 48} 49 50// get_pinned_posts returns a list of all pinned posts. 51pub fn (app &DatabaseAccess) get_pinned_posts() []Post { 52 posts := sql app.db { 53 select from Post where pinned == true 54 } or { [] } 55 return posts 56} 57 58// get_recent_posts returns a list of the ten most recent posts. 59pub fn (app &DatabaseAccess) get_recent_posts() []Post { 60 posts := sql app.db { 61 select from Post order by posted_at desc limit 10 62 } or { [] } 63 return posts 64} 65 66// get_popular_posts returns a list of the ten most liked posts. 67// TODO: make this time-gated (i.e, top ten liked posts of the day) 68pub 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 { 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) bool { 113 sql app.db { 114 update Post set body = new_body, title = new_title 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: query options/filters, such as user:beep, !excluded-text, etc 165pub fn (app &DatabaseAccess) search_for_posts(query string, limit int, offset int) []PostSearchResult { 166 sql_query := "\ 167 SELECT *, CASE 168 WHEN title LIKE '%${query}%' THEN 1 169 WHEN body LIKE '%${query}%' THEN 2 170 END AS priority 171 FROM \"Post\" 172 WHERE title LIKE '%${query}%' OR body LIKE '%${query}%' 173 ORDER BY priority ASC LIMIT ${limit} OFFSET ${offset}" 174 175 queried_posts := app.db.q_strings(sql_query) or { 176 eprintln('search_for_posts error in app.db.q_strings: ${err}') 177 [] 178 } 179 180 posts := queried_posts.map(|it| Post.from_row(it)) 181 return PostSearchResult.from_post_list(app, posts) 182}