public ColumnAtt GetGreaterPath(List <DcColumn> path) { if (path == null) { return(null); } foreach (ColumnAtt p in GreaterPaths) { if (p.Segments == null) { continue; } if (p.Segments.Count != path.Count) { continue; // Different lengths => not equal } bool equal = true; for (int seg = 0; seg < p.Segments.Count && equal; seg++) { if (!StringSimilarity.SameColumnName(p.Segments[seg].Name, path[seg].Name)) { equal = false; } // if (p.Path[seg] != path[seg]) equal = false; // Compare strings as objects } if (equal) { return(p); } } return(null); }
/// <summary> /// Expand one attribute by building its path segments as dimension objects. /// Use the provided list of attributes for expansion recursively. This list essentially represents a schema. /// Also, adjust path names in special cases like empty name or simple structure. /// </summary> public void ExpandAttribute(List <ColumnAtt> attributes, List <DcColumn> columns) // Add and resolve attributes by creating dimension structure from FKs { ColumnAtt att = this; if (att.Segments.Count > 0) { return; // Already expanded (because of recursion) } bool isKey = !string.IsNullOrEmpty(att.RelationalPkName) || att.IsKey; if (string.IsNullOrEmpty(att.RelationalFkName)) // No FK - primitive column - end of recursion { // Find or create a primitive dim segment DcColumn seg = columns.FirstOrDefault(c => c.Input == att.Input && StringSimilarity.SameColumnName(((ColumnRel)c).RelationalFkName, att.RelationalFkName)); if (seg == null) { seg = new ColumnRel(att.RelationalColumnName, att.Input, att.Output, isKey, false); // Maybe copy constructor? ((ColumnRel)seg).RelationalFkName = att.RelationalFkName; columns.Add(seg); } att.InsertLast(seg); // add it to this attribute as a single segment } else { // There is FK - non-primitive column // Find target set and target attribute (name resolution) ColumnAtt tailAtt = attributes.FirstOrDefault(a => StringSimilarity.SameTableName(a.Input.Name, att.RelationalTargetTableName) && StringSimilarity.SameColumnName(a.Name, att.RelationalTargetColumnName)); DcTable gTab = tailAtt.Input; // Find or create a dim segment DcColumn seg = columns.FirstOrDefault(c => c.Input == att.Input && StringSimilarity.SameColumnName(((ColumnRel)c).RelationalFkName, att.RelationalFkName)); if (seg == null) { seg = new ColumnRel(att.RelationalFkName, att.Input, gTab, isKey, false); ((ColumnRel)seg).RelationalFkName = att.RelationalFkName; columns.Add(seg); } att.InsertLast(seg); // add it to this attribute as first segment // // Recursion. Expand tail attribute and add all segments from the tail attribute (continuation) // tailAtt.ExpandAttribute(attributes, columns); att.InsertLast(tailAtt); // Adjust name. How many attributes belong to the same FK as this attribute (FK composition) List <ColumnAtt> fkAtts = attributes.Where(a => a.Input == att.Input && StringSimilarity.SameColumnName(att.RelationalFkName, a.RelationalFkName)).ToList(); if (fkAtts.Count == 1) { seg.Name = att.RelationalColumnName; // Adjust name. For 1-column FK, name of the FK-dim is the column name (not the FK name) } } }
// // What is Set::AddAllNonStoredPaths() ??? // /* * public void ImportSchema(List<string> tableNames = null) * { * if (tableNames == null) * { * tableNames = connection.ReadTables(); * } * * // Create all sets * foreach (string tableName in tableNames) * { * Set set = new Set(tableName); // Create a set * set.RelationalTableName = tableName; * this.AddTable(set, null); * } * * // Load columns and FKs as (complex) paths and (simple) FK-dimensions * foreach (string tableName in tableNames) * { * ImportPaths(tableName); * } * * List<ComTable> sets = Root.GetAllSubsets(); * foreach (ComTable set in sets) * { * foreach (DimPath path in set.GreaterPaths) * { * path.ExpandPath(); * } * } * * foreach (Set set in sets) * { * set.AddAllNonStoredPaths(); * } * } */ protected List <ColumnAtt> LoadAttributes(TableRel table) { // The created paths will be not be added to the schema (should be added manually) // The created paths are empty and do not store any dimensions (should be done/expanded separately by using the meta-data about PKs, FKs etc.) Debug.Assert(!table.IsPrimitive, "Wrong use: cannot load structure for primitive set."); List <ColumnAtt> attributes = new List <ColumnAtt>(); string tableName = table.RelationalTableName; DataTable pks = connection.GetPks(tableName); DataTable fks = connection.GetFks(tableName); DataTable columns = connection.GetColumns(tableName); foreach (DataRow col in columns.Rows) // Process all columns of the table (correspond to primitive paths of the set) { string columnName = col["COLUMN_NAME"].ToString(); string columnType = ((OleDbType)col["DATA_TYPE"]).ToString(); DcTable typeTable = Schema.GetPrimitiveType(columnType); // // Create an attribute object representing this column // ColumnAtt path = table.GetGreaterPathByColumnName(columnName); // It might have been already created (when processing other tables) if (path != null) { continue; } path = new ColumnAtt(columnName, table, typeTable); // // Set relational attribute of the object // path.RelationalColumnName = columnName; // Find PKs this attribute belongs to (among all PKs of this table) foreach (DataRow pk in pks.Rows) { if (!StringSimilarity.SameColumnName(columnName, (string)pk["COLUMN_NAME"])) { continue; } // Found PK this column belongs to path.RelationalPkName = (string)pk["PK_NAME"]; table.RelationalPkName = path.RelationalPkName; // OPTIMIZE: try to do it only once rather than for each attribute and try to identify and exclude multiple PKs (error) //path.IsIdentity = true; // We simply have to override this property as "RelationalPkName != null" or "RelationalPkName == table.RelationalPkName" break; // Assume that a column can belong to only one PK } // Find FKs this attribute belongs to (among all FKs of this table) foreach (DataRow fk in fks.Rows) { if (!StringSimilarity.SameColumnName(columnName, (string)fk["FK_COLUMN_NAME"])) { continue; } // Target PK name fk["PK_NAME"] is not stored and is not used because we assume that there is only one PK path.RelationalFkName = (string)fk["FK_NAME"]; path.RelationalTargetTableName = (string)fk["PK_TABLE_NAME"]; // Name of the target set of the simple dimension (first segment of this complex path) path.RelationalTargetColumnName = (string)fk["PK_COLUMN_NAME"]; // Next path/attribute name belonging to the target set break; // We assume that a column can belong to only one FK and do not continue with the rest of the FK-loop } attributes.Add(path); } return(attributes); }
public ColumnAtt GetGreaterPathByColumnName(string name) { return(GreaterPaths.FirstOrDefault(d => StringSimilarity.SameColumnName(d.RelationalColumnName, name))); }
} // Note that the same field exists also in Dim public DcColumn GetGreaterColByFkName(string name) { return(Columns.FirstOrDefault(d => StringSimilarity.SameColumnName(((ColumnRel)d).RelationalFkName, name))); }