+4
doc/resources.md
+4
doc/resources.md
+15
-6
src/database/post.v
+15
-6
src/database/post.v
···
163
163
// search_for_post searches for posts matching the given query.
164
164
// todo: query options/filters, such as user:beep, !excluded-text, etc
165
165
pub fn (app &DatabaseAccess) search_for_posts(query string, limit int, offset int) []PostSearchResult {
166
-
println('searching, q=${query},l=${limit},o=${offset}')
167
-
//todo: app.db.q_strings('select from Post where title like \'%${query}%\' or body like \'%${query}%\' order by posted_at desc limit limit offset offset')
168
-
posts := sql app.db {
169
-
select from Post where title like '%${query}%' order by posted_at desc limit limit offset offset
170
-
} or { [] }
171
-
println('search results: ${posts.len}')
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))
172
181
return PostSearchResult.from_post_list(app, posts)
173
182
}
+23
src/entity/post.v
+23
src/entity/post.v
···
1
1
module entity
2
2
3
+
import db.pg
3
4
import time
5
+
import util
4
6
5
7
pub struct Post {
6
8
pub mut:
···
15
17
16
18
posted_at time.Time = time.now()
17
19
}
20
+
21
+
// Post.from_row creates a post from the given database row.
22
+
// see src/database/post.v#search_for_posts for usage.
23
+
@[inline]
24
+
pub fn Post.from_row(row pg.Row) Post {
25
+
// this throws a cgen error when put in Post{}
26
+
//todo: report this
27
+
posted_at := time.parse(util.or_throw[string](row.vals[6])) or { panic(err) }
28
+
29
+
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())
34
+
}
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())
38
+
posted_at: posted_at
39
+
}
40
+
}
+21
src/util/none.v
+21
src/util/none.v
···
1
+
module util
2
+
3
+
@[inline]
4
+
pub fn map_or[T, R](val ?T, mapper fn (T) R, or_else R) R {
5
+
return if val == none { or_else } else { mapper(val) }
6
+
}
7
+
8
+
@[inline]
9
+
pub fn map_or_throw[T, R](val ?T, mapper fn (T) R) R {
10
+
return if val == none { panic('value was none: ${val}') } else { mapper(val) }
11
+
}
12
+
13
+
@[inline]
14
+
pub fn map_or_opt[T, R](val ?T, mapper fn (T) ?R, or_else ?R) ?R {
15
+
return if val == none { or_else } else { mapper(val) }
16
+
}
17
+
18
+
@[inline]
19
+
pub fn or_throw[T](val ?T) T {
20
+
return if val == none { panic('value was none: ${val}') } else { val }
21
+
}