Files
GeneralUtils/src/dbUtil.jl
T
2026-07-24 13:03:40 +07:00

312 lines
10 KiB
Julia
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
module dbUtil
export dictToPostgresKeyValueString, generateInsertSQL, generateUpdateSQL
using JSON, DataStructures, Distributions, Random, Dates, UUIDs, DataFrames,
SHA
using ..util
#[PENDING] update code to use JSON
# ---------------------------------------------- 100 --------------------------------------------- #
""" Execute SQL against a PostgreSQL database using LibPQ connection string.
# Arguments
- `pg_conn_str::AbstractString`: PostgreSQL connection string in format "host=... port=... dbname=... user=... password=..."
- `sql::AbstractString`: SQL query to execute
# Returns
- `LibPQ.Result` on success, `nothing` on failure
# Example
```julia
pg_conn_str = "host=localhost port=5432 dbname=mydb user=myuser password=mypass"
sql = "SELECT * FROM wine;"
result = execute_postgres_sql(pg_conn_str, sql)
```
"""
function execute_postgres_sql(pg_conn_str::T, sql::T) where {T<:AbstractString}
db_connection = LibPQ.Connection(pg_conn_str)
result = nothing
try
result = LibPQ.execute(db_connection, sql)
catch e
@error e
LibPQ.close(db_connection)
end
LibPQ.close(db_connection)
return result
end
"""
get_embedding_nats(nats_server_url::String, texts::Vector{String})
Fetch embeddings for a list of texts from a NATS-based embedding service.
# Arguments
- `nats_server_url::String`: NATS server connection URL
- `texts::Vector{String}`: List of text strings to generate embeddings for
# Returns
- `Vector` of embeddings, where each embedding is a vector of floats (Vector{Float32} or Vector{Float64})
# Example
```julia
nats_server_url = "nats://localhost:4222"
texts = ["Hello world", "Another text"]
embeddings = get_embedding_nats(nats_server_url, texts)
```
"""
function get_embedding_nats(nats_server_url::String, texts::Vector{String})
nats_conn = NATS.connect(nats_server_url)
return get_embedding_nats(nats_conn, texts)
end
function get_embedding_nats(nats_conn::NATS.Connection, texts::Vector{String})
println("Generating embeddings for $(length(texts)) texts...")
documents_dict = Dict("documents" => texts)
payloads = [("documents", documents_dict, "dictionary")]
_, msg_envelope_json_str = msghandler.smartpack(
config["externalservice"]["servicesloadbalancer"]["nats"],
payloads;
msg_purpose="embedding",
broker_url=config["nats_server_info"]["url"],
fileserver_url=config["externalservice"]["fileserver"]["url"])
reply = NATS.request(nats_conn,
config["externalservice"]["servicesloadbalancer"]["nats"],
msg_envelope_json_str, timeout=300)
incoming_env_json_str = String(reply.payload)
incoming_env = msghandler.smartunpack(incoming_env_json_str)
embedding_response = incoming_env["payloads"][1][2]
result = []
for i in embedding_response["data"]
embedding_vector = i["embedding"]
push!(result, embedding_vector)
end
return result
end
"""
dictToPostgresKeyValueString - Convert dictionary to PostgreSQL key-value string format
This function takes a dictionary and converts it into a PostgreSQL-compatible key-value string
format suitable for storage in a TEXT field. The output format uses curly braces with comma-separated
key-value pairs, where string values are quoted.
# Function Workflow:
1. Iterates through dictionary key-value pairs
2. Handles nested dictionaries by recursively converting them
3. Wraps string values in double quotes
4. Formats numeric and other values without quotes
5. Returns a PostgreSQL-compatible key-value string enclosed in curly braces
# Arguments:
- `dict::Dict` - Dictionary containing key-value pairs to convert
# Return:
- A String in PostgreSQL key-value format: "{key1: value1, key2: \"value2\", ...}"
# Example
```jldoctest
julia> data = Dict{String, Any}(
"name" => "John",
"age" => 30,
"city" => "New York"
);
julia> dictToPostgresKeyValueString(data)
"{\"name\": \"John\", \"age\": 30, \"city\": \"New York\"}"
```
"""
function dictToPostgresKeyValueString(dict)
parts = []
for (k, v) in dict
if isa(v, Dict)
push!(parts, "\"$k\": " * dict_to_string(v))
elseif isa(v, AbstractString)
push!(parts, "\"$k\": \"$v\"")
else
push!(parts, "\"$k\": $v")
end
end
return "{" * join(parts, ", ") * "}"
end
""" generateInsertSQL - Generate SQL INSERT statement from dictionary data
This function constructs a SQL INSERT statement by extracting values for specified columns
from a dictionary and formatting them into a valid PostgreSQL INSERT query.
# Function Workflow:
1. Iterates through the dictionary key-value pairs
2. Filters keys to only include those present in `columnToInsert`
3. Collects column names and their corresponding values
4. Constructs the final SQL INSERT statement
# Arguments:
- `table_name::String` - Name of the database table to insert into
- `columnToInsert::Vector{Symbol}` - List of column names to include in the INSERT statement
- `data::Dict{Symbol, Any}` - Dictionary containing column-value pairs for the insert
# Return:
- A String containing the SQL INSERT statement
# Example
```jldoctest
julia> using UUIDs
# Insert a single record with specific columns
table_name = "wine"
columnToInsert = [:acidity, :tannin, :country, :region, :winery]
data = Dict{Symbol, Any}(
:grape => "Cabernet Sauvignon",
:acidity => "medium", # using descriptive scale (low/medium/full)
:tannin => "medium-plus", # common wine descriptor
:country => "France",
:description => "A rich and structured red wine with notes of blackcurrant, cedar, and subtle oak.",
:region => "Bordeaux",
:winery => "Château Margaux",
:intensity => "medium", # intensity is usually low/medium/full
:sweetness => "dry", # dry/medium-dry/medium/medium-sweet/sweet
:tasting_notes => "Blackberry, graphite, tobacco, vanilla, and subtle earth.",
:wine_name => "Château Margaux Grand Cru",
:wine_id => "8f3c7a2e-1b4d-4a9f-9c2e-4a8b3d6e5f7a", # UUID-like (valid hex)
:wine_type => "Red",
:other_attributes => Dict{String, Any}(
"vintage" => 2018,
"alcohol_percent" => 13.5,
"ph" => 3.6,
" aging_years" => 24, # years in barrel
" producer_code" => "CM-GRAND"
),
:fizziness => "still",
:serving_temperature => "1618°C",
:additional_search_term => ["Cabernet", "Bordeaux red", "premium wine", "CabSav"]
)
julia> generateInsertSQL(table_name, columnToInsert, data)
"INSERT INTO wine (acidity, tannin, country, region, winery) VALUES ('medium', 'medium-plus', 'France', 'Bordeaux', 'Château Margaux');"
```
"""
function generateInsertSQL(table_name::String, columnToInsert::Vector{Symbol}, data::Dict{Symbol, Any})
columns = String[]
values = String[]
for (key, value) in data
if key columnToInsert
push!(columns, string(key))
value_str = isa(value, AbstractString) ? "'$value'" : "$value"
push!(values, value_str)
end
end
columns_str = join(columns, ", ")
values_str = join(values, ", ")
return "INSERT INTO $table_name ($columns_str) VALUES ($values_str);"
end
function generateInsertSQL(table_name::String, data::AbstractDict{String, Any})
columns = String[]
values = String[]
for (key, value) in data
push!(columns, string(key))
value_str = isa(value, AbstractString) ? "'$value'" : "$value"
push!(values, value_str)
end
columns_str = join(columns, ", ")
values_str = join(values, ", ")
return "INSERT INTO $table_name ($columns_str) VALUES ($values_str);"
end
# ---------------------------------------------- 100 --------------------------------------------- #
""" generateUpdateSQL - Generate SQL UPDATE statement from dictionary data
This function constructs a SQL UPDATE statement by updating multiple columns
based on a primary key condition.
# Arguments:
- `table_name::String` - Name of the database table to update
- `pk_column::Symbol` - The primary key column name
- `pk_value` - The primary key value (used in WHERE clause)
- `data::Dict{Symbol, Any}` - Dictionary containing column-value pairs to update
# Return:
- A String containing the SQL UPDATE statement
# Example
```jldoctest
julia> using UUIDs
# Update multiple columns using a dictionary
table_name = "wine"
pk_column = :wine_id
pk_value = "8f3c7a2e-1b4d-4a9f-9c2e-4a8b3d6e5f7a"
data = Dict{Symbol, Any}(
:acidity => "full",
:tannin => "medium",
:country => "Italy"
)
julia> generateUpdateSQL(table_name, pk_column, pk_value, data)
"UPDATE wine SET acidity = 'full', tannin = 'medium', country = 'Italy' WHERE wine_id = '8f3c7a2e-1b4d-4a9f-9c2e-4a8b3d6e5f7a';"
```
"""
function generateUpdateSQL(table_name::String, pk_column::String, pk_value,
data::AbstractDict{String, Any})
# Build SET clause
set_parts = String[]
for (key, value) in data
if key [pk_column]
value_str = isa(value, AbstractString) ? "'$value'" : "$value"
push!(set_parts, "$(string(key)) = $value_str")
end
end
set_clause = join(set_parts, ", ")
# Handle primary key value
pk_val_str = isa(pk_value, AbstractString) ? "'$pk_value'" : "$pk_value"
return "UPDATE $table_name SET $set_clause WHERE $pk_column = $pk_val_str;"
end
function generateUpdateSQL(table_name::String, pk_dict::AbstractDict{String, Any},
data::AbstractDict{String, Any})
# Build SET clause
set_parts = String[]
for (key, value) in data
if key keys(pk_dict)
value_str = isa(value, AbstractString) ? "'$value'" : "$value"
push!(set_parts, "$(string(key)) = $value_str")
end
end
set_clause = join(set_parts, ", ")
# Build WHERE clause for composite keys
where_parts = String[]
for (col, val) in pk_dict
val_str = isa(val, AbstractString) ? "'$val'" : "$val"
push!(where_parts, "$(string(col)) = $val_str")
end
where_clause = join(where_parts, " AND ")
return "UPDATE $table_name SET $set_clause WHERE $where_clause;"
end
end # module