Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
ATLAS-5156: Import failure due to missing support for handling multip…
…le relationship types under a single attribute
  • Loading branch information
sheetalshah1007 committed Apr 24, 2026
commit 3dacae1a14aa84dc3ba013bf6d457663ec139dd3
Original file line number Diff line number Diff line change
Expand Up @@ -1505,22 +1505,19 @@ private void mapRelationshipAttributes(AtlasEntity entity, AtlasEntityType entit
MetricRecorder metric = RequestContext.get().startMetricRecord("mapRelationshipAttributes");

if (op.equals(CREATE)) {
// Only map attributes present on the entity; unset names are null and must be skipped (not treated as clears).
for (String attrName : entityType.getRelationshipAttributes().keySet()) {
Object attrValue = entity.getRelationshipAttribute(attrName);
String relationType = AtlasEntityUtil.getRelationshipType(attrValue);
AtlasAttribute attribute = entityType.getRelationshipAttribute(attrName, relationType);

mapAttribute(attribute, attrValue, vertex, op, context);
Object attrValue = entity.getRelationshipAttribute(attrName);
if (attrValue != null) {
Comment thread
sheetalshah1007 marked this conversation as resolved.
Outdated
mapRelationshipAttributeWithMultipleTypes(entity, entityType, attrName, attrValue, vertex, op, context);
}
}
} else if (op.equals(UPDATE) || op.equals(PARTIAL_UPDATE)) {
// relationship attributes mapping
// relationship attributes mapping — include null when the key is present (explicit clear of that relationship attribute).
for (String attrName : entityType.getRelationshipAttributes().keySet()) {
if (entity.hasRelationshipAttribute(attrName)) {
Object attrValue = entity.getRelationshipAttribute(attrName);
String relationType = AtlasEntityUtil.getRelationshipType(attrValue);
AtlasAttribute attribute = entityType.getRelationshipAttribute(attrName, relationType);

mapAttribute(attribute, attrValue, vertex, op, context);
Object attrValue = entity.getRelationshipAttribute(attrName);
mapRelationshipAttributeWithMultipleTypes(entity, entityType, attrName, attrValue, vertex, op, context);
}
}
}
Expand All @@ -1533,6 +1530,96 @@ private void mapRelationshipAttributes(AtlasEntity entity, AtlasEntityType entit
LOG.debug("<== mapRelationshipAttributes({}, {})", op, entity.getTypeName());
}

private void mapRelationshipAttributeWithMultipleTypes(AtlasEntity entity, AtlasEntityType entityType, String attrName, Object attrValue, AtlasVertex vertex, EntityOperation op, EntityMutationContext context) throws AtlasBaseException {
LOG.debug("==> mapRelationshipAttributeWithMultipleTypes({}, {})", attrName, entity.getTypeName());
Set<String> relationshipTypeNames = entityType.getAttributeRelationshipTypes(attrName);

if (CollectionUtils.isEmpty(relationshipTypeNames)) {
Comment thread
sheetalshah1007 marked this conversation as resolved.
Outdated
Comment thread
sheetalshah1007 marked this conversation as resolved.
Outdated
// legacy path: infer type from the value and map once, same as before multi-type handling.
String relationType = AtlasEntityUtil.getRelationshipType(attrValue);
AtlasAttribute attribute = entityType.getRelationshipAttribute(attrName, relationType);
mapAttribute(attribute, attrValue, vertex, op, context);

return;
}

if (relationshipTypeNames.size() == 1 && !(attrValue instanceof Collection) && !(attrValue instanceof Map)) {
String onlyRelationshipType = relationshipTypeNames.iterator().next();
AtlasAttribute attribute = entityType.getRelationshipAttribute(attrName, onlyRelationshipType);
mapAttribute(attribute, attrValue, vertex, op, context);

return;
}

// Multi-type split for list/set payloads only. Map-valued relationship attributes (key -> ref) are not handled here;
// they fall through to the else branch as a single value.
if (attrValue instanceof Collection) {
Comment thread
sheetalshah1007 marked this conversation as resolved.
Collection<?> relatedObjects = (Collection<?>) attrValue;

// Group related objects by their appropriate relationship type
// e.g., hive_table elements should use hive_table_db relationship, iceberg_table elements should use iceberg_table_db
Map<String, List<Object>> elementsByRelationshipType = groupElementsByRelationshipType(
relatedObjects, attrName, relationshipTypeNames);

for (Map.Entry<String, List<Object>> entry : elementsByRelationshipType.entrySet()) {
String relationshipTypeName = entry.getKey();
List<Object> filteredElements = entry.getValue();

AtlasAttribute attribute = entityType.getRelationshipAttribute(attrName, relationshipTypeName);

if (attribute != null && CollectionUtils.isNotEmpty(filteredElements)) {
// Use the same collection type as the original (List or Set)
Object filteredValue = createCollectionOfSameType(attrValue, filteredElements);
Comment thread
sheetalshah1007 marked this conversation as resolved.

LOG.debug("Processing relationship type {} for attribute {} with {} elements",
relationshipTypeName, attrName, filteredElements.size());

mapAttribute(attribute, filteredValue, vertex, op, context);
}
}
} else {
// Single element - prefer explicit relationship type from the value
String appropriateRelType = AtlasEntityUtil.getRelationshipType(attrValue);
AtlasAttribute attribute = entityType.getRelationshipAttribute(attrName, appropriateRelType);
mapAttribute(attribute, attrValue, vertex, op, context);
}

LOG.debug("<== mapRelationshipAttributeWithMultipleTypes({}, {})", attrName, entity.getTypeName());
}

private Map<String, List<Object>> groupElementsByRelationshipType(Collection<?> relatedObjects,
String attrName,
Set<String> relationshipTypeNames) {
Map<String, List<Object>> elementsByRelationshipType = new HashMap<>();

// Group related objects by their appropriate relationship type
for (Object element : relatedObjects) {
String relationshipType = AtlasEntityUtil.getRelationshipType(element);

if (StringUtils.isEmpty(relationshipType) && relationshipTypeNames.size() == 1) {
relationshipType = relationshipTypeNames.iterator().next();
}

if (StringUtils.isEmpty(relationshipType) || !relationshipTypeNames.contains(relationshipType)) {
continue;
}

elementsByRelationshipType.computeIfAbsent(relationshipType, k -> new ArrayList<>()).add(element);
}

return elementsByRelationshipType;
}

private Object createCollectionOfSameType(Object originalValue, List<Object> filteredElements) {
if (originalValue instanceof List) {
return filteredElements;
} else if (originalValue instanceof Set) {
return new HashSet<>(filteredElements);
} else {
return filteredElements;
}
}

private void mapAttribute(AtlasAttribute attribute, Object attrValue, AtlasVertex vertex, EntityOperation op, EntityMutationContext context) throws AtlasBaseException {
boolean isDeletedEntity = context.isDeletedEntity(vertex);
AtlasType attrType = attribute.getAttributeType();
Expand Down
Loading