Merge pull request #2915 from BrandonKowalski/feature/collection-updated-after-query-param

Feature/collection-updated-after-query-param
This commit is contained in:
Georges-Antoine Assi
2026-01-18 10:23:07 -05:00
committed by GitHub
3 changed files with 69 additions and 8 deletions

View File

@@ -0,0 +1,33 @@
"""Add indexes on updated_at for collections and smart_collections tables
Revision ID: 0065_collections_updated_at_idx
Revises: 0064_add_updated_at_indexes
Create Date: 2026-01-17
"""
from alembic import op
# revision identifiers, used by Alembic.
revision = "0065_collections_updated_at_idx"
down_revision = "0064_add_updated_at_indexes"
branch_labels = None
depends_on = None
def upgrade():
with op.batch_alter_table("collections", schema=None) as batch_op:
batch_op.create_index("ix_collections_updated_at", ["updated_at"], unique=False)
with op.batch_alter_table("smart_collections", schema=None) as batch_op:
batch_op.create_index(
"ix_smart_collections_updated_at", ["updated_at"], unique=False
)
def downgrade():
with op.batch_alter_table("collections", schema=None) as batch_op:
batch_op.drop_index("ix_collections_updated_at")
with op.batch_alter_table("smart_collections", schema=None) as batch_op:
batch_op.drop_index("ix_smart_collections_updated_at")

View File

@@ -1,9 +1,10 @@
import json
from datetime import datetime
from io import BytesIO
from typing import Annotated
from fastapi import Path as PathVar
from fastapi import Request, UploadFile, status
from fastapi import Query, Request, UploadFile, status
from decorators.auth import protected_route
from endpoints.responses.collection import (
@@ -144,18 +145,26 @@ async def add_smart_collection(
@protected_route(router.get, "", [Scope.COLLECTIONS_READ])
def get_collections(request: Request) -> list[CollectionSchema]:
def get_collections(
request: Request,
updated_after: Annotated[
datetime | None,
Query(
description="Filter collections updated after this datetime (ISO 8601 format with timezone information)."
),
] = None,
) -> list[CollectionSchema]:
"""Get collections endpoint
Args:
request (Request): Fastapi Request object
id (int, optional): Collection id. Defaults to None.
updated_after: Filter collections updated after this datetime
Returns:
list[CollectionSchema]: List of collections
"""
collections = db_collection_handler.get_collections()
collections = db_collection_handler.get_collections(updated_after=updated_after)
return CollectionSchema.for_user(request.user.id, [c for c in collections])
@@ -181,17 +190,28 @@ def get_virtual_collections(
@protected_route(router.get, "/smart", [Scope.COLLECTIONS_READ])
def get_smart_collections(request: Request) -> list[SmartCollectionSchema]:
def get_smart_collections(
request: Request,
updated_after: Annotated[
datetime | None,
Query(
description="Filter smart collections updated after this datetime (ISO 8601 format with timezone information)."
),
] = None,
) -> list[SmartCollectionSchema]:
"""Get smart collections endpoint
Args:
request (Request): Fastapi Request object
updated_after: Filter smart collections updated after this datetime
Returns:
list[SmartCollectionSchema]: List of smart collections
"""
smart_collections = db_collection_handler.get_smart_collections(request.user.id)
smart_collections = db_collection_handler.get_smart_collections(
request.user.id, updated_after=updated_after
)
return SmartCollectionSchema.for_user(
request.user.id, [s for s in smart_collections]
@@ -216,7 +236,7 @@ def get_collection(request: Request, id: int) -> CollectionSchema:
if collection.user_id != request.user.id and not collection.is_public:
raise CollectionPermissionError(id)
return CollectionSchema.model_validate(collection)
@@ -414,7 +434,7 @@ async def delete_collection(
collection = db_collection_handler.get_collection(id)
if not collection:
raise CollectionNotFoundInDatabaseException(id)
if collection.user_id != request.user.id:
raise CollectionPermissionError(id)

View File

@@ -1,5 +1,6 @@
import functools
from collections.abc import Sequence
from datetime import datetime
from typing import Any
from sqlalchemy import delete, insert, literal, or_, select, update
@@ -85,9 +86,12 @@ class DBCollectionsHandler(DBBaseHandler):
@with_roms
def get_collections(
self,
updated_after: datetime | None = None,
query: Query = None, # type: ignore
session: Session = None, # type: ignore
) -> Sequence[Collection]:
if updated_after:
query = query.filter(Collection.updated_at > updated_after)
return session.scalars(query.order_by(Collection.name.asc())).unique().all()
@begin_session
@@ -201,6 +205,7 @@ class DBCollectionsHandler(DBBaseHandler):
def get_smart_collections(
self,
user_id: int | None = None,
updated_after: datetime | None = None,
session: Session = None, # type: ignore
) -> Sequence[SmartCollection]:
query = select(SmartCollection).order_by(SmartCollection.name.asc())
@@ -211,6 +216,9 @@ class DBCollectionsHandler(DBBaseHandler):
(SmartCollection.user_id == user_id) | SmartCollection.is_public
)
if updated_after:
query = query.filter(SmartCollection.updated_at > updated_after)
return session.scalars(query).unique().all()
@begin_session