Skip to content

api/search - #243

Merged
cncases merged 6 commits into
mainfrom
api2
Jul 25, 2026
Merged

api/search#243
cncases merged 6 commits into
mainfrom
api2

Conversation

@cncases

@cncases cncases commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added a new JSON search API at /api/search.
    • JSON search now returns unified search_meta plus a list of full matching case details, including support for keyword search and (when enabled) vector search.
  • UI Updates
    • Updated the search page template to use search_meta consistently for inputs, suggestions, results rendering, pagination, and export behavior.
  • Documentation
    • Updated static/help.txt with revised export examples and added /api/search usage details, including search_type and offset guidance.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a JSON /api/search endpoint backed by shared Tantivy or optional Qdrant search logic. Search results now use serialized metadata and structured case data, while the HTML page, CSV export, routing, public exports, package version, and help documentation consume the updated response shape.

Changes

Search API

Layer / File(s) Summary
Search response and execution
src/controller.rs
Introduces serialized SearchMeta, CaseData, and SearchData structures; extracts shared search execution and returns JSON results or CSV-compatible case data.
HTML search response integration
src/controller.rs, templates/search.html
Updates HTML rendering, search controls, suggestions, exports, case iteration, and pagination to use search_meta and structured cases.
Route, public exposure, and documentation
src/bin/main.rs, src/lib.rs, static/help.txt, Cargo.toml
Registers GET /api/search, re-exports api_search, updates the package version, and documents the endpoint and search parameters.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Router
  participant api_search
  participant SearchBackend
  participant Database
  Client->>Router: GET /api/search with QuerySearch
  Router->>api_search: QuerySearch parameters
  api_search->>SearchBackend: Execute Tantivy or Qdrant search
  SearchBackend-->>api_search: Matching IDs
  api_search->>Database: Load Case records
  Database-->>api_search: CaseData records
  api_search-->>Client: JSON SearchData
Loading

Possibly related PRs

  • cncases/cases#82: Overlaps in controller search/case handling and CSV/export data shaping.
  • cncases/cases#174: Related changes to search_type, enable_vsearch, and Qdrant-backed search flow.

Poem

A rabbit hops through search today,
Tantivy points and Qdrant may.
Cases gather, neatly packed,
JSON and HTML share the track.
Metadata leads the way.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change by pointing to the new /api/search endpoint.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch api2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/controller.rs`:
- Around line 378-407: Update the vsearch handling around the Qdrant
search_points call so failures return a non-2xx error response instead of the
normal success JSON. Remove the precomputed offset-plus-limit total and derive
truthful pagination metadata by requesting limit + 1 results, trimming the extra
item, and setting has_more from its presence; preserve the requested offset and
returned IDs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 87b58462-d3f6-48d8-8c3b-7d760cdddbca

📥 Commits

Reviewing files that changed from the base of the PR and between 42dae1f and 3bf1025.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • src/bin/main.rs
  • src/controller.rs
  • src/lib.rs

Comment thread src/controller.rs Outdated
Comment on lines +378 to +407
let search_limit = limit + offset;
total = search_limit;
if let Ok(search_result) = client
.search_points(
SearchPointsBuilder::new(
&CONFIG.collection_name,
query_vec.into_iter().next().unwrap(),
search_limit as u64,
)
.with_payload(false)
.limit(limit as u64)
.offset(offset as u64),
)
.await
{
for point in &search_result.result {
let id = point
.id
.as_ref()
.unwrap()
.point_id_options
.as_ref()
.unwrap();
if let PointIdOptions::Num(id) = id {
ids.insert(*id as u32);
}
}
} else {
tracing::error!("Qdrant search_points failed");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Return truthful vsearch metadata and an error when Qdrant fails.

total is set to offset + limit before the request, so an empty result at offset 0 reports total: 20. A Qdrant failure only logs and returns that same HTTP 200 JSON shape, making an outage indistinguishable from a valid search. Return a non-2xx response on failure and use verified pagination metadata (for example, has_more from a limit + 1 query) rather than a fabricated total.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/controller.rs` around lines 378 - 407, Update the vsearch handling around
the Qdrant search_points call so failures return a non-2xx error response
instead of the normal success JSON. Remove the precomputed offset-plus-limit
total and derive truthful pagination metadata by requesting limit + 1 results,
trimming the extra item, and setting has_more from its presence; preserve the
requested offset and returned IDs.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/controller.rs (1)

200-238: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Vsearch still reports a fabricated total and swallows Qdrant failures.

total = search_limit (limit + offset) is set before the query executes, so e.g. an empty result at offset 0 still reports total: 20. On search_points failure, only tracing::error! is logged — ids stays empty and the handler still returns a normal 200 JSON/HTML shape, making a Qdrant outage indistinguishable from "no results." This is the same issue raised on a previous commit of this PR and remains unresolved after the search_cases extraction.

