package org.jboss.windup.graph.service; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Iterator; import org.jboss.windup.graph.FramedElementInMemory; import org.jboss.windup.graph.GraphContext; import org.jboss.windup.graph.model.InMemoryVertexFrame; import org.jboss.windup.graph.model.WindupVertexFrame; import org.jboss.windup.graph.service.exception.NonUniqueResultException; import org.jboss.windup.util.ExecutionStatistics; import com.thinkaurelius.titan.core.TitanTransaction; import com.thinkaurelius.titan.core.attribute.Text; import com.thinkaurelius.titan.util.datastructures.IterablesUtil; import com.tinkerpop.blueprints.GraphQuery; import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.frames.FramedGraphQuery; import com.tinkerpop.frames.VertexFrame; import com.tinkerpop.frames.modules.typedgraph.TypeValue; import com.tinkerpop.gremlin.java.GremlinPipeline; public class GraphService<T extends WindupVertexFrame> implements Service<T> { private final Class<T> type; private final GraphContext context; public GraphService(GraphContext context, Class<T> type) { this.context = context; this.type = type; } @SuppressWarnings("unchecked") public static <T extends WindupVertexFrame> T refresh(GraphContext context, T frame) { return (T) context.getFramed().frame(frame.asVertex(), WindupVertexFrame.class); } @Override public void commit() { ExecutionStatistics.performBenchmarked("GraphService.commit", () -> { getGraphContext().getGraph().getBaseGraph().commit(); return null; }); } @Override public long count(final Iterable<?> obj) { return ExecutionStatistics.performBenchmarked("GraphService.count", () -> { GremlinPipeline<Iterable<?>, Object> pipe = new GremlinPipeline<>(); long result = pipe.start(obj).count(); return result; }); } @SuppressWarnings("unchecked") @Override public T createInMemory() { Class<?>[] resolvedTypes = new Class<?>[] { VertexFrame.class, InMemoryVertexFrame.class, type }; return (T) Proxy.newProxyInstance(this.type.getClassLoader(), resolvedTypes, new FramedElementInMemory<>(this.type)); } /** * Create a new instance of the given {@link WindupVertexFrame} type. The ID is generated by the underlying graph database. */ @Override public T create() { return ExecutionStatistics.performBenchmarked("GraphService.create", () -> context.getFramed().addVertex(null, type)); } @Override public T addTypeToModel(final WindupVertexFrame model) { return ExecutionStatistics.performBenchmarked("GraphService.addTypeToModel", () -> GraphService.addTypeToModel(getGraphContext(), model, type)); } protected FramedGraphQuery findAllQuery() { return context.getQuery().type(type); } @Override public Iterable<T> findAll() { return findAllQuery().vertices(type); } @Override public Iterable<T> findAllByProperties(final String[] keys, final String[] vals) { return ExecutionStatistics.performBenchmarked("GraphService.findAllByProperties(" + Arrays.asList(keys) + ")", () -> { FramedGraphQuery query = findAllQuery(); for (int i = 0, j = keys.length; i < j; i++) { String key = keys[i]; String val = vals[i]; query = query.has(key, val); } return query.vertices(type); }); } @Override public Iterable<T> findAllByProperty(final String key, final Object value) { return ExecutionStatistics.performBenchmarked("GraphService.findAllByProperty(" + key + ")", () -> context.getFramed().getVertices(key, value, type)); } @Override public Iterable<T> findAllWithoutProperty(final String key, final Object value) { return ExecutionStatistics.performBenchmarked("GraphService.findAllWithoutProperty(" + key + ")", () -> findAllQuery().hasNot(key, value).vertices(type)); } @Override public Iterable<T> findAllWithoutProperty(final String key) { return ExecutionStatistics.performBenchmarked("GraphService.findAllWithoutProperty(" + key + ")", () -> findAllQuery().hasNot(key).vertices(type)); } @Override public Iterable<T> findAllByPropertyMatchingRegex(final String key, final String... regex) { return ExecutionStatistics.performBenchmarked("GraphService.findAllByPropertyMatchingRegex(" + key + ")", () -> { if (regex.length == 0) return IterablesUtil.emptyIterable(); final String regexFinal; if (regex.length == 1) { regexFinal = regex[0]; } else { StringBuilder builder = new StringBuilder(); builder.append("\\b("); int i = 0; for (String value : regex) { if (i > 0) builder.append("|"); builder.append(value); i++; } builder.append(")\\b"); regexFinal = builder.toString(); } return findAllQuery().has(key, Text.REGEX, regexFinal).vertices(type); }); } /** * Returns the vertex with given ID framed into given interface. */ @Override public T getById(Object id) { return context.getFramed().getVertex(id, this.type); } @Override public T frame(Vertex vertex) { return getGraphContext().getFramed().frame(vertex, this.getType()); } @Override public Class<T> getType() { return this.type; } protected GraphQuery getTypedQuery() { return getGraphContext().getQuery().type(type); } /** * Returns what this' frame has in @TypeValue(). */ protected String getTypeValueForSearch() { TypeValue typeValue = this.type.getAnnotation(TypeValue.class); if (typeValue == null) throw new IllegalArgumentException("Must be annotated with '@TypeValue': " + this.type.getName()); return typeValue.value(); } @Override public T getUnique() throws NonUniqueResultException { Iterable<T> results = findAll(); if (!results.iterator().hasNext()) { return null; } Iterator<T> iterator = results.iterator(); T result = iterator.next(); if (iterator.hasNext()) { throw new NonUniqueResultException("Expected unique value, but returned non-unique."); } return result; } @Override public T getUniqueByProperty(String property, Object value) throws NonUniqueResultException { Iterable<T> results = findAllByProperty(property, value); if (!results.iterator().hasNext()) { return null; } Iterator<T> iterator = results.iterator(); T result = iterator.next(); if (iterator.hasNext()) { throw new NonUniqueResultException("Expected unique value, but returned non-unique."); } return result; } protected T getUnique(GraphQuery framedQuery) { Iterable<Vertex> results = framedQuery.vertices(); if (!results.iterator().hasNext()) { return null; } Iterator<Vertex> iter = results.iterator(); Vertex result = iter.next(); if (iter.hasNext()) { throw new NonUniqueResultException("Expected unique value, but returned non-unique."); } return frame(result); } protected GraphContext getGraphContext() { return context; } @Override public TitanTransaction newTransaction() { return context.getGraph().getBaseGraph().newTransaction(); } /** * Adds the specified type to this frame, and returns a new object that implements this type. */ public static <T extends WindupVertexFrame> T addTypeToModel(GraphContext graphContext, WindupVertexFrame frame, Class<T> type) { Vertex vertex = frame.asVertex(); graphContext.getGraphTypeManager().addTypeToElement(type, vertex); return graphContext.getFramed().frame(vertex, type); } /** * Removes the specified type from the frame. */ public static <T extends WindupVertexFrame> T removeTypeFromModel(GraphContext graphContext, WindupVertexFrame frame, Class<T> type) { Vertex vertex = frame.asVertex(); graphContext.getGraphTypeManager().removeTypeFromElement(type, vertex); return graphContext.getFramed().frame(vertex, type); } @Override public void remove(final T model) { ExecutionStatistics.performBenchmarked("GraphService.commit", () -> { model.asVertex().remove(); return null; }); } }