4788ae7723
Fuzzy search: pg_trgm extension + trigram GIN indexes on name parts and a GET /trees/{id}/persons?q= search ranked by trigram similarity (finds Mueller for 'muller'), privacy-filtered. Living-person protection: the privacy engine now derives possibly-living status (explicit flag, else no death fact + birth within ~100y or unknown) and returns 'redacted' for non-members of public/unlisted trees; the service minimises those records ('Living person', no vitals). Members are unaffected. 31 tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Justin Paul <justin@jpaul.me>
25 lines
1.0 KiB
Python
25 lines
1.0 KiB
Python
"""Fuzzy name search (pg_trgm)."""
|
|
|
|
from tests.conftest import auth, register
|
|
|
|
|
|
async def test_fuzzy_name_search(client):
|
|
h = auth(await register(client, "search@example.com"))
|
|
tid = (await client.post("/api/v1/trees", json={"name": "S"}, headers=h)).json()["id"]
|
|
for given, surname in [("Hans", "Mueller"), ("John", "Smith"), ("Anna", "Vogel")]:
|
|
await client.post(
|
|
f"/api/v1/trees/{tid}/persons",
|
|
json={"given": given, "surname": surname},
|
|
headers=h,
|
|
)
|
|
|
|
# Trigram fuzziness: "muller" should find "Mueller" (not a substring match).
|
|
r = await client.get(f"/api/v1/trees/{tid}/persons", params={"q": "muller"}, headers=h)
|
|
assert r.status_code == 200
|
|
names = [p["primary_name"] or "" for p in r.json()]
|
|
assert any("Mueller" in n for n in names)
|
|
|
|
# Substring search still works.
|
|
r2 = await client.get(f"/api/v1/trees/{tid}/persons", params={"q": "smi"}, headers=h)
|
|
assert any("Smith" in (p["primary_name"] or "") for p in r2.json())
|