use tensorlogic_adapters::{DomainInfo, MergeStrategy, PredicateInfo, SchemaMerger, SymbolTable};
fn main() {
println!("=== Example 26: Schema Merging Strategies ===\n");
println!("✨ Scenario 1: Merge Without Conflicts");
println!("─────────────────────────────────────");
scenario_no_conflicts();
println!();
println!("🥇 Scenario 2: KeepFirst Strategy");
println!("──────────────────────────────────");
scenario_keep_first();
println!();
println!("🥈 Scenario 3: KeepSecond Strategy");
println!("───────────────────────────────────");
scenario_keep_second();
println!();
println!("❌ Scenario 4: FailOnConflict Strategy");
println!("───────────────────────────────────────");
scenario_fail_on_conflict();
println!();
println!("🔗 Scenario 5: Union Strategy");
println!("─────────────────────────────");
scenario_union();
println!();
println!("⚡ Scenario 6: Intersection Strategy");
println!("────────────────────────────────────");
scenario_intersection();
println!();
println!("🌟 Scenario 7: Complex Merge Scenario");
println!("──────────────────────────────────────");
scenario_complex_merge();
println!();
println!("✅ All schema merging scenarios completed!");
}
fn scenario_no_conflicts() {
let mut base = SymbolTable::new();
base.add_domain(DomainInfo::new("Person", 100))
.expect("unwrap");
base.add_predicate(PredicateInfo::new(
"knows",
vec!["Person".to_string(), "Person".to_string()],
))
.expect("unwrap");
let mut incoming = SymbolTable::new();
incoming
.add_domain(DomainInfo::new("Person", 100))
.expect("unwrap"); incoming
.add_domain(DomainInfo::new("Organization", 50))
.expect("unwrap");
incoming
.add_predicate(PredicateInfo::new(
"works_at",
vec!["Person".to_string(), "Organization".to_string()],
))
.expect("unwrap");
println!("Base schema:");
println!(" - Domains: Person");
println!(" - Predicates: knows");
println!("\nIncoming schema:");
println!(" - Domains: Organization");
println!(" - Predicates: works_at");
println!();
let merger = SchemaMerger::new(MergeStrategy::KeepFirst);
let result = merger.merge(&base, &incoming).expect("unwrap");
println!("Merged schema:");
println!(" - Total domains: {}", result.merged.domains.len());
println!(" - Total predicates: {}", result.merged.predicates.len());
println!();
println!("Merge report:");
println!(" - Base domains: {}", result.report.base_domains.len());
println!(
" - Incoming domains: {}",
result.report.incoming_domains.len()
);
println!(" - Conflicts: {}", result.report.conflict_count());
println!("✓ No conflicts detected (disjoint schemas)");
}
fn scenario_keep_first() {
let mut base = SymbolTable::new();
base.add_domain(DomainInfo::new("Person", 100).with_description("Base version"))
.expect("unwrap");
base.add_domain(DomainInfo::new("Organization", 30))
.expect("unwrap");
let mut incoming = SymbolTable::new();
incoming
.add_domain(DomainInfo::new("Person", 200).with_description("Incoming version"))
.expect("unwrap");
incoming
.add_domain(DomainInfo::new("Event", 50))
.expect("unwrap");
println!("Base schema:");
println!(" - Person (cardinality: 100, description: 'Base version')");
println!(" - Organization (cardinality: 30)");
println!("\nIncoming schema:");
println!(" - Person (cardinality: 200, description: 'Incoming version')");
println!(" - Event (cardinality: 50)");
println!();
let merger = SchemaMerger::new(MergeStrategy::KeepFirst);
let result = merger.merge(&base, &incoming).expect("unwrap");
println!("Merge strategy: KeepFirst");
println!("Result:");
let person = result.merged.get_domain("Person").expect("unwrap");
println!(" - Person kept from base:");
println!(" • Cardinality: {} (from base)", person.cardinality);
println!(
" • Description: '{}' (from base)",
person.description.as_deref().unwrap_or("none")
);
println!(" - Total domains: {}", result.merged.domains.len());
println!(
" - Conflicts resolved: {}",
result.report.conflicting_domains.len()
);
println!("\n✓ KeepFirst strategy preserved base schema on conflicts");
}
fn scenario_keep_second() {
let mut base = SymbolTable::new();
base.add_domain(DomainInfo::new("Person", 100))
.expect("unwrap");
base.add_predicate(
PredicateInfo::new("knows", vec!["Person".to_string(), "Person".to_string()])
.with_description("Base version"),
)
.expect("unwrap");
let mut incoming = SymbolTable::new();
incoming
.add_domain(DomainInfo::new("Person", 100))
.expect("unwrap");
incoming
.add_predicate(
PredicateInfo::new("knows", vec!["Person".to_string(), "Person".to_string()])
.with_description("Incoming version with improvements"),
)
.expect("unwrap");
println!("Base schema:");
println!(" - Predicate 'knows' (description: 'Base version')");
println!("\nIncoming schema:");
println!(" - Predicate 'knows' (description: 'Incoming version with improvements')");
println!();
let merger = SchemaMerger::new(MergeStrategy::KeepSecond);
let result = merger.merge(&base, &incoming).expect("unwrap");
println!("Merge strategy: KeepSecond");
println!("Result:");
let knows = result.merged.get_predicate("knows").expect("unwrap");
println!(" - Predicate 'knows' kept from incoming:");
println!(
" • Description: '{}'",
knows.description.as_deref().unwrap_or("none")
);
println!(
" - Conflicts resolved: {}",
result.report.conflicting_predicates.len()
);
println!("\n✓ KeepSecond strategy preferred incoming schema on conflicts");
}
fn scenario_fail_on_conflict() {
let mut base = SymbolTable::new();
base.add_domain(DomainInfo::new("Person", 100))
.expect("unwrap");
let mut incoming = SymbolTable::new();
incoming
.add_domain(DomainInfo::new("Person", 200))
.expect("unwrap");
println!("Base schema:");
println!(" - Person (cardinality: 100)");
println!("\nIncoming schema:");
println!(" - Person (cardinality: 200)");
println!();
let merger = SchemaMerger::new(MergeStrategy::FailOnConflict);
let result = merger.merge(&base, &incoming);
println!("Merge strategy: FailOnConflict");
match result {
Ok(_) => println!("❌ Unexpected: merge should have failed"),
Err(e) => {
println!("✓ Merge failed as expected:");
println!(" Error: {}", e);
}
}
}
fn scenario_union() {
let mut base = SymbolTable::new();
base.add_domain(DomainInfo::new("Person", 100))
.expect("unwrap");
base.add_domain(DomainInfo::new("Organization", 50))
.expect("unwrap");
base.add_predicate(PredicateInfo::new(
"knows",
vec!["Person".to_string(), "Person".to_string()],
))
.expect("unwrap");
let mut incoming = SymbolTable::new();
incoming
.add_domain(DomainInfo::new("Person", 100))
.expect("unwrap"); incoming
.add_domain(DomainInfo::new("Event", 75))
.expect("unwrap");
incoming
.add_predicate(PredicateInfo::new(
"attends",
vec!["Person".to_string(), "Event".to_string()],
))
.expect("unwrap");
println!("Base schema:");
println!(" - Domains: Person (100), Organization (50)");
println!(" - Predicates: knows");
println!("\nIncoming schema:");
println!(" - Domains: Person (100), Event (75)");
println!(" - Predicates: attends");
println!();
let merger = SchemaMerger::new(MergeStrategy::Union);
let result = merger.merge(&base, &incoming).expect("unwrap");
println!("Merge strategy: Union");
println!("Result:");
println!(
" - Total domains: {} (Person, Organization, Event)",
result.merged.domains.len()
);
println!(
" - Total predicates: {} (knows, attends)",
result.merged.predicates.len()
);
println!(" - Conflicts: {}", result.report.conflict_count());
println!("\n✓ Union strategy combined all compatible items");
}
fn scenario_intersection() {
let mut base = SymbolTable::new();
base.add_domain(DomainInfo::new("Person", 100))
.expect("unwrap");
base.add_domain(DomainInfo::new("Organization", 50))
.expect("unwrap");
base.add_predicate(PredicateInfo::new(
"knows",
vec!["Person".to_string(), "Person".to_string()],
))
.expect("unwrap");
let mut incoming = SymbolTable::new();
incoming
.add_domain(DomainInfo::new("Person", 100))
.expect("unwrap");
incoming
.add_domain(DomainInfo::new("Event", 75))
.expect("unwrap");
incoming
.add_predicate(PredicateInfo::new(
"knows",
vec!["Person".to_string(), "Person".to_string()],
))
.expect("unwrap");
println!("Base schema:");
println!(" - Domains: Person (100), Organization (50)");
println!(" - Predicates: knows");
println!("\nIncoming schema:");
println!(" - Domains: Person (100), Event (75)");
println!(" - Predicates: knows");
println!();
let merger = SchemaMerger::new(MergeStrategy::Intersection);
let result = merger.merge(&base, &incoming).expect("unwrap");
println!("Merge strategy: Intersection");
println!("Result:");
println!(
" - Total domains: {} (only Person appears in both)",
result.merged.domains.len()
);
println!(
" - Total predicates: {} (only knows appears in both)",
result.merged.predicates.len()
);
println!(
" - Items from base only: {}",
result.report.base_domains.len() + result.report.base_predicates.len()
);
println!(
" - Items from incoming only: {}",
result.report.incoming_domains.len() + result.report.incoming_predicates.len()
);
println!("\n✓ Intersection strategy kept only common items");
}
fn scenario_complex_merge() {
let mut base = SymbolTable::new();
base.add_domain(DomainInfo::new("Person", 1000).with_description("Base academic system"))
.expect("unwrap");
base.add_domain(DomainInfo::new("Course", 200))
.expect("unwrap");
base.add_domain(DomainInfo::new("Department", 20))
.expect("unwrap");
base.add_predicate(PredicateInfo::new(
"teaches",
vec!["Person".to_string(), "Course".to_string()],
))
.expect("unwrap");
base.add_predicate(PredicateInfo::new(
"enrolled",
vec!["Person".to_string(), "Course".to_string()],
))
.expect("unwrap");
base.add_predicate(PredicateInfo::new(
"member_of",
vec!["Person".to_string(), "Department".to_string()],
))
.expect("unwrap");
base.bind_variable("professor", "Person").expect("unwrap");
base.bind_variable("student", "Person").expect("unwrap");
let mut incoming = SymbolTable::new();
incoming
.add_domain(DomainInfo::new("Person", 1500).with_description("Extended with researchers"))
.expect("unwrap");
incoming
.add_domain(DomainInfo::new("Course", 200))
.expect("unwrap"); incoming
.add_domain(DomainInfo::new("ResearchProject", 100))
.expect("unwrap");
incoming
.add_domain(DomainInfo::new("Publication", 500))
.expect("unwrap");
incoming
.add_predicate(PredicateInfo::new(
"teaches",
vec!["Person".to_string(), "Course".to_string()],
))
.expect("unwrap"); incoming
.add_predicate(PredicateInfo::new(
"works_on",
vec!["Person".to_string(), "ResearchProject".to_string()],
))
.expect("unwrap");
incoming
.add_predicate(PredicateInfo::new(
"authored",
vec!["Person".to_string(), "Publication".to_string()],
))
.expect("unwrap");
incoming
.bind_variable("researcher", "Person")
.expect("unwrap");
incoming.bind_variable("student", "Person").expect("unwrap");
println!("Base schema (Academic System):");
println!(" - Domains: Person (1000), Course (200), Department (20)");
println!(" - Predicates: teaches, enrolled, member_of");
println!(" - Variables: professor, student");
println!();
println!("Incoming schema (Research Extension):");
println!(" - Domains: Person (1500), Course (200), ResearchProject (100), Publication (500)");
println!(" - Predicates: teaches, works_on, authored");
println!(" - Variables: researcher, student");
println!();
let merger = SchemaMerger::new(MergeStrategy::Union);
let result = merger.merge(&base, &incoming).expect("unwrap");
println!("Merge strategy: Union");
println!();
println!("📊 Merge Results:");
println!("─────────────────");
println!("Final schema:");
println!(" - Total domains: {}", result.merged.domains.len());
println!(
" • Person (cardinality: {})",
result
.merged
.get_domain("Person")
.expect("unwrap")
.cardinality
);
println!(
" • Course (cardinality: {})",
result
.merged
.get_domain("Course")
.expect("unwrap")
.cardinality
);
println!(" • Department, ResearchProject, Publication");
println!();
println!(" - Total predicates: {}", result.merged.predicates.len());
println!(" • teaches (from both)");
println!(" • enrolled (from base)");
println!(" • member_of (from base)");
println!(" • works_on (from incoming)");
println!(" • authored (from incoming)");
println!();
println!(" - Total variables: {}", result.merged.variables.len());
println!(" • professor, student, researcher");
println!();
println!("📋 Merge Report:");
println!("────────────────");
println!(" - Base-only domains: {:?}", result.report.base_domains);
println!(
" - Incoming-only domains: {:?}",
result.report.incoming_domains
);
println!(
" - Domain conflicts: {}",
result.report.conflicting_domains.len()
);
if !result.report.conflicting_domains.is_empty() {
for conflict in &result.report.conflicting_domains {
println!(
" • {} (base: {}, incoming: {})",
conflict.name, conflict.base.cardinality, conflict.incoming.cardinality
);
}
}
println!();
println!(
" - Base-only predicates: {:?}",
result.report.base_predicates
);
println!(
" - Incoming-only predicates: {:?}",
result.report.incoming_predicates
);
println!(
" - Predicate conflicts: {}",
result.report.conflicting_predicates.len()
);
println!();
println!(" - Merged variables: {:?}", result.report.merged_variables);
println!(
" - Variable conflicts: {}",
result.report.conflicting_variables.len()
);
println!();
println!(" - Total items merged: {}", result.report.merged_count());
println!(" - Total conflicts: {}", result.report.conflict_count());
println!("\n✓ Complex merge completed successfully!");
println!(" Combined academic and research schemas into unified system");
}