# Create only horizontal node alligning inside multiple clusters for column data lineage

**URL:** <https://forum.graphviz.org/t/create-only-horizontal-node-alligning-inside-multiple-clusters-for-column-data-lineage/2988>\
**Category:** Uncategorized\
**Created:** [May 23, 2025, 1:15pm UTC](https://forum.graphviz.org/t/create-only-horizontal-node-alligning-inside-multiple-clusters-for-column-data-lineage/2988 "2025-05-23T13:15:51Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![hasdfkl](https://avatars.discourse-cdn.com/v4/letter/h/b5e925/32.png) [@hasdfkl](https://forum.graphviz.org/u/hasdfkl)\
**Post date:** [May 23, 2025, 1:15pm UTC](https://forum.graphviz.org/t/create-only-horizontal-node-alligning-inside-multiple-clusters-for-column-data-lineage/2988/1 "2025-05-23T13:15:51Z")

</div>

I would like to visualise the column data lineage of some sql codes.  
So far I have the next graph in graphviz:

```graphviz
digraph {
  graph[splines = ortho,

        rankdir="LR",
        layout = dot,
        ranksep=1
       ]

  node [shape = rectangle, style = filled, fillcolor = "blanchedalmond"]
  edge[color = black, arrowhead=vee, minlen = 1]

# add clusters
  edge [style=invis]
  subgraph cluster_1 {

    label = "Meta";
    style=dotted
     {
      rank=same
      "col_6" -> "col_5" -> "col_4" -> "col_3" -> "col_2" -> "col_1"

    }
  }

  subgraph cluster_2 {
    style=dotted
    label = "prep.sql";
    {
      rank=source
      "prep_col_6" -> "prep_col_5" -> "prep_col_4" -> "prep_col_3" -> "prep_col_2" -> "prep_col_1"
      "prep_col_6" [style=invis]
    }
  }

  subgraph cluster_3 {
    label = "some.sql";
    style=dotted

    node [style=filled]
    {
      rank=min
      "some_col_6" -> "some_col_5" -> "some_col_4" -> "some_col_3" -> "some_col_2" -> "some_col_1"
      // "some_col_1" "some_col_2" "some_col_3" "some_col_4" "some_col_5" "some_col_6"

      "some_col_5" [style=invis]
      "some_col_6" [style=invis]
    }
  }

  / ***********************************************
    define nodes before defining edges - to keep sequence as desired
   *********************************************** /
# draw lines
  edge[constraint=false style=solid] //added
# from survey data to inpu
  col_1 -> prep_col_1 [constraint=true]
  col_2 -> prep_col_2
  col_3 -> prep_col_2
  col_3 -> prep_col_2
  col_4 -> prep_col_2
  col_5 -> prep_col_3
  col_6 -> prep_col_4
  col_6 -> prep_col_5

# from input to cleaning
  prep_col_1 -> some_col_1 [constraint=true]
  prep_col_2 -> some_col_2
  prep_col_3 -> some_col_3
  prep_col_4 -> some_col_3
  prep_col_5 -> some_col_4

  prep_col_1-> prep_col_3 [color=red];
  prep_col_1-> prep_col_5 [color=red];
}

```

It gives some result but not the best: [Online graphviz](https://dreampuf.github.io/GraphvizOnline/?engine=dot#digraph%20%7B%0D%0A%20%20graph%5Bsplines%20%3D%20ortho%2C%0D%0A%0D%0A%20%20%20%20%20%20%20%20rankdir%3D%22LR%22%2C%0D%0A%20%20%20%20%20%20%20%20layout%20%3D%20dot%2C%0D%0A%20%20%20%20%20%20%20%20ranksep%3D1%0D%0A%20%20%20%20%20%20%20%5D%0D%0A%0D%0A%20%20node%20%5Bshape%20%3D%20rectangle%2C%20style%20%3D%20filled%2C%20fillcolor%20%3D%20%22blanchedalmond%22%5D%0D%0A%20%20edge%5Bcolor%20%3D%20black%2C%20arrowhead%3Dvee%2C%20minlen%20%3D%201%5D%0D%0A%0D%0A%0D%0A%23%20add%20clusters%0D%0A%20%20edge%20%5Bstyle%3Dinvis%5D%0D%0A%20%20subgraph%20cluster_1%20%7B%0D%0A%0D%0A%20%20%20%20label%20%3D%20%22Meta%22%3B%0D%0A%20%20%20%20style%3Ddotted%0D%0A%20%20%20%20%20%7B%0D%0A%20%20%20%20%20%20rank%3Dsame%0D%0A%20%20%20%20%20%20%22col_6%22%20-%3E%20%22col_5%22%20-%3E%20%22col_4%22%20-%3E%20%22col_3%22%20-%3E%20%22col_2%22%20-%3E%20%22col_1%22%0D%0A%0D%0A%20%20%20%20%7D%0D%0A%20%20%7D%0D%0A%0D%0A%20%20subgraph%20cluster_2%20%7B%0D%0A%20%20%20%20style%3Ddotted%0D%0A%20%20%20%20label%20%3D%20%22prep.sql%22%3B%0D%0A%20%20%20%20%7B%0D%0A%20%20%20%20%20%20rank%3Dsource%0D%0A%20%20%20%20%20%20%22prep_col_6%22%20-%3E%20%22prep_col_5%22%20-%3E%20%22prep_col_4%22%20-%3E%20%22prep_col_3%22%20-%3E%20%22prep_col_2%22%20-%3E%20%22prep_col_1%22%0D%0A%20%20%20%20%20%20%22prep_col_6%22%20%5Bstyle%3Dinvis%5D%0D%0A%20%20%20%20%7D%0D%0A%20%20%7D%0D%0A%0D%0A%20%20subgraph%20cluster_3%20%7B%0D%0A%20%20%20%20label%20%3D%20%22some.sql%22%3B%0D%0A%20%20%20%20style%3Ddotted%0D%0A%0D%0A%20%20%20%20node%20%5Bstyle%3Dfilled%5D%0D%0A%20%20%20%20%7B%0D%0A%20%20%20%20%20%20rank%3Dmin%0D%0A%20%20%20%20%20%20%22some_col_6%22%20-%3E%20%22some_col_5%22%20-%3E%20%22some_col_4%22%20-%3E%20%22some_col_3%22%20-%3E%20%22some_col_2%22%20-%3E%20%22some_col_1%22%0D%0A%20%20%20%20%20%20%2F%2F%20%22some_col_1%22%20%22some_col_2%22%20%22some_col_3%22%20%22some_col_4%22%20%22some_col_5%22%20%22some_col_6%22%0D%0A%0D%0A%20%20%20%20%20%20%22some_col_5%22%20%20%5Bstyle%3Dinvis%5D%0D%0A%20%20%20%20%20%20%22some_col_6%22%20%20%5Bstyle%3Dinvis%5D%0D%0A%20%20%20%20%7D%0D%0A%20%20%7D%0D%0A%0D%0A%20%20%2F ***********************************************%0D%0A%20%20%20%20define%20nodes%20before%20defining%20edges%20-%20to%20keep%20sequence%20as%20desired%0D%0A%20%20%20*********************************************** %2F%0D%0A%23%20draw%20lines%0D%0A%20%20edge%5Bconstraint%3Dfalse%20style%3Dsolid%5D%20%20%20%2F%2Fadded%0D%0A%23%20from%20survey%20data%20to%20inpu%0D%0A%20%20col_1%20-%3E%20prep_col_1%20%5Bconstraint%3Dtrue%5D%0D%0A%20%20col_2%20-%3E%20prep_col_2%0D%0A%20%20col_3%20-%3E%20prep_col_2%0D%0A%20%20col_3%20-%3E%20prep_col_2%0D%0A%20%20col_4%20-%3E%20prep_col_2%0D%0A%20%20col_5%20-%3E%20prep_col_3%0D%0A%20%20col_6%20-%3E%20prep_col_4%0D%0A%20%20col_6%20-%3E%20prep_col_5%0D%0A%0D%0A%23%20from%20input%20to%20cleaning%0D%0A%20%20prep_col_1%20-%3E%20some_col_1%20%20%5Bconstraint%3Dtrue%5D%0D%0A%20%20prep_col_2%20-%3E%20some_col_2%0D%0A%20%20prep_col_3%20-%3E%20some_col_3%0D%0A%20%20prep_col_4%20-%3E%20some_col_3%0D%0A%20%20prep_col_5%20-%3E%20some_col_4%0D%0A%0D%0A%20%20prep_col_1-%3E%20prep_col_3%20%5Bcolor%3Dred%5D%3B%0D%0A%20%20prep_col_1-%3E%20prep_col_5%20%5Bcolor%3Dred%5D%3B%0D%0A%7D).

What I missed possibility to show red edges inside cluster.  
Means possbility to allign nodes horizontally in all clusters.  
But give to nodes freedom to move inside cluster on any vertical possition .  
Something like this:  
[excalidraw](https://excalidraw.com/#json=0TLD-RzqXjsgw9WdTmi11,oRCjJRT_loTEdCNS8kHw4w)

Is it possible with Graphviz?

---

<div class="post-metadata">

**Author:** ![clutter](https://avatars.discourse-cdn.com/v4/letter/c/e0b2c6/32.png) [@clutter](https://forum.graphviz.org/u/clutter)\
**Post date:** [June 3, 2025, 7:37pm UTC](https://forum.graphviz.org/t/create-only-horizontal-node-alligning-inside-multiple-clusters-for-column-data-lineage/2988/2 "2025-06-03T19:37:55Z")

</div>

You might want to look at record layout. If I understand the need correctly, you can get this format to align nicely using rankdir=LR. The columns can still be endpoints for edges, and the records can be in clusters, so you wouldn’t need a node for each column, but still be able to show the relationship I think. Here’s a (not small) example I recently did to show the data flow for a mapping from a set of database tables to generic format for further processing as a single text file. The dot code is a bit messy, but it would contain what you need to use this format. I can’t seem to embed this to auto-render, so you can copy out the code and give it a try.

[dot]  
digraph structs {  
layout=dot  
// graph [pack=true,packmode=“graph”]  
// splines=ortho  
rankdir=LR

// Two color schemes here. Uncomment the one you want:

// White background:  
// node [shape=record];  
// edge [penwidth=1.0,arrowsize=0.5,color=blue,headclip=false,tailclip=false]  
// graph [label=“2025-05-06 17:19:48-0400 Clutter”,labelloc=b,fontsize=7]  
// subgraph clusterall {graph [label=“TDX Diagram Processing”,labelloc=t,fontsize=25,color=transparent]

// Black background:  
node [shape=record,color=white,fontcolor=white];  
edge [penwidth=1.0,arrowsize=0.5,color=white,headclip=false,tailclip=false]  
graph [bgcolor=black]  
graph [label=“2025-05-06 13:32:00-0400 Clutter”,labelloc=b,fontsize=7,fontc  
olor=white]  
subgraph clusterall {graph [label=“TDX Table Pre-Processing”,labelloc=t,fontsi  
ze=25,fontcolor=white,color=transparent]

// End of color schemes

// TDX TABLES

{  
node [color=firebrick3]

relations [label=“{  
TDX\nConfiguration\nRelationships|  
{  
{CR\_ID}|  
{\<CR\_ParentID\> CR\_ParentID}|  
{\<CR\_ChildID\> CR\_ChildID}|  
{\<CR\_RelTypeID\> CR\_RelTypeID}}  
}”]

relationshipType [label=“{  
TDX\nConfiguration\nRelationship\nTypes|  
{  
{\<CRT\_ID\> CRT\_ID}|  
{\<CRT\_InverseDescription\> CRT\_InverseDescription}}  
}”]

CI [label=“{  
TDX\nConfiguration\nItems|{  
{\<CI\_ID\> CI\_ID}|  
{\<CI\_Name\> CI\_Name}|  
{\<CI\_TypeID\> CI\_TypeID}|  
{\<CI\_ItemID\> CI\_ItemID\nBased on CI\_ComponentID}|  
{\<CI\_ComponentID\> CI\_ComponentID}|  
{CI\_IsActive}|  
{\<CI\_OwningGroupID\> CI\_OwningGroupID}}  
}”]

assets [label=“{  
TDX\nAssets|{  
{\<AS\_ID\> AS\_ID}|  
{\<AS\_ProductModelID\> AS\_ProductModelID}}  
}”]

models [label=“{  
TDX\nProduct\nModels|{  
{\<PM\_ID\> PM\_ID}|  
{\<PM\_Name\> PM\_Name}|  
{\<PM\_ManufacturerID\> PM\_ManufacturerID}}  
}”]

vendors [label=“{  
TDX\nVendors\n$0.Vendors|{  
{\<VND\_ID\> VND\_ID}|  
{\<VND\_Name\> VND\_Name}}  
}”]

components [label=“{  
TDX\nComponents\n$0.CMPNT|{  
{\<CMPNT\_ID\> CMPNT\_ID}|  
{\<CMPNT\_Name\> CMPNT\_Name}}  
}”]

CItypes [label=“{  
TDX\nConfiguration\nItem Types\n$0.CIT|{  
{\<CIT\_ID\> CIT\_ID}|  
{\<CIT\_Name\> CIT\_Name}}  
}”]

groups [label=“{  
TDX\nGroups\n$0.Grp|{  
{\<Grp\_ID\> Grp\_ID}|  
{\<Grp\_Name\> Grp\_Name}}  
}”]

attributes [label=“{  
TDX\nAttributes\n$0.Att|{  
{\<Att\_ID\> Att\_ID}|  
{\<Att\_Name\> Att\_Name}}  
}”]

attributeValues [label=“{  
TDX\nAttribute\nValues|{  
{AV\_ID}|  
{\<AV\_ItemID\> AV\_ItemID\nBased on AV\_ItemComponentID}|  
{\<AV\_ItemComponentID\> AV\_ItemComponentID}|  
{\<AV\_ATTID\> AV\_ATTID}|  
{\<AV\_Value\> AV\_Value}|  
{\<Value\_Types\> Value\_Types\n(Computed)|{  
{AV\_ValueText}|  
{AV\_ValueInt}|  
{AV\_ValueDecimal}|  
{AV\_ValueDateTime}}}}  
}”]

attributeChoices [label=“{  
TDX\nAttribute\nChoices\n$0.ATTC|{  
{\<ATTC\_ID\> ATTC\_ID}|  
{\<ATTC\_Name\> ATTC\_Name}}  
}”]

} // End of TDX table scope

// Intermediate in-memory tables

{  
node [color=cyan3]  
intermediateValues [label=“{  
STREAM\nAttribute\nValues\nProcessing|{  
{\<AV\_ItemComponentID+AV\_ItemID\> AV\_ItemComponentID+AV\_ItemID}|  
{ comment}|  
{\<asset\_type\> asset\_type(for component ID 27)}|  
{\<owning\_group\> owning\_group}}  
}”]  
} // end of intermediate table scope

// TEMP FILES

{  
node [color=darkgoldenrod2]  
// MakeModel temp file  
MakeModel [label=“{  
TEMP\n$0.MakeModel|{  
{\<PM\_ID\> PM\_ID}|  
{\<PM\_Name\> PM\_Name (Model)}|  
{\<PM\_Manufacturer\_ID\> PM\_Manufacturer\_ID}|  
{\<VND\_Name\> VND\_Name (Make)}}  
}”]

// Assets temp file  
tempAssets [label=“{  
TEMP\n$0.assets|{  
{\<27+AS\_ID\> 27+AS\_ID}|  
{\<PM\_Name\> PM\_Name (Model)}|  
{\<VND\_Name\> VND\_Name (Make)}}  
}”]

// Configuration Items temp file  
tempCI [label=“{  
TEMP\n$0.CI|{  
{\<CI\_ComponentID+CI\_ItemID\> CI\_ComponentID+CI\_ItemID}|  
{\<CI\_ID\> CI\_ID}|  
{\<CI\_Name\> CI\_Name}|  
{\<CI\_TypeID\> CI\_TypeID}|  
{\<CI\_ItemID\> CI\_ItemID}|  
{\<CI\_ComponentID\> CI\_ComponentID}|  
{\<Grp\_Name\> Grp\_Name}|  
{\<CIT\_Name\> CIT\_Name}|  
{\<CMPNT\_Name\> CMPNT\_Name}}  
}”]

} // End of temp files scope

// OUTPUT FILES

{  
node [color=forestgreen]  
asset [label=“{  
OUTPUT\nasset.tsv|{  
{ id\nCI\_ID}|  
{\<asset\_name\> asset\_name\nCI\_Name}|  
{ rev\n(blank)}|  
{ owner\nGrp\_Name}|  
{ category\n(blank)}|  
{\<asset\_type\> asset\_type\nAsset: ATTC\_Name where Att\_Name==Type\n&& AV\_ItemCompo  
nentID==27\nCI: CIT\_Name\nElse: CMPNT\_Name}|  
{ comment\nAll custom attribute names/values}|  
{ level\n(blank)}|  
{ location\n(blank)}|  
{ deleted\n(blank)}|  
{ expiration\n(blank)}|  
{ deleted\_time\n(blank)}|  
{ created\_time\n(blank)}|  
{ uuid\n(blank)}}  
}”]

depend [label=“{  
OUTPUT\ndepend.tsv|{  
{\<asset\_a\> asset\_a\nCR\_ChildID}|  
{\<asset\_b\> asset\_b\nCR\_ParentID}|  
{\<link\_type\> link\_type\nCRT\_InverseDescription}}  
}”]

} // End of output file scope

// CI connection to asset  
CI:CI\_ItemID → assets:AS\_ID:w [color=firebrick3]

// Asset connection to Product Models  
assets:AS\_ProductModelID → models:PM\_ID:w [color=firebrick3]

// Relations to relationship types  
relations:CR\_RelTypeID → relationshipType:CRT\_ID:w [color=firebrick3]

// Resolve owning group ID to get name for CI  
CI:CI\_OwningGroupID → groups:Grp\_ID:w [color=firebrick3]

// Resolve attribute ID to get name for attribute value  
attributeValues:AV\_ATTID → attributes:Att\_ID:w [color=firebrick3]

// Attribute Values Item ID to Configuration Items and Assets  
attributeValues:AV\_ItemID → CI:CI\_ID:w [headlabel=“63”] [color=firebrick3]  
attributeValues:AV\_ItemID → assets:AS\_ID:w [headlabel=“27”] [color=firebrick3  
]

// Resolve AV\_Value to a name if the attribute value came from selection list  
attributeValues:AV\_Value → attributeChoices:ATTC\_ID:w [color=firebrick3]

// Relationship Parent and Child ID to depend.tsv  
relations:CR\_ParentID → depend:asset\_b:w [color=forestgreen]  
relations:CR\_ChildID → depend:asset\_a:w [color=forestgreen]

// Relationship Type to depend.tsv  
relationshipType:CRT\_InverseDescription:e → depend:link\_type:w [color=forestgre  
en]

// Resolving the type of component:

// If type 63 (CI) use ConfigurationItemTypes name  
// CItypes:CIT\_Name → asset:asset\_type:w  
// If Component ID is neither 63 (CI) nor 27 (Assets) we use the Component  
// Name for the asset type  
//components:CMPNT\_Name → asset:asset\_type:w

// Combine product models and vendors into MakeModel temp file  
models:PM\_ID:e → MakeModel:PM\_ID:w [color=darkgoldenrod2]  
models:PM\_Name:e → MakeModel:PM\_Name:w [color=darkgoldenrod2]  
models:PM\_ManufacturerID:e → MakeModel:PM\_Manufacturer\_ID:w [color=darkgoldenro  
d2]  
MakeModel:PM\_Manufacturer\_ID:e → vendors:VND\_ID:w [color=firebrick3]  
vendors:VND\_Name:w → MakeModel:VND\_Name:e [color=darkgoldenrod2]

// Now put together the asset temp file  
assets:AS\_ID:e → tempAssets:“27+AS\_ID”:w [color=darkgoldenrod2]  
MakeModel:PM\_Name:e → tempAssets:PM\_Name:w [color=darkgoldenrod2]  
MakeModel:VND\_Name:e → tempAssets:VND\_Name:w [color=darkgoldenrod2]

// Configuration Item temp file  
CI:CI\_ComponentID:e → tempCI:“CI\_ComponentID+CI\_ItemID”:w [color=darkgoldenrod2  
]  
CI:CI\_ItemID:e → tempCI:“CI\_ComponentID+CI\_ItemID”:w [color=darkgoldenrod2]  
CI:CI\_ID:e → tempCI:CI\_ID:w [color=darkgoldenrod2]  
CI:CI\_Name:e → tempCI:CI\_Name:w [color=darkgoldenrod2]  
CI:CI\_TypeID:e → tempCI:CI\_TypeID:w [color=darkgoldenrod2]  
CI:CI\_ItemID:e → tempCI:CI\_ItemID:w [color=darkgoldenrod2]  
CI:CI\_ComponentID:e → tempCI:CI\_ComponentID:w [color=darkgoldenrod2]  
tempCI:CI\_TypeID:e → CItypes:CIT\_ID:w [color=firebrick3]  
CItypes:CIT\_Name:w → tempCI:CIT\_Name:e [color=darkgoldenrod2]  
groups:Grp\_Name:e → tempCI:Grp\_Name:w [color=darkgoldenrod2]  
tempCI:CI\_ComponentID:e → components:CMPNT\_ID:w [color=firebrick3]  
components:CMPNT\_Name:w → tempCI:CMPNT\_Name:e [color=darkgoldenrod2]

// Attribute Values in-memory temp table  
{  
edge [color=cyan3]  
// Populate index  
attributeValues:AV\_ItemID:e → intermediateValues:“AV\_ItemComponentID+AV\_ItemID”  
:w  
attributeValues:AV\_ItemComponentID:e → intermediateValues:“AV\_ItemComponentID+A  
V\_ItemID”:w  
// Attribute name goes to working comment field  
attributes:Att\_Name:e → intermediateValues:comment:w  
// Attribute Choice table value goes to working comment field  
attributeChoices:ATTC\_Name:e → intermediateValues:comment:w  
// if value is the asset type  
attributeChoices:ATTC\_Name:e → intermediateValues:asset\_type:w  
// if value is the owning group  
attributeChoices:ATTC\_Name:e → intermediateValues:owning\_group:w  
// add Make and Model from the asset temp file  
tempAssets:PM\_Name:e → intermediateValues:comment:w  
tempAssets:VND\_Name:e → intermediateValues:comment:w  
// Add the value types  
attributeValues:Value\_Types → intermediateValues:comment:w  
}

// Now join with the temp CI table to output the asset.tsv file  
// The CI ID  
tempCI:CI\_ID:e → asset:id:w [color=forestgreen]  
// The CI name  
tempCI:CI\_Name:e → asset:asset\_name:w [color=forestgreen]  
// The CI group name (for ownership)  
tempCI:Grp\_Name:e → asset:owner:w [color=forestgreen] [color=forestgreen]  
// The Owning Group name (for ownership) from attribute values table  
intermediateValues:owning\_group:e → asset:owner:w [color=forestgreen]  
// Component type for componentID 27 (assets)  
intermediateValues:asset\_type:e → asset:asset\_type:w [color=forestgreen]  
// Component type for compinentID (63) (Configuration Item, CIT\_Name)  
tempCI:CIT\_Name:e → asset:asset\_type:w [color=forestgreen]  
// Component type if not asset or CI (CMPNT\_Name)  
tempCI:CMPNT\_Name:e → asset:asset\_type:w [color=forestgreen]  
// the comment field  
intermediateValues:comment:e → asset:comment:w [color=forestgreen]

} // End subgraph all

} // End graph  
[/dot]
