Gpsirdb / app.py
gopallikehack's picture
Update app.py
5f6decc verified
Raw
History Blame Contribute Delete
3.15 kB
import duckdb
from fastapi import FastAPI, Query
import os
from contextlib import asynccontextmanager
MOUNT_PATH = "/data"
FILES = {
"p2": "TGDATA BY DEADLOX P2.parquet",
"p4": "TGDATA BY DEADLOX P4.parquet"
}
AVAILABLE = {}
@asynccontextmanager
async def lifespan(app: FastAPI):
global AVAILABLE
for key, fname in FILES.items():
path = os.path.join(MOUNT_PATH, fname)
if os.path.exists(path):
AVAILABLE[key] = path
print(f"✅ {key}: {fname}")
yield
app = FastAPI(lifespan=lifespan)
con = duckdb.connect()
# Preload column names for each file (to avoid reading file every time)
COLUMNS = {}
def get_columns(db: str):
if db not in COLUMNS:
path = AVAILABLE[db]
df = con.execute(f"SELECT * FROM '{path}' LIMIT 0").fetchdf()
COLUMNS[db] = list(df.columns)
return COLUMNS[db]
@app.get("/search")
async def search_fast(
q: str = Query(..., min_length=1),
db: str = Query("p4")
):
if db not in AVAILABLE:
return {"error": f"Database {db} not available", "developer": "Gopal Parmar"}
path = AVAILABLE[db]
keyword = q.strip()
# Determine if keyword looks like a phone number (digits only, length 10-15)
is_numeric = keyword.replace('+', '').isdigit() and len(keyword) >= 5
try:
if is_numeric:
# Exact match on phone or user_id columns (fast)
query = f"""
SELECT * FROM '{path}'
WHERE phone = '{keyword}'
OR user_id = '{keyword}'
OR username = '{keyword}'
LIMIT 50
"""
else:
# Text search only on relevant columns (username, first_name, last_name)
query = f"""
SELECT * FROM '{path}'
WHERE username ILIKE '%{keyword}%'
OR first_name ILIKE '%{keyword}%'
OR last_name ILIKE '%{keyword}%'
OR phone ILIKE '%{keyword}%'
LIMIT 50
"""
results_df = con.execute(query).fetchdf()
records = results_df.to_dict(orient='records')
return {
"developer": "Gopal Parmar",
"query": keyword,
"database": db,
"count": len(records),
"columns": get_columns(db),
"results": records
}
except Exception as e:
return {"error": str(e), "developer": "Gopal Parmar"}
@app.get("/search_all")
async def search_all_fast(q: str = Query(...)):
"""Search both databases in parallel (using asyncio)"""
import asyncio
async def search_one(db):
return await search_fast(q, db)
results = await asyncio.gather(search_one("p2"), search_one("p4"))
return {
"developer": "Gopal Parmar",
"query": q,
"p2": results[0],
"p4": results[1]
}
@app.get("/")
async def root():
return {
"message": "Fast Search API",
"developer": "Gopal Parmar",
"available": list(AVAILABLE.keys()),
"note": "Exact match on numbers, limited to 50 results"
}