From cc56ec8695e0eeed58ed1a02c4c9f922d7973ee4 Mon Sep 17 00:00:00 2001 From: Filipriec Date: Sat, 22 Aug 2026 12:23:45 +0200 Subject: [PATCH] tantivy, datafusion and embedded psql are now separate features --- common/Cargo.toml | 6 ++- common/src/lib.rs | 1 + common/src/search_light.rs | 108 +++++++++++++++++++++++++++++++++++++ search/Cargo.toml | 2 +- server | 2 +- 5 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 common/src/search_light.rs diff --git a/common/Cargo.toml b/common/Cargo.toml index cb94cdd9..75d49a76 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -4,6 +4,10 @@ version.workspace = true edition.workspace = true license.workspace = true +[features] +default = [] +tantivy = ["dep:tantivy"] + [dependencies] prost-types = { workspace = true } rust_decimal = { workspace = true } @@ -14,7 +18,7 @@ prost = "0.14.4" serde = { version = "1.0.228", features = ["derive"] } # Search -tantivy = { workspace = true } +tantivy = { workspace = true, optional = true } serde_json.workspace = true tonic-prost = "0.14.6" diff --git a/common/src/lib.rs b/common/src/lib.rs index e6427035..004b5fd1 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,5 +1,6 @@ // common/src/lib.rs +#[cfg_attr(not(feature = "tantivy"), path = "search_light.rs")] pub mod search; pub mod decimal; pub mod grpc_error; diff --git a/common/src/search_light.rs b/common/src/search_light.rs new file mode 100644 index 00000000..743765c0 --- /dev/null +++ b/common/src/search_light.rs @@ -0,0 +1,108 @@ +//! Search constants and dependency-free helpers available without Tantivy. + +pub const F_PG_ID: &str = "pg_id"; +pub const F_ROW_REVISION: &str = "row_revision"; +pub const F_IS_ARCHIVED: &str = "is_archived"; +pub const F_TABLE_DEFINITION_ID: &str = "table_definition_id"; +pub const F_VERSION_NUMBER: &str = "version_number"; +pub const F_TABLE_NAME: &str = "table_name"; +pub const F_ROW_KEY: &str = "row_key"; +pub const F_ALL_TEXT: &str = "all_text"; +pub const F_DATA_WORD: &str = "data_word"; +pub const F_DATA_NGRAM: &str = "data_ngram"; +pub const F_DATA_EXACT: &str = "data_exact"; +pub const JOURNAL_TABLE_NAME: &str = "general_ledger"; +pub const ARCHIVED_ROW_KEY_PREFIX: &str = "__archive__:"; +pub const SEARCH_INDEX_FORMAT_DIRECTORY: &str = "v4"; + +pub fn search_row_key(table_name: &str, row_id: i64) -> String { + format!("{table_name}:{row_id}") +} + +pub fn archived_search_row_key( + table_definition_id: i64, + row_id: i64, + version_number: i64, +) -> String { + format!("{ARCHIVED_ROW_KEY_PREFIX}{table_definition_id}:{row_id}:{version_number}") +} + +pub fn parse_archived_search_row_key(row_key: &str) -> Option<(i64, i64, i64)> { + let encoded = row_key.strip_prefix(ARCHIVED_ROW_KEY_PREFIX)?; + let mut parts = encoded.split(':'); + let table_definition_id = parts.next()?.parse().ok()?; + let row_id = parts.next()?.parse().ok()?; + let version_number = parts.next()?.parse().ok()?; + if parts.next().is_some() { + return None; + } + Some((table_definition_id, row_id, version_number)) +} + +pub fn canonical_exact_search_value(input: &str, field_type: &str) -> Result { + let normalized_type = field_type.trim().to_ascii_lowercase(); + if matches!(normalized_type.as_str(), "numeric" | "money") + || normalized_type.starts_with("decimal(") + { + return input + .parse::() + .map(|value| value.normalize().to_string()) + .map_err(|error| format!("Exact numeric search value is invalid: {error}")); + } + if matches!(normalized_type.as_str(), "int" | "bigint") || normalized_type.starts_with("link(") + { + return input + .parse::() + .map(|value| value.to_string()) + .map_err(|error| format!("Exact integer search value is invalid: {error}")); + } + Ok(input.to_string()) +} + +pub fn canonical_search_number(input: &str) -> String { + input + .parse::() + .map(|value| value.normalize().to_string()) + .unwrap_or_else(|_| input.to_string()) +} + +pub fn normalize_column_name(column: &str) -> String { + column.to_ascii_lowercase() +} + +#[cfg(test)] +mod tests { + use super::{ + archived_search_row_key, canonical_exact_search_value, parse_archived_search_row_key, + }; + + #[test] + fn archived_row_key_round_trips_stable_identity() { + let key = archived_search_row_key(42, 7, 3); + assert_eq!(parse_archived_search_row_key(&key), Some((42, 7, 3))); + assert_eq!(parse_archived_search_row_key("customers:7"), None); + assert_eq!(parse_archived_search_row_key("__archive__:42:7"), None); + } + + #[test] + fn exact_scalar_values_follow_catalog_field_types() { + assert_eq!(canonical_exact_search_value("001", "int").unwrap(), "1"); + assert_eq!(canonical_exact_search_value("001", "bigint").unwrap(), "1"); + assert_eq!( + canonical_exact_search_value("001", "link(adresar)").unwrap(), + "1" + ); + assert_eq!( + canonical_exact_search_value("10.50", "numeric").unwrap(), + "10.5" + ); + assert_eq!( + canonical_exact_search_value("10.50", "money").unwrap(), + "10.5" + ); + assert_eq!( + canonical_exact_search_value("10.50", "decimal(12, 2)").unwrap(), + "10.5" + ); + } +} diff --git a/search/Cargo.toml b/search/Cargo.toml index 6de946fd..cee56c32 100644 --- a/search/Cargo.toml +++ b/search/Cargo.toml @@ -14,6 +14,6 @@ tonic = { workspace = true } tracing = { workspace = true } tantivy = { workspace = true } -common = { path = "../common" } +common = { path = "../common", features = ["tantivy"] } tonic-reflection = "0.14.6" sqlx = { version = "0.9.0", features = ["postgres"] } diff --git a/server b/server index 8454f4dc..276b4d5c 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit 8454f4dc200ce6cc47041f6358112cfcfe274178 +Subproject commit 276b4d5ca1339c00511002122820a51ba18fe23b