DiscoverX

repository·master·Indexed 41 days ago

https://github.com/databrickslabs/discoverx

A Lakehouse administration tool for Databricks and Unity Catalog designed to automate tasks across multiple Delta tables. DiscoverX enables concurrent execution of SQL templates and Python functions, semantic column classification via dx.scan(), and multi-table data operations for GDPR compliance, such as Right of Access and Right of Erasure.

Tokens
4.2K
Snippets
16
Records
21
Agent score
50%

What's inside DiscoverX

  1. How multi-table operations work with SQL templates

    master

    DiscoverX allows you to execute a SQL template concurrently against all Delta tables matching a selection pattern. The results are returned as a single Spark DataFrame containing the union of all results.

    Available SQL Template Variables

    You can use the following variables within your SQL templates:

    • {full_table_name}: The full table name (catalog.schema.table)
    • {table_catalog}: The catalog name
    • {table_schema}: The schema name
    • {table_name}: The table name
    • {stack_string_columns}: A SQL expression stack(N, 'col1', col1, ... , 'colN', colN ) for all N columns of type string
    • {stack_all_columns_as_string}: A SQL expression stack(N, 'col1', cast(col1 AS string), ... , 'colN', cast(colN AS string)) for all N columns

    Common SQL Template Examples

    • Describe details: DESCRIBE DETAIL {full_table_name}
    • Show delta history: SHOW HISTORY {full_table_name}
    • Deep clone: CREATE TABLE IF NOT EXISTS {table_catalog}.{table_schema}_clone.{table_name} DEEP CLONE {full_table_name}
    • Vacuum: VACUUM {full_table_name}
    • Optimize: OPTIMIZE {full_table_name}
    • Tag: ALTER TABLE {full_table_name} SET TAGS ('tag_name' = 'tag_value')
  2. Preview generated SQL using .explain()

    master

    When performing operations like multi-table deletions, you can use the .explain() method instead of .display(). This allows you to preview the generated SQL statements that DiscoverX will execute without actually running them, which is a recommended safety step for destructive operations.

    # Use .explain() to preview the SQL instead of executing it
    dx.from_tables("*.*.*")\\
      .having_columns("user_id")\\
      .with_sql("DELETE FROM {full_table_name} WHERE `user_id` IN (1, 2, 3)")\\
      .explain()
  3. How multi-table operations work with Python functions

    master

    DiscoverX can concurrently apply Python functions to multiple assets. When using Python functions, you have access to a table_info object containing metadata for each table.

    table_info Properties

    • catalog: The catalog name
    • schema: The schema name
    • table: The table name
    • columns: A list of ColumnInfo objects, each containing name, data_type, and partition_index.
    • tags: A list of TagsInfo objects, containing column_tags, table_tags, schema_tags, and catalog_tags.
      • Note: Tags are only populated if the from_tables(...) operation is followed by .with_tags(True).
  4. Vacuum multiple tables using DiscoverX

    master

    To maintain table health and remove unused files from Delta tables, you can use DiscoverX to execute a VACUUM command across multiple tables simultaneously.

    Use the dx.from_tables() method with a wildcard pattern to select your target tables, and chain it with .with_sql() to pass the vacuum command. Use the {full_table_name} placeholder within your SQL string to ensure the command is applied to each table in the selection.

    To automate this, you can schedule a notebook containing this logic in a Databricks Workflow. When running as a job, you can pass the table pattern via the from_tables job parameter.

    dx.from_tables("*.*.*")\
      .with_sql("VACUUM {full_table_name}")\
      .display()
  5. Install and initialize DiscoverX

    master

    To use DiscoverX in a Databricks notebook, install the package using %pip. You can then initialize the DX class to start performing multi-table operations.

    Requirements:

    • A Databricks workspace
    • Unity Catalog
    %pip install dbl-discoverx
    from discoverx import DX
    dx = DX(locale="US")
  6. Perform semantic column classification with dx.scan()

    master

    Use dx.scan() to identify semantic data types (like IP addresses, emails, or SSNs) within your tables. By default, it scans a sample of 10,000 rows from each table specified. The resulting dataset includes a score column, which represents the fraction of matched records against the total records scanned for each rule.

    dx.scan(from_tables="*.*.*")
  7. Execute multi-table data gathering for GDPR Right of Access

    master

    When user data is spread across multiple tables that share a common identifier column (e.g., user_id), you can use DiscoverX to gather all relevant rows in a single operation.

    To do this, use dx.from_tables() to target your schema, filter for tables containing the specific identifier using .having_columns(), and then use .with_sql() to define a query that extracts the data. A common pattern is to use to_json(struct(*)) to capture the entire row content as a JSON string for easy consumption.

    Note on Limitations: This method only selects tables that explicitly contain the specified column; it does not automatically traverse or follow relational links to other tables.

    df = dx.from_tables("*.*.*") \
      .having_columns("user_id") \
      .with_sql("SELECT `user_id`, to_json(struct(*)) AS row_content FROM {full_table_name} WHERE `user_id` = 1") \
      .apply()
  8. Search for values across multiple tables with dx.search()

    master

    Use dx.search() to find a specific value across multiple tables.

    Prerequisite: You must scan for semantic classes before running a search to ensure the classification engine is aware of the schema's semantic structure.

    By default, the search automatically attempts to classify the search term and restricts the search to columns that match those rule classes. You can refine this behavior by explicitly specifying classes or setting a minimum classification score.

    # Basic search across all tables and schemas
    dx.search("example_email@databricks.com", from_tables="*.*.*")
  9. Save and load semantic scan results

    master

    Once a scan is complete, you can persist the results to a Delta table or retrieve them in a different session.

    • To save results: Use dx.save(full_table_name=<your-table-name>).
    • To load results: Use dx.load(full_table_name=<your-table-name>).
    dx.save(full_table_name="my_catalog.my_schema.scan_results")
    
    # Later or in a different session
    dx.load(full_table_name="my_catalog.my_schema.scan_results")
  10. Perform GDPR Right of Erasure (Delete) across multiple tables

    master

    If user data is spread across multiple tables that share a common identifier column (e.g., user_id), you can use DiscoverX to execute a single delete statement across all matching tables simultaneously.

    Use .from_tables() to select your scope, .having_columns() to filter for tables containing the specific identifier, and .with_sql() to define the deletion logic using the {full_table_name} placeholder.

    dx.from_tables("*.*.*")\\
      .having_columns("user_id")\\
      .with_sql("DELETE FROM {full_table_name} WHERE `user_id` IN (1, 2, 3)")\\
      .display() 
  11. Perform multi-table aggregations on classified columns

    master

    Because select_by_classes returns a DataFrame containing metadata about the classified columns, you can perform complex transformations and aggregations. The resulting DataFrame includes a classified_columns structure that allows you to access the original column name and the actual value found.

    To access these fields in a groupby or agg operation, use the following path pattern:

    • classified_columns.<class_name>.column: The name of the original column.
    • classified_columns.<class_name>.value: The actual value extracted from that column.

    Example: Counting occurrences of an IP address per day across all tables.

    df = (dx.select_by_classes(from_tables="*.*.*", by_classes=["dx_iso_date", "dx_ip_v4"])
        .groupby([
            "table_catalog", 
            "table_schema", 
            "table_name", 
            "classified_columns.dx_iso_date.column", 
            "classified_columns.dx_iso_date.value", 
            "classified_columns.dx_ip_v4.column"
        ])
        .agg(func.count("classified_columns.dx_ip_v4.value").alias("count"))
    )
  12. Select columns by semantic class using select_by_classes

    master

    After performing a scan, you can use dx.select_by_classes to extract all columns that have been assigned specific semantic classes across multiple tables. This is useful for gathering all instances of a specific data type (e.g., all email addresses or all ISO dates) regardless of their original column names or table locations.

    Use the following parameters:

    • from_tables: A glob pattern or list of tables to scan (e.g., "*.*.*" for all tables).
    • by_classes: A list of semantic class names to select (e.g., ["dx_iso_date", "dx_email"]).
    • min_score: (Optional) A threshold for the classification confidence score.
    dx.select_by_classes(from_tables="*.*.*", by_classes=["dx_iso_date", "dx_email"], min_score=None)