♻️ Suggested direction
-                    let search_limit = limit + offset;
-                    total = search_limit;
-                    if let Ok(search_result) = client
+                    if let Ok(search_result) = client
                         .search_points(
                             SearchPointsBuilder::new(
                                 &CONFIG.collection_name,
                                 query_vec.into_iter().next().unwrap(),
-                                search_limit as u64,
+                                (limit + 1) as u64,
                             )
                             .with_payload(false)
-                            .limit(limit as u64)
+                            .limit((limit + 1) as u64)
                             .offset(offset as u64),
                         )
                         .await
                     {
+                        // derive has_more from the extra item, trim before use
                         ...
                     } else {
                         tracing::error!("Qdrant search_points failed");
+                        // propagate as a real error instead of a fake 200
                     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/controller.rs` around lines 200 - 238, Update the vsearch branch in the
controller so `total` reflects the actual Qdrant result rather than being preset
to `limit + offset`, and propagate `search_points` failures instead of logging
them and returning an empty successful response. Preserve normal pagination and
result handling for successful searches, while making the handler return its
existing error response shape for Qdrant failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/controller.rs`:
- Around line 360-376: Remove full_text from the serialized CaseData response
while preserving its availability for the CSV export path in search. Update the
CaseData serialization behavior and any related JSON search handling so HTML and
/api/search return only the fields they consume, while the CSV branch continues
using case.full_text.

In `@templates/search.html`:
- Around line 57-65: Update the suggestion link in the search_meta vsearch block
to submit the query parameter named search_type, so clicking it explicitly
selects vsearch; preserve the existing search value and surrounding display
conditions.

---

Outside diff comments:
In `@src/controller.rs`:
- Around line 200-238: Update the vsearch branch in the controller so `total`
reflects the actual Qdrant result rather than being preset to `limit + offset`,
and propagate `search_points` failures instead of logging them and returning an
empty successful response. Preserve normal pagination and result handling for
successful searches, while making the handler return its existing error response
shape for Qdrant failures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 57d320ba-b921-41f7-bd0c-8d5677be2969

📥 Commits

Reviewing files that changed from the base of the PR and between 3bf1025 and 9f26ccc.

📒 Files selected for processing (2)
  • src/controller.rs
  • templates/search.html

Comment thread src/controller.rs
Comment on lines +360 to +376
#[derive(Serialize)]
pub struct CaseData {
id: u32,
preview: String,
doc_id: String,
case_id: String,
case_name: String,
court: String,
case_type: String,
procedure: String,
judgment_date: String,
public_date: String,
parties: String,
cause: String,
legal_basis: String,
full_text: String,
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

CaseData.full_text is unconditionally serialized for every search hit, even though nothing but CSV export needs it.

The HTML template (templates/search.html) only renders case.preview, never case.full_text; the new /api/search JSON endpoint (Lines 354-358) serializes it for every result regardless, and since api_search never branches on search_meta.export, calling it with export=true raises the limit to EXPORT_LIMIT (10000 by default) and returns full document text for up to 10000 hits as JSON. Only the CSV branch of search (Lines 320-334) actually consumes case.full_text. Excluding it from serialization keeps CSV working while avoiding a large, unintended payload/bandwidth cost on the new JSON endpoint.

♻️ Suggested fix
 #[derive(Serialize)]
 pub struct CaseData {
     id: u32,
     preview: String,
     doc_id: String,
     case_id: String,
     case_name: String,
     court: String,
     case_type: String,
     procedure: String,
     judgment_date: String,
     public_date: String,
     parties: String,
     cause: String,
     legal_basis: String,
+    #[serde(skip_serializing)]
     full_text: String,
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[derive(Serialize)]
pub struct CaseData {
id: u32,
preview: String,
doc_id: String,
case_id: String,
case_name: String,
court: String,
case_type: String,
procedure: String,
judgment_date: String,
public_date: String,
parties: String,
cause: String,
legal_basis: String,
full_text: String,
}
#[derive(Serialize)]
pub struct CaseData {
id: u32,
preview: String,
doc_id: String,
case_id: String,
case_name: String,
court: String,
case_type: String,
procedure: String,
judgment_date: String,
public_date: String,
parties: String,
cause: String,
legal_basis: String,
#[serde(skip_serializing)]
full_text: String,
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/controller.rs` around lines 360 - 376, Remove full_text from the
serialized CaseData response while preserving its availability for the CSV
export path in search. Update the CaseData serialization behavior and any
related JSON search handling so HTML and /api/search return only the fields they
consume, while the CSV branch continues using case.full_text.

Comment thread templates/search.html

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@static/help.txt`:
- Around line 108-110: Update the optional-parameter list in help.txt to
document the supported export parameter used by QuerySearch and search_cases,
including its effect on the API result limit; if /api/search does not support
it, explicitly state that instead. Align the wording with the shared handler
contract in QuerySearch and search_cases from src/controller.rs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 46a52e6f-cae1-485a-bf7c-b01cdb912ca1

📥 Commits

Reviewing files that changed from the base of the PR and between 9f26ccc and 4a68053.

📒 Files selected for processing (2)
  • src/controller.rs
  • static/help.txt
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/controller.rs

Comment thread static/help.txt
Comment on lines +108 to +110
可选参数:
- search_type: keyword(默认关键词搜索)/ vsearch(语义搜索,刑事案件可用)
- offset: 偏移量,默认0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Document the supported export parameter.

QuerySearch accepts export, and search_cases uses it to change the API result limit. Add it to this parameter list, or explicitly state that it is unsupported for /api/search, so the documentation matches the shared handler contract in src/controller.rs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@static/help.txt` around lines 108 - 110, Update the optional-parameter list
in help.txt to document the supported export parameter used by QuerySearch and
search_cases, including its effect on the API result limit; if /api/search does
not support it, explicitly state that instead. Align the wording with the shared
handler contract in QuerySearch and search_cases from src/controller.rs.

@cncases
cncases merged commit edd4555 into main Jul 25, 2026
4 checks passed
@cncases
cncases deleted the api2 branch July 25, 2026 03:35
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.

1 participant