implementation of tantivy datastore + updated RPC calls to deal with tantivy + docs
This commit is contained in:
182
src/rpc.rs
182
src/rpc.rs
@@ -14,6 +14,7 @@ use crate::admin_meta;
|
||||
pub enum BackendType {
|
||||
Redb,
|
||||
Sled,
|
||||
Tantivy, // Full-text search backend (no KV storage)
|
||||
// Future: InMemory, Custom(String)
|
||||
}
|
||||
|
||||
@@ -112,6 +113,53 @@ pub trait Rpc {
|
||||
/// Set database public/private status
|
||||
#[method(name = "setDatabasePublic")]
|
||||
async fn set_database_public(&self, db_id: u64, public: bool) -> RpcResult<bool>;
|
||||
|
||||
// ----- Full-text (Tantivy) minimal RPC endpoints -----
|
||||
|
||||
/// Create a new FT index in a Tantivy-backed DB
|
||||
#[method(name = "ftCreate")]
|
||||
async fn ft_create(
|
||||
&self,
|
||||
db_id: u64,
|
||||
index_name: String,
|
||||
schema: Vec<(String, String, Vec<String>)>,
|
||||
) -> RpcResult<bool>;
|
||||
|
||||
/// Add or replace a document in an FT index
|
||||
#[method(name = "ftAdd")]
|
||||
async fn ft_add(
|
||||
&self,
|
||||
db_id: u64,
|
||||
index_name: String,
|
||||
doc_id: String,
|
||||
score: f64,
|
||||
fields: HashMap<String, String>,
|
||||
) -> RpcResult<bool>;
|
||||
|
||||
/// Search an FT index
|
||||
#[method(name = "ftSearch")]
|
||||
async fn ft_search(
|
||||
&self,
|
||||
db_id: u64,
|
||||
index_name: String,
|
||||
query: String,
|
||||
filters: Option<Vec<(String, String)>>,
|
||||
limit: Option<usize>,
|
||||
offset: Option<usize>,
|
||||
return_fields: Option<Vec<String>>,
|
||||
) -> RpcResult<serde_json::Value>;
|
||||
|
||||
/// Delete a document by id from an FT index
|
||||
#[method(name = "ftDel")]
|
||||
async fn ft_del(&self, db_id: u64, index_name: String, doc_id: String) -> RpcResult<bool>;
|
||||
|
||||
/// Get FT index info
|
||||
#[method(name = "ftInfo")]
|
||||
async fn ft_info(&self, db_id: u64, index_name: String) -> RpcResult<serde_json::Value>;
|
||||
|
||||
/// Drop an FT index
|
||||
#[method(name = "ftDrop")]
|
||||
async fn ft_drop(&self, db_id: u64, index_name: String) -> RpcResult<bool>;
|
||||
}
|
||||
|
||||
/// RPC Server implementation
|
||||
@@ -187,13 +235,14 @@ impl RpcServerImpl {
|
||||
}
|
||||
|
||||
// Create server instance with resolved backend
|
||||
let is_tantivy = matches!(effective_backend, crate::options::BackendType::Tantivy);
|
||||
let db_option = DBOption {
|
||||
dir: self.base_dir.clone(),
|
||||
port: 0, // Not used for RPC-managed databases
|
||||
debug: false,
|
||||
encryption_key: None,
|
||||
encrypt: false,
|
||||
backend: effective_backend,
|
||||
backend: effective_backend.clone(),
|
||||
admin_secret: self.admin_secret.clone(),
|
||||
};
|
||||
|
||||
@@ -203,7 +252,10 @@ impl RpcServerImpl {
|
||||
server.selected_db = db_id;
|
||||
|
||||
// Lazily open/create physical storage according to admin meta (per-db encryption)
|
||||
let _ = server.current_storage();
|
||||
// Skip for Tantivy backend (no KV storage to open)
|
||||
if !is_tantivy {
|
||||
let _ = server.current_storage();
|
||||
}
|
||||
|
||||
// Store the server
|
||||
let mut servers = self.servers.write().await;
|
||||
@@ -290,6 +342,7 @@ impl RpcServerImpl {
|
||||
let backend = match server.option.backend {
|
||||
crate::options::BackendType::Redb => BackendType::Redb,
|
||||
crate::options::BackendType::Sled => BackendType::Sled,
|
||||
crate::options::BackendType::Tantivy => BackendType::Tantivy,
|
||||
};
|
||||
|
||||
DatabaseInfo {
|
||||
@@ -340,18 +393,20 @@ impl RpcServer for RpcServerImpl {
|
||||
let opt_backend = match backend {
|
||||
BackendType::Redb => crate::options::BackendType::Redb,
|
||||
BackendType::Sled => crate::options::BackendType::Sled,
|
||||
BackendType::Tantivy => crate::options::BackendType::Tantivy,
|
||||
};
|
||||
admin_meta::set_database_backend(&self.base_dir, self.backend.clone(), &self.admin_secret, db_id, opt_backend.clone())
|
||||
.map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned(-32000, e.0, None::<()>))?;
|
||||
|
||||
// Create server instance using base_dir, chosen backend and admin secret
|
||||
let is_tantivy_new = matches!(opt_backend, crate::options::BackendType::Tantivy);
|
||||
let option = DBOption {
|
||||
dir: self.base_dir.clone(),
|
||||
port: 0, // Not used for RPC-managed databases
|
||||
debug: false,
|
||||
encryption_key: None, // per-db key is stored in admin DB 0
|
||||
encrypt: false, // encryption decided per-db at open time
|
||||
backend: opt_backend,
|
||||
backend: opt_backend.clone(),
|
||||
admin_secret: self.admin_secret.clone(),
|
||||
};
|
||||
|
||||
@@ -359,7 +414,10 @@ impl RpcServer for RpcServerImpl {
|
||||
server.selected_db = db_id;
|
||||
|
||||
// Initialize storage to create physical <id>.db with proper encryption from admin meta
|
||||
let _ = server.current_storage();
|
||||
// Skip for Tantivy backend (no KV storage to initialize)
|
||||
if !is_tantivy_new {
|
||||
let _ = server.current_storage();
|
||||
}
|
||||
|
||||
// Store the server in cache
|
||||
let mut servers = self.servers.write().await;
|
||||
@@ -420,6 +478,7 @@ impl RpcServer for RpcServerImpl {
|
||||
let db_ids = self.discover_databases().await;
|
||||
let mut stats = HashMap::new();
|
||||
|
||||
|
||||
stats.insert("total_databases".to_string(), serde_json::json!(db_ids.len()));
|
||||
stats.insert("uptime".to_string(), serde_json::json!(
|
||||
std::time::SystemTime::now()
|
||||
@@ -431,6 +490,121 @@ impl RpcServer for RpcServerImpl {
|
||||
Ok(stats)
|
||||
}
|
||||
|
||||
// ----- Full-text (Tantivy) minimal RPC endpoints -----
|
||||
|
||||
async fn ft_create(
|
||||
&self,
|
||||
db_id: u64,
|
||||
index_name: String,
|
||||
schema: Vec<(String, String, Vec<String>)>,
|
||||
) -> RpcResult<bool> {
|
||||
let server = self.get_or_create_server(db_id).await?;
|
||||
if db_id == 0 {
|
||||
return Err(jsonrpsee::types::ErrorObjectOwned::owned(-32000, "FT not allowed on DB 0", None::<()>));
|
||||
}
|
||||
if !matches!(server.option.backend, crate::options::BackendType::Tantivy) {
|
||||
return Err(jsonrpsee::types::ErrorObjectOwned::owned(-32000, "DB backend is not Tantivy", None::<()>));
|
||||
}
|
||||
crate::search_cmd::ft_create_cmd(&*server, index_name, schema)
|
||||
.await
|
||||
.map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned(-32000, e.0, None::<()>))?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn ft_add(
|
||||
&self,
|
||||
db_id: u64,
|
||||
index_name: String,
|
||||
doc_id: String,
|
||||
score: f64,
|
||||
fields: HashMap<String, String>,
|
||||
) -> RpcResult<bool> {
|
||||
let server = self.get_or_create_server(db_id).await?;
|
||||
if db_id == 0 {
|
||||
return Err(jsonrpsee::types::ErrorObjectOwned::owned(-32000, "FT not allowed on DB 0", None::<()>));
|
||||
}
|
||||
if !matches!(server.option.backend, crate::options::BackendType::Tantivy) {
|
||||
return Err(jsonrpsee::types::ErrorObjectOwned::owned(-32000, "DB backend is not Tantivy", None::<()>));
|
||||
}
|
||||
crate::search_cmd::ft_add_cmd(&*server, index_name, doc_id, score, fields)
|
||||
.await
|
||||
.map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned(-32000, e.0, None::<()>))?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn ft_search(
|
||||
&self,
|
||||
db_id: u64,
|
||||
index_name: String,
|
||||
query: String,
|
||||
filters: Option<Vec<(String, String)>>,
|
||||
limit: Option<usize>,
|
||||
offset: Option<usize>,
|
||||
return_fields: Option<Vec<String>>,
|
||||
) -> RpcResult<serde_json::Value> {
|
||||
let server = self.get_or_create_server(db_id).await?;
|
||||
if db_id == 0 {
|
||||
return Err(jsonrpsee::types::ErrorObjectOwned::owned(-32000, "FT not allowed on DB 0", None::<()>));
|
||||
}
|
||||
if !matches!(server.option.backend, crate::options::BackendType::Tantivy) {
|
||||
return Err(jsonrpsee::types::ErrorObjectOwned::owned(-32000, "DB backend is not Tantivy", None::<()>));
|
||||
}
|
||||
let proto = crate::search_cmd::ft_search_cmd(
|
||||
&*server,
|
||||
index_name,
|
||||
query,
|
||||
filters.unwrap_or_default(),
|
||||
limit,
|
||||
offset,
|
||||
return_fields,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned(-32000, e.0, None::<()>))?;
|
||||
Ok(serde_json::json!({ "resp": proto.encode() }))
|
||||
}
|
||||
|
||||
async fn ft_del(&self, db_id: u64, index_name: String, doc_id: String) -> RpcResult<bool> {
|
||||
let server = self.get_or_create_server(db_id).await?;
|
||||
if db_id == 0 {
|
||||
return Err(jsonrpsee::types::ErrorObjectOwned::owned(-32000, "FT not allowed on DB 0", None::<()>));
|
||||
}
|
||||
if !matches!(server.option.backend, crate::options::BackendType::Tantivy) {
|
||||
return Err(jsonrpsee::types::ErrorObjectOwned::owned(-32000, "DB backend is not Tantivy", None::<()>));
|
||||
}
|
||||
crate::search_cmd::ft_del_cmd(&*server, index_name, doc_id)
|
||||
.await
|
||||
.map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned(-32000, e.0, None::<()>))?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn ft_info(&self, db_id: u64, index_name: String) -> RpcResult<serde_json::Value> {
|
||||
let server = self.get_or_create_server(db_id).await?;
|
||||
if db_id == 0 {
|
||||
return Err(jsonrpsee::types::ErrorObjectOwned::owned(-32000, "FT not allowed on DB 0", None::<()>));
|
||||
}
|
||||
if !matches!(server.option.backend, crate::options::BackendType::Tantivy) {
|
||||
return Err(jsonrpsee::types::ErrorObjectOwned::owned(-32000, "DB backend is not Tantivy", None::<()>));
|
||||
}
|
||||
let proto = crate::search_cmd::ft_info_cmd(&*server, index_name)
|
||||
.await
|
||||
.map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned(-32000, e.0, None::<()>))?;
|
||||
Ok(serde_json::json!({ "resp": proto.encode() }))
|
||||
}
|
||||
|
||||
async fn ft_drop(&self, db_id: u64, index_name: String) -> RpcResult<bool> {
|
||||
let server = self.get_or_create_server(db_id).await?;
|
||||
if db_id == 0 {
|
||||
return Err(jsonrpsee::types::ErrorObjectOwned::owned(-32000, "FT not allowed on DB 0", None::<()>));
|
||||
}
|
||||
if !matches!(server.option.backend, crate::options::BackendType::Tantivy) {
|
||||
return Err(jsonrpsee::types::ErrorObjectOwned::owned(-32000, "DB backend is not Tantivy", None::<()>));
|
||||
}
|
||||
crate::search_cmd::ft_drop_cmd(&*server, index_name)
|
||||
.await
|
||||
.map_err(|e| jsonrpsee::types::ErrorObjectOwned::owned(-32000, e.0, None::<()>))?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn add_access_key(&self, db_id: u64, key: String, permissions: String) -> RpcResult<bool> {
|
||||
let perms = match permissions.to_lowercase().as_str() {
|
||||
"read" => Permissions::Read,
|
||||
|
Reference in New Issue
Block a user