Person page: server-side search; stop loading the whole tree
The person page fetched the entire tree on every open — all persons (to build a
name map + power the relative pickers) and all events (to find partnership
events). On a 2k-person tree that's a ~230KB person list + ~600KB event list per
view. Now it loads only what the page shows:
Frontend:
- The relationship & spouse pickers use the backend's fuzzy pg_trgm search
(debounced, typo-tolerant) instead of substring-filtering a preloaded array —
better search, and no need to preload every person. PersonCombobox gained an
`onSearch` server mode (client `people` mode still works).
- The page drops the all-persons and all-events fetches; it resolves just this
person's relatives' names via GET /persons?ids=..., and reads partnership
events from the per-person events endpoint.
Backend:
- GET /trees/{id}/persons?ids=a,b,c — batch by id (privacy-filtered, names
batched), for relative-name display.
- list_events_for_person (member path) now also returns the person's partnership
events, so the page needn't scan every event in the tree.
Adversarial review (frontend logic + backend/privacy) found no issues. Suite 105
passing.
Signed-off-by: Justin Paul <justin@jpaul.me>
This commit is contained in:
@@ -4,9 +4,10 @@ engine. Every event has exactly one subject — a Person or a partnership."""
|
||||
import uuid
|
||||
from datetime import date
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy import or_, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.enums import RelationshipType
|
||||
from app.models.event import Event
|
||||
from app.models.person import Person
|
||||
from app.models.place import Place
|
||||
@@ -124,12 +125,30 @@ async def list_events_for_person(
|
||||
return await public_view_service.list_public_person_events(
|
||||
session, viewer_id=viewer_id, tree=tree, person_id=person_id
|
||||
)
|
||||
# Member view: this person's own events PLUS their partnership events (which
|
||||
# live on the relationship and show on both partners). Returning both here
|
||||
# means the person page doesn't have to load every event in the tree.
|
||||
partner_rel_ids = (
|
||||
select(Relationship.id)
|
||||
.where(
|
||||
Relationship.tree_id == tree.id,
|
||||
Relationship.type == RelationshipType.partnership,
|
||||
Relationship.deleted_at.is_(None),
|
||||
or_(
|
||||
Relationship.person_from_id == person_id,
|
||||
Relationship.person_to_id == person_id,
|
||||
),
|
||||
)
|
||||
)
|
||||
stmt = (
|
||||
select(Event)
|
||||
.where(
|
||||
Event.tree_id == tree.id,
|
||||
Event.person_id == person_id,
|
||||
Event.deleted_at.is_(None),
|
||||
or_(
|
||||
Event.person_id == person_id,
|
||||
Event.relationship_id.in_(partner_rel_ids),
|
||||
),
|
||||
)
|
||||
.order_by(Event.date_start.nulls_last(), Event.created_at)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user