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

Configure Feed

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

feat: searchActors personalized sort

+35 -5
+5 -1
api/so/sprk/actor/searchActorsTypeahead.ts
··· 66 66 }; 67 67 } 68 68 69 - const res = await ctx.dataplane.search.actorsTypeahead(term, params.limit); 69 + const res = await ctx.dataplane.search.actorsTypeahead( 70 + term, 71 + params.limit, 72 + params.hydrateCtx.viewer, 73 + ); 70 74 return { 71 75 dids: res.dids, 72 76 };
+30 -4
data-plane/routes/search.ts
··· 93 93 }; 94 94 } 95 95 96 - async actorsTypeahead(term: string, limit = 10) { 96 + async actorsTypeahead(term: string, limit = 10, viewerDid?: string | null) { 97 97 const cleanedTerm = cleanQuery(term); 98 98 if (!cleanedTerm) { 99 99 return { ··· 127 127 } 128 128 : { $text: { $search: cleanedTerm } }; 129 129 const matchingProfiles = await this.db.models.Profile.find(profileQuery) 130 - .select("authorDid -_id") 130 + .select("authorDid followersCount -_id") 131 131 .limit(candidateLimit * 2) 132 132 .lean(); 133 + 134 + const followerCountMap = new Map<string, number>( 135 + matchingProfiles.map((p) => [p.authorDid, p.followersCount ?? 0]), 136 + ); 133 137 134 138 const handleDidSet = new Set(handleDids); 135 139 const handleProfileDidSet = new Set( ··· 143 147 .map((profile) => profile.authorDid) 144 148 .filter((did) => !includedDids.has(did) && !handleDidSet.has(did)); 145 149 146 - const dids = [...handleProfileDids, ...textProfileDids].slice(0, safeLimit); 150 + // Sort each group by follower count descending 151 + const byFollowers = (a: string, b: string) => 152 + (followerCountMap.get(b) ?? 0) - (followerCountMap.get(a) ?? 0); 153 + handleProfileDids.sort(byFollowers); 154 + textProfileDids.sort(byFollowers); 155 + 156 + let candidates = [...handleProfileDids, ...textProfileDids].slice( 157 + 0, 158 + safeLimit * 2, 159 + ); 160 + 161 + // Boost accounts the viewer already follows to the front 162 + if (viewerDid && candidates.length > 0) { 163 + const viewerFollows = await this.db.models.Follow.find({ 164 + authorDid: viewerDid, 165 + subject: { $in: candidates }, 166 + }).select("subject -_id").lean(); 167 + const followedSet = new Set(viewerFollows.map((f) => f.subject)); 168 + candidates = [ 169 + ...candidates.filter((did) => followedSet.has(did)), 170 + ...candidates.filter((did) => !followedSet.has(did)), 171 + ]; 172 + } 147 173 148 174 return { 149 - dids, 175 + dids: candidates.slice(0, safeLimit), 150 176 }; 151 177 } 152 178