use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SearchMode {
Semantic,
Keyword,
Hybrid,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct QueryBody {
pub query: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_k: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filters: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_score: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub search_mode: Option<SearchMode>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rerank: Option<bool>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct QueryResult {
pub id: String,
pub text: String,
pub score: f64,
pub document_id: Option<String>,
pub chunk_index: Option<u32>,
pub metadata: serde_json::Value,
}
#[derive(Debug, Clone, Deserialize)]
pub struct QueryResponse {
pub results: Vec<QueryResult>,
pub query: String,
pub collection: String,
pub total: u32,
}
#[derive(Debug, Clone, Serialize)]
pub struct MultiQueryBody {
pub query: String,
pub collections: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_k: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filters: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_score: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub search_mode: Option<SearchMode>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rerank: Option<bool>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MultiQueryResult {
pub id: String,
pub text: String,
pub score: f64,
pub document_id: Option<String>,
pub chunk_index: Option<u32>,
pub collection: String,
pub metadata: serde_json::Value,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MultiQueryResponse {
pub results: Vec<MultiQueryResult>,
pub query: String,
pub collections: Vec<String>,
pub total: u32,
}
#[derive(Debug, Clone, Serialize)]
pub struct BatchQueryItem {
pub collection: String,
pub query: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_k: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filters: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_score: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub search_mode: Option<SearchMode>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rerank: Option<bool>,
}
#[derive(Debug, Clone, Serialize)]
pub struct BatchQueryBody {
pub queries: Vec<BatchQueryItem>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BatchQueryResponse {
pub results: Vec<QueryResponse>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_search_mode_serializes_lowercase() {
let json = serde_json::to_value(SearchMode::Hybrid).unwrap();
assert_eq!(json, "hybrid");
}
#[test]
fn test_serialize_query_body_skips_none() {
let body = QueryBody {
query: "hello world".into(),
..Default::default()
};
let json = serde_json::to_value(&body).unwrap();
assert_eq!(json["query"], "hello world");
assert!(json.get("top_k").is_none());
assert!(json.get("search_mode").is_none());
}
#[test]
fn test_deserialize_query_response() {
let json = r#"{
"results": [{"id":"r1","text":"hello","score":0.95,"document_id":"d1","chunk_index":0,"metadata":{}}],
"query": "hello",
"collection": "docs",
"total": 1
}"#;
let resp: QueryResponse = serde_json::from_str(json).unwrap();
assert_eq!(resp.total, 1);
assert_eq!(resp.results[0].score, 0.95);
assert_eq!(resp.results[0].document_id, Some("d1".into()));
}
#[test]
fn test_deserialize_multi_query_response() {
let json = r#"{
"results": [{"id":"r1","text":"hi","score":0.9,"document_id":null,"chunk_index":null,"collection":"docs","metadata":{}}],
"query": "hi",
"collections": ["docs"],
"total": 1
}"#;
let resp: MultiQueryResponse = serde_json::from_str(json).unwrap();
assert_eq!(resp.results[0].collection, "docs");
assert_eq!(resp.results[0].document_id, None);
}
#[test]
fn test_deserialize_batch_query_response() {
let json = r#"{"results":[{"results":[],"query":"test","collection":"a","total":0}]}"#;
let resp: BatchQueryResponse = serde_json::from_str(json).unwrap();
assert_eq!(resp.results.len(), 1);
assert_eq!(resp.results[0].collection, "a");
}
}