# Chart Entity The Chart entity represents visualizations and reports in BI tools (e.g., Looker, Tableau, Superset). This guide covers chart operations in SDK V2. ## Creating a Chart ### Minimal Chart Only tool and id are required: ```java Chart chart = Chart.builder() .tool("looker") .id("my_sales_chart") .build(); ``` ### With Metadata Add title, description, and custom properties: ```java Chart chart = Chart.builder() .tool("tableau") .id("sales_dashboard_chart_1") .title("Sales Performance by Region") .description("Monthly sales broken down by geographic region") .build(); ``` ### With Custom Properties ```java Map properties = new HashMap<>(); properties.put("dashboard", "executive_dashboard"); properties.put("refresh_schedule", "hourly"); Chart chart = Chart.builder() .tool("looker") .id("revenue_chart") .title("Revenue Trends") .customProperties(properties) .build(); ``` ## URN Construction Chart URNs follow the pattern: ``` urn:li:chart:({tool},{id}) ``` **Example:** ```java Chart chart = Chart.builder() .tool("looker") .id("my_chart") .build(); ChartUrn urn = chart.getChartUrn(); // urn:li:chart:(looker,my_chart) ``` ## Supported BI Tools Common tool identifiers: - `looker` - Looker - `tableau` - Tableau - `superset` - Apache Superset - `powerbi` - Power BI - `metabase` - Metabase - `redash` - Redash - `mode` - Mode Analytics ## Chart Operations ### Adding Tags Add tags to categorize and classify your charts: ```java // Simple tag (automatically adds "urn:li:tag:" prefix) chart.addTag("pii"); chart.addTag("financial"); // Or use full URN chart.addTag("urn:li:tag:production"); ``` ### Managing Owners Add owners with different ownership types: ```java import com.linkedin.common.OwnershipType; // Add technical owner chart.addOwner("urn:li:corpuser:data_team", OwnershipType.TECHNICAL_OWNER); // Add business owner chart.addOwner("urn:li:corpuser:sales_team", OwnershipType.BUSINESS_OWNER); // Add data steward chart.addOwner("urn:li:corpuser:compliance_team", OwnershipType.DATA_STEWARD); // Remove an owner chart.removeOwner("urn:li:corpuser:old_owner"); ``` ### Adding Glossary Terms Link charts to business glossary terms: ```java chart.addTerm("urn:li:glossaryTerm:SalesMetrics"); chart.addTerm("urn:li:glossaryTerm:QuarterlyReporting"); // Remove a term chart.removeTerm("urn:li:glossaryTerm:OldTerm"); ``` ### Setting Domain Organize charts into domains: ```java // Set domain chart.setDomain("urn:li:domain:Sales"); // Remove domain chart.setDomain(null); // or chart.removeDomain(); ``` ### Setting Description and Title Update chart description and title using patch-based updates: ```java chart.setDescription("Updated chart description"); chart.setTitle("New Chart Title"); ``` ### Managing Custom Properties Add, update, or remove custom properties: ```java // Add individual properties chart.addCustomProperty("refresh_schedule", "hourly"); chart.addCustomProperty("chart_type", "bar"); // Set all properties at once (replaces existing) Map props = new HashMap<>(); props.put("dashboard_url", "https://dashboard.example.com"); props.put("author", "data_team"); chart.setCustomProperties(props); // Remove a property chart.removeCustomProperty("old_property"); ``` ## Lineage Operations Chart lineage defines the data flow relationships between charts and the datasets they consume. This is essential for impact analysis, data governance, and understanding data dependencies. ### Setting Input Datasets Define which datasets a chart consumes: ```java import com.linkedin.common.urn.DatasetUrn; import java.util.Arrays; // Create dataset URNs DatasetUrn salesDataset = DatasetUrn.createFromString( "urn:li:dataset:(urn:li:dataPlatform:snowflake,sales.transactions,PROD)"); DatasetUrn customerDataset = DatasetUrn.createFromString( "urn:li:dataset:(urn:li:dataPlatform:snowflake,sales.customers,PROD)"); // Set all input datasets at once (replaces existing) chart.setInputDatasets(Arrays.asList(salesDataset, customerDataset)); ``` ### Adding Individual Input Datasets Add input datasets incrementally: ```java // Add datasets one at a time chart.addInputDataset(salesDataset); chart.addInputDataset(customerDataset); // This pattern is useful when: // - Building lineage incrementally // - Discovering datasets during chart analysis // - Adding new data sources to existing chart ``` ### Removing Input Datasets Remove datasets that are no longer consumed: ```java DatasetUrn legacyDataset = DatasetUrn.createFromString( "urn:li:dataset:(urn:li:dataPlatform:postgres,legacy.old_table,PROD)"); // Remove specific dataset from lineage chart.removeInputDataset(legacyDataset); ``` ### Retrieving Input Datasets Get the list of datasets a chart consumes: ```java // Load chart from DataHub ChartUrn chartUrn = new ChartUrn("looker", "my_chart"); Chart chart = client.entities().get(chartUrn); // Get all input datasets List inputDatasets = chart.getInputDatasets(); System.out.println("Chart consumes " + inputDatasets.size() + " datasets:"); for (DatasetUrn dataset : inputDatasets) { System.out.println(" - " + dataset); } ``` ### Complete Lineage Example ```java // Create chart with comprehensive lineage Chart salesChart = Chart.builder() .tool("tableau") .id("sales_dashboard_chart") .title("Sales Performance") .build(); // Define input datasets DatasetUrn transactions = DatasetUrn.createFromString( "urn:li:dataset:(urn:li:dataPlatform:snowflake,sales.transactions,PROD)"); DatasetUrn customers = DatasetUrn.createFromString( "urn:li:dataset:(urn:li:dataPlatform:snowflake,sales.customers,PROD)"); DatasetUrn products = DatasetUrn.createFromString( "urn:li:dataset:(urn:li:dataPlatform:snowflake,sales.products,PROD)"); // Set lineage salesChart.setInputDatasets(Arrays.asList(transactions, customers, products)); // Add metadata salesChart.addTag("sales") .addOwner("urn:li:corpuser:data_team", OwnershipType.TECHNICAL_OWNER) .setDomain("urn:li:domain:Sales"); // Save to DataHub client.entities().upsert(salesChart); ``` ## Chart-Specific Properties Charts have several specialized properties beyond basic metadata: ### Chart Type Set the visualization type: ```java // Available types: BAR, LINE, PIE, TABLE, TEXT, BOXPLOT, AREA, SCATTER chart.setChartType("BAR"); // Get chart type String chartType = chart.getChartType(); ``` ### Access Level Control chart visibility: ```java // Available levels: PUBLIC, PRIVATE chart.setAccess("PUBLIC"); // Get access level String access = chart.getAccess(); ``` ### External and Chart URLs Set URLs for accessing the chart: ```java // External URL - link to view chart in source BI tool chart.setExternalUrl("https://looker.company.com/dashboards/123"); // Chart URL - direct URL to chart (may be different from external URL) chart.setChartUrl("https://looker.company.com/embed/charts/456"); // Get URLs String externalUrl = chart.getExternalUrl(); String chartUrl = chart.getChartUrl(); ``` ### Last Refreshed Timestamp Track when chart data was last updated: ```java // Set timestamp (milliseconds since epoch) long currentTime = System.currentTimeMillis(); chart.setLastRefreshed(currentTime); // Or use a specific time long specificTime = Instant.parse("2025-10-29T10:00:00Z").toEpochMilli(); chart.setLastRefreshed(specificTime); // Get last refreshed time Long lastRefreshed = chart.getLastRefreshed(); if (lastRefreshed != null) { Instant refreshTime = Instant.ofEpochMilli(lastRefreshed); System.out.println("Last refreshed: " + refreshTime); } ``` ### Complete Properties Example ```java import java.time.Instant; Chart chart = Chart.builder() .tool("looker") .id("sales_performance") .title("Sales Performance Dashboard") .build(); // Set all chart-specific properties chart.setChartType("BAR") .setAccess("PUBLIC") .setExternalUrl("https://looker.company.com/dashboards/sales") .setChartUrl("https://looker.company.com/embed/charts/sales_performance") .setLastRefreshed(System.currentTimeMillis()); // Set lineage DatasetUrn salesDataset = DatasetUrn.createFromString( "urn:li:dataset:(urn:li:dataPlatform:snowflake,sales.transactions,PROD)"); chart.addInputDataset(salesDataset); // Add metadata chart.addTag("sales") .setDomain("urn:li:domain:Sales"); client.entities().upsert(chart); ``` ## Complete Example Here's a comprehensive example showing all chart operations: ```java import com.linkedin.common.OwnershipType; import datahub.client.v2.DataHubClientV2; import datahub.client.v2.entity.Chart; import java.io.IOException; import java.util.concurrent.ExecutionException; public class ChartExample { public static void main(String[] args) throws IOException, ExecutionException, InterruptedException { // Create client DataHubClientV2 client = DataHubClientV2.builder() .server("http://localhost:8080") .build(); try { // Create chart with basic metadata Chart chart = Chart.builder() .tool("looker") .id("regional_sales_chart") .title("Regional Sales Performance") .description("Quarterly sales broken down by region and product category") .build(); // Add tags for categorization chart.addTag("sales") .addTag("executive") .addTag("quarterly-review"); // Add owners chart.addOwner("urn:li:corpuser:sales_team", OwnershipType.TECHNICAL_OWNER) .addOwner("urn:li:corpuser:bi_team", OwnershipType.DATA_STEWARD); // Add glossary terms chart.addTerm("urn:li:glossaryTerm:SalesMetrics") .addTerm("urn:li:glossaryTerm:QuarterlyReporting"); // Set domain chart.setDomain("urn:li:domain:Sales"); // Add custom properties chart.addCustomProperty("dashboard", "executive_overview") .addCustomProperty("chart_type", "bar") .addCustomProperty("data_source", "snowflake") .addCustomProperty("refresh_schedule", "hourly"); // Upsert to DataHub (emits all accumulated patches) client.entities().upsert(chart); System.out.println("Successfully created chart: " + chart.getUrn()); System.out.println("Total patches: " + chart.getPendingPatches().size()); } finally { client.close(); } } } ``` ## Builder Options Reference | Method | Required | Description | | ----------------------- | -------- | ---------------------------------------------- | | `tool(String)` | ✅ Yes | BI tool identifier (e.g., "looker", "tableau") | | `id(String)` | ✅ Yes | Chart identifier within the tool | | `title(String)` | No | Chart title | | `description(String)` | No | Chart description | | `customProperties(Map)` | No | Map of custom key-value properties | ## Patch-Based Operations Chart entities now support patch-based operations similar to Dataset. All mutations (addTag, addOwner, etc.) create patch MCPs that accumulate until save(). This enables: - **Efficient batching**: Multiple operations in a single network call - **Incremental updates**: Only modified fields are sent to the server - **Fluent chaining**: Build complex metadata in a readable way Available patch operations: | Operation | Description | | ----------------------------------- | ---------------------------------- | | `addTag(String)` | Add a tag to the chart | | `removeTag(String)` | Remove a tag from the chart | | `addOwner(String, OwnershipType)` | Add an owner with ownership type | | `removeOwner(String)` | Remove an owner from the chart | | `addTerm(String)` | Add a glossary term to the chart | | `removeTerm(String)` | Remove a glossary term | | `setDomain(String)` | Set the domain for the chart | | `removeDomain()` | Remove the domain from the chart | | `setDescription(String)` | Update chart description | | `setTitle(String)` | Update chart title | | `addCustomProperty(String, String)` | Add or update a custom property | | `removeCustomProperty(String)` | Remove a custom property | | `setCustomProperties(Map)` | Replace all custom properties | | `setInputDatasets(List)` | Set input datasets (lineage) | | `addInputDataset(DatasetUrn)` | Add an input dataset (lineage) | | `removeInputDataset(DatasetUrn)` | Remove an input dataset (lineage) | | `setExternalUrl(String)` | Set external URL for the chart | | `setChartUrl(String)` | Set chart URL | | `setLastRefreshed(long)` | Set last refreshed timestamp | | `setChartType(String)` | Set chart type (BAR, LINE, etc.) | | `setAccess(String)` | Set access level (PUBLIC, PRIVATE) | See [Patch Operations Guide](./patch-operations.md) for more details on how patch-based updates work. ## Common Patterns ### Creating Multiple Charts ```java List chartIds = Arrays.asList("chart1", "chart2", "chart3"); for (String chartId : chartIds) { Chart chart = Chart.builder() .tool("looker") .id(chartId) .title("Chart " + chartId) .build(); client.entities().upsert(chart); } ``` ### Linking Charts to Dashboards Use custom properties to track relationships: ```java Chart chart = Chart.builder() .tool("tableau") .id("sales_chart") .build(); Map props = new HashMap<>(); props.put("dashboard_id", "executive_dashboard"); props.put("position", "top_left"); chart.setCustomProperties(props); client.entities().upsert(chart); ``` ## Updating Charts ### Load and Modify ```java // Load existing chart ChartUrn urn = new ChartUrn("looker", "my_chart"); Chart chart = client.entities().get(urn); // Modify chart.setDescription("Updated description"); // Save changes client.entities().update(chart); ``` ## Next Steps - **[Dataset Entity Guide](./dataset-entity.md)** - Comprehensive dataset operations - **[Entities Overview](./entities-overview.md)** - Common patterns across entities - **[Patch Operations](./patch-operations.md)** - Understanding incremental updates ## Examples ### Basic Chart Creation ```java {{ inline /metadata-integration/java/examples/src/main/java/io/datahubproject/examples/v2/ChartCreateExample.java show_path_as_comment }} ``` ### Comprehensive Chart with Metadata and Lineage ```java {{ inline /metadata-integration/java/examples/src/main/java/io/datahubproject/examples/v2/ChartFullExample.java show_path_as_comment }} ``` ### Chart Lineage Operations ```java {{ inline /metadata-integration/java/examples/src/main/java/io/datahubproject/examples/v2/ChartLineageExample.java show_path_as_comment }} ```