Add cursor pagination and filtering to `GET /api/v1/search`
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 50/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- elasticsearch, nodejs, typescript
Research direction
Start with src/search/search.service.ts and src/search/search.controller.ts, then read the shared src/common/pagination/cursor-pagination.service.ts and its spec. Replace the untyped search and indexPost signatures with a DTO in src/search/dto/search.dto.ts, and add mocked Elasticsearch unit tests for paging, filtering and the limit ceiling. Done means GET /search returns { results, nextCursor, total }, an oversized limit returns 400, and docs/openapi.json is regenerated.
Written by the indexing model from the issue text.
Description
Problem
src/search/search.service.ts is a 27-line stub with two methods and no pagination, no filtering, and no size limit:
async search(query: string) {
const result = await this.esService.search({
index: "posts",
query: { multi_match: { query, fields: ["title", "content"] } },
});
return result.hits.hits.map((hit) => hit._source);
}
Elasticsearch defaults to 10 results per request, so this silently truncates — and because there is no size or from, a caller asking for more than 10 results cannot get them at all. indexPost(post: any) also takes an untyped parameter, and src/search/search.controller.ts exposes only POST /search/index and GET /search.
Separately, the repo already has a working cursor-pagination implementation at src/common/pagination/cursor-pagination.service.ts with a spec, and the README documents cursor pagination as the standard for the GraphQL gateway. Search should use the same convention.
Why it matters
This is the only search entry point, and it is effectively unusable for anything beyond browsing the first ten hits. Users cannot paginate, filter by field, sort, or get a total count — all standard expectations for a search endpoint, and all things the generated SDK is expected to expose.
Suggested Direction
- Reuse the existing pagination utility. Read
src/common/pagination/cursor-pagination.service.tsand use its cursor scheme for consistency rather than inventing offset pagination. - Add a typed search DTO (
src/search/dto/search.dto.ts) withq,limit(with a@Max()ceiling — see the pagination-bounds issue),cursor, and filter/sort fields, replacingsearch(query: string)andindexPost(post: any). - Add filtering by whatever fields the indexed documents actually carry. Check what populates the
postsindex before deciding — if nothing does, that is a separate bug worth filing. - Return metadata, not a bare array:
{ results, nextCursor, total }. The current bare-array return is why the GraphQL gateway had to add pagination separately (seedocs/GRAPHQL_GATEWAY.md). - Set an explicit
size, and considertrack_total_hitsif a total count is returned.
Constraints
- Keep the existing index name (
posts) and field mappings (title,content) unless there is a reason to change them; changing the mapping requires a reindex. - Guard against unbounded
sizeso a client cannot request an enormous result set. ElasticsearchServiceis already a dependency — do not add a new Elasticsearch client.- Update
docs/openapi.jsonvianpm run openapi:export.
Acceptance Criteria
-
GET /searchaccepts a typed query DTO and returns{ results, nextCursor, total }. - Pagination uses the shared cursor utility; a
limitabove the ceiling returns400. -
indexPostno longer takes ananyparameter. - Results beyond the first page are retrievable via cursor.
- Unit tests cover paging, filtering, and the limit ceiling with a mocked Elasticsearch service.
-
npm run openapi:exportis regenerated.
- Dominant language
- TypeScript
- Stars
- 7
- Forks
- 55
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 5
Getting set up
- Ships a Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from SourceXXL/alian_structure-api
-
good first issue help wanted security
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
SourceXXL/alian_structure-api#266 ·
-
Parallelize the cache-warming loop in `cache-warming.service.ts` — warmers run strictly sequentiallyPossibly taken @syntaxsuuvam-spec claimed this 3 days ago. Opengood first issue performance
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
SourceXXL/alian_structure-api#240 ·
-
documentation help wanted intermediate refactor
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
SourceXXL/alian_structure-api#215 ·
-
good first issue refactor tooling
Difficulty 2/5 1-3 hours Newbie friendliness 80/100
SourceXXL/alian_structure-api#199 ·
-
help wanted intermediate refactor
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
SourceXXL/alian_structure-api#196 ·
All issues in SourceXXL/alian_structure-api
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
needs triage
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
homarr-labs/homarr#7191 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
AvaProtocol/ava-sdk-js#276 ·
Maintainers usually reply within 1 day
-
useFragment does not require `variables` when the fragment's variables type has required fieldsOpen
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
apollographql/apollo-client#13493 ·
Maintainers usually reply within 1 day