M6 1902a: richer facet query + saved-filters storage (backend)
GET /api/notes gains combinable, AND-ed facets alongside the existing filter/date/sort: multiple ?label= (notes with ALL), ?color, ?kind, ?has_reminder, ?has_attachment, and ?q (full-text over title+body, ranked) — so the facet bar's text box searches, not just filters. All optional; invalid color/kind → 400. saved_filters table (migration 0021) + /api/saved-filters CRUD (list / create / rename+repoint / delete, owner-scoped). `params` is a JSON facet dict mirroring the query surface; clean_params() whitelists facet keys so a saved view can't accumulate junk. Tests (DB-free): _truthy, clean_params key-whitelisting, saved-filters auth-guards. UI (facet bar + saved-views sidebar) lands next. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
"""saved_filters (richer search + saved views — M6 1902)
|
||||
|
||||
Revision ID: 0021
|
||||
Revises: 0020
|
||||
Create Date: 2026-07-23
|
||||
|
||||
A named facet combination the user can re-apply in one click. `params` is a
|
||||
JSON-encoded facet dict matching the GET /api/notes query.
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
|
||||
revision = "0021"
|
||||
down_revision = "0020"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"saved_filters",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("owner_id", UUID(as_uuid=True), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False),
|
||||
sa.Column("name", sa.Text(), nullable=False),
|
||||
sa.Column("params", sa.Text(), nullable=False, server_default="{}"),
|
||||
sa.Column("position", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
|
||||
)
|
||||
op.create_index("ix_saved_filters_owner", "saved_filters", ["owner_id", "position"])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index("ix_saved_filters_owner", table_name="saved_filters")
|
||||
op.drop_table("saved_filters")
|
||||
Reference in New Issue
Block a user