
## Manage asset relationships with attributes

URL: https://docs.atlan.com/product/capabilities/build-apps/sdks/python/how-tos/manage-relationship-attributes

> Create, retrieve, and delete attributed relationships programmatically using the Atlan Python SDK (pyatlan).

# Manage asset relationships with attributes

Atlan supports relationships between assets that can include attributes, similar to how assets themselves have attributes. These relationship-level attributes provide additional context and metadata about the connection between assets.

The SDK enables you to create, retrieve, and delete these attributed relationships programmatically.

## Relationships with attribute support

The following relationship types support attributes that you can set via the SDK:

> *Available attributed relationships — see full content on the documentation site.*

## Add user-defined relationship

This example demonstrates how to add `UserDefRelationship` between glossary terms. While this relationship type is currently visible in the Atlan UI for glossary terms, you can create any other supported relationship types that have attributes ([as listed above](#relationships-with-attribute-support)) between any asset types using similar steps shown in the snippet below. All these relationships will be persisted in the backend (metastore) even if they're not currently visible in the Atlan UI.

### Python

```python showLineNumbers title="Add user-defined relationship between terms"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import AtlasGlossaryTerm
from pyatlan.model.assets.relations import UserDefRelationship

client = AtlanClient()

# Create updater for the source term

term1_to_update = AtlasGlossaryTerm.updater( # (1)
 qualified_name="FpWBfqOfP0qZQ6tCpK10n@nrrnNyRABZTc6CEKwHh73",
 name="Policy",
 glossary_guid="55aaaa56-d8cd-4f19-8026-10d511a9b071"
)

# Create reference to the target term

term2 = AtlasGlossaryTerm.ref_by_guid(
 "f558a01a-2e16-440c-ba2d-fed2099e540a"
) # (2)

# Create the user-defined relationship with attributes

udr = UserDefRelationship( # (3)
 from_type_label="Sold by",
 to_type_label="Sells"
)

# Build and assign the relationship

term1_to_update.user_def_relationship_to = [ # (4)
 udr.user_def_relationship_to(term2)
]

# Save the relationship

response = client.asset.save(term1_to_update) # (5)
```

1. Create an updater for the source term using `.updater()` with the `qualified_name`, `name`, and `glossary_guid` of the term you want to add the relationship to.
2. Create a reference to the target term using `ref_by_guid()` or `ref_by_qualified_name()`.
3. Define the relationship by creating a `UserDefRelationship` object with attributes like `from_type_label` and `to_type_label`.
4. Build the relationship using the `user_def_relationship_to()` method and assign it to the `user_def_relationship_to` attribute.
5. Save the changes using `client.asset.save()` to persist the relationship in Atlan.

### Java

:::warning[Coming soon]
:::

### Raw REST API

```json showLineNumbers title="POST /api/meta/entity/bulk"
{
 "entities": [
 {
 "typeName": "AtlasGlossaryTerm",
 "attributes": {
 "name": "Policy", // (1)
 "qualifiedName": "FpWBfqOfP0qZQ6tCpK10n@nrrnNyRABZTc6CEKwHh73",
 "anchor": {
 "typeName": "AtlasBusiness Graph",
 "guid": "55aaaa56-d8cd-4f19-8026-10d511a9b071"
 },
 "userDefRelationshipTo": [
 {
 "typeName": "AtlasGlossaryTerm", // (2)
 "guid": "f558a01a-2e16-440c-ba2d-fed2099e540a",
 "relationshipAttributes": {
 "typeName": "UserDefRelationship", // (3)
 "attributes": {
 "toTypeLabel": "Sells",
 "fromTypeLabel": "Sold by"
 }
 },
 "relationshipType": "UserDefRelationship"
 }
 ]
 }
 }
 ]
}
```

1. Provide source asset details: Include the required attributes `name`, `qualifiedName`, and glossary `guid` for the term you want to add the relationship to.
2. Reference the target asset: Create a reference to the target term using its `guid`.
3. Define relationship attributes: Provide the necessary attributes for the `UserDefRelationship`, including `toTypeLabel` and `fromTypeLabel`.

:::tip[Relationship visibility]
User-defined relationships between glossary terms are visible in the Atlan UI. For other asset types, relationships are stored in the backend but may not be visible in the UI until support is added.
:::
![user_def_relationship.png](https://docs.atlan.com/img/product/capabilities/build-apps/sdks/python/user-def-relationship.png)

## Remove user-defined relationship

This example demonstrates how to remove `UserDefRelationship` between glossary terms. You can use the same approach to remove any attributed relationship type ([as listed above](#relationships-with-attribute-support)) between any asset types using the steps shown in the snippet below.

### Python

```python showLineNumbers title="Remove user-defined relationship between terms"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import AtlasGlossaryTerm

client = AtlanClient()

# Create updater for the source term

term1_to_update = AtlasGlossaryTerm.updater( # (1)
 qualified_name="FpWBfqOfP0qZQ6tCpK10n@nrrnNyRABZTc6CEKwHh73",
 name="Policy",
 glossary_guid="55aaaa56-d8cd-4f19-8026-10d511a9b071"
)

# Remove all outgoing relationships by setting to empty list

term1_to_update.user_def_relationship_to = [] # (2)

# Save the changes to remove the relationship

response = client.asset.save(term1_to_update) # (3)
```

1. Create an updater for the source term using `.updater()` with the `qualified_name`, `name`, and `glossary_guid` of the term you want to remove relationships from.
2. Clear relationships by assigning an empty list `[]` to `user_def_relationship_to` to remove all existing outgoing relationships.
3. Save the changes using `client.asset.save()` to persist the removal in Atlan.

### Java

:::warning[Coming soon]
:::

### Raw REST API

```json showLineNumbers title="POST /api/meta/entity/bulk"
{
 "entities": [
 {
 "typeName": "AtlasGlossaryTerm",
 "attributes": {
 "anchor": { // (1)
 "typeName": "AtlasBusiness Graph",
 "guid": "55aaaa56-d8cd-4f19-8026-10d511a9b071"
 },
 "name": "Policy",
 "qualifiedName": "FpWBfqOfP0qZQ6tCpK10n@nrrnNyRABZTc6CEKwHh73",
 "userDefRelationshipTo": [] // (2)
 }
 }
 ]
}
```

1. Provide source asset details: Include the required attributes `name`, `qualifiedName`, and glossary `guid` for the term you want to add the relationship to.
2. Clear relationships by assigning an empty array `[]` to `userDefRelationshipTo` to remove all existing outgoing relationships.

:::warning[Complete removal]
Setting `user_def_relationship_to = []` removes **all** outgoing user-defined relationships from the asset. To remove only specific relationships while keeping others, you would need to retrieve the existing relationships first and reconstruct the list without the unwanted ones.
:::

## Retrieve user-defined relationship

This example demonstrates how to retrieve `UserDefRelationship` between glossary terms. You can use the same approach to retrieve any attributed relationship type ([as listed above](#relationships-with-attribute-support)) between any asset types. While `UserDefRelationship` is visible in the UI for glossary terms, all relationships are persisted in the backend regardless of UI visibility.

### Python

```python showLineNumbers title="Retrieve user-defined relationships between terms"
from pyatlan.client.atlan import AtlanClient
from pyatlan.model.assets import AtlasGlossaryTerm
from pyatlan.model.assets.relations import UserDefRelationship
from pyatlan.model.query import FluentSearch

client = AtlanClient()

# Build search request for both terms in the relationship

request = (
 FluentSearch()
 .select()
 .where_some(AtlasGlossaryTerm.GUID.eq("f558a01a-2e16-440c-ba2d-fed2099e540a")) # (1)
 .where_some(AtlasGlossaryTerm.GUID.eq("540ee0f6-bb26-4b1a-88b7-31cfc26746b4")) # (2)
 .include_on_results(AtlasGlossaryTerm.USER_DEF_RELATIONSHIP_TO) # (3)
 .include_on_results(AtlasGlossaryTerm.USER_DEF_RELATIONSHIP_FROM) # (4)
 .include_relationship_attributes(True) # (5)
 .to_request()
)

# Execute the search

results = client.asset.search(request) # (6)
assert results and results.count == 2 # (7)

# Access the source term (with outgoing relationship)

source_term = results.current_page()[0] # (8)
if source_term.user_def_relationship_to:
 relationship = source_term.user_def_relationship_to[0]
 print(f"Source term GUID: {relationship.guid}")
 print(f"Relationship type: {relationship.type_name}")
 print(f"Relationship attributes: {relationship.attributes.relationship_attributes.attributes}")

# Access the target term (with incoming relationship)

target_term = results.current_page()[1]
if target_term.user_def_relationship_from:
 relationship = target_term.user_def_relationship_from[0]
 print(f"Target term GUID: {relationship.guid}")
 print(f"Relationship type: {relationship.type_name}")
 print(f"Relationship attributes: {relationship.attributes.relationship_attributes.attributes}")
```

1. Search for source term: Provide the GUID of the source term (relationship origin - `USER_DEF_RELATIONSHIP_TO`).
2. Search for target term: Provide the GUID of the target term (relationship destination - `USER_DEF_RELATIONSHIP_FROM`).
3. Include outgoing relationships: Make sure results include the `USER_DEF_RELATIONSHIP_TO` attribute.
4. Include incoming relationships: Make sure results include the `USER_DEF_RELATIONSHIP_FROM` attribute.
5. Include relationship attributes: Set to `True` to retrieve attributes for each relationship.
6. Execute search: Run the search request using `client.asset.search()`.
7. Verify results: Since we're retrieving two specific terms with relationships, `results.count` should be `2`.
8. Access results: Iterate through results or use `current_page()[index]` to access specific terms.

### Java

:::warning[Coming soon]
:::

### Raw REST API

```json showLineNumbers title="POST /api/meta/search/indexsearch"
{
 "attributes": [
 "userDefRelationshipTo", // (1)
 "userDefRelationshipFrom" // (2)
 ],
 "dsl": {
 "from": 0,
 "size": 100,
 "aggregations": {},
 "track_total_hits": true,
 "query": {
 "bool": {
 "must": [
 {
 "term": {
 "__superTypeNames.keyword": {
 "value": "Referenceable"
 }
 }
 }
 ],
 "should": [
 {
 "term": {
 "__guid": {
 "value": "f558a01a-2e16-440c-ba2d-fed2099e540a", // (3)
 "case_insensitive": false
 }
 }
 },
 {
 "term": {
 "__guid": {
 "value": "540ee0f6-bb26-4b1a-88b7-31cfc26746b4", // (4)
 "case_insensitive": false
 }
 }
 }
 ],
 "filter": [
 {
 "term": {
 "__superTypeNames.keyword": {
 "value": "Asset"
 }
 }
 },
 {
 "term": {
 "__state": {
 "value": "ACTIVE"
 }
 }
 },
 {
 "term": {
 "__superTypeNames.keyword": {
 "value": "Referenceable"
 }
 }
 }
 ],
 "minimum_should_match": 1
 }
 },
 "sort": [
 {
 "__guid": {
 "order": "asc"
 }
 }
 ]
 },
 "relationAttributes": [
 "name"
 ],
 "includeRelationshipAttributes": true // (5)
}
```

1. Include outgoing relationships: Make sure results include the `USER_DEF_RELATIONSHIP_TO` attribute.
2. Include incoming relationships: Make sure results include the `USER_DEF_RELATIONSHIP_FROM` attribute.
3. Search for source term: Provide the GUID of the source term (relationship origin - `USER_DEF_RELATIONSHIP_TO`).
4. Search for target term: Provide the GUID of the target term (relationship destination - `USER_DEF_RELATIONSHIP_FROM`).
5. Include relationship attributes: Set to `true` to retrieve attributes for each relationship.

:::tip[Relationship direction]
- `USER_DEF_RELATIONSHIP_TO`: Outgoing relationships from this asset
- `USER_DEF_RELATIONSHIP_FROM`: Incoming relationships to this asset
:::
:::note[Accessing attributes]
Relationship attributes are nested under `relationship.attributes.relationship_attributes.attributes`. Make sure to include `include_relationship_attributes(True)` in your search to retrieve these values.
:::

---
