diff --git a/e2e/src/test/java/com/arcadedb/e2e/JdbcQueriesTest.java b/e2e/src/test/java/com/arcadedb/e2e/JdbcQueriesTest.java index fc38ae8bff..527e1f298f 100644 --- a/e2e/src/test/java/com/arcadedb/e2e/JdbcQueriesTest.java +++ b/e2e/src/test/java/com/arcadedb/e2e/JdbcQueriesTest.java @@ -88,7 +88,7 @@ void bigResultSetSQLQuery() throws Exception { void simpleGremlinQuery() throws Exception { try (final Statement st = conn.createStatement()) { - try (final ResultSet rs = st.executeQuery("{gremlin}g.V().limit(1)")) { + try (final ResultSet rs = st.executeQuery("{gremlin}g.V().hasLabel('Beer').limit(1)")) { assertThat(rs.next()).isTrue(); assertThat(rs.getString("name")).isNotBlank(); diff --git a/engine/src/main/java/com/arcadedb/graph/GraphEngine.java b/engine/src/main/java/com/arcadedb/graph/GraphEngine.java index 6f2eef6892..ddeecf59f9 100644 --- a/engine/src/main/java/com/arcadedb/graph/GraphEngine.java +++ b/engine/src/main/java/com/arcadedb/graph/GraphEngine.java @@ -176,9 +176,7 @@ public List newEdges(VertexInternal sourceVertex, final List edges = new ArrayList<>(connections.size()); final List> outEdgePairs = new ArrayList<>(); - for (int i = 0; i < connections.size(); ++i) { - final CreateEdgeOperation connection = connections.get(i); - + for (final CreateEdgeOperation connection : connections) { final MutableEdge edge; final Identifiable destinationVertex = connection.destinationVertex; @@ -552,7 +550,10 @@ public boolean isVertexConnectedTo(final VertexInternal vertex, final Identifiab return false; } - public boolean isVertexConnectedTo(final VertexInternal vertex, final Identifiable toVertex, final Vertex.DIRECTION direction, + public boolean isVertexConnectedTo( + final VertexInternal vertex, + final Identifiable toVertex, + final Vertex.DIRECTION direction, final String edgeType) { if (toVertex == null) throw new IllegalArgumentException("Destination vertex is null"); @@ -563,8 +564,13 @@ public boolean isVertexConnectedTo(final VertexInternal vertex, final Identifiab if (edgeType == null) throw new IllegalArgumentException("Edge type is null"); - final int[] bucketFilter = vertex.getDatabase().getSchema().getType(edgeType).getBuckets(true).stream() - .mapToInt(x -> x.getFileId()).toArray(); + final int[] bucketFilter = vertex.getDatabase() + .getSchema() + .getType(edgeType) + .getBuckets(true) + .stream() + .mapToInt(x -> x.getFileId()) + .toArray(); if (direction == Vertex.DIRECTION.OUT || direction == Vertex.DIRECTION.BOTH) { final EdgeLinkedList outEdges = getEdgeHeadChunk(vertex, Vertex.DIRECTION.OUT); diff --git a/engine/src/main/java/com/arcadedb/query/sql/executor/CreateEdgeExecutionPlanner.java b/engine/src/main/java/com/arcadedb/query/sql/executor/CreateEdgeExecutionPlanner.java index f49dbbd6ca..2281b23d43 100644 --- a/engine/src/main/java/com/arcadedb/query/sql/executor/CreateEdgeExecutionPlanner.java +++ b/engine/src/main/java/com/arcadedb/query/sql/executor/CreateEdgeExecutionPlanner.java @@ -20,6 +20,7 @@ import com.arcadedb.database.Database; import com.arcadedb.exception.CommandSQLParsingException; +import com.arcadedb.index.TypeIndex; import com.arcadedb.query.sql.parser.CreateEdgeStatement; import com.arcadedb.query.sql.parser.Expression; import com.arcadedb.query.sql.parser.Identifier; @@ -28,8 +29,10 @@ import com.arcadedb.query.sql.parser.JsonArray; import com.arcadedb.query.sql.parser.UpdateItem; import com.arcadedb.schema.DocumentType; +import com.arcadedb.schema.EdgeType; -import java.util.*; +import java.util.ArrayList; +import java.util.List; /** * Created by luigidellaquila on 08/08/16. @@ -85,7 +88,19 @@ public InsertExecutionPlan createExecutionPlan(final CommandContext context) { handleGlobalLet(result, new Identifier("$__ARCADEDB_CREATE_EDGE_toV"), rightExpression, context); final String uniqueIndexName; - uniqueIndexName = null; +// if (context.getDatabase().getSchema().existsType(targetClass.getStringValue())) { +// final EdgeType type = (EdgeType) context.getDatabase().getSchema().getType(targetClass.getStringValue()); +// uniqueIndexName = type.getAllIndexes(true) +// .stream() +// .filter(TypeIndex::isUnique) +// .filter(x -> x.getPropertyNames().size() == 2 +// && x.getPropertyNames().contains("@out") +// && x.getPropertyNames().contains("@in")) +// .map(TypeIndex::getName) +// .findFirst() +// .orElse(null); +// } else + uniqueIndexName = null; result.chain( new CreateEdgesStep(targetClass, targetBucketName, uniqueIndexName, new Identifier("$__ARCADEDB_CREATE_EDGE_fromV"), diff --git a/engine/src/main/java/com/arcadedb/query/sql/executor/CreateEdgesStep.java b/engine/src/main/java/com/arcadedb/query/sql/executor/CreateEdgesStep.java index db889cef6f..711b9daa35 100755 --- a/engine/src/main/java/com/arcadedb/query/sql/executor/CreateEdgesStep.java +++ b/engine/src/main/java/com/arcadedb/query/sql/executor/CreateEdgesStep.java @@ -32,7 +32,11 @@ import com.arcadedb.index.IndexCursor; import com.arcadedb.query.sql.parser.Identifier; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; /** * Created by luigidellaquila on 28/11/16. @@ -58,8 +62,14 @@ public class CreateEdgesStep extends AbstractExecutionStep { private boolean initiated = false; - public CreateEdgesStep(final Identifier targetClass, final Identifier targetBucketName, final String uniqueIndex, - final Identifier fromAlias, final Identifier toAlias, final boolean unidirectional, final boolean ifNotExists, + public CreateEdgesStep( + final Identifier targetClass, + final Identifier targetBucketName, + final String uniqueIndex, + final Identifier fromAlias, + final Identifier toAlias, + final boolean unidirectional, + final boolean ifNotExists, final CommandContext context) { super(context); this.targetClass = targetClass; @@ -91,8 +101,15 @@ public Result next() { @Override public Result next(final Object[] properties) { - if (currentTo == null) + if (currentTo == null) { loadNextFromTo(); + if(edgeToUpdate != null && !ifNotExists) { + System.out.println("edgeToUpdate = " + edgeToUpdate); + currentTo = null; + currentBatch++; + return new UpdatableResult(edgeToUpdate); + } + } final long begin = context.isProfiling() ? System.nanoTime() : 0; try { @@ -103,11 +120,21 @@ public Result next(final Object[] properties) { if (currentTo == null) throw new CommandExecutionException("Invalid TO vertex for edge"); - if (ifNotExists) + if (ifNotExists) { if (context.getDatabase().getGraphEngine() - .isVertexConnectedTo((VertexInternal) currentFrom, currentTo, Vertex.DIRECTION.OUT, targetClass.getStringValue())) - // SKIP CREATING EDGE - return null; + .isVertexConnectedTo((VertexInternal) currentFrom, currentTo, Vertex.DIRECTION.OUT, targetClass.getStringValue())) { + + for (Edge existingEdge : context.getDatabase().getGraphEngine() + .getEdges((VertexInternal) currentFrom, Vertex.DIRECTION.OUT, targetClass.getStringValue())) { + + if (existingEdge.getOut().equals(currentTo)) { + currentTo = null; + currentBatch++; + return new UpdatableResult(existingEdge.modify()); + } + } + } + } final String target = targetBucket != null ? "bucket:" + targetBucket.getStringValue() : targetClass.getStringValue(); @@ -119,7 +146,7 @@ public Result next(final Object[] properties) { currentBatch++; return result; } finally { - if( context.isProfiling() ) { + if (context.isProfiling()) { cost += (System.nanoTime() - begin); } } @@ -223,7 +250,7 @@ protected void loadNextFromTo() { this.currentTo = null; } } finally { - if( context.isProfiling() ) { + if (context.isProfiling()) { cost += (System.nanoTime() - begin); } } @@ -271,7 +298,7 @@ public String prettyPrint(final int depth, final int indent) { result += spaces + " FOR EACH y in " + toAlias + "\n"; result += spaces + " CREATE EDGE " + targetClass + " FROM x TO y " + (unidirectional ? "UNIDIRECTIONAL" : "BIDIRECTIONAL"); - if ( context.isProfiling() ) + if (context.isProfiling()) result += " (" + getCostFormatted() + ")"; if (targetBucket != null) @@ -287,8 +314,14 @@ public boolean canBeCached() { @Override public ExecutionStep copy(final CommandContext context) { - return new CreateEdgesStep(targetClass == null ? null : targetClass.copy(), targetBucket == null ? null : targetBucket.copy(), - uniqueIndexName, fromAlias == null ? null : fromAlias.copy(), toAlias == null ? null : toAlias.copy(), unidirectional, - ifNotExists, context); + return new CreateEdgesStep( + targetClass == null ? null : targetClass.copy(), + targetBucket == null ? null : targetBucket.copy(), + uniqueIndexName, + fromAlias == null ? null : fromAlias.copy(), + toAlias == null ? null : toAlias.copy(), + unidirectional, + ifNotExists, + context); } } diff --git a/engine/src/test/java/com/arcadedb/graph/BasicGraphTest.java b/engine/src/test/java/com/arcadedb/graph/BasicGraphTest.java index a3e5bc08fc..802ed4274c 100644 --- a/engine/src/test/java/com/arcadedb/graph/BasicGraphTest.java +++ b/engine/src/test/java/com/arcadedb/graph/BasicGraphTest.java @@ -32,14 +32,12 @@ import com.arcadedb.query.sql.function.SQLFunctionAbstract; import com.arcadedb.schema.EdgeType; import com.arcadedb.schema.Schema; - -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; -import java.util.*; -import java.util.concurrent.atomic.*; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; -import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; @@ -490,7 +488,8 @@ public void customFunction() { try { ((SQLQueryEngine) database.getQueryEngine("sql")).getFunctionFactory().register(new SQLFunctionAbstract("ciao") { @Override - public Object execute(final Object iThis, final Identifiable iCurrentRecord, final Object iCurrentResult, final Object[] iParams, + public Object execute(final Object iThis, final Identifiable iCurrentRecord, final Object iCurrentResult, + final Object[] iParams, final CommandContext iContext) { return "Ciao"; } @@ -575,7 +574,7 @@ public void rollbackEdge() { } @Test - public void reuseRollbackedTx() { + public void reuseRollBackedTx() { final AtomicReference v1RID = new AtomicReference<>(); database.transaction(() -> { @@ -655,6 +654,7 @@ public void edgeUnivocitySQL() { v1[0] = database.newVertex(VERTEX1_TYPE_NAME).set("id", 1001).save(); v2[0] = database.newVertex(VERTEX1_TYPE_NAME).set("id", 1002).save(); + final ResultSet result = database.command("sql", "create edge OnlyOneBetweenVertices from ? to ?", v1[0], v2[0]); assertThat(result.hasNext()).isTrue(); }); @@ -673,7 +673,14 @@ public void edgeUnivocitySQL() { // EXPECTED } - database.transaction(() -> database.command("sql", "create edge OnlyOneBetweenVertices from ? to ? IF NOT EXISTS", v1[0], v2[0])); + try { + database.transaction( + () -> database.command("sql", "create edge OnlyOneBetweenVertices from ? to ? IF NOT EXISTS", v1[0], v2[0])); + fail(""); + } catch (final DuplicatedKeyException ex) { + // EXPECTED + } + } @Test diff --git a/engine/src/test/java/com/arcadedb/query/select/SelectExecutionIT.java b/engine/src/test/java/com/arcadedb/query/select/SelectExecutionTest.java similarity index 92% rename from engine/src/test/java/com/arcadedb/query/select/SelectExecutionIT.java rename to engine/src/test/java/com/arcadedb/query/select/SelectExecutionTest.java index 791d2dc887..4976aae2e1 100644 --- a/engine/src/test/java/com/arcadedb/query/select/SelectExecutionIT.java +++ b/engine/src/test/java/com/arcadedb/query/select/SelectExecutionTest.java @@ -25,24 +25,25 @@ import com.arcadedb.schema.Schema; import com.arcadedb.schema.Type; import com.arcadedb.serializer.json.JSONObject; - -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.util.*; -import java.util.concurrent.*; -import java.util.stream.*; +import java.util.List; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Luca Garulli (l.garulli@arcadedata.com) */ -public class SelectExecutionIT extends TestHelper { +public class SelectExecutionTest extends TestHelper { - public SelectExecutionIT() { + public SelectExecutionTest() { autoStartTx = false; } @@ -61,12 +62,17 @@ protected void beginTest() { } for (int i = 1; i < 100; i++) { - final Vertex root = database.select().fromType("Vertex").where().property("id").eq().value(0).vertices().nextOrNull(); - assertNotNull(root); - assertEquals(0, root.getInteger("id")); - - root.newEdge("Edge", database.select().fromType("Vertex").where().property("id").eq().value(i).vertices().nextOrNull(), - true).save(); + final Vertex root = database.select() + .fromType("Vertex") + .where().property("id").eq().value(0).vertices() + .nextOrNull(); + assertThat(root).isNotNull(); + assertThat(root.getInteger("id")).isEqualTo(0); + root.newEdge("Edge", database.select() + .fromType("Vertex") + .where().property("id").eq().value(i) + .vertices().nextOrNull(), true) + .save(); } }); } @@ -356,11 +362,8 @@ public void okLike() { @Test public void okILike() { - final SelectCompiled select = database.select() - .fromType("Vertex")// - .where() - .property("name").ilike().value("j%") - .compile(); + final SelectCompiled select = database.select().fromType("Vertex")// + .where().property("name").ilike().value("j%").compile(); for (int i = 0; i < 100; i++) { final SelectIterator result = select.parameter("value", i).vertices(); @@ -409,13 +412,15 @@ private void expectingException(final Runnable callback, final Class { + database.command("sqlscript", """ + CREATE VERTEX TYPE vex; + CREATE EDGE TYPE edg; + CREATE PROPERTY edg.label STRING; + CREATE VERTEX vex; + CREATE VERTEX vex; + CREATE VERTEX vex; + """); + }); + + database.transaction(() -> { + final ResultSet rs = database.query("SQL", """ + select from vex + """); + assertThat(rs.stream().count()).isEqualTo(3); + }); + // CREATE EDGES FROM #1:0 TO [#1:1,#1:2] + database.transaction(() -> { + final ResultSet rs = database.command("sql", """ + CREATE EDGE edg FROM #1:0 TO [#1:1,#1:2] IF NOT EXISTS + """); + assertThat(rs.stream().count()).isEqualTo(2); + }); + + // CREATE AGAIN (should not create any edge) + database.transaction(() -> { + final ResultSet rs = database.command("sql", """ + CREATE EDGE edg FROM #1:0 TO [#1:1,#1:2] IF NOT EXISTS + """); + assertThat(rs.hasNext()).isTrue(); + assertThat(rs.stream().count()).isEqualTo(2); + }); + + // CREATE AGAIN (should create 1 edge) + database.transaction(() -> { + final ResultSet rs = database.command("sql", """ + CREATE EDGE edg FROM #1:0 TO [#1:1,#1:2,#1:0] IF NOT EXISTS + """); + assertThat(rs.hasNext()).isTrue(); + assertThat(rs.stream().count()).isEqualTo(3); + }); + + } }