package org.vertexium.cypher.executor; import com.google.common.collect.ImmutableList; import org.vertexium.VertexiumException; import org.vertexium.cypher.VertexiumCypherQueryContext; import org.vertexium.cypher.VertexiumCypherResult; import org.vertexium.cypher.VertexiumCypherScope; import org.vertexium.cypher.ast.model.*; import org.vertexium.cypher.exceptions.VertexiumCypherTypeErrorException; import org.vertexium.util.VertexiumLogger; import org.vertexium.util.VertexiumLoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import static com.google.common.base.Preconditions.checkNotNull; public class QueryExecutor { private static final VertexiumLogger LOGGER = VertexiumLoggerFactory.getLogger(QueryExecutor.class); public VertexiumCypherResult execute(VertexiumCypherQueryContext ctx, CypherStatement statement) { LOGGER.debug("execute: %s", statement); checkNotNull(statement, "statement is required"); CypherAstBase query = statement.getQuery(); return executeQuery(ctx, query); } private VertexiumCypherScope executeQuery(VertexiumCypherQueryContext ctx, CypherAstBase query) { if (query instanceof CypherQuery) { return execute(ctx, (CypherQuery) query); } else if (query instanceof CypherUnion) { return executeUnion(ctx, (CypherUnion) query); } throw new VertexiumCypherTypeErrorException(query, CypherQuery.class, CypherUnion.class); } private VertexiumCypherScope executeUnion(VertexiumCypherQueryContext ctx, CypherUnion union) { VertexiumCypherScope leftResults = executeQuery(ctx, union.getLeft()); VertexiumCypherScope rightResults = executeQuery(ctx, union.getRight()); return leftResults.concat(rightResults, !union.isAll(), null); } private VertexiumCypherScope execute(VertexiumCypherQueryContext ctx, CypherQuery cypherQuery) { VertexiumCypherScope scope = VertexiumCypherScope.newSingleItemScope(VertexiumCypherScope.newEmptyItem()); ImmutableList<CypherClause> clauses = cypherQuery.getClauses(); AtomicInteger clauseIndex = new AtomicInteger(0); for (; clauseIndex.intValue() < clauses.size(); clauseIndex.incrementAndGet()) { scope = execute(ctx, clauses, clauseIndex, scope); } if (clauses.get(clauses.size() - 1) instanceof CypherReturnClause) { return scope; } return VertexiumCypherScope.newEmpty(); } private VertexiumCypherScope execute( VertexiumCypherQueryContext ctx, ImmutableList<CypherClause> clauses, AtomicInteger clauseIndex, VertexiumCypherScope previousScope ) { VertexiumCypherScope results; CypherClause clause = clauses.get(clauseIndex.get()); if (clause instanceof CypherCreateClause) { results = ctx.getCreateClauseExecutor().execute(ctx, (CypherCreateClause) clause, previousScope); } else if (clause instanceof CypherReturnClause) { results = ctx.getReturnClauseExecutor().execute(ctx, (CypherReturnClause) clause, previousScope); } else if (clause instanceof CypherMatchClause) { List<CypherMatchClause> matchClauses = getSimilarClauses(clauses, clauseIndex.get(), CypherMatchClause.class); results = ctx.getMatchClauseExecutor().execute(ctx, matchClauses, previousScope); clauseIndex.addAndGet(matchClauses.size() - 1); } else if (clause instanceof CypherUnwindClause) { List<CypherUnwindClause> unwindClauses = getSimilarClauses(clauses, clauseIndex.get(), CypherUnwindClause.class); results = ctx.getUnwindClauseExecutor().execute(ctx, unwindClauses, previousScope); clauseIndex.addAndGet(unwindClauses.size() - 1); } else if (clause instanceof CypherWithClause) { results = ctx.getWithClauseExecutor().execute(ctx, (CypherWithClause) clause, previousScope); } else if (clause instanceof CypherMergeClause) { results = ctx.getMergeClauseExecutor().execute(ctx, (CypherMergeClause) clause, previousScope); } else if (clause instanceof CypherDeleteClause) { results = ctx.getDeleteClauseExecutor().execute(ctx, (CypherDeleteClause) clause, previousScope); } else if (clause instanceof CypherSetClause) { results = ctx.getSetClauseExecutor().execute(ctx, (CypherSetClause) clause, previousScope); } else if (clause instanceof CypherRemoveClause) { results = ctx.getRemoveClauseExecutor().execute(ctx, (CypherRemoveClause) clause, previousScope); } else { throw new VertexiumException("clause not implemented \"" + clause.getClass().getName() + "\": " + clause); } return results; } @SuppressWarnings("unchecked") private <T extends CypherClause> List<T> getSimilarClauses(ImmutableList<CypherClause> clauses, int clauseIndex, Class<T> clazz) { List<T> matchClauses = new ArrayList<T>(); matchClauses.add((T) clauses.get(clauseIndex)); while (clauseIndex + 1 < clauses.size() && clazz.isInstance(clauses.get(clauseIndex + 1))) { matchClauses.add((T) clauses.get(clauseIndex + 1)); clauseIndex++; } return matchClauses; } }