package org.jboss.windup.config.query; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import com.google.common.collect.Iterables; import org.jboss.forge.furnace.util.Predicate; import org.jboss.windup.config.GraphRewrite; import org.jboss.windup.config.Variables; import org.jboss.windup.config.condition.GraphCondition; import org.jboss.windup.config.operation.Iteration; import org.jboss.windup.config.selectors.FramesSelector; import org.jboss.windup.graph.GraphTypeManager; import org.jboss.windup.graph.frames.VertexFromFramedIterable; import org.jboss.windup.graph.model.WindupVertexFrame; import org.jboss.windup.util.ExecutionStatistics; import org.jboss.windup.util.Task; import org.ocpsoft.rewrite.config.ConditionBuilder; import org.ocpsoft.rewrite.context.EvaluationContext; import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.frames.FramedGraphQuery; import com.tinkerpop.frames.structures.FramedVertexIterable; import com.tinkerpop.gremlin.java.GremlinPipeline; import com.tinkerpop.pipes.PipeFunction; public class Query extends GraphCondition implements QueryBuilderFind, QueryBuilderFrom, QueryBuilderWith, QueryBuilderPiped { private String outputVar = Iteration.DEFAULT_VARIABLE_LIST_STRING; private final List<QueryGremlinCriterion> pipelineCriteria = new ArrayList<>(); private Class<? extends WindupVertexFrame> searchType; private FramesSelector framesSelector; private Predicate<WindupVertexFrame> resultFilter; private Query() { } /** * Begin this {@link Query} with all Frame instances that are the result of the provided GremlinQueryCriterion. */ public static QueryBuilderPiped gremlin(final QueryGremlinCriterion criterion) { return new Query().piped(criterion); } /** * Begin this {@link Query} with all {@link WindupVertexFrame} instances of the given type. */ public static QueryBuilderFind fromType(Class<? extends WindupVertexFrame> type) { final Query query = new Query(); // this query is going to be added after evaluate() method, because in some cases we need gremlin and in some // frames query.searchType = type; return query; } /** * Excludes Vertices that are of the provided type. */ @Override public QueryBuilderFind excludingType(final Class<? extends WindupVertexFrame> type) { pipelineCriteria.add(new QueryGremlinCriterion() { @Override public void query(GraphRewrite event, GremlinPipeline<Vertex, Vertex> pipeline) { pipeline.filter(new PipeFunction<Vertex, Boolean>() { @Override public Boolean compute(Vertex argument) { return !GraphTypeManager.hasType(type, argument); } }); } }); return this; } /** * Includes Vertices that are of the provided type. */ @Override public QueryBuilderFind includingType(final Class<? extends WindupVertexFrame> type) { pipelineCriteria.add(new QueryGremlinCriterion() { @Override public void query(GraphRewrite event, GremlinPipeline<Vertex, Vertex> pipeline) { pipeline.filter(new PipeFunction<Vertex, Boolean>() { @Override public Boolean compute(Vertex argument) { return GraphTypeManager.hasType(type, argument); } }); } }); return this; } /** * Begin this {@link Query} with results of a prior {@link Query}, read from the variable with the given name. */ public static QueryBuilderFrom from(final String sourceVarName) { final Query query = new Query(); query.setInputVariablesName(sourceVarName); return query; } @Override public ConditionBuilder as(String outputVarName) { outputVar = outputVarName; return this; } /* * Evaluators */ @Override public boolean evaluate(final GraphRewrite event, final EvaluationContext context) { final String queryStr = toString(); return ExecutionStatistics.performBenchmarked(queryStr, new Task<Boolean>() { public Boolean execute() { Query.this.setInitialFramesSelector(createInitialFramesSelector(Query.this)); Iterable<? extends WindupVertexFrame> result = framesSelector.getFrames(event, context); if (resultFilter != null) { com.google.common.base.Predicate<WindupVertexFrame> guavaPred= new com.google.common.base.Predicate<WindupVertexFrame>() { @Override public boolean apply(WindupVertexFrame input) { return resultFilter.accept(input); } }; result = Iterables.filter(result, guavaPred); } setResults(event, outputVar, result); return result.iterator().hasNext(); } }); } /* * Criteria */ @Override public QueryBuilderWith withProperty(String property, Object searchValue) { return withProperty(property, QueryPropertyComparisonType.EQUALS, searchValue); } @Override public QueryBuilderWith withProperty(String property, Iterable<?> values) { pipelineCriteria.add(new QueryPropertyCriterion(property, QueryPropertyComparisonType.CONTAINS_ANY_TOKEN, values)); return this; } @Override public QueryBuilderWith withProperty(String property) { pipelineCriteria.add(new QueryPropertyCriterion(property, QueryPropertyComparisonType.DEFINED, null)); return this; } @Override public QueryBuilderWith withoutProperty(String property) { pipelineCriteria.add(new QueryPropertyCriterion(property, QueryPropertyComparisonType.NOT_DEFINED, null)); return this; } private static FramesSelector createInitialFramesSelector(final Query query) { return new FramesSelector() { @Override public Iterable<WindupVertexFrame> getFrames(GraphRewrite event, EvaluationContext context) { Iterable<Vertex> startingVertices = getStartingVertices(event); GremlinPipeline<Vertex, Vertex> pipeline = new GremlinPipeline<>(startingVertices); Set<WindupVertexFrame> frames = new HashSet<>(); for (QueryGremlinCriterion c : query.getPipelineCriteria()) { c.query(event, pipeline); } FramedVertexIterable<WindupVertexFrame> framedVertexIterable = new FramedVertexIterable<>( event.getGraphContext().getFramed(), pipeline, WindupVertexFrame.class); for (WindupVertexFrame frame : framedVertexIterable) { frames.add(frame); } return frames; } private Iterable<Vertex> getStartingVertices(GraphRewrite event) { boolean hasStartingVerticesVariable = query.getInputVariablesName() != null && !query.getInputVariablesName().isEmpty(); Iterable<Vertex> startingVertices; if (hasStartingVerticesVariable) { // save the type as a gremlin criterion if (query.searchType != null) { query.piped(new QueryTypeCriterion(query.searchType)); } Variables variables = (Variables) event.getRewriteContext().get(Variables.class); Iterable<? extends WindupVertexFrame> frames = variables.findVariable(query.getInputVariablesName()); return new VertexFromFramedIterable(frames); } else { FramedGraphQuery framesQueryType = event.getGraphContext().getFramed().query(); if (query.searchType != null) { new QueryTypeCriterion(query.searchType).query(framesQueryType); startingVertices = framesQueryType.vertices(); return startingVertices; } } return event.getGraphContext().getGraph().getVertices(); } }; } @Override public QueryBuilderWith withProperty(String property, Object searchValue, Object... searchValues) { List<Object> values = new LinkedList<>(); values.add(searchValue); values.addAll(Arrays.asList(searchValues)); return withProperty(property, values); } @Override public QueryBuilderWith withProperty(String property, QueryPropertyComparisonType searchType, Object searchValue) { pipelineCriteria.add(new QueryPropertyCriterion(property, searchType, searchValue)); return this; } @Override public QueryBuilderPiped piped(QueryGremlinCriterion criterion) { pipelineCriteria.add(criterion); return this; } private void setInitialFramesSelector(FramesSelector selector) { this.framesSelector = selector; } public Collection<QueryGremlinCriterion> getPipelineCriteria() { return pipelineCriteria; } @Override @SuppressWarnings("unchecked") public <FRAMETYPE extends WindupVertexFrame> QueryBuilderAs filteredBy(Predicate<FRAMETYPE> predicate) { this.resultFilter = (Predicate<WindupVertexFrame>) predicate; return this; } public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Query"); if (searchType != null) { builder.append(".fromType(").append(searchType.getName()).append(")"); } if (!pipelineCriteria.isEmpty()) { builder.append(".gremlin()"); for (QueryGremlinCriterion criterion : pipelineCriteria) { builder.append(criterion); } } builder.append(".as(" + outputVar + ")"); return builder.toString(); } }