Write on the margins of the internet. Powered by the AT Protocol.
margin.at
extension
web
atproto
comments
1package db
2
3import (
4 "time"
5)
6
7func (db *DB) CreateAnnotation(a *Annotation) error {
8 _, err := db.Exec(db.Rebind(`
9 INSERT INTO annotations (uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid)
10 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
11 ON CONFLICT(uri) DO UPDATE SET
12 motivation = excluded.motivation,
13 body_value = excluded.body_value,
14 body_format = excluded.body_format,
15 body_uri = excluded.body_uri,
16 target_source = excluded.target_source,
17 target_hash = excluded.target_hash,
18 target_title = excluded.target_title,
19 selector_json = excluded.selector_json,
20 tags_json = excluded.tags_json,
21 indexed_at = excluded.indexed_at,
22 cid = excluded.cid
23 `), a.URI, a.AuthorDID, a.Motivation, a.BodyValue, a.BodyFormat, a.BodyURI, a.TargetSource, a.TargetHash, a.TargetTitle, a.SelectorJSON, a.TagsJSON, a.CreatedAt, a.IndexedAt, a.CID)
24 return err
25}
26
27func (db *DB) GetAnnotationByURI(uri string) (*Annotation, error) {
28 var a Annotation
29 err := db.QueryRow(db.Rebind(`
30 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
31 FROM annotations
32 WHERE uri = ?
33 `), uri).Scan(&a.URI, &a.AuthorDID, &a.Motivation, &a.BodyValue, &a.BodyFormat, &a.BodyURI, &a.TargetSource, &a.TargetHash, &a.TargetTitle, &a.SelectorJSON, &a.TagsJSON, &a.CreatedAt, &a.IndexedAt, &a.CID)
34 if err != nil {
35 return nil, err
36 }
37 return &a, nil
38}
39
40func (db *DB) GetAnnotationsByTargetHash(targetHash string, limit, offset int) ([]Annotation, error) {
41 rows, err := db.Query(db.Rebind(`
42 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
43 FROM annotations
44 WHERE target_hash = ?
45 ORDER BY created_at DESC
46 LIMIT ? OFFSET ?
47 `), targetHash, limit, offset)
48 if err != nil {
49 return nil, err
50 }
51 defer rows.Close()
52
53 return scanAnnotations(rows)
54}
55
56func (db *DB) GetAnnotationsByAuthor(authorDID string, limit, offset int) ([]Annotation, error) {
57 rows, err := db.Query(db.Rebind(`
58 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
59 FROM annotations
60 WHERE author_did = ?
61 ORDER BY created_at DESC
62 LIMIT ? OFFSET ?
63 `), authorDID, limit, offset)
64 if err != nil {
65 return nil, err
66 }
67 defer rows.Close()
68
69 return scanAnnotations(rows)
70}
71
72func (db *DB) GetMarginAnnotationsByAuthor(authorDID string, limit, offset int) ([]Annotation, error) {
73 rows, err := db.Query(db.Rebind(`
74 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
75 FROM annotations
76 WHERE author_did = ? AND uri NOT LIKE '%network.cosmik%'
77 ORDER BY created_at DESC
78 LIMIT ? OFFSET ?
79 `), authorDID, limit, offset)
80 if err != nil {
81 return nil, err
82 }
83 defer rows.Close()
84
85 return scanAnnotations(rows)
86}
87
88func (db *DB) GetSembleAnnotationsByAuthor(authorDID string, limit, offset int) ([]Annotation, error) {
89 rows, err := db.Query(db.Rebind(`
90 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
91 FROM annotations
92 WHERE author_did = ? AND uri LIKE '%network.cosmik%'
93 ORDER BY created_at DESC
94 LIMIT ? OFFSET ?
95 `), authorDID, limit, offset)
96 if err != nil {
97 return nil, err
98 }
99 defer rows.Close()
100
101 return scanAnnotations(rows)
102}
103
104func (db *DB) GetAnnotationsByMotivation(motivation string, limit, offset int) ([]Annotation, error) {
105 rows, err := db.Query(db.Rebind(`
106 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
107 FROM annotations
108 WHERE motivation = ?
109 ORDER BY created_at DESC
110 LIMIT ? OFFSET ?
111 `), motivation, limit, offset)
112 if err != nil {
113 return nil, err
114 }
115 defer rows.Close()
116
117 return scanAnnotations(rows)
118}
119
120func (db *DB) GetRecentAnnotations(limit, offset int) ([]Annotation, error) {
121 rows, err := db.Query(db.Rebind(`
122 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
123 FROM annotations
124 ORDER BY created_at DESC
125 LIMIT ? OFFSET ?
126 `), limit, offset)
127 if err != nil {
128 return nil, err
129 }
130 defer rows.Close()
131
132 return scanAnnotations(rows)
133}
134
135func (db *DB) GetPopularAnnotations(limit, offset int) ([]Annotation, error) {
136 since := time.Now().AddDate(0, 0, -14)
137 rows, err := db.Query(db.Rebind(`
138 SELECT
139 a.uri, a.author_did, a.motivation, a.body_value, a.body_format,
140 a.body_uri, a.target_source, a.target_hash, a.target_title,
141 a.selector_json, a.tags_json, a.created_at, a.indexed_at, a.cid
142 FROM annotations a
143 LEFT JOIN (
144 SELECT subject_uri, COUNT(*) as cnt FROM likes GROUP BY subject_uri
145 ) l ON l.subject_uri = a.uri
146 LEFT JOIN (
147 SELECT root_uri, COUNT(*) as cnt FROM replies GROUP BY root_uri
148 ) r ON r.root_uri = a.uri
149 WHERE a.created_at > ? AND (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) > 0
150 ORDER BY (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) DESC, a.created_at DESC
151 LIMIT ? OFFSET ?
152 `), since, limit, offset)
153 if err != nil {
154 return nil, err
155 }
156 defer rows.Close()
157
158 return scanAnnotations(rows)
159}
160
161func (db *DB) GetShelvedAnnotations(limit, offset int) ([]Annotation, error) {
162 olderThan := time.Now().AddDate(0, 0, -1)
163 since := time.Now().AddDate(0, 0, -14)
164 rows, err := db.Query(db.Rebind(`
165 SELECT
166 a.uri, a.author_did, a.motivation, a.body_value, a.body_format,
167 a.body_uri, a.target_source, a.target_hash, a.target_title,
168 a.selector_json, a.tags_json, a.created_at, a.indexed_at, a.cid
169 FROM annotations a
170 LEFT JOIN (
171 SELECT subject_uri, COUNT(*) as cnt FROM likes GROUP BY subject_uri
172 ) l ON l.subject_uri = a.uri
173 LEFT JOIN (
174 SELECT root_uri, COUNT(*) as cnt FROM replies GROUP BY root_uri
175 ) r ON r.root_uri = a.uri
176 WHERE a.created_at < ? AND a.created_at > ? AND (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) = 0
177 ORDER BY RANDOM()
178 LIMIT ? OFFSET ?
179 `), olderThan, since, limit, offset)
180 if err != nil {
181 return nil, err
182 }
183 defer rows.Close()
184
185 return scanAnnotations(rows)
186}
187
188func (db *DB) GetMarginAnnotations(limit, offset int) ([]Annotation, error) {
189 rows, err := db.Query(db.Rebind(`
190 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
191 FROM annotations
192 WHERE uri NOT LIKE '%network.cosmik%'
193 ORDER BY created_at DESC
194 LIMIT ? OFFSET ?
195 `), limit, offset)
196 if err != nil {
197 return nil, err
198 }
199 defer rows.Close()
200
201 return scanAnnotations(rows)
202}
203
204func (db *DB) GetSembleAnnotations(limit, offset int) ([]Annotation, error) {
205 rows, err := db.Query(db.Rebind(`
206 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
207 FROM annotations
208 WHERE uri LIKE '%network.cosmik%'
209 ORDER BY created_at DESC
210 LIMIT ? OFFSET ?
211 `), limit, offset)
212 if err != nil {
213 return nil, err
214 }
215 defer rows.Close()
216
217 return scanAnnotations(rows)
218}
219
220func (db *DB) GetAnnotationsByTag(tag string, limit, offset int) ([]Annotation, error) {
221 pattern := "%\"" + tag + "\"%"
222 rows, err := db.Query(db.Rebind(`
223 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
224 FROM annotations
225 WHERE tags_json LIKE ?
226 ORDER BY created_at DESC
227 LIMIT ? OFFSET ?
228 `), pattern, limit, offset)
229 if err != nil {
230 return nil, err
231 }
232 defer rows.Close()
233
234 return scanAnnotations(rows)
235}
236
237func (db *DB) GetMarginAnnotationsByTag(tag string, limit, offset int) ([]Annotation, error) {
238 pattern := "%\"" + tag + "\"%"
239 rows, err := db.Query(db.Rebind(`
240 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
241 FROM annotations
242 WHERE tags_json LIKE ? AND uri NOT LIKE '%network.cosmik%'
243 ORDER BY created_at DESC
244 LIMIT ? OFFSET ?
245 `), pattern, limit, offset)
246 if err != nil {
247 return nil, err
248 }
249 defer rows.Close()
250
251 return scanAnnotations(rows)
252}
253
254func (db *DB) GetSembleAnnotationsByTag(tag string, limit, offset int) ([]Annotation, error) {
255 pattern := "%\"" + tag + "\"%"
256 rows, err := db.Query(db.Rebind(`
257 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
258 FROM annotations
259 WHERE tags_json LIKE ? AND uri LIKE '%network.cosmik%'
260 ORDER BY created_at DESC
261 LIMIT ? OFFSET ?
262 `), pattern, limit, offset)
263 if err != nil {
264 return nil, err
265 }
266 defer rows.Close()
267
268 return scanAnnotations(rows)
269}
270
271func (db *DB) DeleteAnnotation(uri string) error {
272 _, err := db.Exec(db.Rebind(`DELETE FROM annotations WHERE uri = ?`), uri)
273 return err
274}
275
276func (db *DB) UpdateAnnotation(uri, bodyValue, tagsJSON, cid string) error {
277 _, err := db.Exec(db.Rebind(`
278 UPDATE annotations
279 SET body_value = ?, tags_json = ?, cid = ?, indexed_at = ?
280 WHERE uri = ?
281 `), bodyValue, tagsJSON, cid, time.Now(), uri)
282 return err
283}
284
285func (db *DB) GetAnnotationsByTagAndAuthor(tag, authorDID string, limit, offset int) ([]Annotation, error) {
286 pattern := "%\"" + tag + "\"%"
287 rows, err := db.Query(db.Rebind(`
288 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
289 FROM annotations
290 WHERE author_did = ? AND tags_json LIKE ?
291 ORDER BY created_at DESC
292 LIMIT ? OFFSET ?
293 `), authorDID, pattern, limit, offset)
294 if err != nil {
295 return nil, err
296 }
297 defer rows.Close()
298
299 return scanAnnotations(rows)
300}
301
302func (db *DB) GetMarginAnnotationsByTagAndAuthor(tag, authorDID string, limit, offset int) ([]Annotation, error) {
303 pattern := "%\"" + tag + "\"%"
304 rows, err := db.Query(db.Rebind(`
305 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
306 FROM annotations
307 WHERE author_did = ? AND tags_json LIKE ? AND uri NOT LIKE '%network.cosmik%'
308 ORDER BY created_at DESC
309 LIMIT ? OFFSET ?
310 `), authorDID, pattern, limit, offset)
311 if err != nil {
312 return nil, err
313 }
314 defer rows.Close()
315
316 return scanAnnotations(rows)
317}
318
319func (db *DB) GetSembleAnnotationsByTagAndAuthor(tag, authorDID string, limit, offset int) ([]Annotation, error) {
320 pattern := "%\"" + tag + "\"%"
321 rows, err := db.Query(db.Rebind(`
322 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
323 FROM annotations
324 WHERE author_did = ? AND tags_json LIKE ? AND uri LIKE '%network.cosmik%'
325 ORDER BY created_at DESC
326 LIMIT ? OFFSET ?
327 `), authorDID, pattern, limit, offset)
328 if err != nil {
329 return nil, err
330 }
331 defer rows.Close()
332
333 return scanAnnotations(rows)
334}
335
336func (db *DB) GetAnnotationsByAuthorAndTargetHash(authorDID, targetHash string, limit, offset int) ([]Annotation, error) {
337 rows, err := db.Query(db.Rebind(`
338 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
339 FROM annotations
340 WHERE author_did = ? AND target_hash = ?
341 ORDER BY created_at DESC
342 LIMIT ? OFFSET ?
343 `), authorDID, targetHash, limit, offset)
344 if err != nil {
345 return nil, err
346 }
347 defer rows.Close()
348
349 return scanAnnotations(rows)
350}
351
352func (db *DB) GetAnnotationsByURIs(uris []string) ([]Annotation, error) {
353 if len(uris) == 0 {
354 return []Annotation{}, nil
355 }
356
357 query := db.Rebind(`
358 SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid
359 FROM annotations
360 WHERE uri IN (` + buildPlaceholders(len(uris)) + `)
361 `)
362
363 args := make([]interface{}, len(uris))
364 for i, uri := range uris {
365 args[i] = uri
366 }
367
368 rows, err := db.Query(query, args...)
369 if err != nil {
370 return nil, err
371 }
372 defer rows.Close()
373
374 return scanAnnotations(rows)
375}
376
377func (db *DB) GetAnnotationURIs(authorDID string) ([]string, error) {
378 rows, err := db.Query(db.Rebind(`
379 SELECT uri FROM annotations WHERE author_did = ?
380 `), authorDID)
381 if err != nil {
382 return nil, err
383 }
384 defer rows.Close()
385
386 var uris []string
387 for rows.Next() {
388 var uri string
389 if err := rows.Scan(&uri); err != nil {
390 return nil, err
391 }
392 uris = append(uris, uri)
393 }
394 return uris, nil
395}