@@ -238,21 +238,22 @@ def search_score_sets(db: Session, owner_or_contributor: Optional[User], search:
238238 score_sets : list [ScoreSet ] = (
239239 query .join (ScoreSet .experiment )
240240 .options (
241- # Use selectinload for one-to-many experiment relationships to avoid row
242- # multiplication in the main query. joinedload would LEFT OUTER JOIN these
243- # into the main SQL query, and because they're nested inside contains_eager,
244- # SQLAlchemy's subquery-wrapping logic doesn't protect the LIMIT clause from
245- # being applied to multiplied rows rather than unique score sets. This would
246- # cause the count of returned score sets to be less than the requested limit,
247- # and the count query would be triggered even when the number of unique score
248- # sets in the main query results exceeds the limit.
241+ # Use selectinload for ALL relationships loaded via the main query. The presence of
242+ # contains_eager disables SQLAlchemy's subquery-wrapping logic for the ENTIRE query,
243+ # not just the relationships nested inside it. This means any joinedload that adds a
244+ # LEFT OUTER JOIN to the main SQL query — even for many-to-one relationships — can
245+ # corrupt the LIMIT clause by applying it to joined rows rather than unique score sets,
246+ # causing fewer results than expected and suppressing the count query fallback.
247+ # The only JOINs that should remain in the main query are the explicit experiment
248+ # INNER JOIN (required by contains_eager) and the superseding score set LEFT OUTER JOIN
249+ # added by the filter builder.
249250 contains_eager (ScoreSet .experiment ).options (
250- joinedload (Experiment .experiment_set ),
251+ selectinload (Experiment .experiment_set ),
251252 selectinload (Experiment .keyword_objs ).joinedload (
252253 ExperimentControlledKeywordAssociation .controlled_keyword
253254 ),
254- joinedload (Experiment .created_by ),
255- joinedload (Experiment .modified_by ),
255+ selectinload (Experiment .created_by ),
256+ selectinload (Experiment .modified_by ),
256257 selectinload (Experiment .doi_identifiers ),
257258 selectinload (Experiment .publication_identifier_associations ).joinedload (
258259 ExperimentPublicationIdentifierAssociation .publication
@@ -272,12 +273,12 @@ def search_score_sets(db: Session, owner_or_contributor: Optional[User], search:
272273 ),
273274 ),
274275 ),
275- joinedload (ScoreSet .license ),
276- joinedload (ScoreSet .doi_identifiers ),
277- joinedload (ScoreSet .publication_identifier_associations ).joinedload (
276+ selectinload (ScoreSet .license ),
277+ selectinload (ScoreSet .doi_identifiers ),
278+ selectinload (ScoreSet .publication_identifier_associations ).joinedload (
278279 ScoreSetPublicationIdentifierAssociation .publication
279280 ),
280- joinedload (ScoreSet .target_genes ).options (
281+ selectinload (ScoreSet .target_genes ).options (
281282 joinedload (TargetGene .ensembl_offset ).joinedload (EnsemblOffset .identifier ),
282283 joinedload (TargetGene .refseq_offset ).joinedload (RefseqOffset .identifier ),
283284 joinedload (TargetGene .uniprot_offset ).joinedload (UniprotOffset .identifier ),
0 commit comments