Resources
Hierarchical, asset-like entities and the relationships between them. Create resources and the edges between them in one call; the server returns the persisted graph.
A resource's externalId is its identity: unique per tenant, stored exactly as you send
it, and compared without case. Mirror the tag your operation already maintains —
COM-99-PT-1034 is stored as COM-99-PT-1034, not rewritten.
External ids & naming →
Look up
Fetch by numeric id or external id (you can mix them). Lookup ignores case, so pump_1 and
PUMP_1 resolve to the same resource; what comes back keeps the spelling it was created
with.
- Java
- Python
- Rust
import ai.intellistream.datahub.models.IdCollection;
Resource pump = client.resources().getById(5677892).getItems().iterator().next();
DataWrapper<Resource> some = client.resources().byIds(List.of(
IdCollection.createFromExternalId("pump_1"),
IdCollection.createFromId(5677892)));
# pass entity objects, external-id strings, or numeric ids
resources = client.resources.by_ids(["pump_1", 5677892])
use dataplatform_rust_sdk::generic::IdAndExtId;
let resources = api.resources.by_ids(&vec![
IdAndExtId::from_external_id("pump_1"),
IdAndExtId::from_id(5677892),
]).await?;
Create resources and relations
Pass the resource forms (nodes) and the relation forms (edges); the call returns the
created graph — nodes plus server-assigned edges. Each resource needs at least one
label (a type tag such as Plant or Pump) — a node with none is rejected with
400 resource.needs.at.least.one.label. Labels and relationship types are both
upper-cased by the server. External ids are not: they are stored verbatim.
The call is all-or-nothing. Every external id in the batch is validated before anything
is written, so one item rejected by the
naming policy means nothing is created and the 400
names every offending item, not just the first. If the policy is set to warn instead, the
response carries a warnings array next to
items.
Two endpoint rules apply to every edge, on create and on update (an update can retarget an edge or change its type):
- A relation to a dataset must use the
BELONGS_TOrelationship type — that is the relation the dataset hierarchy and membership are built from, and anything else is rejected with a400. - A dataset → time-series edge is accepted only when the series has no dataset yet, or
already belongs to that very dataset (creating a series inside a dataset produces exactly
that membership edge). A series in a different dataset is rejected with a
400— a time-series has one dataset.
- Java
- Python
- Rust
ResourceForm plant = new ResourceForm();
plant.setExternalId("plant_oslo");
plant.setName("Oslo Plant");
plant.setLabels(List.of("Plant"));
ResourceForm pump = new ResourceForm();
pump.setExternalId("pump_1");
pump.setName("Pump 1");
pump.setLabels(List.of("Pump"));
RelForm contains = new RelForm();
contains.setName("contains");
contains.setFromExternalId("plant_oslo");
contains.setToExternalId("pump_1");
GraphDataWrapper<Resource, EdgeProxy> created = client.resources()
.create(List.of(plant, pump), List.of(contains));
System.out.println(created.getNodes().size() + " resources, "
+ created.getRelations().size() + " relations");
import datahub_sdk
plant = datahub_sdk.Resource(external_id="plant_oslo", name="Oslo Plant", labels=["Plant"])
pump = datahub_sdk.Resource(external_id="pump_1", name="Pump 1", labels=["Pump"])
contains = datahub_sdk.RelForm.by_external_ids("plant_oslo", "pump_1", "contains")
result = client.resources.create([plant, pump], [contains])
print(len(result.nodes), "resources,", len(result.relations), "relations")
use dataplatform_rust_sdk::resources::Resource;
use dataplatform_rust_sdk::relations::RelForm;
let mut plant = Resource::new();
plant.external_id = "plant_oslo".into();
plant.name = "Oslo Plant".into();
plant.labels = Some(vec!["Plant".into()]);
let mut pump = Resource::new();
pump.external_id = "pump_1".into();
pump.name = "Pump 1".into();
pump.labels = Some(vec!["Pump".into()]);
let contains = RelForm::by_external_ids("plant_oslo", "pump_1", "contains");
let created = api.resources.create(vec![plant, pump], vec![contains]).await?;
Search
Free-text / fuzzy search across resources.
- Java
- Python
- Rust
ResourceSearch search = new ResourceSearch();
search.setLimit(10);
search.getSearch().setQuery("pump");
DataWrapper<Resource> matches = client.resources().search(search);
Use filter(new ResourceRetreiver()) for structured filters (labels, metadata, parent).
form = datahub_sdk.SearchAndFilterForm(query="pump", limit=10)
matches = client.resources.search(form)
use dataplatform_rust_sdk::generic::{SearchAndFilterForm, SearchForm};
let form = SearchAndFilterForm {
search: Some(SearchForm { name: None, description: None, query: Some("pump".into()) }),
limit: Some(10),
filter: None,
};
let matches = api.resources.search(&form).await?;
Delete
Delete by id or external id; returns the removed graph.
- Java
- Python
- Rust
client.resources().delete(List.of(IdCollection.createFromExternalId("pump_1")));
client.resources.delete(["pump_1"])
api.resources.delete(&vec![IdAndExtId::from_external_id("pump_1")]).await?;
Traverse the graph
fetchRelated walks the graph outward from a starting resource and returns the
connected sub-graph — a ResourceNetwork of nodes, the edges between them, and
their labels. Traversal is undirected and bounded by depth (-1 = the whole
connected component), optionally filtered to specific relationship types. Use it for
relationship reasoning — root-cause correlation, blast radius — that a flat lookup
can't do. See Correlate alarms with the graph.
- Java
- Python
- Rust
// convenience: within `depth` hops of an external id
ResourceNetwork net = client.resources().fetchRelated("sensor_a", 5);
// or the full form, filtering which relationship types to follow
RelatedResourcesForm form = new RelatedResourcesForm();
form.setExternalId("sensor_a");
form.setDepth(5);
form.setRelationshipTypes(List.of("PART_OF"));
ResourceNetwork filtered = client.resources().fetchRelated(form);
net.nodes().forEach(n -> System.out.println(n.getExternalId()));
net = client.resources.fetch_related(
external_id="sensor_a", depth=5, relationship_types=["PART_OF"])
for node in net.nodes:
print(node.external_id)
for edge in net.edges:
print(edge.start, "->", edge.end, edge.relationship_type)
use dataplatform_rust_sdk::resources::RelatedResourcesForm;
let net = api.resources.fetch_related(
&RelatedResourcesForm::from_external_id("sensor_a")
.with_depth(5)
.with_relationship_types(vec!["PART_OF".into()])).await?;
for node in net.nodes() {
println!("{}", node.external_id);
}