Due Cards reads only the ids and members of the boards it may show, and asks for a plain boardId list.

The publication fetched every board the user may see - every public board of
the instance among them, whole - to read its _id and members. It asks for those
two fields only. And with no assigned-only board, the usual case, its board
scope is { boardId: { $in } } instead of an $or with an empty second branch,
which MongoDB and FerretDB both answer from the { boardId, archived, type,
dueAt } index. A board where the user is assigned-only still shows only the
user's assigned cards. On the database speed test (615,070 cards) Due Cards on
FerretDB + SQLite went from 34 s to 12.8 s, with FerretDB's own changes.

Thanks to xet7 !
This commit is contained in:
Lauri Ojansivu committed 2026-10-24 18:17:14 +03:00
1 parent f1e447dd36
commit c64b3a3b1d
1 file changed
+15 -5
+15 -5
View File
@@ -364,12 +364,15 @@ Meteor.publish('dueCards', async function(allUsers = false, limit = 200, skip =
// Get user's board memberships for efficient filtering
// #3249: signed in, so 'instance' boards count as well.
// Only the ids and memberships are read below. Fetching whole board documents
// - every public board of the instance among them - made this the slowest
// query of the database speed test (docs/Databases/FerretDB/1/SpeedTest).
const visibleBoards = await ReactiveCache.getBoards({
$or: [
...withoutMembershipSelectors(true),
{ members: { $elemMatch: { userId, isActive: true } } }
]
});
}, { fields: { _id: 1, members: 1 } });
// On a board where this user is an assigned-only member, only the cards
// assigned to them are theirs to see - "all users" included.
const userBoards = visibleBoards.filter(board => !isAssignedOnlyMember(board, userId)).map(board => board._id);
@@ -380,14 +383,21 @@ Meteor.publish('dueCards', async function(allUsers = false, limit = 200, skip =
}
// Build optimized selector
// With no assigned-only board - the usual case - the board scope is a plain
// boardId list, which both MongoDB and FerretDB answer from the
// { boardId, archived, type, dueAt } index; the $or form is only needed when
// some boards narrow to the user's assigned cards.
const boardScope = assignedOnlyBoards.length
? { $or: [
{ boardId: { $in: userBoards } },
{ boardId: { $in: assignedOnlyBoards }, assignees: userId },
] }
: { boardId: { $in: userBoards } };
const selector = {
type: 'cardType-card',
archived: false,
dueAt: { $exists: true, $nin: [null, ''] },
$and: [{ $or: [
{ boardId: { $in: userBoards } },
{ boardId: { $in: assignedOnlyBoards }, assignees: userId },
] }],
$and: [boardScope],
};
// Add user filtering if not showing all users