Skip to content

Commit bdadeaf

Browse files
fix(api/search): update group db schema to have a vector search
1 parent 642a285 commit bdadeaf

3 files changed

Lines changed: 77 additions & 15 deletions

File tree

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""add group search vector
2+
3+
Revision ID: y3z4a5b6c7d8
4+
Revises: e2f3a4b5c6d7
5+
Create Date: 2026-07-06 16:20:00.000000
6+
7+
"""
8+
9+
from typing import Sequence, Union
10+
11+
import sqlalchemy as sa
12+
import sqlalchemy_utils
13+
from alembic import op
14+
15+
16+
revision: str = "y3z4a5b6c7d8"
17+
down_revision: Union[str, None] = "e2f3a4b5c6d7"
18+
branch_labels: Union[str, Sequence[str], None] = None
19+
depends_on: Union[str, Sequence[str], None] = None
20+
21+
22+
def upgrade() -> None:
23+
op.add_column(
24+
"group",
25+
sa.Column(
26+
"search_vector",
27+
sqlalchemy_utils.types.ts_vector.TSVectorType(),
28+
nullable=True,
29+
),
30+
)
31+
op.create_index(
32+
"ix_group_search_vector",
33+
"group",
34+
["search_vector"],
35+
unique=False,
36+
postgresql_using="gin",
37+
)
38+
op.execute(
39+
"""
40+
UPDATE "group"
41+
SET search_vector = to_tsvector(
42+
'pg_catalog.simple',
43+
concat_ws(' ', name, description, group_type)
44+
)
45+
"""
46+
)
47+
op.execute(
48+
"""
49+
CREATE TRIGGER "group_search_vector_update"
50+
BEFORE INSERT OR UPDATE ON "group"
51+
FOR EACH ROW EXECUTE PROCEDURE
52+
tsvector_update_trigger(
53+
'search_vector',
54+
'pg_catalog.simple',
55+
'name',
56+
'description',
57+
'group_type'
58+
)
59+
"""
60+
)
61+
62+
63+
def downgrade() -> None:
64+
op.execute('DROP TRIGGER IF EXISTS "group_search_vector_update" ON "group"')
65+
op.drop_index("ix_group_search_vector", table_name="group", postgresql_using="gin")
66+
op.drop_column("group", "search_vector")

api/search.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from fastapi import APIRouter
1717
from fastapi_pagination import paginate
1818
from fastapi_pagination.utils import disable_installed_extensions_check
19-
from sqlalchemy import select, func, text, or_
19+
from sqlalchemy import select, func, text
2020
from sqlalchemy.orm import Session, selectinload
2121

2222
from api.pagination import CustomPage
@@ -203,23 +203,15 @@ def _get_asset_results(session: Session, q: str, limit: int) -> list[dict]:
203203

204204

205205
def _get_project_results(session: Session, q: str, limit: int) -> list[dict]:
206-
search_term = f"%{q.strip()}%"
207-
query = (
208-
select(Group)
209-
.where(
210-
or_(
211-
Group.name.ilike(search_term),
212-
Group.description.ilike(search_term),
213-
Group.group_type.ilike(search_term),
214-
)
215-
)
216-
.order_by(Group.name)
217-
.limit(limit)
218-
.options(
206+
query = search(
207+
select(Group).options(
219208
selectinload(Group.thing_associations).selectinload(
220209
GroupThingAssociation.thing
221210
)
222-
)
211+
),
212+
q,
213+
vector=Group.search_vector,
214+
limit=limit,
223215
)
224216

225217
projects = session.scalars(query).all()

db/group.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from sqlalchemy import String, Integer, ForeignKey, UniqueConstraint
2020
from sqlalchemy.ext.associationproxy import association_proxy, AssociationProxy
2121
from sqlalchemy.orm import relationship, Mapped, mapped_column
22+
from sqlalchemy_utils import TSVectorType
2223

2324
from core.constants import SRID_WGS84
2425
from db.base import Base, AutoBaseMixin, ReleaseMixin, lexicon_term
@@ -36,6 +37,9 @@ class Group(Base, AutoBaseMixin, ReleaseMixin):
3637
Geometry(geometry_type="MULTIPOLYGON", srid=SRID_WGS84, spatial_index=True)
3738
)
3839
group_type: Mapped[Optional[str]] = lexicon_term(nullable=True)
40+
search_vector: Mapped[TSVectorType] = mapped_column(
41+
TSVectorType("name", "description", "group_type")
42+
)
3943

4044
# Foreign Keys
4145
parent_group_id: Mapped[Optional[int]] = mapped_column(

0 commit comments

Comments
 (0)