Rust Client
A modern, async Rust client library for hlquery, designed with a consistent API structure for familiarity and ease of use.
Features
- Async/Await Support: Built on Tokio for modern async Rust
- Type-safe: Strong typing throughout with serde for JSON handling
- Consistent API Design: Familiar structure for developers
- Authentication Support: Bearer token and X-API-Key authentication
- Comprehensive Validation: Input validation for all operations
- Error Handling: Rich error types with thiserror
- Minimal Dependencies: Optimized for performance
Installation
Add this to your Cargo.toml:
[dependencies]
hlquery-rust-client = { path = "../api/rust" }
tokio = { version = "1", features = ["full"] }
Or if published to crates.io:
[dependencies]
hlquery-rust-client = "1.0"
tokio = { version = "1", features = ["full"] }
Quick Start
Basic Usage
use hlquery_rust_client::Client;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize client
let client = Client::new("http://localhost:9200", None)?;
// Health check
let health = client.health().await?;
println!("Status: {}", health.get_status_code());
// List collections
let collections = client.list_collections(0, 10).await?;
if collections.is_success() {
let body = collections.get_body();
if let Some(collections_array) = body.get("collections").and_then(|c| c.as_array()) {
println!("Found {} collections", collections_array.len());
}
}
Ok(())
}
With Authentication
use hlquery_rust_client::Client;
use std::collections::HashMap;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Method 1: Set token in constructor
let mut options = HashMap::new();
options.insert("token".to_string(), "your_token_here".to_string());
options.insert("auth_method".to_string(), "bearer".to_string());
let client = Client::new("http://localhost:9200", Some(options))?;
// Method 2: Set token dynamically
let client = Client::new("http://localhost:9200", None)?;
client.set_auth_token("your_token_here".to_string(), "bearer".to_string());
// Method 3: Use X-API-Key
client.set_auth_token("your_key_here".to_string(), "api-key".to_string());
Ok(())
}
API Reference
Client Initialization
let client = Client::new(base_url, options)?;
Parameters:
base_url(string, required): Base URL of hlquery server (e.g.,"http://localhost:9200")options(Option<HashMap<String, String>>, optional): Client options"token": Authentication token"auth_method": Authentication method ("bearer"or"api-key")"timeout": Request timeout in milliseconds (default: 30000)
Health & Status
// Health check
let health = client.health().await?;
// Server statistics
let stats = client.stats().await?;
// Server information
let info = client.info().await?;
Collections API
// List collections
let collections = client.list_collections(0, 10).await?;
// Get collection details
let collection = client.get_collection("my_collection").await?;
// Compatibility alias: fields are returned in collection metadata
let fields = client.get_collection_fields("my_collection").await?;
// Get collection language information
let language = client.get_collection_language("my_collection").await?;
// Using the collections API handler
let collections_api = client.collections();
// Create collection
let schema = serde_json::json!({
"fields": [
{ "name": "title", "type": "string" },
{ "name": "content", "type": "string" }
]
});
let result = collections_api.create("new_collection", schema).await?;
// Update collection schema
let result = collections_api.update("collection_name", schema).await?;
// Delete collection
let result = collections_api.delete("collection_name").await?;
Documents API
use std::collections::HashMap;
// List documents
let mut params = HashMap::new();
params.insert("offset".to_string(), "0".to_string());
params.insert("limit".to_string(), "10".to_string());
let docs = client.list_documents("collection", Some(params)).await?;
// Get document by ID
let doc = client.get_document("collection", "doc_id").await?;
// Using the documents API handler
let documents_api = client.documents();
// Add document
let new_doc = serde_json::json!({
"id": "doc_1",
"title": "New Document",
"content": "Document content"
});
let result = documents_api.add("collection", new_doc).await?;
// Update document
let updated_doc = serde_json::json!({
"title": "Updated Document"
});
let result = documents_api.update("collection", "doc_id", updated_doc).await?;
// Delete document
let result = documents_api.delete("collection", "doc_id").await?;
// Bulk import
let bulk_docs = vec![
serde_json::json!({ "id": "doc1", "title": "Doc 1" }),
serde_json::json!({ "id": "doc2", "title": "Doc 2" })
];
let result = documents_api.import("collection", bulk_docs).await?;
Search API
use std::collections::HashMap;
// Simple search
let mut params = HashMap::new();
params.insert("q".to_string(), "search query".to_string());
params.insert("query_by".to_string(), "title,content".to_string());
params.insert("limit".to_string(), "10".to_string());
let results = client.search("collection", params).await?;
// Search with filters
let mut params = HashMap::new();
params.insert("q".to_string(), "query".to_string());
params.insert("query_by".to_string(), "title".to_string());
params.insert("filter_by".to_string(), "category:electronics".to_string());
params.insert("sort_by".to_string(), "price:asc".to_string());
params.insert("limit".to_string(), "20".to_string());
let results = client.search("collection", params).await?;
// Vector search
let mut params = HashMap::new();
params.insert("vector_query".to_string(), "[0.1,0.2,0.3,0.4,0.5]".to_string());
params.insert("limit".to_string(), "10".to_string());
params.insert("threshold".to_string(), "0.5".to_string());
let results = client.vector_search("collection", params).await?;
// Multi-search
let searches = vec![
serde_json::json!({
"collection": "col1",
"q": "query1",
"query_by": "title"
}),
serde_json::json!({
"collection": "col2",
"q": "query2",
"query_by": "content"
})
];
let results = client.search_api().multi_search(searches).await?;
// GET is also supported when explicitly required:
let results = client.search_api().multi_search_with_method(searches, "GET").await?;
Response Handling
All API methods return a Result<Response>:
let response = client.health().await?;
// Check success
if response.is_success() {
let body = response.get_body();
// Process response...
}
// Check status code
let status_code = response.get_status_code();
// Get error message
if response.is_error() {
if let Some(error) = response.get_error() {
println!("Error: {}", error);
}
}
Error Handling
The client uses Rust's Result type for error handling:
use hlquery_rust_client::{Client, HlqueryError};
match client.search("collection", params).await {
Ok(response) => {
// Handle success
}
Err(HlqueryError::RequestError(e)) => {
println!("Request failed: {}", e);
}
Err(HlqueryError::ValidationError(msg)) => {
println!("Validation failed: {}", msg);
}
Err(HlqueryError::AuthenticationError(msg)) => {
println!("Auth failed: {}", msg);
}
Err(e) => {
println!("Error: {}", e);
}
}
Input Validation
The Rust client includes comprehensive input validation for all operations:
use hlquery_rust_client::utils::validator;
// Validate collection name
if let Err(e) = validator::validate_collection_name("my_collection") {
println!("Invalid collection name: {}", e);
}
// Validate document ID
if let Err(e) = validator::validate_document_id("doc_123") {
println!("Invalid document ID: {}", e);
}
// Validate search parameters
let mut params = HashMap::new();
params.insert("q".to_string(), "query".to_string());
if let Err(e) = validator::validate_search_params(¶ms) {
println!("Invalid search params: {}", e);
}
Examples
Run Examples
# Comprehensive example
cargo run --example example
# Basic usage
cargo run --example basic_usage
# Collections examples
cargo run --example collections
# Documents examples
cargo run --example documents
# Search examples
cargo run --example search
With Authentication Token
cargo run --example example your_token_here
Requirements
- Rust 1.70+ (Edition 2021)
- Tokio runtime (for async support)
Dependencies
reqwest: HTTP clientserde/serde_json: JSON serializationurl: URL parsingthiserror: Error handlingmd5: Token generationurlencoding: URL encodingtokio: Async runtime
Architecture
The client follows a modular architecture:
- Client: Main entry point, coordinates all operations
- Request: Handles HTTP requests and authentication
- Response: Wraps HTTP responses with helper methods
- Collections: Collection management operations
- Documents: Document CRUD operations
- Search: Search operations
- Utils: Configuration, validation, and authentication utilities
Directory Structure
rust/
├── Cargo.toml # Rust package definition
├── README.md # Documentation
├── LICENSE # BSD-3-Clause license
│
├── src/ # Source code
│ ├── lib.rs # Main entry point
│ ├── client.rs # Main client class
│ ├── request.rs # HTTP request handler
│ ├── response.rs # Response wrapper
│ ├── error.rs # Error types
│ ├── collections.rs # Collections API
│ ├── documents.rs # Documents API
│ ├── search.rs # Search API
│ └── utils/ # Utility modules
│ ├── mod.rs # Module exports
│ ├── config.rs # Configuration utilities
│ ├── validator.rs # Input validation
│ └── auth.rs # Authentication utilities
│
└── examples/ # Example code
├── example.rs # Comprehensive example
├── basic_usage.rs # Basic usage examples
├── collections.rs # Collection management
├── documents.rs # Document CRUD
└── search.rs # Search examples
License
BSD 3-Clause License
Copyright (C) 2021-2026, Carlos F. Ferry <carlos.ferry@gmail.com>
Support
- Rust API Guide: See comprehensive Rust API documentation with detailed examples
- API Documentation: See main hlquery API docs
- Examples: Check the
examples/directory inapi/rust/ - Issues: Report issues in the main hlquery repository
Contributing
Contributions are welcome! The client follows Rust best practices:
- Use
async/awaitfor all I/O operations - Return
Result<T>for error handling - Use
serdefor JSON serialization - Follow Rust naming conventions (snake_case)
- Include comprehensive input validation