-
Notifications
You must be signed in to change notification settings - Fork 1
/
photos.ts
92 lines (84 loc) · 2.6 KB
/
photos.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/**
* Example of a photo gallery which doesn't change much, and you want to display
* it with offset-based pagination. That is, each page has 10 items, and you can
* jump to any page in O(log(n)) time.
* The paginated list is sorted by _creationTime.
*
* Also demonstrates aggregates automatically updating when the underlying table changes.
*/
import { TableAggregate } from "@convex-dev/aggregate";
import {
internalMutation as rawInternalMutation,
mutation as rawMutation,
query,
} from "./_generated/server";
import { components } from "./_generated/api";
import { DataModel } from "./_generated/dataModel";
import { v } from "convex/values";
import {
customCtx,
customMutation,
} from "convex-helpers/server/customFunctions";
import { Triggers } from "convex-helpers/server/triggers";
const photos = new TableAggregate<{
Namespace: string;
Key: number;
DataModel: DataModel;
TableName: "photos";
}>(components.photos, {
namespace: (doc) => doc.album,
sortKey: (doc) => doc._creationTime,
});
const triggers = new Triggers<DataModel>();
triggers.register("photos", photos.trigger());
const mutation = customMutation(rawMutation, customCtx(triggers.wrapDB));
const internalMutation = customMutation(
rawInternalMutation,
customCtx(triggers.wrapDB)
);
export const init = internalMutation({
args: {},
handler: async (ctx) => {
// rootLazy can be false because the table doesn't change much, and this
// makes aggregates faster (this is entirely optional).
// Also reducing node size uses less bandwidth, since nodes are smaller.
await photos.clearAll(ctx, {
maxNodeSize: 4,
rootLazy: false,
});
},
});
export const addPhoto = mutation({
args: {
album: v.string(),
url: v.string(),
},
returns: v.id("photos"),
handler: async (ctx, args) => {
return await ctx.db.insert("photos", { album: args.album, url: args.url });
},
});
/**
* Call this with {offset:0, numItems:10} to get the first page of photos,
* then {offset:10, numItems:10} to get the second page, etc.
*/
export const pageOfPhotos = query({
args: {
album: v.string(),
offset: v.number(),
numItems: v.number(),
},
returns: v.array(v.string()),
handler: async (ctx, { offset, numItems, album }) => {
const { key: firstPhotoCreationTime } = await photos.at(ctx, offset, {
namespace: album,
});
const photoDocs = await ctx.db
.query("photos")
.withIndex("by_album_creation_time", (q) =>
q.eq("album", album).gte("_creationTime", firstPhotoCreationTime)
)
.take(numItems);
return photoDocs.map((doc) => doc.url);
},
});