-
-
Notifications
You must be signed in to change notification settings - Fork 4
[codex] Add gigs dashboard search #337
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d670e09
7104275
da06c30
8352d7e
b0e4174
9855758
ce51036
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,11 @@ | ||
| { | ||
| "index.html": { | ||
| "file": "assets/index-C35mI0Gj.js", | ||
| "file": "assets/index-DUbmN0NW.js", | ||
| "name": "index", | ||
| "src": "index.html", | ||
| "isEntry": true, | ||
| "css": [ | ||
| "assets/index-C6NyLxSa.css" | ||
| "assets/index-BoK8s4aw.css" | ||
| ] | ||
| } | ||
| } |
Large diffs are not rendered by default.
This file was deleted.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| """Add a trigram index for dashboard gig search.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from alembic import op | ||
|
|
||
| revision = "20260613_0200" | ||
| down_revision = "20260613_0100" | ||
| branch_labels = None | ||
| depends_on = None | ||
|
|
||
|
|
||
| def upgrade() -> None: | ||
| """Index the leading-wildcard expression used by dashboard gig search.""" | ||
| op.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm") | ||
| with op.get_context().autocommit_block(): | ||
| op.execute( | ||
| "DROP INDEX CONCURRENTLY IF EXISTS idx_engagements_dashboard_search_trgm" | ||
| ) | ||
| op.execute( | ||
| """ | ||
| CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_engagements_dashboard_search_trgm | ||
| ON engagements USING gin ( | ||
| ( | ||
| coalesce(title, '') || ' ' || | ||
| coalesce(body_raw, '') || ' ' || | ||
| coalesce(body_normalized, '') | ||
| ) gin_trgm_ops | ||
| ) | ||
| """ | ||
| ) | ||
|
|
||
|
|
||
| def downgrade() -> None: | ||
| """Remove the dashboard gig search trigram index.""" | ||
| with op.get_context().autocommit_block(): | ||
| # Skill tag search remains unindexed here because array_to_string(text[], ...) | ||
| # cannot be used in an expression index on this Postgres setup. | ||
| op.execute( | ||
| "DROP INDEX CONCURRENTLY IF EXISTS idx_engagements_dashboard_search_trgm" | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,6 +71,20 @@ class EngagementApplicationSource(StrEnum): | |
| _BRACKETED_STATUS_RE = re.compile(r"^\s*[\[(]\s*([A-Z][A-Z0-9 _-]{2,})\s*[\])]\s*") | ||
|
|
||
|
|
||
| def _ilike_contains_pattern(value: str) -> str: | ||
| escaped = value.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_") | ||
| return f"%{escaped}%" | ||
|
|
||
|
|
||
| _DASHBOARD_ENGAGEMENT_TEXT_SEARCH_SQL = """ | ||
| ( | ||
| coalesce(e.title, '') || ' ' || | ||
| coalesce(e.body_raw, '') || ' ' || | ||
| coalesce(e.body_normalized, '') | ||
| ) | ||
| """ | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class DiscordEngagementInput: | ||
| """Discord-origin gig data used to create/update an engagement.""" | ||
|
|
@@ -1026,6 +1040,7 @@ def list_dashboard_engagements( | |
| include_historical: bool = False, | ||
| status: EngagementStatus | None = None, | ||
| engagement_id: str | None = None, | ||
| query: str | None = None, | ||
| limit: int = 50, | ||
| ) -> list[dict[str, Any]]: | ||
| """Return dashboard-visible gigs with nested application summaries.""" | ||
|
|
@@ -1042,6 +1057,24 @@ def list_dashboard_engagements( | |
| params.append(status.value) | ||
| elif engagement_id is None and not include_historical: | ||
| conditions.append("e.status IN ('recruiting', 'filled', 'unknown')") | ||
| normalized_query = query.strip() if query is not None else "" | ||
| if normalized_query: | ||
| like_query = _ilike_contains_pattern(normalized_query) | ||
| tag_token = normalized_query.removeprefix("#") | ||
| poster_token = normalized_query.removeprefix("@") | ||
| tag_query = _ilike_contains_pattern(tag_token or normalized_query) | ||
| poster_query = _ilike_contains_pattern(poster_token or normalized_query) | ||
| conditions.append( | ||
|
Comment on lines
+1060
to
+1067
|
||
| f""" | ||
| ( | ||
| {_DASHBOARD_ENGAGEMENT_TEXT_SEARCH_SQL} ILIKE %s ESCAPE '\\' | ||
| OR coalesce(array_to_string(e.required_skills, ' '), '') ILIKE %s ESCAPE '\\' | ||
| OR coalesce(array_to_string(e.preferred_skills, ' '), '') ILIKE %s ESCAPE '\\' | ||
| OR coalesce(e.posted_by_discord_user_id, '') ILIKE %s ESCAPE '\\' | ||
| ) | ||
|
Comment on lines
+1068
to
+1074
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Search predicate is missing channel/posting-type/candidate matching paths. This WHERE block currently searches engagement text, skills, and poster ID only. Queries for channel name, posting type, or nested candidate/application values will return false negatives against the intended search scope. 🤖 Prompt for AI Agents |
||
| """ | ||
| ) | ||
| params.extend([like_query, tag_query, tag_query, poster_query]) | ||
|
Comment on lines
+1060
to
+1077
|
||
| params.append(max(1, min(limit, 500))) | ||
| sql = f""" | ||
| SELECT | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.