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_post 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 println('searching, q=${query},l=${limit},o=${offset}')
167 posts := sql app.db {
168 select from Post where title like '%${query}%' order by posted_at desc limit limit offset offset
169 } or { [] }
170 println('search results: ${posts.len}')
171 return PostSearchResult.from_post_list(app, posts)
172}