/*- * ============LICENSE_START======================================================= * SDC * ================================================================================ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= */ package org.openecomp.sdc.be.dao.janusgraph; import static org.apache.commons.collections.CollectionUtils.isEmpty; import fj.data.Either; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.StreamSupport; import lombok.Getter; import org.apache.commons.collections.MapUtils; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Element; import org.apache.tinkerpop.gremlin.structure.Property; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.janusgraph.core.JanusGraph; import org.janusgraph.core.JanusGraphEdge; import org.janusgraph.core.JanusGraphQuery; import org.janusgraph.core.JanusGraphVertex; import org.janusgraph.core.JanusGraphVertexQuery; import org.janusgraph.core.PropertyKey; import org.janusgraph.graphdb.query.JanusGraphPredicate; import org.openecomp.sdc.be.dao.api.exception.JanusGraphException; import org.openecomp.sdc.be.dao.jsongraph.GraphVertex; import org.openecomp.sdc.be.dao.jsongraph.types.EdgeLabelEnum; import org.openecomp.sdc.be.dao.jsongraph.types.EdgePropertyEnum; import org.openecomp.sdc.be.dao.jsongraph.types.JsonParseFlagEnum; import org.openecomp.sdc.be.dao.jsongraph.types.VertexTypeEnum; import org.openecomp.sdc.be.dao.jsongraph.utils.JsonParserUtils; import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels; import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum; import org.openecomp.sdc.be.datatypes.enums.GraphPropertyEnum; import org.openecomp.sdc.be.datatypes.enums.ModelTypeEnum; import org.openecomp.sdc.be.datatypes.tosca.ToscaDataDefinition; import org.openecomp.sdc.common.jsongraph.util.CommonUtility; import org.openecomp.sdc.common.jsongraph.util.CommonUtility.LogLevelEnum; import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode; import org.openecomp.sdc.common.log.wrappers.Logger; import org.springframework.beans.factory.annotation.Qualifier; public class JanusGraphDao { private static final Logger logger = Logger.getLogger(JanusGraphDao.class); private static final String FAILED_TO_GET_BY_CRITERIA_FOR_TYPE_AND_PROPERTIES = "Failed to get by criteria for type '{}' and properties '{}'"; @Getter private final JanusGraphClient janusGraphClient; public JanusGraphDao(@Qualifier("janusgraph-client") JanusGraphClient janusGraphClient) { this.janusGraphClient = janusGraphClient; logger.info("** JanusGraphDao created"); } public JanusGraphOperationStatus commit() { logger.debug("#commit - The operation succeeded. Doing commit..."); return janusGraphClient.commit(); } public JanusGraphOperationStatus rollback() { logger.debug("#rollback - The operation failed. Doing rollback..."); return janusGraphClient.rollback(); } /** * @param graphVertex * @return */ public Either createVertex(GraphVertex graphVertex) { logger.trace("try to create vertex for ID [{}]", graphVertex.getUniqueId()); Either graph = janusGraphClient.getGraph(); if (graph.isLeft()) { try { JanusGraph tGraph = graph.left().value(); JanusGraphVertex vertex = tGraph.addVertex(); setVertexProperties(vertex, graphVertex); graphVertex.setVertex(vertex); return Either.left(graphVertex); } catch (Exception e) { logger .error(EcompLoggerErrorCode.DATA_ERROR, "JanusGraphDao", "Failed to create Node for ID '{}'", (Object) graphVertex.getUniqueId(), e); return Either.right(JanusGraphClient.handleJanusGraphException(e)); } } else { logger.debug("Failed to create vertex for ID '{}' {}", graphVertex.getUniqueId(), graph.right().value()); return Either.right(graph.right().value()); } } /** * @param name * @param value * @param label * @return */ public Either getVertexByPropertyAndLabel(GraphPropertyEnum name, Object value, VertexTypeEnum label) { return getVertexByPropertyAndLabel(name, value, label, JsonParseFlagEnum.ParseAll); } public Either getVertexByLabel(VertexTypeEnum label) { return janusGraphClient.getGraph().left().map(graph -> graph.query().has(GraphPropertyEnum.LABEL.getProperty(), label.getName()).vertices()) .left().bind(janusGraphVertices -> getFirstFoundVertex(JsonParseFlagEnum.NoParse, janusGraphVertices)); } private Either getFirstFoundVertex(JsonParseFlagEnum parseFlag, Iterable vertices) { Iterator iterator = vertices.iterator(); if (iterator.hasNext()) { JanusGraphVertex vertex = iterator.next(); GraphVertex graphVertex = createAndFill(vertex, parseFlag); return Either.left(graphVertex); } return Either.right(JanusGraphOperationStatus.NOT_FOUND); } /** * @param name * @param value * @param label * @param parseFlag * @return */ public Either getVertexByPropertyAndLabel(GraphPropertyEnum name, Object value, VertexTypeEnum label, JsonParseFlagEnum parseFlag) { Either graph = janusGraphClient.getGraph(); if (graph.isLeft()) { try { JanusGraph tGraph = graph.left().value(); @SuppressWarnings("unchecked") Iterable vertices = tGraph.query().has(name.getProperty(), value) .has(GraphPropertyEnum.LABEL.getProperty(), label.getName()).vertices(); java.util.Iterator iterator = vertices.iterator(); if (iterator.hasNext()) { JanusGraphVertex vertex = iterator.next(); GraphVertex graphVertex = createAndFill(vertex, parseFlag); return Either.left(graphVertex); } logger.debug("No vertex in graph for key = {} and value = {} label = {}", name, value, label); return Either.right(JanusGraphOperationStatus.NOT_FOUND); } catch (Exception e) { logger.debug("Failed to get vertex in graph for key ={} and value = {} label = {}", name, value, label); return Either.right(JanusGraphClient.handleJanusGraphException(e)); } } else { logger.debug("No vertex in graph for key ={} and value = {} label = {} error :{}", name, value, label, graph.right().value()); return Either.right(graph.right().value()); } } /** * @param id * @return */ public Either getVertexById(String id) { return getVertexById(id, JsonParseFlagEnum.ParseAll); } /** * @param id * @param parseFlag * @return */ public Either getVertexById(String id, JsonParseFlagEnum parseFlag) { Either graph = janusGraphClient.getGraph(); if (id == null) { logger.debug("No vertex in graph for id = {} ", id); return Either.right(JanusGraphOperationStatus.NOT_FOUND); } if (graph.isLeft()) { try { JanusGraph tGraph = graph.left().value(); @SuppressWarnings("unchecked") Iterable vertices = tGraph.query() .has(GraphPropertyEnum.UNIQUE_ID.getProperty(), id).vertices(); java.util.Iterator iterator = vertices.iterator(); if (iterator.hasNext()) { JanusGraphVertex vertex = iterator.next(); GraphVertex graphVertex = createAndFill(vertex, parseFlag); return Either.left(graphVertex); } else { logger.debug("No vertex in graph for id = {}", id); return Either.right(JanusGraphOperationStatus.NOT_FOUND); } } catch (Exception e) { logger.debug("Failed to get vertex in graph for id {} ", id); return Either.right(JanusGraphClient.handleJanusGraphException(e)); } } else { logger.debug("No vertex in graph for id {} error : {}", id, graph.right().value()); return Either.right(graph.right().value()); } } private void setVertexProperties(JanusGraphVertex vertex, GraphVertex graphVertex) throws IOException { if (graphVertex.getMetadataProperties() != null) { for (Map.Entry entry : graphVertex.getMetadataProperties().entrySet()) { if (entry.getValue() != null) { vertex.property(entry.getKey().getProperty(), entry.getValue()); } } } vertex.property(GraphPropertyEnum.LABEL.getProperty(), graphVertex.getLabel().getName()); Map json = graphVertex.getJson(); if (json != null) { String jsonStr = JsonParserUtils.toJson(json); vertex.property(GraphPropertyEnum.JSON.getProperty(), jsonStr); } Map jsonMetadata = graphVertex.getMetadataJson(); if (jsonMetadata != null) { String jsonMetadataStr = JsonParserUtils.toJson(jsonMetadata); vertex.property(GraphPropertyEnum.METADATA.getProperty(), jsonMetadataStr); } } public void setVertexProperties(Vertex vertex, Map properties) { for (Map.Entry entry : properties.entrySet()) { if (entry.getValue() != null) { vertex.property(entry.getKey(), entry.getValue()); } } } private GraphVertex createAndFill(JanusGraphVertex vertex, JsonParseFlagEnum parseFlag) { GraphVertex graphVertex = new GraphVertex(); graphVertex.setVertex(vertex); parseVertexProperties(graphVertex, parseFlag); return graphVertex; } public void parseVertexProperties(GraphVertex graphVertex, JsonParseFlagEnum parseFlag) { JanusGraphVertex vertex = graphVertex.getVertex(); Map properties = getVertexProperties(vertex); VertexTypeEnum label = VertexTypeEnum.getByName((String) (properties.get(GraphPropertyEnum.LABEL))); for (Map.Entry entry : properties.entrySet()) { GraphPropertyEnum key = entry.getKey(); switch (key) { case UNIQUE_ID: graphVertex.setUniqueId((String) entry.getValue()); break; case LABEL: graphVertex.setLabel(VertexTypeEnum.getByName((String) entry.getValue())); break; case COMPONENT_TYPE: String type = (String) entry.getValue(); if (type != null) { graphVertex.setType(ComponentTypeEnum.valueOf(type)); } break; case JSON: if (parseFlag == JsonParseFlagEnum.ParseAll || parseFlag == JsonParseFlagEnum.ParseJson) { String json = (String) entry.getValue(); Map jsonObj = JsonParserUtils.toMap(json, label.getClassOfJson()); graphVertex.setJson(jsonObj); } break; case METADATA: if (parseFlag == JsonParseFlagEnum.ParseAll || parseFlag == JsonParseFlagEnum.ParseMetadata) { String json = (String) entry.getValue(); Map metadatObj = JsonParserUtils.toMap(json); graphVertex.setMetadataJson(metadatObj); } break; default: graphVertex.addMetadataProperty(key, entry.getValue()); break; } } } public JanusGraphOperationStatus createEdge(GraphVertex from, GraphVertex to, EdgeLabelEnum label, Map properties) { return createEdge(from.getVertex(), to.getVertex(), label, properties); } public JanusGraphOperationStatus createEdge(Vertex from, Vertex to, EdgeLabelEnum label, Map properties) { logger.trace("Try to connect {} with {} label {} properties {}", from == null ? "NULL" : from.property(GraphPropertyEnum.UNIQUE_ID.getProperty()), to == null ? "NULL" : to.property(GraphPropertyEnum.UNIQUE_ID.getProperty()), label, properties); if (from == null || to == null) { logger.trace("No JanusGraph vertex for id from {} or id to {}", from == null ? "NULL" : from.property(GraphPropertyEnum.UNIQUE_ID.getProperty()), to == null ? "NULL" : to.property(GraphPropertyEnum.UNIQUE_ID.getProperty())); return JanusGraphOperationStatus.NOT_FOUND; } Edge edge = from.addEdge(label.name(), to); JanusGraphOperationStatus status; try { setEdgeProperties(edge, properties); status = JanusGraphOperationStatus.OK; } catch (IOException e) { logger.error(EcompLoggerErrorCode.DATA_ERROR, "JanusGraphDao", "Failed to set properties on edge properties [{}]", properties, e); status = JanusGraphOperationStatus.GENERAL_ERROR; } return status; } public Map getVertexProperties(Element element) { Map result = new HashMap<>(); if (element != null && CollectionUtils.isNotEmpty(element.keys())) { Map propertyMap = ElementHelper.propertyMap(element, element.keys().toArray(new String[element.keys().size()])); for (Entry entry : propertyMap.entrySet()) { String key = entry.getKey(); Object value = entry.getValue().value(); GraphPropertyEnum valueOf = GraphPropertyEnum.getByProperty(key); if (valueOf != null) { result.put(valueOf, value); } } // add print to properties that can't be converted by enum } return result; } public Map getEdgeProperties(Element element) { Map result = new HashMap<>(); if (element != null && CollectionUtils.isNotEmpty(element.keys())) { Map propertyMap = ElementHelper.propertyMap(element, element.keys().toArray(new String[element.keys().size()])); for (Entry entry : propertyMap.entrySet()) { String key = entry.getKey(); Object value = entry.getValue().value(); EdgePropertyEnum valueOf = EdgePropertyEnum.getByProperty(key); if (valueOf != null) { if (valueOf == EdgePropertyEnum.INSTANCES) { List list = JsonParserUtils.toList((String) value, String.class); result.put(valueOf, list); } else { result.put(valueOf, value); } } } } return result; } public void setEdgeProperties(Element element, Map properties) throws IOException { if (properties != null && !properties.isEmpty()) { Object[] propertyKeyValues = new Object[properties.size() * 2]; int i = 0; for (Entry entry : properties.entrySet()) { propertyKeyValues[i++] = entry.getKey().getProperty(); Object value = entry.getValue(); if (entry.getKey() == EdgePropertyEnum.INSTANCES) { String jsonStr = JsonParserUtils.toJson(value); propertyKeyValues[i++] = jsonStr; } else { propertyKeyValues[i++] = entry.getValue(); } } ElementHelper.attachProperties(element, propertyKeyValues); } } public Either, JanusGraphOperationStatus> getByCriteria(VertexTypeEnum type, Map props) { return getByCriteria(type, props, JsonParseFlagEnum.ParseAll); } public Either, JanusGraphOperationStatus> getByCriteria(VertexTypeEnum type, Map props, JsonParseFlagEnum parseFlag) { Either graph = janusGraphClient.getGraph(); if (graph.isLeft()) { try { JanusGraph tGraph = graph.left().value(); JanusGraphQuery query = tGraph.query(); if (type != null) { query = query.has(GraphPropertyEnum.LABEL.getProperty(), type.getName()); } if (props != null && !props.isEmpty()) { for (Map.Entry entry : props.entrySet()) { query = query.has(entry.getKey().getProperty(), entry.getValue()); } } Iterable vertices = query.vertices(); if (vertices == null) { return Either.right(JanusGraphOperationStatus.NOT_FOUND); } Iterator iterator = vertices.iterator(); List result = new ArrayList<>(); while (iterator.hasNext()) { JanusGraphVertex vertex = iterator.next(); getVertexProperties(vertex); GraphVertex graphVertex = createAndFill(vertex, parseFlag); result.add(graphVertex); } logger.debug("Number of fetched nodes in graph for criteria : from type = {} and properties = {} is {}", type, props, result.size()); if (result.size() == 0) { return Either.right(JanusGraphOperationStatus.NOT_FOUND); } return Either.left(result); } catch (Exception e) { logger.debug("Failed get by criteria for type = {} and properties = {}", type, props, e); return Either.right(JanusGraphClient.handleJanusGraphException(e)); } } else { logger.debug("Failed get by criteria for type ={} and properties = {} error : {}", type, props, graph.right().value()); return Either.right(graph.right().value()); } } public Either, JanusGraphOperationStatus> getByCriteria(final VertexTypeEnum type, final Map props, final Map hasNotProps, final JsonParseFlagEnum parseFlag, final String model) { return getByCriteria(type, props, hasNotProps, null, parseFlag, model, false); } public Either, JanusGraphOperationStatus> getByCriteria(final VertexTypeEnum type, final Map props, final Map hasNotProps, final JsonParseFlagEnum parseFlag, final String model, final boolean includeNormativeExtensionModels) { return getByCriteria(type, props, hasNotProps, null, parseFlag, model, includeNormativeExtensionModels); } public Either, JanusGraphOperationStatus> getByCriteria(final VertexTypeEnum type, final Map hasProps, final Map hasNotProps, final Map> predicates, final JsonParseFlagEnum parseFlag, final String model) { return getByCriteria(type, hasProps, hasNotProps, predicates, parseFlag, model, false); } public Either, JanusGraphOperationStatus> getByCriteria(final VertexTypeEnum type, final Map hasProps, final Map hasNotProps, final Map> predicates, final JsonParseFlagEnum parseFlag, final String model, final boolean includeNormativeExtensionModels) { Either graph = janusGraphClient.getGraph(); if (graph.isLeft()) { try { JanusGraphQuery query = getJanusGraphQuery(type, hasProps, hasNotProps, predicates, graph.left().value()); final Iterable vertices = query.vertices(); if (vertices == null || !vertices.iterator().hasNext()) { return Either.right(JanusGraphOperationStatus.NOT_FOUND); } final Predicate filterPredicate = StringUtils.isEmpty(model) ? this::vertexNotConnectedToAnyModel : vertex -> vertexValidForModel(vertex, model, includeNormativeExtensionModels); final List verticesForModel = StreamSupport.stream(vertices.spliterator(), false).filter(filterPredicate) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(verticesForModel)) { return Either.right(JanusGraphOperationStatus.NOT_FOUND); } final List result = new ArrayList<>(); verticesForModel.forEach(vertex -> result.add(createAndFill(vertex, parseFlag))); logger.debug("Number of fetched nodes in graph for criteria : from type '{}' and properties '{}' is '{}'", type, hasProps, result.size()); return Either.left(result); } catch (Exception e) { logger.debug(FAILED_TO_GET_BY_CRITERIA_FOR_TYPE_AND_PROPERTIES, type, hasProps, e); return Either.right(JanusGraphClient.handleJanusGraphException(e)); } } else { logger.debug("Failed to get by criteria for type '{}' and properties '{}'. Error : '{}'", type, hasProps, graph.right().value()); return Either.right(graph.right().value()); } } public Either, JanusGraphOperationStatus> getByCriteria(VertexTypeEnum type, Map props, Map hasNotProps, JsonParseFlagEnum parseFlag) { return getByCriteria(type, props, hasNotProps, null, parseFlag); } public Either, JanusGraphOperationStatus> getByCriteria(final VertexTypeEnum type, final Map hasProps, final Map hasNotProps, final Map> predicates, final JsonParseFlagEnum parseFlag) { final Either graph = janusGraphClient.getGraph(); if (graph.isLeft()) { try { JanusGraphQuery query = getJanusGraphQuery(type, hasProps, hasNotProps, predicates, graph.left().value()); Iterable vertices = query.vertices(); if (vertices == null || !vertices.iterator().hasNext()) { return Either.right(JanusGraphOperationStatus.NOT_FOUND); } List result = new ArrayList<>(); vertices.forEach(vertex -> result.add(createAndFill(vertex, parseFlag))); logger.debug("Number of fetched nodes in graph for criteria : from type '{}' and properties '{}' is '{}'", type, hasProps, result.size()); return Either.left(result); } catch (Exception e) { logger.debug(FAILED_TO_GET_BY_CRITERIA_FOR_TYPE_AND_PROPERTIES, type, hasProps, e); return Either.right(JanusGraphClient.handleJanusGraphException(e)); } } else { logger.debug("Failed to get by criteria for type '{}' and properties '{}'. Error : '{}'", type, hasProps, graph.right().value()); return Either.right(graph.right().value()); } } /** * Finds the vertices that have the given invariant id and any additional property provided. * * @param invariantUuid the invariant uuid * @param additionalPropertiesToMatch any additional property to match along with the {@link GraphPropertyEnum#INVARIANT_UUID} * @return the list of vertex that has the given invariant uuid * @throws JanusGraphException if the find operation was returned an error status */ public List findAllVertexByInvariantUuid(final String invariantUuid, final Map additionalPropertiesToMatch) { final Map propertiesToMatch = new EnumMap<>(GraphPropertyEnum.class); if (MapUtils.isNotEmpty(additionalPropertiesToMatch)) { propertiesToMatch.putAll(additionalPropertiesToMatch); } propertiesToMatch.put(GraphPropertyEnum.INVARIANT_UUID, invariantUuid); final Either, JanusGraphOperationStatus> vertexEither = getByCriteria(null, propertiesToMatch, JsonParseFlagEnum.ParseMetadata); if (vertexEither.isRight()) { final JanusGraphOperationStatus status = vertexEither.right().value(); if (status == JanusGraphOperationStatus.NOT_FOUND) { return Collections.emptyList(); } final String errorMsg = String.format("Couldn't fetch vertex with invariantUUId '%s'. Status was '%s'", invariantUuid, status); throw new JanusGraphException(status, errorMsg); } final List vertices = vertexEither.left().value(); if (vertices == null || vertices.isEmpty()) { return Collections.emptyList(); } return vertices; } private boolean vertexValidForModel(final JanusGraphVertex vertex, final String model, final boolean includeNormativeExtensions) { final String vertexLabel = (String) vertex.property(GraphPropertyEnum.LABEL.getProperty()).value(); final VertexTypeEnum vertexType = VertexTypeEnum.getByName(vertexLabel); final GraphEdgeLabels edgeLabel = VertexTypeEnum.TOPOLOGY_TEMPLATE.equals(vertexType) ? GraphEdgeLabels.MODEL : GraphEdgeLabels.MODEL_ELEMENT; final Either>, JanusGraphOperationStatus> modelVertices = getParentVertices(vertex, edgeLabel); return modelVertices.isLeft() && modelVertices.left().value().stream() .anyMatch(vertexPair -> modelVertexMatchesModel(vertexPair.getLeft(), model, includeNormativeExtensions)); } private boolean modelVertexMatchesModel(final JanusGraphVertex modelVertex, final String model, final boolean includeNormativeExtensions) { if (model.equals((String) modelVertex.property("name").value())) { return true; } final Either>, JanusGraphOperationStatus> derivedModels = getParentVertices(modelVertex, GraphEdgeLabels.DERIVED_FROM); if (derivedModels.isLeft() && derivedModels.left().value().stream() .anyMatch(derivedModel -> modelVertexMatchesModel(derivedModel.left, model, includeNormativeExtensions))) { return true; } if (includeNormativeExtensions && isANormativeExtension(modelVertex)) { final Either, JanusGraphOperationStatus> derivedFromModels = getChildrenVertices(modelVertex, EdgeLabelEnum.DERIVED_FROM); return derivedFromModels.isLeft() && derivedFromModels.left().value().stream() .anyMatch(derivedFromModel -> model.equals((String) derivedFromModel.property("name").value())); } return false; } private boolean isANormativeExtension(final JanusGraphVertex modelVertex) { return ModelTypeEnum.NORMATIVE_EXTENSION.getValue().equals((String) modelVertex.property(GraphPropertyEnum.MODEL_TYPE.getProperty()).value()); } private Either>, JanusGraphOperationStatus> getParentVertices( final JanusGraphVertex rootVertex, final GraphEdgeLabels edgeType) { return getEdgeVertices(rootVertex, Direction.IN, edgeType); } private Either>, JanusGraphOperationStatus> getEdgeVertices( final JanusGraphVertex rootVertex, final Direction direction, final GraphEdgeLabels edgeType) { final List> immutablePairs = new ArrayList<>(); final Iterator edgesCreatorIterator = rootVertex.edges(direction, edgeType.getProperty()); if (edgesCreatorIterator != null) { while (edgesCreatorIterator.hasNext()) { Edge edge = edgesCreatorIterator.next(); JanusGraphVertex vertex = Direction.OUT.equals(direction) ? (JanusGraphVertex) edge.inVertex() : (JanusGraphVertex) edge.outVertex(); ImmutablePair immutablePair = new ImmutablePair<>(vertex, edge); immutablePairs.add(immutablePair); } } if (immutablePairs.isEmpty()) { return Either.right(JanusGraphOperationStatus.NOT_FOUND); } return Either.left(immutablePairs); } private boolean vertexNotConnectedToAnyModel(final JanusGraphVertex vertex) { String vt = (String) vertex.property(GraphPropertyEnum.LABEL.getProperty()).value(); VertexTypeEnum vertexType = VertexTypeEnum.getByName(vt); EdgeLabelEnum edgeLabel = VertexTypeEnum.TOPOLOGY_TEMPLATE.equals(vertexType) ? EdgeLabelEnum.MODEL : EdgeLabelEnum.MODEL_ELEMENT; return !vertex.edges(Direction.IN, edgeLabel.name()).hasNext(); } public Either, JanusGraphOperationStatus> getCatalogOrArchiveVertices(boolean isCatalog) { Either graphEither = janusGraphClient.getGraph(); if (graphEither.isLeft()) { try { JanusGraph graph = graphEither.left().value(); String vertexType = isCatalog ? VertexTypeEnum.CATALOG_ROOT.getName() : VertexTypeEnum.ARCHIVE_ROOT.getName(); Iterable vertexIterable = graph.query().has(GraphPropertyEnum.LABEL.getProperty(), vertexType).vertices(); if (vertexIterable == null) { logger.debug("Failed to fetch catalog vertex"); return Either.right(JanusGraphOperationStatus.GENERAL_ERROR); } JanusGraphVertex catalogVertex = vertexIterable.iterator().next(); if (catalogVertex == null) { logger.debug("Failed to fetch catalog vertex"); return Either.right(JanusGraphOperationStatus.GENERAL_ERROR); } String edgeLabel = isCatalog ? EdgeLabelEnum.CATALOG_ELEMENT.name() : EdgeLabelEnum.ARCHIVE_ELEMENT.name(); Iterator adjacentVertices = catalogVertex.vertices(Direction.OUT, edgeLabel); return Either.left(adjacentVertices); } catch (Exception e) { logger.debug("Failed get by criteria: ", e); return Either.right(JanusGraphClient.handleJanusGraphException(e)); } } else { logger.debug("Failed get by criteria : {}", graphEither.right().value()); return Either.right(graphEither.right().value()); } } private void buildMultipleNegateQueryFromList(Map.Entry entry, JanusGraphQuery query) { for (Object listItem : (List) entry.getValue()) { query.hasNot(entry.getKey().getProperty(), listItem); } } /** * @param parentVertex * @param edgeLabel * @param parseFlag * @return */ public Either getChildVertex(GraphVertex parentVertex, EdgeLabelEnum edgeLabel, JsonParseFlagEnum parseFlag) { Either, JanusGraphOperationStatus> childrenVertices = getChildrenVertices(parentVertex, edgeLabel, parseFlag); if (childrenVertices.isRight()) { return Either.right(childrenVertices.right().value()); } return Either.left(childrenVertices.left().value().get(0)); } /** * @param parentVertex * @param edgeLabel * @return */ public Either getChildVertex(Vertex parentVertex, EdgeLabelEnum edgeLabel) { Either, JanusGraphOperationStatus> childrenVertices = getChildrenVertices(parentVertex, edgeLabel); if (childrenVertices.isRight()) { return Either.right(childrenVertices.right().value()); } return Either.left(childrenVertices.left().value().get(0)); } public Either getParentVertex(GraphVertex parentVertex, EdgeLabelEnum edgeLabel, JsonParseFlagEnum parseFlag) { Either, JanusGraphOperationStatus> childrenVertices = getParentVertices(parentVertex, edgeLabel, parseFlag); if (childrenVertices.isRight()) { return Either.right(childrenVertices.right().value()); } if (isEmpty(childrenVertices.left().value())) { return Either.right(JanusGraphOperationStatus.NOT_FOUND); } return Either.left(childrenVertices.left().value().get(0)); } public Either getParentVertex(Vertex parentVertex, EdgeLabelEnum edgeLabel) { Either, JanusGraphOperationStatus> childrenVertices = getParentVertices(parentVertex, edgeLabel); if (childrenVertices.isRight()) { return Either.right(childrenVertices.right().value()); } if (isEmpty(childrenVertices.left().value())) { return Either.right(JanusGraphOperationStatus.NOT_FOUND); } return Either.left(childrenVertices.left().value().get(0)); } /** * @param parentVertex * @param edgeLabel * @param parseFlag * @return */ public Either, JanusGraphOperationStatus> getChildrenVertices(GraphVertex parentVertex, EdgeLabelEnum edgeLabel, JsonParseFlagEnum parseFlag) { return getAdjacentVertices(parentVertex, edgeLabel, parseFlag, Direction.OUT); } public Either, JanusGraphOperationStatus> getParentVertices(GraphVertex parentVertex, EdgeLabelEnum edgeLabel, JsonParseFlagEnum parseFlag) { return getAdjacentVertices(parentVertex, edgeLabel, parseFlag, Direction.IN); } public Either, JanusGraphOperationStatus> getParentVertices(Vertex parentVertex, EdgeLabelEnum edgeLabel) { return getAdjacentVertices(parentVertex, edgeLabel, Direction.IN); } private Either, JanusGraphOperationStatus> getAdjacentVertices(Vertex parentVertex, EdgeLabelEnum edgeLabel, Direction direction) { List list = new ArrayList<>(); try { Either graphRes = janusGraphClient.getGraph(); if (graphRes.isRight()) { logger.error("Failed to retrieve graph. status is {}", graphRes); return Either.right(graphRes.right().value()); } Iterator edgesCreatorIterator = parentVertex.edges(direction, edgeLabel.name()); if (edgesCreatorIterator != null) { while (edgesCreatorIterator.hasNext()) { Edge edge = edgesCreatorIterator.next(); JanusGraphVertex vertex; if (direction == Direction.IN) { vertex = (JanusGraphVertex) edge.outVertex(); } else { vertex = (JanusGraphVertex) edge.inVertex(); } list.add(vertex); } } if (list.isEmpty()) { return Either.right(JanusGraphOperationStatus.NOT_FOUND); } } catch (Exception e) { logger.error("Failed to perform graph operation ", e); Either.right(JanusGraphClient.handleJanusGraphException(e)); } return Either.left(list); } /** * @param parentVertex * @param edgeLabel * @return */ public Either, JanusGraphOperationStatus> getChildrenVertices(Vertex parentVertex, EdgeLabelEnum edgeLabel) { return getAdjacentVertices(parentVertex, edgeLabel, Direction.OUT); } private Either, JanusGraphOperationStatus> getAdjacentVertices(GraphVertex parentVertex, EdgeLabelEnum edgeLabel, JsonParseFlagEnum parseFlag, Direction direction) { List list = new ArrayList<>(); Either, JanusGraphOperationStatus> adjacentVertices = getAdjacentVertices(parentVertex.getVertex(), edgeLabel, direction); if (adjacentVertices.isRight()) { return Either.right(adjacentVertices.right().value()); } adjacentVertices.left().value().stream().forEach(vertex -> list.add(createAndFill((JanusGraphVertex) vertex, parseFlag))); return Either.left(list); } /** * Searches Edge by received label and criteria * * @param vertex * @param label * @param properties * @return found edge or JanusGraphOperationStatus */ public Either getBelongingEdgeByCriteria(GraphVertex vertex, EdgeLabelEnum label, Map properties) { Either result = null; Edge matchingEdge = null; String notFoundMsg = "No edges in graph for criteria"; try { JanusGraphVertexQuery query = vertex.getVertex().query().labels(label.name()); if (properties != null && !properties.isEmpty()) { for (Map.Entry entry : properties.entrySet()) { query = query.has(entry.getKey().getProperty(), entry.getValue()); } } Iterable edges = query.edges(); if (edges == null) { CommonUtility.addRecordToLog(logger, LogLevelEnum.DEBUG, notFoundMsg); result = Either.right(JanusGraphOperationStatus.NOT_FOUND); } else { Iterator eIter = edges.iterator(); if (eIter.hasNext()) { matchingEdge = eIter.next(); } else { CommonUtility.addRecordToLog(logger, LogLevelEnum.DEBUG, notFoundMsg); result = Either.right(JanusGraphOperationStatus.NOT_FOUND); } } if (result == null) { result = Either.left(matchingEdge); } } catch (Exception e) { CommonUtility.addRecordToLog(logger, LogLevelEnum.DEBUG, "Exception occured during getting edge by criteria for component with id {}. {}", vertex.getUniqueId(), e); return Either.right(JanusGraphClient.handleJanusGraphException(e)); } return result; } public Either getEdgeByChildrenVertexProperties(GraphVertex vertex, EdgeLabelEnum label, Map properties) { Either result = null; Edge matchingEdge = null; String notFoundMsg = "No edges in graph for criteria"; try { Iterator edges = vertex.getVertex().edges(Direction.OUT, label.name()); while (edges.hasNext()) { matchingEdge = edges.next(); Vertex childV = matchingEdge.inVertex(); Map vertexProperties = getVertexProperties(childV); Optional> findNotMatch = properties.entrySet().stream() .filter(e -> vertexProperties.get(e.getKey()) == null || !vertexProperties.get(e.getKey()).equals(e.getValue())).findFirst(); if (!findNotMatch.isPresent()) { result = Either.left(matchingEdge); } } if (result == null) { //no match CommonUtility.addRecordToLog(logger, LogLevelEnum.DEBUG, notFoundMsg); result = Either.right(JanusGraphOperationStatus.NOT_FOUND); } } catch (Exception e) { CommonUtility.addRecordToLog(logger, LogLevelEnum.DEBUG, "Exception occured during getting edge by criteria for component with id {}. {}", vertex.getUniqueId(), e); return Either.right(JanusGraphClient.handleJanusGraphException(e)); } return result; } /** * Deletes Edge by received label and criteria * * @param vertex * @param label * @param properties * @return */ public Either deleteBelongingEdgeByCriteria(GraphVertex vertex, EdgeLabelEnum label, Map properties) { Either result = null; try { result = getBelongingEdgeByCriteria(vertex, label, properties); if (result.isLeft()) { Edge edge = result.left().value(); CommonUtility .addRecordToLog(logger, LogLevelEnum.TRACE, "Going to delete an edge with the label {} belonging to the vertex {} ", label.name(), vertex.getUniqueId()); edge.remove(); result = Either.left(edge); } else { CommonUtility .addRecordToLog(logger, LogLevelEnum.DEBUG, "Failed to find an edge with the label {} belonging to the vertex {} ", label.name(), vertex.getUniqueId()); } } catch (Exception e) { CommonUtility .addRecordToLog(logger, LogLevelEnum.DEBUG, "Exception occured during deleting an edge by criteria for the component with id {}. {}", vertex == null ? "NULL" : vertex.getUniqueId(), e); return Either.right(JanusGraphClient.handleJanusGraphException(e)); } return result; } @SuppressWarnings("unchecked") /** * Deletes an edge between vertices fromVertex and toVertex according to received label * * @param fromVertex * @param toVertex * @param label * @return */ public Either deleteEdge(GraphVertex fromVertex, GraphVertex toVertex, EdgeLabelEnum label) { return deleteEdge(fromVertex.getVertex(), label, fromVertex.getUniqueId(), toVertex.getUniqueId(), false); } public Either deleteAllEdges(GraphVertex fromVertex, GraphVertex toVertex, EdgeLabelEnum label) { return deleteEdge(fromVertex.getVertex(), label, fromVertex.getUniqueId(), toVertex.getUniqueId(), true); } private Either deleteEdge(JanusGraphVertex fromVertex, EdgeLabelEnum label, String uniqueIdFrom, String uniqueIdTo, boolean deleteAll) { Either result = null; Vertex problemV = null; try { Iterable edges = fromVertex.query().labels(label.name()).edges(); Iterator eIter = edges.iterator(); while (eIter.hasNext()) { Edge edge = eIter.next(); problemV = edge.inVertex(); String currVertexUniqueId = null; try { currVertexUniqueId = edge.inVertex().value(GraphPropertyEnum.UNIQUE_ID.getProperty()); } catch (Exception e) { // AutoHealing procedure logger.info("Corrupted vertex and edge were found and deleted ", e); if (problemV != null) { Map props = getVertexProperties(problemV); logger.debug("problematic Vertex properties:"); logger.debug("props size: {}", props.size()); for (Map.Entry entry : props.entrySet()) { logger.debug("{}:{}", entry.getKey(), entry.getValue()); } Either, JanusGraphOperationStatus> childrenVertices = getChildrenVertices(problemV, EdgeLabelEnum.VERSION); if (childrenVertices.isLeft()) { childrenVertices.left().value().size(); logger.debug("number of children that problematic Vertex has: {}", props.size()); } try { edge.remove(); } catch (Exception e1) { logger.debug("failed to remove problematic edge.", e1); } try { problemV.remove(); } catch (Exception e2) { logger.debug("failed to remove problematic vertex.", e2); } } continue; } if (currVertexUniqueId != null && currVertexUniqueId.equals(uniqueIdTo)) { CommonUtility.addRecordToLog(logger, LogLevelEnum.TRACE, "Going to delete an edge with the label {} between vertices {} and {}. ", label.name(), uniqueIdFrom, uniqueIdTo); edge.remove(); result = Either.left(edge); if (!deleteAll) { break; } } } if (result == null) { CommonUtility.addRecordToLog(logger, LogLevelEnum.DEBUG, "Failed to delete an edge with the label {} between vertices {} and {}. ", label.name(), uniqueIdFrom, uniqueIdTo); result = Either.right(JanusGraphOperationStatus.NOT_FOUND); } } catch (Exception e) { CommonUtility.addRecordToLog(logger, LogLevelEnum.DEBUG, "Exception occured during deleting an edge with the label {} between vertices {} and {}. {}", label.name(), uniqueIdFrom, uniqueIdTo, e); return Either.right(JanusGraphClient.handleJanusGraphException(e)); } return result; } public JanusGraphOperationStatus deleteEdgeByDirection(GraphVertex fromVertex, Direction direction, EdgeLabelEnum label) { try { Iterator edges = fromVertex.getVertex().edges(direction, label.name()); while (edges.hasNext()) { Edge edge = edges.next(); edge.remove(); } } catch (Exception e) { logger.debug("Failed to remove from vertex {} edges {} by direction {} ", fromVertex.getUniqueId(), label, direction, e); return JanusGraphClient.handleJanusGraphException(e); } return JanusGraphOperationStatus.OK; } /** * Updates vertex properties. Note that graphVertex argument should contain updated data * * @param graphVertex * @return */ public Either updateVertex(GraphVertex graphVertex) { CommonUtility.addRecordToLog(logger, LogLevelEnum.TRACE, "Going to update metadata of vertex with uniqueId {}. ", graphVertex.getUniqueId()); try { graphVertex.updateMetadataJsonWithCurrentMetadataProperties(); setVertexProperties(graphVertex.getVertex(), graphVertex); } catch (Exception e) { CommonUtility .addRecordToLog(logger, LogLevelEnum.DEBUG, "Failed to update metadata of vertex with uniqueId {}. ", graphVertex.getUniqueId(), e); return Either.right(JanusGraphClient.handleJanusGraphException(e)); } return Either.left(graphVertex); } /** * Fetches vertices by uniqueId according to received parse flag * * @param verticesToGet * @return */ public Either, JanusGraphOperationStatus> getVerticesByUniqueIdAndParseFlag( Map> verticesToGet) { Either, JanusGraphOperationStatus> result = null; Map vertices = new HashMap<>(); JanusGraphOperationStatus titatStatus; Either getVertexRes = null; for (Map.Entry> entry : verticesToGet.entrySet()) { if (entry.getValue().getKey() == GraphPropertyEnum.UNIQUE_ID) { getVertexRes = getVertexById(entry.getKey(), entry.getValue().getValue()); } else if (entry.getValue().getKey() == GraphPropertyEnum.USERID) { getVertexRes = getVertexByPropertyAndLabel(entry.getValue().getKey(), entry.getKey(), VertexTypeEnum.USER, entry.getValue().getValue()); } if (getVertexRes == null) { titatStatus = JanusGraphOperationStatus.ILLEGAL_ARGUMENT; CommonUtility .addRecordToLog(logger, LogLevelEnum.DEBUG, "Invalid vertex type label {} has been received. ", entry.getValue().getKey(), titatStatus); return Either.right(titatStatus); } if (getVertexRes.isRight()) { titatStatus = getVertexRes.right().value(); CommonUtility .addRecordToLog(logger, LogLevelEnum.DEBUG, "Failed to get vertex by id {} . Status is {}. ", entry.getKey(), titatStatus); result = Either.right(titatStatus); break; } else { vertices.put(entry.getKey(), getVertexRes.left().value()); } } if (result == null) { result = Either.left(vertices); } return result; } /** * Creates edge between "from" and "to" vertices with specified label and properties extracted from received edge * * @param from * @param to * @param label * @param edgeToCopy * @return */ public JanusGraphOperationStatus createEdge(Vertex from, Vertex to, EdgeLabelEnum label, Edge edgeToCopy) { return createEdge(from, to, label, getEdgeProperties(edgeToCopy)); } public JanusGraphOperationStatus replaceEdgeLabel(Vertex fromVertex, Vertex toVertex, Edge prevEdge, EdgeLabelEnum prevLabel, EdgeLabelEnum newLabel) { CommonUtility .addRecordToLog(logger, LogLevelEnum.TRACE, "Going to replace edge with label {} to {} between vertices {} and {}", prevLabel, newLabel, fromVertex != null ? fromVertex.property(GraphPropertyEnum.UNIQUE_ID.getProperty()) : "NULL", toVertex != null ? toVertex.property(GraphPropertyEnum.UNIQUE_ID.getProperty()) : "NULL"); JanusGraphOperationStatus result = createEdge(fromVertex, toVertex, newLabel, prevEdge); if (result == JanusGraphOperationStatus.OK) { prevEdge.remove(); } return result; } /** * Replaces previous label of edge with new label * * @param fromVertex * @param toVertex * @param prevLabel * @param newLabel * @return */ public JanusGraphOperationStatus replaceEdgeLabel(Vertex fromVertex, Vertex toVertex, EdgeLabelEnum prevLabel, EdgeLabelEnum newLabel) { Iterator prevEdgeIter = toVertex.edges(Direction.IN, prevLabel.name()); if (prevEdgeIter == null || !prevEdgeIter.hasNext()) { CommonUtility .addRecordToLog(logger, LogLevelEnum.DEBUG, "Failed to replace edge with label {} to {} between vertices {} and {}", prevLabel, newLabel, fromVertex.property(GraphPropertyEnum.UNIQUE_ID.getProperty()), toVertex.property(GraphPropertyEnum.UNIQUE_ID.getProperty())); return JanusGraphOperationStatus.NOT_FOUND; } else { return replaceEdgeLabel(fromVertex, toVertex, prevEdgeIter.next(), prevLabel, newLabel); } } /** * Updates metadata properties of vertex on graph. Json metadata property of the vertex will be updated with received properties too. * * @param vertex * @param properties * @return */ public JanusGraphOperationStatus updateVertexMetadataPropertiesWithJson(Vertex vertex, Map properties) { try { if (!MapUtils.isEmpty(properties)) { String jsonMetadataStr = (String) vertex.property(GraphPropertyEnum.METADATA.getProperty()).value(); Map jsonMetadataMap = JsonParserUtils.toMap(jsonMetadataStr); for (Map.Entry property : properties.entrySet()) { vertex.property(property.getKey().getProperty(), property.getValue()); jsonMetadataMap.put(property.getKey().getProperty(), property.getValue()); } vertex.property(GraphPropertyEnum.METADATA.getProperty(), JsonParserUtils.toJson(jsonMetadataMap)); } } catch (Exception e) { CommonUtility.addRecordToLog(logger, LogLevelEnum.DEBUG, "Exception occurred during update vertex metadata properties with json{}. {}", vertex.property(GraphPropertyEnum.UNIQUE_ID.getProperty()), e.getMessage()); return JanusGraphClient.handleJanusGraphException(e); } return JanusGraphOperationStatus.OK; } public JanusGraphOperationStatus disassociateAndDeleteLast(GraphVertex vertex, Direction direction, EdgeLabelEnum label) { try { Iterator edges = vertex.getVertex().edges(direction, label.name()); while (edges.hasNext()) { Edge edge = edges.next(); Vertex secondVertex; Direction reverseDirection; if (direction == Direction.IN) { secondVertex = edge.outVertex(); reverseDirection = Direction.OUT; } else { secondVertex = edge.inVertex(); reverseDirection = Direction.IN; } edge.remove(); CommonUtility.addRecordToLog(logger, LogLevelEnum.TRACE, "Edge {} with direction {} was removed from {}", label.name(), direction, vertex.getVertex()); Iterator restOfEdges = secondVertex.edges(reverseDirection, label.name()); if (!restOfEdges.hasNext()) { secondVertex.remove(); CommonUtility.addRecordToLog(logger, LogLevelEnum.TRACE, "This was last edge . Vertex {} was removed ", vertex.getUniqueId()); } } } catch (Exception e) { CommonUtility.addRecordToLog(logger, LogLevelEnum.DEBUG, "Exception occured during deleting an edge with the label {} direction {} from vertex {}. {}", label.name(), direction, vertex.getUniqueId(), e); return JanusGraphClient.handleJanusGraphException(e); } return JanusGraphOperationStatus.OK; } public Object getProperty(JanusGraphVertex vertex, String key) { PropertyKey propertyKey = janusGraphClient.getGraph().left().value().getPropertyKey(key); return vertex.valueOrNull(propertyKey); } public Object getProperty(Edge edge, EdgePropertyEnum key) { Object value = null; try { Property property = edge.property(key.getProperty()); if (property != null) { value = property.orElse(null); if (value != null && key == EdgePropertyEnum.INSTANCES) { return JsonParserUtils.toList((String) value, String.class); } return value; } } catch (Exception e) { } return value; } /** * @param vertexA * @param vertexB * @param label * @param direction * @return */ public JanusGraphOperationStatus moveEdge(GraphVertex vertexA, GraphVertex vertexB, EdgeLabelEnum label, Direction direction) { JanusGraphOperationStatus result = deleteEdgeByDirection(vertexA, direction, label); if (result != JanusGraphOperationStatus.OK) { logger.error("Failed to disassociate {} from element {}. error {} ", label, vertexA.getUniqueId(), result); return result; } JanusGraphOperationStatus createRelation; if (direction == Direction.IN) { createRelation = createEdge(vertexB, vertexA, label, null); } else { createRelation = createEdge(vertexA, vertexB, label, null); } if (createRelation != JanusGraphOperationStatus.OK) { return createRelation; } return JanusGraphOperationStatus.OK; } public Either getBelongingEdgeByCriteria(String parentId, EdgeLabelEnum label, Map properties) { Either getVertexRes = getVertexById(parentId, JsonParseFlagEnum.NoParse); if (getVertexRes.isRight()) { return Either.right(getVertexRes.right().value()); } return getBelongingEdgeByCriteria(getVertexRes.left().value(), label, properties); } private JanusGraphQuery getJanusGraphQuery(final VertexTypeEnum type, final Map hasProps, final Map hasNotProps, final Map> predicates, final JanusGraph graph) { JanusGraphQuery query = graph.query(); if (type != null) { query = query.has(GraphPropertyEnum.LABEL.getProperty(), type.getName()); } if (hasProps != null && !hasProps.isEmpty()) { for (Entry entry : hasProps.entrySet()) { query = query.has(entry.getKey().getProperty(), entry.getValue()); } } if (hasNotProps != null && !hasNotProps.isEmpty()) { for (Entry entry : hasNotProps.entrySet()) { if (entry.getValue() instanceof List) { buildMultipleNegateQueryFromList(entry, query); } else { query = query.hasNot(entry.getKey().getProperty(), entry.getValue()); } } } if (predicates != null && !predicates.isEmpty()) { for (Entry> entry : predicates.entrySet()) { query = query.has(entry.getKey(), entry.getValue().getKey(), entry.getValue().getValue()); } } return query; } }