update
This commit is contained in:
+122
@@ -252,6 +252,45 @@ julia> g, id_to_table, table_to_id = GeneralUtils.harvest_db_undirected_schema_g
|
|||||||
julia> vertices(g)
|
julia> vertices(g)
|
||||||
10
|
10
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Integration Guide: Finding Related Tables for User Questions
|
||||||
|
This function is designed to work together with `extract_column_metadata` and `resolve_semantic_cluster` to answer user questions by identifying related tables:
|
||||||
|
|
||||||
|
```julia
|
||||||
|
# Step 1: Extract column metadata and generate embeddings for semantic search
|
||||||
|
pg_conn_str = "host=localhost port=5432 dbname=winedb user=admin password=secret"
|
||||||
|
metadata_df = GeneralUtils.extract_column_metadata(pg_conn_str)
|
||||||
|
embedding_ready = GeneralUtils.generate_embedding_payloads(metadata_df)
|
||||||
|
|
||||||
|
# Use only text content for embedding
|
||||||
|
embedding_ready_2 = [i["text_content"] for i in embedding_ready]
|
||||||
|
table_embedding = get_embedding(embedding_ready_2)
|
||||||
|
|
||||||
|
# Get embedding for user question
|
||||||
|
_user_question_embedding = get_embedding([question])
|
||||||
|
user_question_embedding = Float64.(_user_question_embedding["data"][1]["embedding"])
|
||||||
|
|
||||||
|
# Calculate similarity between question and all columns
|
||||||
|
user_question_similarity = []
|
||||||
|
for i in table_embedding["data"]
|
||||||
|
i_data = i["embedding"]
|
||||||
|
i_float = Float64.(i_data)
|
||||||
|
r = 1 - Distances.cosine_dist(i_float, user_question_embedding)
|
||||||
|
push!(user_question_similarity, r)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Step 2: Find top related tables
|
||||||
|
new_df = hcat(metadata_df, DataFrame(user_question_similarity = user_question_similarity))
|
||||||
|
sorted_df = sort(new_df, :user_question_similarity, rev=true)
|
||||||
|
_top_20_tables = unique(sorted_df[1:20, :table_name])
|
||||||
|
top_20_tables = [i for i in _top_20_tables]
|
||||||
|
|
||||||
|
# Step 3: Build schema graph and resolve table relationships
|
||||||
|
g, id_to_table, table_to_id = GeneralUtils.harvest_db_undirected_schema_graph(pg_conn_str)
|
||||||
|
table_relationship = GeneralUtils.resolve_semantic_cluster(top_20_tables, g, table_to_id, id_to_table)
|
||||||
|
|
||||||
|
# table_relationship now contains tables in the order they should be joined
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function harvest_db_undirected_schema_graph(pg_conn_str)
|
function harvest_db_undirected_schema_graph(pg_conn_str)
|
||||||
conn = LibPQ.Connection(pg_conn_str)
|
conn = LibPQ.Connection(pg_conn_str)
|
||||||
@@ -371,6 +410,21 @@ products id integer Product ID true false
|
|||||||
products price numeric Product price false false col_products_price
|
products price numeric Product price false false col_products_price
|
||||||
products user_id integer Reference to user false true col_products_user_id
|
products user_id integer Reference to user false true col_products_user_id
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Integration Guide
|
||||||
|
This function is the first step in the semantic table discovery pipeline. Use it with `generate_embedding_payloads` to prepare data for vector similarity search:
|
||||||
|
|
||||||
|
```julia
|
||||||
|
# Step 1: Extract metadata
|
||||||
|
metadata_df = GeneralUtils.extract_column_metadata(pg_conn_str)
|
||||||
|
|
||||||
|
# Step 2: Generate embedding payloads for semantic search
|
||||||
|
embedding_ready = GeneralUtils.generate_embedding_payloads(metadata_df)
|
||||||
|
|
||||||
|
# Use text_content field for embedding
|
||||||
|
text_content_list = [i["text_content"] for i in embedding_ready]
|
||||||
|
embeddings = get_embedding(text_content_list)
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function extract_column_metadata(pg_conn_str::String)::DataFrame
|
function extract_column_metadata(pg_conn_str::String)::DataFrame
|
||||||
conn = LibPQ.Connection(pg_conn_str)
|
conn = LibPQ.Connection(pg_conn_str)
|
||||||
@@ -457,6 +511,30 @@ julia> payloads = GeneralUtils.generate_embedding_payloads(df)
|
|||||||
Dict("id" => "col_users_name", "text_content" => "Table: users | Column: name | Type: text | Description: User name", "metadata" => Dict("table" => "users", "column" => "name", "type" => "text"))
|
Dict("id" => "col_users_name", "text_content" => "Table: users | Column: name | Type: text | Description: User name", "metadata" => Dict("table" => "users", "column" => "name", "type" => "text"))
|
||||||
Dict("id" => "col_users_email", "text_content" => "Table: users | Column: email | Type: text | Description: User email", "metadata" => Dict("table" => "users", "column" => "email", "type" => "text"))
|
Dict("id" => "col_users_email", "text_content" => "Table: users | Column: email | Type: text | Description: User email", "metadata" => Dict("table" => "users", "column" => "email", "type" => "text"))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Integration Guide
|
||||||
|
This function transforms column metadata into a format suitable for semantic search.
|
||||||
|
Use `text_content` field to generate embeddings, then compare against user question embeddings:
|
||||||
|
|
||||||
|
```julia
|
||||||
|
# 1. Extract metadata
|
||||||
|
metadata_df = GeneralUtils.extract_column_metadata(pg_conn_str)
|
||||||
|
|
||||||
|
# 2. Generate payloads
|
||||||
|
embedding_ready = GeneralUtils.generate_embedding_payloads(metadata_df)
|
||||||
|
|
||||||
|
# 3. Extract text content for embedding (only this field is used for similarity)
|
||||||
|
text_content_list = [i["text_content"] for i in embedding_ready]
|
||||||
|
|
||||||
|
# 4. Generate embeddings for all columns
|
||||||
|
column_embeddings = get_embedding(text_content_list)
|
||||||
|
|
||||||
|
# 5. Generate embedding for user question
|
||||||
|
question_embedding = get_embedding([user_question])
|
||||||
|
|
||||||
|
# 6. Calculate cosine similarity to find most relevant columns/tables
|
||||||
|
# (See harvest_db_undirected_schema_graph for full integration example)
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function generate_embedding_payloads(df::DataFrame)::Vector{Dict}
|
function generate_embedding_payloads(df::DataFrame)::Vector{Dict}
|
||||||
payloads = Dict[]
|
payloads = Dict[]
|
||||||
@@ -537,6 +615,50 @@ julia> vector_hits = ["users", "products"]
|
|||||||
julia> GeneralUtils.resolve_semantic_cluster(vector_hits, g, table_to_id, id_to_table)
|
julia> GeneralUtils.resolve_semantic_cluster(vector_hits, g, table_to_id, id_to_table)
|
||||||
["users", "orders", "products"]
|
["users", "orders", "products"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Integration Guide: Understanding Table Relationships
|
||||||
|
This function determines how tables from vector search results are connected in the database schema.
|
||||||
|
The returned list represents the optimal join order for constructing SQL queries.
|
||||||
|
|
||||||
|
**How tables are linked:**
|
||||||
|
- **Explicit Foreign Keys**: Direct relationships defined by `FOREIGN KEY` constraints in PostgreSQL
|
||||||
|
- **Implicit Relationships**: Tables sharing similar column naming patterns (e.g., `user_id` in both `users` and `orders` tables)
|
||||||
|
|
||||||
|
**Example output interpretation:**
|
||||||
|
```julia
|
||||||
|
# Input: Top 20 tables from semantic search
|
||||||
|
top_tables = ["users", "products", "payments"]
|
||||||
|
|
||||||
|
# Output: Tables in join order
|
||||||
|
table_relationship = ["users", "orders", "payments"]
|
||||||
|
# Interpretation:
|
||||||
|
# 1. Start with 'users' table
|
||||||
|
# 2. Join 'orders' via foreign key (likely users.id -> orders.user_id)
|
||||||
|
# 3. Join 'payments' via foreign key (likely orders.id -> payments.order_id)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Full workflow example:**
|
||||||
|
```julia
|
||||||
|
# Step 1: Get column embeddings and find semantically related tables
|
||||||
|
metadata_df = GeneralUtils.extract_column_metadata(pg_conn_str)
|
||||||
|
embedding_ready = GeneralUtils.generate_embedding_payloads(metadata_df)
|
||||||
|
text_content = [i["text_content"] for i in embedding_ready]
|
||||||
|
table_embedding = get_embedding(text_content)
|
||||||
|
|
||||||
|
# Step 2: Calculate similarity with user question
|
||||||
|
question_embedding = Float64.(get_embedding([question])["data"][1]["embedding"])
|
||||||
|
similarities = [1 - Distances.cosine_dist(Float64.(i["embedding"]), question_embedding)
|
||||||
|
for i in table_embedding["data"]]
|
||||||
|
|
||||||
|
# Step 3: Extract top related tables
|
||||||
|
top_tables = unique(metadata_df[sortperm(similarities)[1:20], :table_name])
|
||||||
|
|
||||||
|
# Step 4: Build schema graph and resolve relationships
|
||||||
|
g, id_to_table, table_to_id = GeneralUtils.harvest_db_undirected_schema_graph(pg_conn_str)
|
||||||
|
related_tables = GeneralUtils.resolve_semantic_cluster(top_tables, g, table_to_id, id_to_table)
|
||||||
|
|
||||||
|
# related_tables now contains tables in optimal join order for SQL query construction
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function resolve_semantic_cluster(
|
function resolve_semantic_cluster(
|
||||||
vector_hits::Vector{String},
|
vector_hits::Vector{String},
|
||||||
|
|||||||
Reference in New Issue
Block a user