[READ ONLY MIRROR] Spark Social AppView Server github.com/sprksocial/server
atproto deno hono lexicon
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

at eb947da8a3a8a7d485ff132b3ff0db4a8baaac19 131 lines 3.9 kB view raw
1import { Database } from "../db/index.ts"; 2 3// Types for MongoDB aggregation results 4interface AggregationResult { 5 _id: string; 6 count: number; 7} 8 9export class Interactions { 10 private db: Database; 11 12 constructor(db: Database) { 13 this.db = db; 14 } 15 16 async getInteractionCounts(refs: Array<{ uri: string }>) { 17 const uris = refs.map((ref) => ref.uri); 18 if (uris.length === 0) { 19 return { likes: [], replies: [], reposts: [], quotes: [] }; 20 } 21 22 // Get pre-computed counts from Post and Reply documents 23 const [posts, replies] = await Promise.all([ 24 this.db.models.Post.find( 25 { uri: { $in: uris } }, 26 { uri: 1, likeCount: 1, replyCount: 1, repostCount: 1 }, 27 ), 28 this.db.models.Reply.find( 29 { uri: { $in: uris } }, 30 { uri: 1, likeCount: 1, replyCount: 1, repostCount: 1 }, 31 ), 32 ]); 33 34 // Create lookup maps from pre-computed counts 35 const likesMap = new Map<string, number>(); 36 const repliesMap = new Map<string, number>(); 37 const repostsMap = new Map<string, number>(); 38 39 for (const post of posts) { 40 likesMap.set(post.uri, post.likeCount ?? 0); 41 repliesMap.set(post.uri, post.replyCount ?? 0); 42 repostsMap.set(post.uri, post.repostCount ?? 0); 43 } 44 45 for (const reply of replies) { 46 likesMap.set(reply.uri, reply.likeCount ?? 0); 47 repliesMap.set(reply.uri, reply.replyCount ?? 0); 48 } 49 50 return { 51 likes: uris.map((uri) => likesMap.get(uri) ?? 0), 52 replies: uris.map((uri) => repliesMap.get(uri) ?? 0), 53 reposts: uris.map((uri) => repostsMap.get(uri) ?? 0), 54 }; 55 } 56 57 async getCountsForUsers(dids: string[]) { 58 if (dids.length === 0) { 59 return { 60 followers: [], 61 following: [], 62 posts: [], 63 feeds: [], 64 }; 65 } 66 67 const [followers, following, posts, feeds] = await Promise.all([ 68 // Count followers for each DID 69 this.db.models.Follow.aggregate([ 70 { $match: { subject: { $in: dids } } }, 71 { $group: { _id: "$subject", count: { $sum: 1 } } }, 72 ]), 73 // Count following for each DID 74 this.db.models.Follow.aggregate([ 75 { $match: { authorDid: { $in: dids } } }, 76 { $group: { _id: "$authorDid", count: { $sum: 1 } } }, 77 ]), 78 // Count posts for each DID 79 this.db.models.Post.aggregate([ 80 { $match: { authorDid: { $in: dids } } }, 81 { $group: { _id: "$authorDid", count: { $sum: 1 } } }, 82 ]), 83 // Count generators for each DID 84 this.db.models.Generator.aggregate([ 85 { $match: { authorDid: { $in: dids } } }, 86 { $group: { _id: "$authorDid", count: { $sum: 1 } } }, 87 ]), 88 ]); 89 90 // Create lookup maps 91 const followersMap = new Map( 92 followers.map((item: AggregationResult) => [item._id, item.count]), 93 ); 94 const followingMap = new Map( 95 following.map((item: AggregationResult) => [item._id, item.count]), 96 ); 97 const postsMap = new Map( 98 posts.map((item: AggregationResult) => [item._id, item.count]), 99 ); 100 const feedsMap = new Map( 101 feeds.map((item: AggregationResult) => [item._id, item.count]), 102 ); 103 104 return { 105 followers: dids.map((did) => followersMap.get(did) ?? 0), 106 following: dids.map((did) => followingMap.get(did) ?? 0), 107 posts: dids.map((did) => postsMap.get(did) ?? 0), 108 feeds: dids.map((did) => feedsMap.get(did) ?? 0), 109 }; 110 } 111 112 async getSoundUsageCounts(uris: string[]) { 113 if (uris.length === 0) { 114 return { uses: [] }; 115 } 116 117 // Count how many posts reference each sound URI 118 const usageAgg = await this.db.models.Post.aggregate([ 119 { $match: { "sound.uri": { $in: uris } } }, 120 { $group: { _id: "$sound.uri", count: { $sum: 1 } } }, 121 ]); 122 123 const usageMap = new Map( 124 usageAgg.map((item: AggregationResult) => [item._id, item.count]), 125 ); 126 127 return { 128 uses: uris.map((uri) => usageMap.get(uri) ?? 0), 129 }; 130 } 131}