+14
-3
src/database/post.v
+14
-3
src/database/post.v
···
164
164
// todo: levenshtein distance, query options/filters (user:beep, !excluded-text,
165
165
// etc)
166
166
pub fn (app &DatabaseAccess) search_for_posts(query string, limit int, offset int) []PostSearchResult {
167
-
queried_posts := app.db.exec_param_many('SELECT * FROM search_for_posts($1, $2, $3)', [query, limit.str(), offset.str()]) or {
167
+
queried_posts := app.db.exec_param_many_result('SELECT * FROM search_for_posts($1, $2, $3)', [query, limit.str(), offset.str()]) or {
168
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.
178
+
pub 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}')
169
181
[]
170
182
}
171
-
posts := queried_posts.map(|it| Post.from_row(it))
172
-
return PostSearchResult.from_post_list(app, posts)
183
+
return if n.len == 0 { 0 } else { util.or_throw(n[0].vals[0]).int() }
173
184
}
+15
-3
src/database/user.v
+15
-3
src/database/user.v
···
2
2
3
3
import entity { User, Notification, Like, LikeCache, Post }
4
4
import util
5
+
import db.pg
5
6
6
7
// new_user creates a new user and returns their struct after creation.
7
8
pub fn (app &DatabaseAccess) new_user(user User) ?User {
···
216
217
// search_for_users searches for posts matching the given query.
217
218
// todo: query options/filters, such as created-after:<date>, created-before:<date>, etc
218
219
pub fn (app &DatabaseAccess) search_for_users(query string, limit int, offset int) []User {
219
-
queried_users := app.db.exec_param_many('SELECT * FROM search_for_users($1, $2, $3)', [query, limit.str(), offset.str()]) or {
220
+
queried_users := app.db.exec_param_many_result('SELECT * FROM search_for_users($1, $2, $3)', [query, limit.str(), offset.str()]) or {
220
221
eprintln('search_for_users error in app.db.error: ${err}')
222
+
pg.Result{}
223
+
}
224
+
users := queried_users.rows.map(fn [queried_users] (it pg.Row) User {
225
+
return User.from_row(queried_users, it)
226
+
})
227
+
return users
228
+
}
229
+
230
+
// get_user_count gets the number of registered users in the database.
231
+
pub fn (app &DatabaseAccess) get_user_count() int {
232
+
n := app.db.exec('SELECT COUNT(id) FROM "User"') or {
233
+
eprintln('get_user_count error in app.db.error: ${err}')
221
234
[]
222
235
}
223
-
users := queried_users.map(|it| User.from_row(it))
224
-
return users
236
+
return if n.len == 0 { 0 } else { util.or_throw(n[0].vals[0]).int() }
225
237
}
+17
-9
src/entity/post.v
+17
-9
src/entity/post.v
···
21
21
// Post.from_row creates a post object from the given database row.
22
22
// see src/database/post.v#search_for_posts for usage.
23
23
@[inline]
24
-
pub fn Post.from_row(row pg.Row) Post {
24
+
pub fn Post.from_row(res pg.Result, row pg.Row) Post {
25
+
// curry some arguments for cleanliness
26
+
c := fn [res, row] (key string) ?string {
27
+
return util.get_row_col(res, row, key)
28
+
}
29
+
ct := fn [res, row] (key string) string {
30
+
return util.get_row_col_or_throw(res, row, key)
31
+
}
32
+
25
33
// this throws a cgen error when put in Post{}
26
34
//todo: report this
27
-
posted_at := time.parse(util.or_throw[string](row.vals[6])) or { panic(err) }
35
+
posted_at := time.parse(ct('posted_at')) or { panic(err) }
28
36
29
37
return Post{
30
-
id: util.or_throw[string](row.vals[0]).int()
31
-
author_id: util.or_throw[string](row.vals[1]).int()
32
-
replying_to: if row.vals[2] == none { ?int(none) } else {
33
-
util.map_or_throw[string, int](row.vals[2], |it| it.int())
38
+
id: ct('id').int()
39
+
author_id: ct('author_id').int()
40
+
replying_to: if c('replying_to') == none { none } else {
41
+
util.map_or_throw[string, int](ct('replying_to'), |it| it.int())
34
42
}
35
-
title: util.or_throw[string](row.vals[3])
36
-
body: util.or_throw[string](row.vals[4])
37
-
pinned: util.map_or_throw[string, bool](row.vals[5], |it| it.bool())
43
+
title: ct('title')
44
+
body: ct('body')
45
+
pinned: util.map_or_throw[string, bool](ct('pinned'), |it| it.bool())
38
46
posted_at: posted_at
39
47
}
40
48
}
+19
-11
src/entity/user.v
+19
-11
src/entity/user.v
···
44
44
// User.from_row creates a user object from the given database row.
45
45
// see src/database/user.v#search_for_users for usage.
46
46
@[inline]
47
-
pub fn User.from_row(row pg.Row) User {
47
+
pub fn User.from_row(res pg.Result, row pg.Row) User {
48
+
// curry some arguments for cleanliness
49
+
c := fn [res, row] (key string) ?string {
50
+
return util.get_row_col(res, row, key)
51
+
}
52
+
ct := fn [res, row] (key string) string {
53
+
return util.get_row_col_or_throw(res, row, key)
54
+
}
55
+
48
56
// this throws a cgen error when put in User{}
49
57
//todo: report this
50
-
created_at := time.parse(util.or_throw[string](row.vals[10])) or { panic(err) }
58
+
created_at := time.parse(ct('created_at')) or { panic(err) }
51
59
52
60
return User{
53
-
id: util.or_throw[string](row.vals[0]).int()
54
-
username: util.or_throw[string](row.vals[1])
55
-
nickname: if row.vals[2] == none { ?string(none) } else {
56
-
util.or_throw[string](row.vals[2])
61
+
id: ct('id').int()
62
+
username: ct('username')
63
+
nickname: if c('nickname') == none { none } else {
64
+
ct('nickname')
57
65
}
58
66
password: 'haha lol, nope'
59
67
password_salt: 'haha lol, nope'
60
-
muted: util.map_or_throw[string, bool](row.vals[5], |it| it.bool())
61
-
admin: util.map_or_throw[string, bool](row.vals[6], |it| it.bool())
62
-
theme: util.or_throw[string](row.vals[7])
63
-
bio: util.or_throw[string](row.vals[8])
64
-
pronouns: util.or_throw[string](row.vals[9])
68
+
muted: util.map_or_throw[string, bool](util.get_row_col(res, row, 'muted'), |it| it.bool())
69
+
admin: util.map_or_throw[string, bool](util.get_row_col(res, row, 'admin'), |it| it.bool())
70
+
theme: ct('theme')
71
+
bio: ct('bio')
72
+
pronouns: ct('pronouns')
65
73
created_at: created_at
66
74
}
67
75
}
+6
-1
src/templates/about.html
+6
-1
src/templates/about.html
···
11
11
@if app.config.instance.source != ''
12
12
<p>source: <a href="@{app.config.instance.source}">@{app.config.instance.source}</a></p>
13
13
@end
14
+
15
+
<br>
16
+
17
+
<p>users: @{app.get_user_count()}</p>
18
+
<p>posts: @{app.get_post_count()}</p>
14
19
</div>
15
20
16
21
<script>
17
22
document.getElementById('built_at').innerText = new Date(@{app.built_at} * 1000).toLocaleString()
18
23
</script>
19
24
20
-
@include 'partial/footer.html'
25
+
@include 'partial/footer.html'
+13
src/util/row.v
+13
src/util/row.v
···
1
+
module util
2
+
3
+
import db.pg
4
+
5
+
@[inline]
6
+
pub fn get_row_col(res pg.Result, row pg.Row, key string) ?string {
7
+
return row.vals[res.cols[key]]
8
+
}
9
+
10
+
@[inline]
11
+
pub fn get_row_col_or_throw(res pg.Result, row pg.Row, key string) string {
12
+
return util.or_throw(row.vals[res.cols[key]])
13
+
}