diff --git a/data-plane/db/models.ts b/data-plane/db/models.ts index 12a514d..20265d6 100644 --- a/data-plane/db/models.ts +++ b/data-plane/db/models.ts @@ -165,7 +165,8 @@ export const likeSchema = new Schema({ viaCid: { type: String, required: false }, }) .index({ authorDid: 1, subject: 1 }, { unique: true }) - .index({ subject: 1, createdAt: -1 }); + .index({ subject: 1, createdAt: -1 }) + .index({ subject: 1, authorDid: 1 }); // follows @@ -177,7 +178,8 @@ export const followSchema = new Schema({ subject: { type: String, required: true, index: true }, }) .index({ authorDid: 1, subject: 1 }, { unique: true }) - .index({ subject: 1, createdAt: -1 }); + .index({ subject: 1, createdAt: -1 }) + .index({ subject: 1, authorDid: 1 }); // blocks @@ -260,7 +262,8 @@ export const repostSchema = new Schema({ viaCid: { type: String, required: false }, }) .index({ subject: 1, createdAt: -1 }) - .index({ authorDid: 1, createdAt: -1 }); + .index({ authorDid: 1, createdAt: -1 }) + .index({ subject: 1, authorDid: 1 }); // posts @@ -340,7 +343,9 @@ export const replySchema = new Schema({ likeCount: { type: Number, required: true, default: 0 }, replyCount: { type: Number, required: true, default: 0 }, }) - .index({ reply: 1, createdAt: -1 }); + .index({ reply: 1, createdAt: -1 }) + .index({ "reply.parent.uri": 1, authorDid: 1 }) + .index({ "reply.root.uri": 1, createdAt: -1 }); // stories diff --git a/data-plane/routes/follows.ts b/data-plane/routes/follows.ts index f0bd266..be37be7 100644 --- a/data-plane/routes/follows.ts +++ b/data-plane/routes/follows.ts @@ -105,31 +105,40 @@ export class Follows { async getFollowsFollowing(viewerDid: string, subjectDids: string[]) { /* - * 1. Get all the people Alice is following - * 2. Get all the people Dan is followed by - * 3. Find the intersection + * Find people the viewer follows who also follow each subject. + * Uses aggregation to avoid fetching all followers of popular accounts. */ const results: FollowsFollowing[] = []; - for (const subjectDid of subjectDids) { - // Get people who follow the subject (Dan's followers) - const subjectFollowers = await this.db.models.Follow.find({ - subject: subjectDid, - }); + // Get all people the viewer follows (bounded by viewer's follow count) + const viewerFollows = await this.db.models.Follow.find({ + authorDid: viewerDid, + }).select("subject"); + + const viewerFollowsDids = viewerFollows.map((f) => f.subject); - const followerDids = subjectFollowers.map((f) => f.authorDid); + if (viewerFollowsDids.length === 0) { + // Viewer follows no one, so no known followers possible + return { + results: subjectDids.map((targetDid) => + new FollowsFollowing({ targetDid, dids: [] }) + ), + }; + } - // Find which of these followers Alice also follows + for (const subjectDid of subjectDids) { + // Find which of the viewer's follows also follow the subject + // This query is bounded by the viewer's follow count, not the subject's follower count const mutualConnections = await this.db.models.Follow.find({ - authorDid: viewerDid, - subject: { $in: followerDids }, - }); + authorDid: { $in: viewerFollowsDids }, + subject: subjectDid, + }).select("authorDid"); results.push( new FollowsFollowing({ targetDid: subjectDid, - dids: mutualConnections.map((connection) => connection.subject), + dids: mutualConnections.map((connection) => connection.authorDid), }), ); } diff --git a/data-plane/routes/interactions.ts b/data-plane/routes/interactions.ts index 2fe82a1..2db45ce 100644 --- a/data-plane/routes/interactions.ts +++ b/data-plane/routes/interactions.ts @@ -161,7 +161,7 @@ export class Interactions { // Get all DIDs the viewer follows const viewerFollows = await this.db.models.Follow.find({ authorDid: viewerDid, - }); + }).select("subject"); const followedDids = viewerFollows.map((f) => f.subject); if (followedDids.length === 0) { @@ -173,15 +173,21 @@ export class Interactions { this.db.models.Like.find({ subject: { $in: subjectUris }, authorDid: { $in: followedDids }, - }).sort({ indexedAt: -1 }), + }) + .select("uri cid subject authorDid indexedAt") + .sort({ indexedAt: -1 }), this.db.models.Repost.find({ subject: { $in: subjectUris }, authorDid: { $in: followedDids }, - }).sort({ indexedAt: -1 }), + }) + .select("uri cid subject authorDid indexedAt") + .sort({ indexedAt: -1 }), this.db.models.Reply.find({ "reply.parent.uri": { $in: subjectUris }, authorDid: { $in: followedDids }, - }).sort({ indexedAt: -1 }), + }) + .select("uri cid reply.parent.uri authorDid indexedAt text") + .sort({ indexedAt: -1 }), ]); // Build result map keyed by subject URI