Compare commits
8 Commits
1cedd58708
...
v0.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36249739d3 | ||
|
|
bbd7c29681 | ||
|
|
2818a5f280 | ||
|
|
946ee9677c | ||
|
|
ec16930569 | ||
|
|
accfb4f346 | ||
|
|
e5ce96e210 | ||
|
|
a506cd8f08 |
113
Cargo.lock
generated
113
Cargo.lock
generated
@@ -493,11 +493,12 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "canvas"
|
||||
version = "0.5.0"
|
||||
version = "0.5.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"crossterm",
|
||||
"derivative",
|
||||
"once_cell",
|
||||
"ratatui",
|
||||
"regex",
|
||||
@@ -584,7 +585,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "client"
|
||||
version = "0.5.0"
|
||||
version = "0.5.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -598,9 +599,14 @@ dependencies = [
|
||||
"prost 0.13.5",
|
||||
"prost-types 0.13.5",
|
||||
"ratatui",
|
||||
"regex",
|
||||
"rstest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"steel-core",
|
||||
"steel-decimal",
|
||||
"strum 0.27.2",
|
||||
"strum_macros 0.27.2",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-test",
|
||||
@@ -635,7 +641,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "common"
|
||||
version = "0.5.0"
|
||||
version = "0.5.10"
|
||||
dependencies = [
|
||||
"prost 0.13.5",
|
||||
"prost-build 0.14.1",
|
||||
@@ -936,6 +942,17 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derivative"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
@@ -1959,11 +1976,11 @@ checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||
dependencies = [
|
||||
"regex-automata 0.1.10",
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2080,12 +2097,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"overload",
|
||||
"winapi",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2243,12 +2259,6 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "ownedbytes"
|
||||
version = "0.9.0"
|
||||
@@ -2756,7 +2766,7 @@ dependencies = [
|
||||
"itertools 0.13.0",
|
||||
"lru",
|
||||
"paste",
|
||||
"strum",
|
||||
"strum 0.26.3",
|
||||
"unicode-segmentation",
|
||||
"unicode-truncate",
|
||||
"unicode-width 0.2.0",
|
||||
@@ -2810,17 +2820,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.9",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
dependencies = [
|
||||
"regex-syntax 0.6.29",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2831,15 +2832,9 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
@@ -3100,7 +3095,7 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||
|
||||
[[package]]
|
||||
name = "search"
|
||||
version = "0.5.0"
|
||||
version = "0.5.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"common",
|
||||
@@ -3199,7 +3194,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "server"
|
||||
version = "0.5.0"
|
||||
version = "0.5.10"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bcrypt",
|
||||
@@ -3210,6 +3205,7 @@ dependencies = [
|
||||
"futures",
|
||||
"jsonwebtoken",
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
"prost 0.13.5",
|
||||
"prost-build 0.14.1",
|
||||
"prost-types 0.13.5",
|
||||
@@ -3234,6 +3230,7 @@ dependencies = [
|
||||
"tonic",
|
||||
"tonic-reflection",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"uuid",
|
||||
"validator",
|
||||
]
|
||||
@@ -3756,9 +3753,15 @@ version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
"strum_macros 0.26.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.27.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.26.4"
|
||||
@@ -3772,6 +3775,18 @@ dependencies = [
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.27.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
@@ -3830,7 +3845,7 @@ dependencies = [
|
||||
"fnv",
|
||||
"once_cell",
|
||||
"plist",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
@@ -3936,7 +3951,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d60769b80ad7953d8a7b2c70cdfe722bbcdcac6bccc8ac934c40c034d866fc18"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax",
|
||||
"utf8-ranges",
|
||||
]
|
||||
|
||||
@@ -4318,9 +4333,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
@@ -4330,9 +4345,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.30"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4341,9 +4356,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.34"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
||||
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
@@ -4362,14 +4377,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.19"
|
||||
version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
||||
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"regex-automata",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
|
||||
@@ -5,7 +5,7 @@ resolver = "2"
|
||||
[workspace.package]
|
||||
# TODO: idk how to do the name, fix later
|
||||
# name = "komp_ac"
|
||||
version = "0.5.0"
|
||||
version = "0.5.10"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-or-later"
|
||||
authors = ["Filip Priečinský <filippriec@gmail.com>"]
|
||||
|
||||
@@ -16,10 +16,5 @@ cargo watch -x 'run --package client -- client'
|
||||
|
||||
Client with tracing:
|
||||
```
|
||||
ENABLE_TRACING=1 RUST_LOG=client=debug cargo watch -x 'run --package client -- client'
|
||||
```
|
||||
|
||||
Client with debug that cant be traced
|
||||
```
|
||||
cargo run --package client --features ui-debug -- client
|
||||
```
|
||||
|
||||
2
canvas
2
canvas
Submodule canvas updated: 6f1cda36d8...5227bed900
2
client
2
client
Submodule client updated: 615c317a66...398d54f6e3
@@ -3,5 +3,9 @@ syntax = "proto3";
|
||||
package komp_ac.common;
|
||||
|
||||
message Empty {}
|
||||
message CountResponse { int64 count = 1; }
|
||||
message PositionRequest { int64 position = 1; }
|
||||
message CountResponse {
|
||||
int64 count = 1;
|
||||
}
|
||||
message PositionRequest {
|
||||
int64 position = 1;
|
||||
}
|
||||
|
||||
@@ -12,17 +12,14 @@ service TableDefinition {
|
||||
// Creates a new table (and schema if missing) with system columns,
|
||||
// linked-table foreign keys, user-defined columns, and optional indexes.
|
||||
// Also inserts metadata and default validation rules. Entirely transactional.
|
||||
rpc PostTableDefinition(PostTableDefinitionRequest)
|
||||
returns (TableDefinitionResponse);
|
||||
rpc PostTableDefinition(PostTableDefinitionRequest) returns (TableDefinitionResponse);
|
||||
|
||||
// Lists all profiles (schemas) and their tables with declared dependencies.
|
||||
// This provides a tree-like overview of table relationships.
|
||||
rpc GetProfileTree(komp_ac.common.Empty)
|
||||
returns (ProfileTreeResponse);
|
||||
rpc GetProfileTree(komp_ac.common.Empty) returns (ProfileTreeResponse);
|
||||
|
||||
// Drops a table and its metadata, then deletes the profile if it becomes empty.
|
||||
rpc DeleteTable(DeleteTableRequest)
|
||||
returns (DeleteTableResponse);
|
||||
rpc DeleteTable(DeleteTableRequest) returns (DeleteTableResponse);
|
||||
}
|
||||
|
||||
// A single link to another table within the same profile (schema).
|
||||
|
||||
@@ -33,6 +33,15 @@ service TableScript {
|
||||
// - UPSERTS into table_scripts on (table_definitions_id, target_column)
|
||||
// and saves a normalized dependency list into script_dependencies
|
||||
rpc PostTableScript(PostTableScriptRequest) returns (TableScriptResponse);
|
||||
|
||||
// Fetch all stored scripts for a specific table.
|
||||
//
|
||||
// Behavior:
|
||||
// - Resolves the table from (profile_name, table_name)
|
||||
// - Returns the stored, transformed script from table_scripts
|
||||
// - Includes normalized dependency metadata from script_dependencies
|
||||
// - Returns an empty scripts list when the table has no scripts
|
||||
rpc GetTableScripts(GetTableScriptsRequest) returns (GetTableScriptsResponse);
|
||||
}
|
||||
|
||||
// Request to create or update a script bound to a specific table and column.
|
||||
@@ -99,3 +108,32 @@ message TableScriptResponse {
|
||||
// - Warning if many dependencies may affect performance
|
||||
string warnings = 2;
|
||||
}
|
||||
|
||||
message GetTableScriptsRequest {
|
||||
// Required. Profile (schema) name.
|
||||
string profile_name = 1;
|
||||
|
||||
// Required. Table name within the profile.
|
||||
string table_name = 2;
|
||||
}
|
||||
|
||||
message GetTableScriptsResponse {
|
||||
repeated StoredTableScript scripts = 1;
|
||||
}
|
||||
|
||||
message StoredTableScript {
|
||||
int64 id = 1;
|
||||
string target_column = 2;
|
||||
string target_column_type = 3;
|
||||
string script = 4;
|
||||
string description = 5;
|
||||
repeated ScriptDependency dependencies = 6;
|
||||
}
|
||||
|
||||
message ScriptDependency {
|
||||
string target_table = 1;
|
||||
string dependency_type = 2;
|
||||
string column = 3;
|
||||
int64 index = 4;
|
||||
string query_fragment = 5;
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@ service TableStructureService {
|
||||
// - Normalizes data_type text (details under TableColumn.data_type)
|
||||
// - Returns an empty list if the table is validated but has no visible
|
||||
// columns in information_schema (e.g., physical table missing)
|
||||
rpc GetTableStructure(GetTableStructureRequest)
|
||||
returns (TableStructureResponse);
|
||||
rpc GetTableStructure(GetTableStructureRequest) returns (TableStructureResponse);
|
||||
}
|
||||
|
||||
// Request identifying the profile (schema) and table to inspect.
|
||||
|
||||
@@ -94,11 +94,9 @@ message PatternRules {
|
||||
|
||||
// Service to fetch validations for a table
|
||||
service TableValidationService {
|
||||
rpc GetTableValidation(GetTableValidationRequest)
|
||||
returns (TableValidationResponse);
|
||||
rpc GetTableValidation(GetTableValidationRequest) returns (TableValidationResponse);
|
||||
|
||||
rpc UpdateFieldValidation(UpdateFieldValidationRequest)
|
||||
returns (UpdateFieldValidationResponse);
|
||||
rpc UpdateFieldValidation(UpdateFieldValidationRequest) returns (UpdateFieldValidationResponse);
|
||||
}
|
||||
|
||||
message UpdateFieldValidationRequest {
|
||||
|
||||
@@ -37,7 +37,6 @@ pub mod proto {
|
||||
pub mod table_validation {
|
||||
include!("proto/komp_ac.table_validation.rs");
|
||||
}
|
||||
pub const FILE_DESCRIPTOR_SET: &[u8] =
|
||||
include_bytes!("proto/descriptor.bin");
|
||||
pub const FILE_DESCRIPTOR_SET: &[u8] = include_bytes!("proto/descriptor.bin");
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -68,6 +68,48 @@ pub struct TableScriptResponse {
|
||||
#[prost(string, tag = "2")]
|
||||
pub warnings: ::prost::alloc::string::String,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GetTableScriptsRequest {
|
||||
/// Required. Profile (schema) name.
|
||||
#[prost(string, tag = "1")]
|
||||
pub profile_name: ::prost::alloc::string::String,
|
||||
/// Required. Table name within the profile.
|
||||
#[prost(string, tag = "2")]
|
||||
pub table_name: ::prost::alloc::string::String,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GetTableScriptsResponse {
|
||||
#[prost(message, repeated, tag = "1")]
|
||||
pub scripts: ::prost::alloc::vec::Vec<StoredTableScript>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct StoredTableScript {
|
||||
#[prost(int64, tag = "1")]
|
||||
pub id: i64,
|
||||
#[prost(string, tag = "2")]
|
||||
pub target_column: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "3")]
|
||||
pub target_column_type: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "4")]
|
||||
pub script: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "5")]
|
||||
pub description: ::prost::alloc::string::String,
|
||||
#[prost(message, repeated, tag = "6")]
|
||||
pub dependencies: ::prost::alloc::vec::Vec<ScriptDependency>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ScriptDependency {
|
||||
#[prost(string, tag = "1")]
|
||||
pub target_table: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "2")]
|
||||
pub dependency_type: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "3")]
|
||||
pub column: ::prost::alloc::string::String,
|
||||
#[prost(int64, tag = "4")]
|
||||
pub index: i64,
|
||||
#[prost(string, tag = "5")]
|
||||
pub query_fragment: ::prost::alloc::string::String,
|
||||
}
|
||||
/// Generated client implementations.
|
||||
pub mod table_script_client {
|
||||
#![allow(
|
||||
@@ -217,6 +259,42 @@ pub mod table_script_client {
|
||||
);
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// Fetch all stored scripts for a specific table.
|
||||
///
|
||||
/// Behavior:
|
||||
/// - Resolves the table from (profile_name, table_name)
|
||||
/// - Returns the stored, transformed script from table_scripts
|
||||
/// - Includes normalized dependency metadata from script_dependencies
|
||||
/// - Returns an empty scripts list when the table has no scripts
|
||||
pub async fn get_table_scripts(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::GetTableScriptsRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::GetTableScriptsResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/komp_ac.table_script.TableScript/GetTableScripts",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new(
|
||||
"komp_ac.table_script.TableScript",
|
||||
"GetTableScripts",
|
||||
),
|
||||
);
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Generated server implementations.
|
||||
@@ -256,6 +334,20 @@ pub mod table_script_server {
|
||||
tonic::Response<super::TableScriptResponse>,
|
||||
tonic::Status,
|
||||
>;
|
||||
/// Fetch all stored scripts for a specific table.
|
||||
///
|
||||
/// Behavior:
|
||||
/// - Resolves the table from (profile_name, table_name)
|
||||
/// - Returns the stored, transformed script from table_scripts
|
||||
/// - Includes normalized dependency metadata from script_dependencies
|
||||
/// - Returns an empty scripts list when the table has no scripts
|
||||
async fn get_table_scripts(
|
||||
&self,
|
||||
request: tonic::Request<super::GetTableScriptsRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::GetTableScriptsResponse>,
|
||||
tonic::Status,
|
||||
>;
|
||||
}
|
||||
/// Manages column-computation scripts for user-defined tables.
|
||||
/// Each script belongs to a single table (table_definition_id) and populates
|
||||
@@ -390,6 +482,51 @@ pub mod table_script_server {
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
"/komp_ac.table_script.TableScript/GetTableScripts" => {
|
||||
#[allow(non_camel_case_types)]
|
||||
struct GetTableScriptsSvc<T: TableScript>(pub Arc<T>);
|
||||
impl<
|
||||
T: TableScript,
|
||||
> tonic::server::UnaryService<super::GetTableScriptsRequest>
|
||||
for GetTableScriptsSvc<T> {
|
||||
type Response = super::GetTableScriptsResponse;
|
||||
type Future = BoxFuture<
|
||||
tonic::Response<Self::Response>,
|
||||
tonic::Status,
|
||||
>;
|
||||
fn call(
|
||||
&mut self,
|
||||
request: tonic::Request<super::GetTableScriptsRequest>,
|
||||
) -> Self::Future {
|
||||
let inner = Arc::clone(&self.0);
|
||||
let fut = async move {
|
||||
<T as TableScript>::get_table_scripts(&inner, request).await
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
let accept_compression_encodings = self.accept_compression_encodings;
|
||||
let send_compression_encodings = self.send_compression_encodings;
|
||||
let max_decoding_message_size = self.max_decoding_message_size;
|
||||
let max_encoding_message_size = self.max_encoding_message_size;
|
||||
let inner = self.inner.clone();
|
||||
let fut = async move {
|
||||
let method = GetTableScriptsSvc(inner);
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let mut grpc = tonic::server::Grpc::new(codec)
|
||||
.apply_compression_config(
|
||||
accept_compression_encodings,
|
||||
send_compression_encodings,
|
||||
)
|
||||
.apply_max_message_size_config(
|
||||
max_decoding_message_size,
|
||||
max_encoding_message_size,
|
||||
);
|
||||
let res = grpc.unary(method, req).await;
|
||||
Ok(res)
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
_ => {
|
||||
Box::pin(async move {
|
||||
let mut response = http::Response::new(
|
||||
|
||||
@@ -48,8 +48,7 @@ pub fn register_slovak_tokenizers(index: &Index) -> tantivy::Result<()> {
|
||||
let tokenizer_manager = index.tokenizers();
|
||||
|
||||
// TOKENIZER for `prefix_edge`: Edge N-gram (1-4 chars)
|
||||
let edge_tokenizer =
|
||||
TextAnalyzer::builder(NgramTokenizer::new(1, 4, true)?)
|
||||
let edge_tokenizer = TextAnalyzer::builder(NgramTokenizer::new(1, 4, true)?)
|
||||
.filter(RemoveLongFilter::limit(40))
|
||||
.filter(LowerCaser)
|
||||
.filter(AsciiFoldingFilter)
|
||||
@@ -57,8 +56,7 @@ pub fn register_slovak_tokenizers(index: &Index) -> tantivy::Result<()> {
|
||||
tokenizer_manager.register("slovak_prefix_edge", edge_tokenizer);
|
||||
|
||||
// TOKENIZER for `prefix_full`: Simple word tokenizer
|
||||
let full_tokenizer =
|
||||
TextAnalyzer::builder(SimpleTokenizer::default())
|
||||
let full_tokenizer = TextAnalyzer::builder(SimpleTokenizer::default())
|
||||
.filter(RemoveLongFilter::limit(40))
|
||||
.filter(LowerCaser)
|
||||
.filter(AsciiFoldingFilter)
|
||||
@@ -66,8 +64,7 @@ pub fn register_slovak_tokenizers(index: &Index) -> tantivy::Result<()> {
|
||||
tokenizer_manager.register("slovak_prefix_full", full_tokenizer);
|
||||
|
||||
// NGRAM TOKENIZER: For substring matching.
|
||||
let ngram_tokenizer =
|
||||
TextAnalyzer::builder(NgramTokenizer::new(3, 3, false)?)
|
||||
let ngram_tokenizer = TextAnalyzer::builder(NgramTokenizer::new(3, 3, false)?)
|
||||
.filter(RemoveLongFilter::limit(40))
|
||||
.filter(LowerCaser)
|
||||
.filter(AsciiFoldingFilter)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
{
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
mermaid-cli
|
||||
# Rust toolchain
|
||||
rustc
|
||||
cargo
|
||||
@@ -21,6 +22,9 @@
|
||||
clippy
|
||||
cargo-watch
|
||||
rust-analyzer
|
||||
cargo-tarpaulin
|
||||
cargo-flamegraph
|
||||
rust-code-analysis
|
||||
|
||||
# C build tools (for your linker issue)
|
||||
gcc
|
||||
@@ -38,6 +42,7 @@
|
||||
# Protocol Buffers compiler for gRPC
|
||||
protobuf
|
||||
protoc-gen-doc
|
||||
buf
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
|
||||
@@ -4,18 +4,15 @@ use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use tantivy::collector::TopDocs;
|
||||
use tantivy::query::{
|
||||
BooleanQuery, BoostQuery, FuzzyTermQuery, Occur, Query, QueryParser,
|
||||
TermQuery,
|
||||
BooleanQuery, BoostQuery, FuzzyTermQuery, Occur, Query, QueryParser, TermQuery,
|
||||
};
|
||||
use tantivy::schema::{IndexRecordOption, Value};
|
||||
use tantivy::{Index, TantivyDocument, Term};
|
||||
use tonic::{Request, Response, Status};
|
||||
|
||||
use common::proto::komp_ac::search::{
|
||||
search_response::Hit, SearchRequest, SearchResponse,
|
||||
};
|
||||
pub use common::proto::komp_ac::search::searcher_server::SearcherServer;
|
||||
use common::proto::komp_ac::search::searcher_server::Searcher;
|
||||
pub use common::proto::komp_ac::search::searcher_server::SearcherServer;
|
||||
use common::proto::komp_ac::search::{search_response::Hit, SearchRequest, SearchResponse};
|
||||
use common::search::register_slovak_tokenizers;
|
||||
use sqlx::{PgPool, Row};
|
||||
use tracing::info;
|
||||
@@ -86,22 +83,15 @@ impl Searcher for SearcherService {
|
||||
qualified_table
|
||||
);
|
||||
|
||||
let rows = sqlx::query(&sql)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Status::internal(format!(
|
||||
"DB query for default results failed: {}",
|
||||
e
|
||||
))
|
||||
let rows = sqlx::query(&sql).fetch_all(&self.pool).await.map_err(|e| {
|
||||
Status::internal(format!("DB query for default results failed: {}", e))
|
||||
})?;
|
||||
|
||||
let hits: Vec<Hit> = rows
|
||||
.into_iter()
|
||||
.map(|row| {
|
||||
let id: i64 = row.try_get("id").unwrap_or_default();
|
||||
let json_data: serde_json::Value =
|
||||
row.try_get("data").unwrap_or_default();
|
||||
let json_data: serde_json::Value = row.try_get("data").unwrap_or_default();
|
||||
Hit {
|
||||
id,
|
||||
// Score is 0.0 as this is not a relevance-ranked search
|
||||
@@ -111,7 +101,10 @@ impl Searcher for SearcherService {
|
||||
})
|
||||
.collect();
|
||||
|
||||
info!("--- SERVER: Successfully processed empty query. Returning {} default hits. ---", hits.len());
|
||||
info!(
|
||||
"--- SERVER: Successfully processed empty query. Returning {} default hits. ---",
|
||||
hits.len()
|
||||
);
|
||||
return Ok(Response::new(SearchResponse { hits }));
|
||||
}
|
||||
// --- END OF MODIFIED LOGIC ---
|
||||
@@ -131,15 +124,15 @@ impl Searcher for SearcherService {
|
||||
Status::internal(format!("Failed to register Slovak tokenizers: {}", e))
|
||||
})?;
|
||||
|
||||
let reader = index.reader().map_err(|e| {
|
||||
Status::internal(format!("Failed to create index reader: {}", e))
|
||||
})?;
|
||||
let reader = index
|
||||
.reader()
|
||||
.map_err(|e| Status::internal(format!("Failed to create index reader: {}", e)))?;
|
||||
let searcher = reader.searcher();
|
||||
let schema = index.schema();
|
||||
|
||||
let pg_id_field = schema.get_field("pg_id").map_err(|_| {
|
||||
Status::internal("Schema is missing the 'pg_id' field.")
|
||||
})?;
|
||||
let pg_id_field = schema
|
||||
.get_field("pg_id")
|
||||
.map_err(|_| Status::internal("Schema is missing the 'pg_id' field."))?;
|
||||
|
||||
// --- Query Building Logic (no changes here) ---
|
||||
let prefix_edge_field = schema.get_field("prefix_edge").unwrap();
|
||||
@@ -158,25 +151,17 @@ impl Searcher for SearcherService {
|
||||
{
|
||||
let mut must_clauses: Vec<(Occur, Box<dyn Query>)> = Vec::new();
|
||||
for word in &words {
|
||||
let edge_term =
|
||||
Term::from_field_text(prefix_edge_field, word);
|
||||
let full_term =
|
||||
Term::from_field_text(prefix_full_field, word);
|
||||
let edge_term = Term::from_field_text(prefix_edge_field, word);
|
||||
let full_term = Term::from_field_text(prefix_full_field, word);
|
||||
|
||||
let per_word_query = BooleanQuery::new(vec![
|
||||
(
|
||||
Occur::Should,
|
||||
Box::new(TermQuery::new(
|
||||
edge_term,
|
||||
IndexRecordOption::Basic,
|
||||
)),
|
||||
Box::new(TermQuery::new(edge_term, IndexRecordOption::Basic)),
|
||||
),
|
||||
(
|
||||
Occur::Should,
|
||||
Box::new(TermQuery::new(
|
||||
full_term,
|
||||
IndexRecordOption::Basic,
|
||||
)),
|
||||
Box::new(TermQuery::new(full_term, IndexRecordOption::Basic)),
|
||||
),
|
||||
]);
|
||||
must_clauses.push((Occur::Must, Box::new(per_word_query) as Box<dyn Query>));
|
||||
@@ -184,8 +169,7 @@ impl Searcher for SearcherService {
|
||||
|
||||
if !must_clauses.is_empty() {
|
||||
let prefix_query = BooleanQuery::new(must_clauses);
|
||||
let boosted_query =
|
||||
BoostQuery::new(Box::new(prefix_query), 4.0);
|
||||
let boosted_query = BoostQuery::new(Box::new(prefix_query), 4.0);
|
||||
query_layers.push((Occur::Should, Box::new(boosted_query)));
|
||||
}
|
||||
}
|
||||
@@ -195,8 +179,7 @@ impl Searcher for SearcherService {
|
||||
// ===============================
|
||||
{
|
||||
let last_word = words.last().unwrap();
|
||||
let fuzzy_term =
|
||||
Term::from_field_text(prefix_full_field, last_word);
|
||||
let fuzzy_term = Term::from_field_text(prefix_full_field, last_word);
|
||||
let fuzzy_query = FuzzyTermQuery::new(fuzzy_term, 2, true);
|
||||
let boosted_query = BoostQuery::new(Box::new(fuzzy_query), 3.0);
|
||||
query_layers.push((Occur::Should, Box::new(boosted_query)));
|
||||
@@ -206,8 +189,7 @@ impl Searcher for SearcherService {
|
||||
// LAYER 3: PHRASE MATCHING WITH SLOP (MEDIUM PRIORITY, Boost: 2.0)
|
||||
// ===============================
|
||||
if words.len() > 1 {
|
||||
let slop_parser =
|
||||
QueryParser::for_index(&index, vec![prefix_full_field]);
|
||||
let slop_parser = QueryParser::for_index(&index, vec![prefix_full_field]);
|
||||
let slop_query_str = format!("\"{}\"~3", normalized_query);
|
||||
if let Ok(slop_query) = slop_parser.parse_query(&slop_query_str) {
|
||||
let boosted_query = BoostQuery::new(slop_query, 2.0);
|
||||
@@ -219,11 +201,8 @@ impl Searcher for SearcherService {
|
||||
// LAYER 4: NGRAM SUBSTRING MATCHING (LOWEST PRIORITY, Boost: 1.0)
|
||||
// ===============================
|
||||
{
|
||||
let ngram_parser =
|
||||
QueryParser::for_index(&index, vec![text_ngram_field]);
|
||||
if let Ok(ngram_query) =
|
||||
ngram_parser.parse_query(&normalized_query)
|
||||
{
|
||||
let ngram_parser = QueryParser::for_index(&index, vec![text_ngram_field]);
|
||||
if let Ok(ngram_query) = ngram_parser.parse_query(&normalized_query) {
|
||||
let boosted_query = BoostQuery::new(ngram_query, 1.0);
|
||||
query_layers.push((Occur::Should, Box::new(boosted_query)));
|
||||
}
|
||||
@@ -244,9 +223,9 @@ impl Searcher for SearcherService {
|
||||
// Step 1: Extract (score, pg_id) from Tantivy results.
|
||||
let mut scored_ids: Vec<(f32, u64)> = Vec::new();
|
||||
for (score, doc_address) in top_docs {
|
||||
let doc: TantivyDocument = searcher.doc(doc_address).map_err(|e| {
|
||||
Status::internal(format!("Failed to retrieve document: {}", e))
|
||||
})?;
|
||||
let doc: TantivyDocument = searcher
|
||||
.doc(doc_address)
|
||||
.map_err(|e| Status::internal(format!("Failed to retrieve document: {}", e)))?;
|
||||
if let Some(pg_id_value) = doc.get_first(pg_id_field) {
|
||||
if let Some(pg_id) = pg_id_value.as_u64() {
|
||||
scored_ids.push((score, pg_id));
|
||||
@@ -255,8 +234,7 @@ impl Searcher for SearcherService {
|
||||
}
|
||||
|
||||
// Step 2: Fetch all corresponding rows from Postgres in a single query.
|
||||
let pg_ids: Vec<i64> =
|
||||
scored_ids.iter().map(|(_, id)| *id as i64).collect();
|
||||
let pg_ids: Vec<i64> = scored_ids.iter().map(|(_, id)| *id as i64).collect();
|
||||
let qualified_table = format!("gen.\"{}\"", table_name);
|
||||
let query_str = format!(
|
||||
"SELECT id, to_jsonb(t) AS data FROM {} t WHERE id = ANY($1)",
|
||||
@@ -267,9 +245,7 @@ impl Searcher for SearcherService {
|
||||
.bind(&pg_ids)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Status::internal(format!("Database query failed: {}", e))
|
||||
})?;
|
||||
.map_err(|e| Status::internal(format!("Database query failed: {}", e)))?;
|
||||
|
||||
// Step 3: Map the database results by ID for quick lookup.
|
||||
let mut content_map: HashMap<i64, String> = HashMap::new();
|
||||
@@ -284,9 +260,7 @@ impl Searcher for SearcherService {
|
||||
let hits: Vec<Hit> = scored_ids
|
||||
.into_iter()
|
||||
.filter_map(|(score, pg_id)| {
|
||||
content_map
|
||||
.get(&(pg_id as i64))
|
||||
.map(|content_json| Hit {
|
||||
content_map.get(&(pg_id as i64)).map(|content_json| Hit {
|
||||
id: pg_id as i64,
|
||||
score,
|
||||
content_json: content_json.clone(),
|
||||
@@ -294,7 +268,10 @@ impl Searcher for SearcherService {
|
||||
})
|
||||
.collect();
|
||||
|
||||
info!("--- SERVER: Successfully processed search. Returning {} hits. ---", hits.len());
|
||||
info!(
|
||||
"--- SERVER: Successfully processed search. Returning {} hits. ---",
|
||||
hits.len()
|
||||
);
|
||||
|
||||
let response = SearchResponse { hits };
|
||||
Ok(Response::new(response))
|
||||
|
||||
2
server
2
server
Submodule server updated: e497676789...6b0c3e63b4
Reference in New Issue
Block a user