OpenMetadata/openmetadata-sdk
Mohit Yadav 7bb8e40b65
Fix column filtering on Lineage (#25353)
* Fix Column Filtering and add path preserve

* Preserve only column with matching filter

* Add Test

* update param

* Add UI work

* Lanaguage

* Add proper translations for column-filter locale keys (#25360)

* Initial plan

* Add proper translations for column-filter locale keys across all 18 languages

Co-authored-by: karanh37 <33024356+karanh37@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: karanh37 <33024356+karanh37@users.noreply.github.com>

* fix filtering

* Fix ui : Dropdown filters (Domains, Owners, Tag, Tier, Service, etc.) were not showing in the Impact Analysis view and normal lineage view.

* put back searchbox for column level

* Fix query_filter not working for tag/domain/tier in lineage APIs -> table level filtering

* fix: hasNodeLevelFilters bypassing ES filters causing empty results

* fix: tag filter incorrectly sent to column_filter on table-level page

* Fix Impact Analysis search and filtering with path preservation
  Summary of changes:
  - Backend: Path preservation for search, accurate pagination counts, wildcard query parsing, OR logic for name/displayName
  - Frontend: Column-level search now matches both table names and column names

* Table level: Search → query_filter (matches table names)                    Column level: Search → column_filter only (matches column names)

* Fix column impact analysis: depth-aware filtering, tag aggregation, and nested column support

* address gitar bot feedback : lineage filter — add service to path preservation, fix OR semantics, rename preserve_paths, guard NPE on fromEntity

* fix: use unfiltered depth counts in lineage pagination info, remove 10k doc fetch

* fix: Impact Analysis — fix upstream BFS, always run BFS unfiltered and apply query filter as in-memory post-filter to support multi-depth traversal, fix column
  filter OR-within-type semantics, rename preserve_paths param, and add integration tests

   instead of passing queryFilter into the BFS (which blocked traversal through non-matching intermediate nodes), we now run BFS with no
  filter to discover the full graph topology, then apply the filter after all nodes and edges are collected using the existing
  applyInMemoryFiltersWithPathPreservationForEntityCount.

* fix: lineage Impact Analysis — unfiltered BFS with post-filter for multi-depth traversal, upstream BFS direction fix, remove dead ES query column filter code, fix stale useCallback deps, add SDK methods and integration tests

* fix: remove column_filter from UI calls where backend doesn't support it (exportAsync, platformLineage, dataQualityLineage, paginationInfo), fix stale useCallback deps in LineageProvider

* fix: Impact Analysis — unfiltered BFS for multi-depth filter traversal, upstream direction fix, table/column tag separation, dead code cleanup, stale UI deps, node depth dropdown fix

* fix: remove dead columnFilter plumbing from CustomControls, clear column filters on Table mode switch, fix QueryFilterParser search+filter OR logic, add search combo integration tests, log warn on tag fetch failure

* fix: depth-based pagination sort

* ui: performance optimization — avoids redundant lookups

* handle matchesMultipleFiltersWithMetadata

* fix: upstream/downstream count not updating in table view

* fix UI changes

* fix api issues

* fix: Impact Analysis — move to ES-native filtering with unfiltered BFS, filtered pagination counts, tag name enrichment

* address comments

* fix: Impact Analysis — ES-native filtered traversal, batch tag enrichment, depth filtering with filters, SDK entityType support

* fix tests

* fix failing tests

* fix backend test

* add tests for code coverage

* add tests for code coverage

* fix: add id.keyword sub-field to  ES index mappings to fix lineage filter dropdowns for topics, dashboards, and other non-table entities

* address comments

* fix service type filter case

* address gitar bot feedback

* fix tests

* fix build

* Fix the bugs

* Fix the bugs

* Fix all things related to  Lineag, Impact Analysis

* Update generated TypeScript types

* Fix all things related to  Lineag, Impact Analysis

* Fix Mapping for ids for container and test suite

* test: enhance lineage spec to cover all the missing cases (#26796)

* test: enhance lineage spec to cover all the missing cases

* fix searchIndex mapping

* fix tests

* added filter spec

* fix filter issues

* fix lineageSearchSelect

* update database service filter tests

* iterate over all the entity for service filter

* update impact analysis fixes

* update tests management

* add missing test case

* fix tests

* fix column level lineage tests

* fix apiEndpoint issue

* improved lineage connection assertion

* fix tests

* fix column level linage issues

* fix missing import

* update test import from pages

* fix mlModel spell issue

* fix node pagination and right panel spec

* refactor lineage tests to improve entity creation and visibility checks

* fix license header

* fix build

* fix tests

* fix tests

* UI linter fixes

* address comments

* fix unit tests

* remove redundant method

* improve tests

* fix impact analysis tests

* fix impact analysis

* Fix Export via Async and add tests

* update tests

* fix issues

* Spotless fix

* fix impact analysis

* Fix issue with lineage export

* Fix serviceType filtering

* fix multiple calls issue

* fix lint issues

* fix uni tests

* fix test issues

* fix lineage settings spec

* fix all the tests

* Remove fix me

* fix lint issue

* fix failing specs

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: karanh37 <33024356+karanh37@users.noreply.github.com>
Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
Co-authored-by: sonika-shah <58761340+sonika-shah@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Sriharsha Chintalapani <harsha@getcollate.io>
Co-authored-by: Sriharsha Chintalapani <harshach@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-06 09:01:15 -07:00
..
src Fix column filtering on Lineage (#25353) 2026-04-06 09:01:15 -07:00
pom.xml Add Json Logging (#26357) 2026-03-31 16:15:07 -07:00
README.md Implement Modern Fluent API Pattern for OpenMetadata Java Client (#23239) 2025-09-29 16:07:02 -07:00

OpenMetadata SDK

A modern Java SDK for OpenMetadata that provides a fluent API for all operations.

Installation

Add the OpenMetadata SDK to your project:

<dependency>
  <groupId>org.open-metadata</groupId>
  <artifactId>openmetadata-sdk</artifactId>
  <version>1.5.0-SNAPSHOT</version>
</dependency>

Quick Start

Initialize the SDK

import org.openmetadata.sdk.config.OpenMetadataConfig;
import org.openmetadata.sdk.client.OpenMetadataClient;
import org.openmetadata.sdk.entities.*;
import org.openmetadata.sdk.api.*;

// Configure the client
OpenMetadataConfig config = OpenMetadataConfig.builder()
    .serverUrl("http://localhost:8585")
    .apiKey("your-api-key")
    .build();

// Initialize the client
OpenMetadataClient.initialize(config);

// Set default client for static APIs
Table.setDefaultClient(OpenMetadataClient.getInstance());
User.setDefaultClient(OpenMetadataClient.getInstance());
Search.setDefaultClient(OpenMetadataClient.getInstance());
Lineage.setDefaultClient(OpenMetadataClient.getInstance());
Bulk.setDefaultClient(OpenMetadataClient.getInstance());

Entity Operations

Tables

// Create a table
CreateTable request = new CreateTable()
    .withName("my_table")
    .withDatabase("my_database")
    .withColumns(columns);
Table table = Table.create(request);

// Retrieve a table by ID
Table table = Table.retrieve("table-id");

// Retrieve by fully qualified name with specific fields
Table table = Table.retrieveByName(
    "service.database.schema.table", 
    "owners,tags,columns"
);

// List tables with pagination
for (Table table : Table.list().autoPagingIterable()) {
    System.out.println(table.getName());
}

// List with filters
TableListParams params = TableListParams.builder()
    .limit(50)
    .database("my_database")
    .fields("owners,tags")
    .build();
TableCollection tables = Table.list(params);

// Update a table
table.setDescription("Updated description");
Table updated = table.save();

// Delete a table
Table.delete("table-id");

// Delete with options
Table.delete("table-id", true, true); // recursive, hardDelete

// Export/Import CSV
String csv = Table.exportCsv("table-name");
Table.importCsv("table-name", csvData);

Users

// Create a user
CreateUser request = new CreateUser()
    .withName("john.doe")
    .withEmail("john@example.com")
    .withIsAdmin(false);
User user = User.create(request);

// Retrieve a user
User user = User.retrieve("user-id");
User user = User.retrieveByName("john.doe", "teams,roles");

// List users
for (User user : User.list().autoPagingIterable()) {
    System.out.println(user.getEmail());
}

// Update a user
user.setDisplayName("John Doe");
User updated = user.save();

// Delete a user
User.delete("user-id");

All Entity Types Supported

The SDK provides the same fluent API for all 40+ OpenMetadata entity types:

  • Data Assets: Table, Database, DatabaseSchema, Dashboard, Pipeline, Topic, Container, Query, StoredProcedure, DashboardDataModel, SearchIndex, MlModel, Report
  • Services: DatabaseService, MessagingService, DashboardService, PipelineService, MlModelService, StorageService, SearchService, MetadataService, ApiService
  • Teams & Users: User, Team, Role, Policy
  • Governance: Glossary, GlossaryTerm, Classification, Tag, DataProduct, Domain
  • Quality: TestCase, TestSuite, TestDefinition, DataQualityDashboard
  • Ingestion: Ingestion, Workflow, Connection
  • Other: Type, Webhook, Kpi, Application, Persona, DocStore, Page, SearchQuery

Search Operations

// Simple search
String results = Search.search("customer");

// Search with index
String results = Search.search("customer", "table_search_index");

// Advanced search with pagination and sorting
String results = Search.search(
    "customer", 
    "table_search_index", 
    0, 100, 
    "name.keyword", "asc"
);

// Suggestions
String suggestions = Search.suggest("cust");
String suggestions = Search.suggest("cust", "table_search_index", 10);

// Aggregations
String aggregations = Search.aggregate("type:Table", "table_search_index", "database");

// Advanced search with custom request
Map<String, Object> searchRequest = Map.of(
    "query", Map.of("match", Map.of("name", "customer")),
    "size", 50
);
String results = Search.searchAdvanced(searchRequest);

// Reindex operations
Search.reindex("table");
Search.reindexAll();

// Using the builder
String results = Search.builder()
    .query("customer")
    .index("table_search_index")
    .from(0)
    .size(100)
    .sortField("name.keyword")
    .sortOrder("asc")
    .execute();

Lineage Operations

// Get lineage for an entity
String lineage = Lineage.getLineage("entity-fqn");

// Get lineage with depth control
String lineage = Lineage.getLineage("entity-fqn", "3", "2");

// Get entity lineage by type and ID
String lineage = Lineage.getEntityLineage("table", "entity-id");
String lineage = Lineage.getEntityLineage("table", "entity-id", "3", "2");

// Add lineage relationship
Lineage.addLineage(
    "source-entity-id", "table",
    "target-entity-id", "dashboard"
);

// Delete lineage
Lineage.deleteLineage("from-entity", "to-entity");

// Export lineage
String export = Lineage.exportLineage("table", "entity-id");

// Using the builder
String lineage = Lineage.builder()
    .entityType("table")
    .entityId("entity-id")
    .upstreamDepth(3)
    .downstreamDepth(2)
    .execute();

Bulk Operations

// Bulk create entities
List<Object> entities = Arrays.asList(entity1, entity2, entity3);
String result = Bulk.create("table", entities);

// Bulk update entities
List<Object> updates = Arrays.asList(updatedEntity1, updatedEntity2);
String result = Bulk.update("table", updates);

// Bulk delete entities
List<String> ids = Arrays.asList("id1", "id2", "id3");
String result = Bulk.delete("table", ids);

// Bulk add tags
List<String> entityIds = Arrays.asList("id1", "id2");
List<String> tags = Arrays.asList("tag1", "tag2");
Bulk.addTags("table", entityIds, tags);

// Bulk remove tags
Bulk.removeTags("table", entityIds, tags);

// Get operation status
String status = Bulk.getOperationStatus("operation-id");

// Using the builder
String result = Bulk.builder()
    .entityType("table")
    .entities(entities)
    .forCreate()
    .execute();

// Builder for bulk delete
String result = Bulk.builder()
    .entityType("table")
    .entityIds(ids)
    .forDelete()
    .execute();

Async Operations

All operations support async execution:

// Async entity operations
CompletableFuture<Table> future = Table.createAsync(request);
CompletableFuture<Table> future = Table.retrieveAsync("id");
CompletableFuture<Void> future = Table.deleteAsync("id", true, true);

// Async search
CompletableFuture<String> future = Search.searchAsync("query");
CompletableFuture<String> future = Search.suggestAsync("query");
CompletableFuture<String> future = Search.reindexAsync("table");

// Async lineage
CompletableFuture<String> future = Lineage.getLineageAsync("entity");
CompletableFuture<String> future = Lineage.addLineageAsync(lineageRequest);
CompletableFuture<String> future = Lineage.exportLineageAsync("table", "id");

// Async bulk operations
CompletableFuture<String> future = Bulk.createAsync("table", entities);
CompletableFuture<String> future = Bulk.deleteAsync("table", ids);

// Handle async results
future.thenAccept(result -> {
    System.out.println("Operation completed: " + result);
}).exceptionally(error -> {
    System.err.println("Operation failed: " + error);
    return null;
});

Advanced Configuration

// Full configuration options
OpenMetadataConfig config = OpenMetadataConfig.builder()
    .serverUrl("https://metadata.company.com")
    .apiKey("api-key")
    .connectionTimeoutMillis(30000)
    .readTimeoutMillis(60000)
    .writeTimeoutMillis(60000)
    .maxRetries(3)
    .retryIntervalMillis(1000)
    .build();

// JWT authentication
OpenMetadataConfig config = OpenMetadataConfig.builder()
    .serverUrl("https://metadata.company.com")
    .jwtToken("eyJhbGciOiJ...")
    .build();

Error Handling

try {
    Table table = Table.retrieve("table-id");
} catch (OpenMetadataException e) {
    if (e.getStatusCode() == 404) {
        System.err.println("Table not found");
    } else if (e.getStatusCode() == 401) {
        System.err.println("Authentication failed");
    } else {
        System.err.println("Error: " + e.getMessage());
    }
}

Auto-Pagination

The SDK automatically handles pagination for list operations:

// Iterate through all tables
for (Table table : Table.list().autoPagingIterable()) {
    processTable(table);
}

// Manual pagination control
TableListParams params = TableListParams.builder()
    .limit(100)
    .after("cursor-token")
    .build();
TableCollection tables = Table.list(params);

Thread Safety

The OpenMetadataClient is thread-safe and can be shared across multiple threads. The static API methods use a shared default client instance.

Examples

See the examples directory for complete working examples:

  • Basic CRUD operations
  • Bulk data import/export
  • Lineage management
  • Search and discovery
  • Async operations
  • Error handling

Contributing

Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.