protected void deleteEdge(AtlasEdge edge, boolean updateReverseAttribute, boolean force) throws AtlasException { // update reverse attribute if (updateReverseAttribute) { AttributeInfo attributeInfo = getAttributeForEdge(edge.getLabel()); if (attributeInfo.reverseAttributeName != null) { deleteEdgeBetweenVertices( edge.getInVertex(), edge.getOutVertex(), attributeInfo.reverseAttributeName); } } deleteEdge(edge, force); }
/** * Force delete is used to remove struct/trait in case of entity updates * * @param edge * @param typeCategory * @param isComposite * @param forceDeleteStructTrait * @return returns true if the edge reference is hard deleted * @throws AtlasException */ public boolean deleteEdgeReference( AtlasEdge edge, DataTypes.TypeCategory typeCategory, boolean isComposite, boolean forceDeleteStructTrait) throws AtlasException { LOG.debug("Deleting {}", string(edge)); boolean forceDelete = (typeCategory == DataTypes.TypeCategory.STRUCT || typeCategory == DataTypes.TypeCategory.TRAIT) ? forceDeleteStructTrait : false; if (typeCategory == DataTypes.TypeCategory.STRUCT || typeCategory == DataTypes.TypeCategory.TRAIT || (typeCategory == DataTypes.TypeCategory.CLASS && isComposite)) { // If the vertex is of type struct/trait, delete the edge and then the reference vertex as the // vertex is not shared by any other entities. // If the vertex is of type class, and its composite attribute, this reference vertex' // lifecycle is controlled // through this delete, hence delete the edge and the reference vertex. AtlasVertex vertexForDelete = edge.getInVertex(); // If deleting the edge and then the in vertex, reverse attribute shouldn't be updated deleteEdge(edge, false, forceDelete); deleteTypeVertex(vertexForDelete, typeCategory, forceDelete); } else { // If the vertex is of type class, and its not a composite attributes, the reference // AtlasVertex' lifecycle is not controlled // through this delete. Hence just remove the reference edge. Leave the reference AtlasVertex // as is // If deleting just the edge, reverse attribute should be updated for any references // For example, for the department type system, if the person's manager edge is deleted, // subordinates of manager should be updated deleteEdge(edge, true, false); } return !softDelete || forceDelete; }
/** * Deletes the edge between outvertex and inVertex. The edge is for attribute attributeName of * outVertex * * @param outVertex * @param inVertex * @param attributeName * @throws AtlasException */ protected void deleteEdgeBetweenVertices(Vertex outVertex, Vertex inVertex, String attributeName) throws AtlasException { LOG.debug( "Removing edge from {} to {} with attribute name {}", string(outVertex), string(inVertex), attributeName); String typeName = GraphHelper.getTypeName(outVertex); String outId = GraphHelper.getIdFromVertex(outVertex); Id.EntityState state = GraphHelper.getState(outVertex); if ((outId != null && RequestContext.get().isDeletedEntity(outId)) || state == Id.EntityState.DELETED) { // If the reference vertex is marked for deletion, skip updating the reference return; } IDataType type = typeSystem.getDataType(IDataType.class, typeName); AttributeInfo attributeInfo = getFieldMapping(type).fields.get(attributeName); String propertyName = GraphHelper.getQualifiedFieldName(type, attributeName); String edgeLabel = EDGE_LABEL_PREFIX + propertyName; Edge edge = null; switch (attributeInfo.dataType().getTypeCategory()) { case CLASS: // If its class attribute, its the only edge between two vertices if (attributeInfo.multiplicity.nullAllowed()) { edge = GraphHelper.getEdgeForLabel(outVertex, edgeLabel); if (shouldUpdateReverseAttribute) { GraphHelper.setProperty(outVertex, propertyName, null); } } else { // Cannot unset a required attribute. throw new NullRequiredAttributeException( "Cannot unset required attribute " + GraphHelper.getQualifiedFieldName(type, attributeName) + " on " + string(outVertex) + " edge = " + edgeLabel); } break; case ARRAY: // If its array attribute, find the right edge between the two vertices and update array // property List<String> elements = outVertex.getProperty(propertyName); if (elements != null) { elements = new ArrayList<>( elements); // Make a copy, else list.remove reflects on titan.getProperty() for (String elementEdgeId : elements) { Edge elementEdge = graphHelper.getEdgeByEdgeId(outVertex, edgeLabel, elementEdgeId); if (elementEdge == null) { continue; } Vertex elementVertex = elementEdge.getVertex(Direction.IN); if (elementVertex.getId().toString().equals(inVertex.getId().toString())) { edge = elementEdge; // TODO element.size includes deleted items as well. should exclude if (!attributeInfo.multiplicity.nullAllowed() && elements.size() <= attributeInfo.multiplicity.lower) { // Deleting this edge would violate the attribute's lower bound. throw new NullRequiredAttributeException( "Cannot remove array element from required attribute " + GraphHelper.getQualifiedFieldName(type, attributeName) + " on " + string(outVertex) + " " + string(elementEdge)); } if (shouldUpdateReverseAttribute) { // if composite attribute, remove the reference as well. else, just remove the edge // for example, when table is deleted, process still references the table // but when column is deleted, table will not reference the deleted column LOG.debug( "Removing edge {} from the array attribute {}", string(elementEdge), attributeName); elements.remove(elementEdge.getId().toString()); GraphHelper.setProperty(outVertex, propertyName, elements); break; } } } } break; case MAP: // If its map attribute, find the right edge between two vertices and update map property List<String> keys = outVertex.getProperty(propertyName); if (keys != null) { keys = new ArrayList<>( keys); // Make a copy, else list.remove reflects on titan.getProperty() for (String key : keys) { String keyPropertyName = GraphHelper.getQualifiedNameForMapKey(propertyName, key); String mapEdgeId = outVertex.getProperty(keyPropertyName); Edge mapEdge = graphHelper.getEdgeByEdgeId(outVertex, keyPropertyName, mapEdgeId); Vertex mapVertex = mapEdge.getVertex(Direction.IN); if (mapVertex.getId().toString().equals(inVertex.getId().toString())) { // TODO keys.size includes deleted items as well. should exclude if (attributeInfo.multiplicity.nullAllowed() || keys.size() > attributeInfo.multiplicity.lower) { edge = mapEdge; } else { // Deleting this entry would violate the attribute's lower bound. throw new NullRequiredAttributeException( "Cannot remove map entry " + keyPropertyName + " from required attribute " + GraphHelper.getQualifiedFieldName(type, attributeName) + " on " + string(outVertex) + " " + string(mapEdge)); } if (shouldUpdateReverseAttribute) { // remove this key LOG.debug( "Removing edge {}, key {} from the map attribute {}", string(mapEdge), key, attributeName); keys.remove(key); GraphHelper.setProperty(outVertex, propertyName, keys); GraphHelper.setProperty(outVertex, keyPropertyName, null); } break; } } } break; case STRUCT: case TRAIT: break; default: throw new IllegalStateException( "There can't be an edge from " + string(outVertex) + " to " + string(inVertex) + " with attribute name " + attributeName + " which is not class/array/map attribute"); } if (edge != null) { deleteEdge(edge, false); RequestContext requestContext = RequestContext.get(); GraphHelper.setProperty( outVertex, Constants.MODIFICATION_TIMESTAMP_PROPERTY_KEY, requestContext.getRequestTime()); requestContext.recordEntityUpdate(outId); } }