Skip to content

Commit 6771e02

Browse files
committed
Add prospects search endpoint and bump version
Bump __version__ to 1.2.1 and add a new /prospects/search endpoint. The search route accepts query, page and limit parameters, performs full-text search against the search_vector column using plainto_tsquery('english'), returns paginated results with total/pages metadata, and includes basic error handling and DB resource cleanup. Also update the prospects root links to expose the new search URL.
1 parent 63b592c commit 6771e02

2 files changed

Lines changed: 51 additions & 4 deletions

File tree

app/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""NX AI - FastAPI/Python/Postgres/tsvector"""
22

33
# Current Version
4-
__version__ = "1.2.0"
4+
__version__ = "1.2.1"

app/api/prospects/prospects.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,10 @@ def root() -> dict:
1414
meta = make_meta("success", "Prospects start")
1515
data = [
1616
{"init": f"{base_url}/prospects/init"},
17-
{"single": f"{base_url}/prospects/101"},
18-
{"read": f"{base_url}/prospects/read"},
17+
{"search": f"{base_url}/prospects/search/?query=example"},
1918
]
2019
return {"meta": meta, "data": data}
2120

22-
2321
# endpoint: /prospects/read
2422
@router.get("/prospects/read")
2523
def prospects_read(
@@ -61,6 +59,55 @@ def prospects_read(
6159
"data": data,
6260
}
6361

62+
from typing import Optional
63+
64+
# endpoint: /prospects/search
65+
@router.get("/prospects/search")
66+
def prospects_search(query: Optional[str] = Query(None, description="Search query string"),
67+
page: int = Query(1, ge=1, description="Page number (1-based)"),
68+
limit: int = Query(50, ge=1, le=500, description="Records per page (default 50, max 500)")) -> dict:
69+
"""Search prospects using full-text search on search_vector column."""
70+
meta = make_meta("success", f"Search prospects for query: {query}")
71+
data = []
72+
total = 0
73+
if not query or not query.strip():
74+
meta = make_meta("error", "Query parameter is required for search.")
75+
return {"meta": meta, "data": [], "pagination": {"page": page, "limit": limit, "total": 0, "pages": 0}}
76+
conn_gen = get_db_connection()
77+
conn = next(conn_gen)
78+
cur = conn.cursor()
79+
offset = (page - 1) * limit
80+
try:
81+
# Count total matches
82+
cur.execute("SELECT COUNT(*) FROM prospects WHERE search_vector @@ plainto_tsquery('english', %s);", (query,))
83+
count_row = cur.fetchone() if cur.description is not None else None
84+
total = count_row[0] if count_row is not None else 0
85+
# Fetch paginated results
86+
cur.execute("SELECT * FROM prospects WHERE search_vector @@ plainto_tsquery('english', %s) OFFSET %s LIMIT %s;", (query, offset, limit))
87+
if cur.description is not None:
88+
columns = [desc[0] for desc in cur.description]
89+
rows = cur.fetchall()
90+
data = [dict(zip(columns, row)) for row in rows]
91+
else:
92+
data = []
93+
except Exception as e:
94+
meta = make_meta("error", f"Search failed: {str(e)}")
95+
data = []
96+
total = 0
97+
finally:
98+
cur.close()
99+
conn.close()
100+
return {
101+
"meta": meta,
102+
"pagination": {
103+
"page": page,
104+
"limit": limit,
105+
"total": total,
106+
"pages": (total // limit) + (1 if total % limit else 0)
107+
},
108+
"data": data,
109+
}
110+
64111
# endpoint: /prospects/init
65112
@router.get("/prospects/init")
66113
def prospects_init() -> dict:

0 commit comments

Comments
 (0)