mirror of https://github.com/buster-so/buster.git
hotfix: relationship tweak for LLM
This commit is contained in:
parent
f98e23f15f
commit
86c275e70d
|
@ -21,25 +21,25 @@ models:
|
||||||
description: Minimum number of logins
|
description: Minimum number of logins
|
||||||
description: Customers with logins above threshold and active subscription
|
description: Customers with logins above threshold and active subscription
|
||||||
metrics:
|
metrics:
|
||||||
# Metric using entity columns, requires deduplication for many-to-many
|
# Metric using relationship columns, requires deduplication for many-to-many
|
||||||
- name: popular_product_revenue
|
- name: popular_product_revenue
|
||||||
expr: SUM(revenue) WHERE culture_products.product_count > 5
|
expr: SUM(revenue) WHERE culture_products.product_count > 5
|
||||||
description: Revenue from cultures with popular products
|
description: Revenue from cultures with popular products
|
||||||
entities:
|
relationships:
|
||||||
- name: logins
|
- name: logins
|
||||||
primary_key: cultureid
|
source_col: cultureid
|
||||||
foreign_key: cultureid
|
ref_col: cultureid
|
||||||
type: LEFT # Explicitly set, but LLM could override
|
type: LEFT # Explicitly set, but LLM could override
|
||||||
cardinality: one-to-many
|
cardinality: one-to-many
|
||||||
description: Links to login activity
|
description: Links to login activity
|
||||||
- name: subscriptions
|
- name: subscriptions
|
||||||
primary_key: cultureid
|
source_col: cultureid
|
||||||
foreign_key: cultureid
|
ref_col: cultureid
|
||||||
cardinality: one-to-one
|
cardinality: one-to-one
|
||||||
description: Links to subscription data (no type, LLM decides)
|
description: Links to subscription data (no type, LLM decides)
|
||||||
- name: culture_products
|
- name: culture_products
|
||||||
primary_key: cultureid
|
source_col: cultureid
|
||||||
foreign_key: cultureid
|
ref_col: cultureid
|
||||||
cardinality: many-to-many
|
cardinality: many-to-many
|
||||||
description: Links to product associations (many-to-many via junction)
|
description: Links to product associations (many-to-many via junction)
|
||||||
|
|
||||||
|
@ -52,10 +52,10 @@ models:
|
||||||
measures:
|
measures:
|
||||||
- name: login_count
|
- name: login_count
|
||||||
description: Number of logins
|
description: Number of logins
|
||||||
entities:
|
relationships:
|
||||||
- name: culture
|
- name: culture
|
||||||
primary_key: cultureid
|
source_col: cultureid
|
||||||
foreign_key: cultureid
|
ref_col: cultureid
|
||||||
cardinality: many-to-one
|
cardinality: many-to-one
|
||||||
|
|
||||||
# Model for subscriptions
|
# Model for subscriptions
|
||||||
|
@ -67,10 +67,10 @@ models:
|
||||||
- name: subscription_status
|
- name: subscription_status
|
||||||
description: Current subscription status
|
description: Current subscription status
|
||||||
options: ["active", "inactive"]
|
options: ["active", "inactive"]
|
||||||
entities:
|
relationships:
|
||||||
- name: culture
|
- name: culture
|
||||||
primary_key: cultureid
|
source_col: cultureid
|
||||||
foreign_key: cultureid
|
ref_col: cultureid
|
||||||
cardinality: one-to-one
|
cardinality: one-to-one
|
||||||
|
|
||||||
# Junction model for many-to-many between culture and products
|
# Junction model for many-to-many between culture and products
|
||||||
|
@ -84,12 +84,12 @@ models:
|
||||||
measures:
|
measures:
|
||||||
- name: product_count
|
- name: product_count
|
||||||
description: Number of products in this association
|
description: Number of products in this association
|
||||||
entities:
|
relationships:
|
||||||
- name: culture
|
- name: culture
|
||||||
primary_key: cultureid
|
source_col: cultureid
|
||||||
foreign_key: cultureid
|
ref_col: cultureid
|
||||||
cardinality: many-to-many
|
cardinality: many-to-many
|
||||||
- name: products
|
- name: products
|
||||||
primary_key: productid
|
source_col: productid
|
||||||
foreign_key: productid
|
ref_col: productid
|
||||||
cardinality: many-to-many
|
cardinality: many-to-many
|
|
@ -13,7 +13,7 @@
|
||||||
type: string # Optional, inferred if omitted
|
type: string # Optional, inferred if omitted
|
||||||
metrics:
|
metrics:
|
||||||
- name: string # Required
|
- name: string # Required
|
||||||
expr: string # Required, can use model.column from entities
|
expr: string # Required, can use model.column from relationships
|
||||||
description: string # Optional
|
description: string # Optional
|
||||||
args: # Optional, required only if expr contains arguments, default: null
|
args: # Optional, required only if expr contains arguments, default: null
|
||||||
- name: string # Required
|
- name: string # Required
|
||||||
|
@ -21,16 +21,16 @@
|
||||||
description: string # Optional
|
description: string # Optional
|
||||||
filters:
|
filters:
|
||||||
- name: string # Required
|
- name: string # Required
|
||||||
expr: string # Required, can use model.column from entities
|
expr: string # Required, can use model.column from relationships
|
||||||
description: string # Optional
|
description: string # Optional
|
||||||
args: # Optional, required only if expr contains arguments, default: null
|
args: # Optional, required only if expr contains arguments, default: null
|
||||||
- name: string # Required
|
- name: string # Required
|
||||||
type: string # Required
|
type: string # Required
|
||||||
description: string # Optional
|
description: string # Optional
|
||||||
entities:
|
relationships:
|
||||||
- name: string # Required
|
- name: string # Required
|
||||||
primary_key: string # Required
|
source_col: string # Required
|
||||||
foreign_key: string # Required
|
ref_col: string # Required
|
||||||
type: string # Optional, e.g., "LEFT", "INNER"; LLM decides if omitted
|
type: string # Optional, e.g., "LEFT", "INNER"; LLM decides if omitted
|
||||||
cardinality: string # Optional, e.g., "one-to-many", "many-to-many"
|
cardinality: string # Optional, e.g., "one-to-many", "many-to-many"
|
||||||
description: string # Optional
|
description: string # Optional
|
||||||
|
|
|
@ -18,7 +18,7 @@ pub struct Model {
|
||||||
pub metrics: Vec<Metric>,
|
pub metrics: Vec<Metric>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub filters: Vec<Filter>,
|
pub filters: Vec<Filter>,
|
||||||
#[serde(rename = "entities", default)] // Added default
|
#[serde(default)] // Added default
|
||||||
pub relationships: Vec<Relationship>,
|
pub relationships: Vec<Relationship>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -439,23 +439,9 @@ fn to_deploy_request(model: &Model, sql_content: String) -> DeployDatasetsReques
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert entity relationships
|
// Note: Relationships are now preserved in the yml_file field rather than being converted to entity_relationships.
|
||||||
let entity_relationships: Option<Vec<DeployDatasetsEntityRelationshipsRequest>> =
|
// This allows the full semantic model structure (including relationships with all their metadata like
|
||||||
if !model.relationships.is_empty() {
|
// cardinality, descriptions, etc.) to be preserved and processed by the backend.
|
||||||
Some(
|
|
||||||
model
|
|
||||||
.relationships
|
|
||||||
.iter()
|
|
||||||
.map(|rel| DeployDatasetsEntityRelationshipsRequest {
|
|
||||||
name: rel.name.clone(),
|
|
||||||
expr: rel.source_col.clone(), // Assuming foreign_key is the expression for the relationship for now
|
|
||||||
type_: rel.type_.clone().unwrap_or_else(|| "LEFT".to_string()), // Default to LEFT if not specified
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let data_source_name = model.data_source_name.clone()
|
let data_source_name = model.data_source_name.clone()
|
||||||
.expect("data_source_name missing after validation, should be resolved by resolve_model_configurations");
|
.expect("data_source_name missing after validation, should be resolved by resolve_model_configurations");
|
||||||
|
@ -464,7 +450,7 @@ fn to_deploy_request(model: &Model, sql_content: String) -> DeployDatasetsReques
|
||||||
);
|
);
|
||||||
|
|
||||||
// Serialize the input Model to YAML to be stored in the yml_file field of the request.
|
// Serialize the input Model to YAML to be stored in the yml_file field of the request.
|
||||||
// This captures the full semantic definition as sent.
|
// This captures the full semantic definition as sent, including relationships with all their metadata.
|
||||||
let yml_content_for_request = serde_yaml::to_string(&model).unwrap_or_else(|e| {
|
let yml_content_for_request = serde_yaml::to_string(&model).unwrap_or_else(|e| {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Error serializing model {} to YAML for deploy request: {}. Using empty string.",
|
"Error serializing model {} to YAML for deploy request: {}. Using empty string.",
|
||||||
|
@ -484,7 +470,7 @@ fn to_deploy_request(model: &Model, sql_content: String) -> DeployDatasetsReques
|
||||||
database: model.database.clone(),
|
database: model.database.clone(),
|
||||||
description: model.description.clone().unwrap_or_default(),
|
description: model.description.clone().unwrap_or_default(),
|
||||||
sql_definition: Some(sql_content),
|
sql_definition: Some(sql_content),
|
||||||
entity_relationships,
|
entity_relationships: None, // Relationships are now preserved in yml_file instead
|
||||||
columns,
|
columns,
|
||||||
yml_file: Some(yml_content_for_request), // Store the YAML of the model being deployed
|
yml_file: Some(yml_content_for_request), // Store the YAML of the model being deployed
|
||||||
}
|
}
|
||||||
|
@ -1191,7 +1177,7 @@ mod tests {
|
||||||
name: test_model
|
name: test_model
|
||||||
description: "Test model"
|
description: "Test model"
|
||||||
|
|
||||||
dimension:
|
dimensions:
|
||||||
- name: dim1
|
- name: dim1
|
||||||
description: "First dimension"
|
description: "First dimension"
|
||||||
type: "string"
|
type: "string"
|
||||||
|
@ -1362,19 +1348,15 @@ models:
|
||||||
};
|
};
|
||||||
|
|
||||||
let sql_content = "SELECT * FROM test_schema.test_model";
|
let sql_content = "SELECT * FROM test_schema.test_model";
|
||||||
let request = to_deploy_request(&model, sql_content.to_string()); // Call the restored function
|
let request = to_deploy_request(&model, sql_content.to_string());
|
||||||
|
|
||||||
assert_eq!(request.name, "test_model");
|
assert_eq!(request.name, "test_model");
|
||||||
assert_eq!(request.columns.len(), 2); // 1 dim, 1 measure
|
assert_eq!(request.columns.len(), 2); // 1 dim, 1 measure
|
||||||
assert_eq!(request.columns[0].name, "dim1");
|
assert_eq!(request.columns[0].name, "dim1");
|
||||||
assert_eq!(request.columns[0].searchable, true);
|
assert_eq!(request.columns[0].searchable, true);
|
||||||
assert_eq!(request.columns[1].name, "measure1");
|
assert_eq!(request.columns[1].name, "measure1");
|
||||||
assert!(request.entity_relationships.is_some());
|
// The model has empty relationships, so entity_relationships should be None
|
||||||
assert_eq!(request.entity_relationships.as_ref().unwrap().len(), 1);
|
assert!(request.entity_relationships.is_none());
|
||||||
assert_eq!(
|
|
||||||
request.entity_relationships.as_ref().unwrap()[0].name,
|
|
||||||
"related_model"
|
|
||||||
);
|
|
||||||
let expected_yml_content = serde_yaml::to_string(&model)?;
|
let expected_yml_content = serde_yaml::to_string(&model)?;
|
||||||
assert_eq!(request.yml_file, Some(expected_yml_content));
|
assert_eq!(request.yml_file, Some(expected_yml_content));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue