mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-15 13:08:12 -07:00
global: snapshot
This commit is contained in:
@@ -93,12 +93,41 @@ fn collect_positions_bottom_up(
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a list of positions contains incompatible values.
|
||||
///
|
||||
/// Positions are incompatible if there are multiple different non-Identity positions,
|
||||
/// meaning different pattern instances use different naming conventions.
|
||||
fn has_incompatible_positions(positions: &[FieldNamePosition]) -> bool {
|
||||
let non_identity: Vec<_> = positions
|
||||
.iter()
|
||||
.filter(|p| !matches!(p, FieldNamePosition::Identity))
|
||||
.collect();
|
||||
|
||||
if non_identity.len() <= 1 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if all non-identity positions are the same
|
||||
let first = &non_identity[0];
|
||||
non_identity.iter().skip(1).any(|p| p != first)
|
||||
}
|
||||
|
||||
/// Merge multiple observed positions for each field into a single position.
|
||||
/// Uses the first non-Identity position found, as Identity from root-level
|
||||
/// instances is now handled by passing empty `acc`.
|
||||
///
|
||||
/// Returns an empty map if any field has incompatible positions across instances,
|
||||
/// which will cause `is_parameterizable()` to return false for the pattern.
|
||||
fn merge_field_positions(
|
||||
field_positions: &HashMap<String, Vec<FieldNamePosition>>,
|
||||
) -> HashMap<String, FieldNamePosition> {
|
||||
// First check for incompatible positions
|
||||
for positions in field_positions.values() {
|
||||
if has_incompatible_positions(positions) {
|
||||
// Incompatible positions found - pattern cannot be parameterized
|
||||
return HashMap::new();
|
||||
}
|
||||
}
|
||||
|
||||
// All positions are compatible, proceed with merge
|
||||
field_positions
|
||||
.iter()
|
||||
.filter_map(|(field_name, positions)| {
|
||||
|
||||
@@ -7,6 +7,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
|
||||
use brk_types::{Index, TreeNode, extract_json_type};
|
||||
|
||||
use crate::analysis::names::analyze_pattern_level;
|
||||
use crate::{IndexSetPattern, PatternField, child_type_name};
|
||||
|
||||
/// Get the first leaf name from a tree node.
|
||||
@@ -116,22 +117,55 @@ fn collect_indexes_from_tree(
|
||||
/// For cohort-level instances, returns the common prefix or suffix among all leaves.
|
||||
pub fn get_pattern_instance_base(node: &TreeNode) -> String {
|
||||
let leaf_names = get_all_leaf_names(node);
|
||||
if leaf_names.is_empty() {
|
||||
find_common_base(&leaf_names)
|
||||
}
|
||||
|
||||
/// Find the common base from a set of metric names.
|
||||
/// Tries prefix, suffix, then strips first/last segments and retries.
|
||||
fn find_common_base(names: &[String]) -> String {
|
||||
if names.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
|
||||
// First try to find a common prefix
|
||||
let common_prefix = find_common_prefix_at_underscore(&leaf_names);
|
||||
// Try common prefix
|
||||
let common_prefix = find_common_prefix_at_underscore(names);
|
||||
if !common_prefix.is_empty() {
|
||||
return common_prefix.trim_end_matches('_').to_string();
|
||||
}
|
||||
|
||||
// If no common prefix, try to find a common suffix
|
||||
let common_suffix = find_common_suffix_at_underscore(&leaf_names);
|
||||
// Try common suffix
|
||||
let common_suffix = find_common_suffix_at_underscore(names);
|
||||
if !common_suffix.is_empty() {
|
||||
return common_suffix.trim_start_matches('_').to_string();
|
||||
}
|
||||
|
||||
// If neither works, the common part may be in the middle.
|
||||
// Strip the first underscore segment (varying prefix) and try again.
|
||||
let stripped_prefix: Vec<String> = names
|
||||
.iter()
|
||||
.filter_map(|name| name.split_once('_').map(|(_, rest)| rest.to_string()))
|
||||
.collect();
|
||||
|
||||
if stripped_prefix.len() == names.len() {
|
||||
let common_prefix = find_common_prefix_at_underscore(&stripped_prefix);
|
||||
if !common_prefix.is_empty() {
|
||||
return common_prefix.trim_end_matches('_').to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// Try stripping last segment (varying suffix) and look for common suffix
|
||||
let stripped_suffix: Vec<String> = names
|
||||
.iter()
|
||||
.filter_map(|name| name.rsplit_once('_').map(|(rest, _)| rest.to_string()))
|
||||
.collect();
|
||||
|
||||
if stripped_suffix.len() == names.len() {
|
||||
let common_suffix = find_common_suffix_at_underscore(&stripped_suffix);
|
||||
if !common_suffix.is_empty() {
|
||||
return common_suffix.trim_start_matches('_').to_string();
|
||||
}
|
||||
}
|
||||
|
||||
String::new()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user