Skip to content

Implement global search#16

Open
nOBS1 wants to merge 1 commit into
gitearn-io:mainfrom
nOBS1:feat-global-search
Open

Implement global search#16
nOBS1 wants to merge 1 commit into
gitearn-io:mainfrom
nOBS1:feat-global-search

Conversation

@nOBS1
Copy link
Copy Markdown

@nOBS1 nOBS1 commented May 12, 2026

Closes #3

Adds a global search feature across resources visible to the current user.

Changes:

  • Adds /api/v1/search/ for authenticated search requests
  • Searches items for normal users, limited to their own visible records
  • Includes matching users in results for superusers
  • Adds a navbar search input with a dropdown result list
  • Adds backend coverage for blank queries, superuser search, and normal-user visibility boundaries

Validation:

  • npm run build
  • uv tool run poetry run ruff check app
  • uv tool run poetry run mypy app

Note: local pytest for app/tests/api/routes/test_search.py requires PostgreSQL at localhost:5432/gitearn; my local Docker engine was unavailable, so the test file was added but not fully executed locally.

Copilot AI review requested due to automatic review settings May 12, 2026 15:57
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an authenticated “global search” feature that queries resources visible to the current user, exposing it via a new backend endpoint and a navbar search UI for quick lookup.

Changes:

  • Backend: introduce GET /api/v1/search/ and response models for unified item/user search results (users only for superusers).
  • Frontend: add a navbar search input with an inline dropdown showing result suggestions.
  • Tests: add API tests for blank-query handling, superuser user+item results, and normal-user visibility constraints.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
frontend/src/components/Common/Navbar.tsx Adds global search input and results dropdown powered by React Query.
frontend/src/client/services.ts Adds SearchService.search() OpenAPI client method.
frontend/src/client/models.ts Adds SearchResult / SearchResultsPublic client-side types.
backend/app/tests/api/routes/test_search.py Adds API route tests for search behavior and access constraints.
backend/app/models.py Adds SearchResult / SearchResultsPublic response models.
backend/app/api/routes/search.py Implements the /search/ endpoint querying items (and users for superusers).
backend/app/api/main.py Registers the new search router under /api/v1/search.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +33 to +40
const [searchTerm, setSearchTerm] = useState("")
const query = searchTerm.trim()
const showResults = query.length >= 2
const searchResults = useQuery({
queryKey: ["global-search", query],
queryFn: () => SearchService.search({ q: query }),
enabled: showResults,
})
Comment on lines +47 to +70
if current_user.is_superuser and len(results) < result_limit:
user_statement = (
select(User)
.where(
or_(
col(User.email).ilike(pattern),
col(User.full_name).ilike(pattern),
)
)
.limit(result_limit - len(results))
)
users = session.exec(user_statement).all()
results.extend(
SearchResult(
type="user",
id=user.id,
title=user.full_name or user.email,
description=user.email,
)
for user in users
if user.id is not None
)

return SearchResultsPublic(data=results, count=len(results))
Comment on lines +24 to +35
result_limit = min(max(limit, 1), 50)
pattern = f"%{query}%"
item_filters = or_(
col(Item.title).ilike(pattern),
col(Item.description).ilike(pattern),
)
item_statement = select(Item).where(item_filters).limit(result_limit)

if not current_user.is_superuser:
item_statement = item_statement.where(Item.owner_id == current_user.id)

items = session.exec(item_statement).all()
Comment on lines +69 to +84
response = client.get(
f"{settings.API_V1_STR}/search/",
headers=normal_user_token_headers,
params={"q": "needle"},
)

assert response.status_code == 200
content = response.json()
assert content["data"] == [
{
"type": "item",
"id": own_item.id,
"title": own_item.title,
"description": own_item.description,
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ Implment global search feature

2 participants