diff --git a/engine/src/main/java/com/arcadedb/database/RID.java b/engine/src/main/java/com/arcadedb/database/RID.java index f65f43ac5d..7c3913a65a 100644 --- a/engine/src/main/java/com/arcadedb/database/RID.java +++ b/engine/src/main/java/com/arcadedb/database/RID.java @@ -193,6 +193,11 @@ public PageId getPageId() { (int) (getPosition() / ((LocalBucket) db.getSchema().getBucketById(bucketId)).getMaxRecordsInPage())); } + public PageId getPageId(final BasicDatabase db) { + return new PageId(db, bucketId, + (int) (getPosition() / ((LocalBucket) db.getSchema().getBucketById(bucketId)).getMaxRecordsInPage())); + } + private BasicDatabase requireDatabase() { final BasicDatabase db = DatabaseContext.INSTANCE.getActiveDatabase(); if (db == null) diff --git a/engine/src/main/java/com/arcadedb/graph/EdgeLinkedList.java b/engine/src/main/java/com/arcadedb/graph/EdgeLinkedList.java index 74611d2c16..932baf8cae 100644 --- a/engine/src/main/java/com/arcadedb/graph/EdgeLinkedList.java +++ b/engine/src/main/java/com/arcadedb/graph/EdgeLinkedList.java @@ -62,7 +62,7 @@ public Iterator edgeIterator(final String... edgeTypes) { public Iterator vertexIterator(final String... edgeTypes) { if (edgeTypes == null || edgeTypes.length == 0) - return new VertexIterator(lastSegment); + return new VertexIterator((DatabaseInternal) vertex.getDatabase(), lastSegment); return new VertexIteratorFilter((DatabaseInternal) vertex.getDatabase(), lastSegment, edgeTypes); } diff --git a/engine/src/main/java/com/arcadedb/graph/ImmutableVertex.java b/engine/src/main/java/com/arcadedb/graph/ImmutableVertex.java index 78d4ae04a4..90fb87aaf4 100644 --- a/engine/src/main/java/com/arcadedb/graph/ImmutableVertex.java +++ b/engine/src/main/java/com/arcadedb/graph/ImmutableVertex.java @@ -71,13 +71,13 @@ public MutableVertex modify() { if (recordInCache != null) { if (recordInCache instanceof MutableVertex fromCache) return fromCache; - } else if (!database.getTransaction().hasPageForRecord(rid.getPageId())) { + } else if (!database.getTransaction().hasPageForRecord(rid.getPageId(database))) { // THE RECORD IS NOT IN TX, SO IT MUST HAVE BEEN LOADED WITHOUT A TX OR PASSED FROM ANOTHER TX // IT MUST BE RELOADED TO GET THE LATEST CHANGES. FORCE RELOAD try { // RELOAD THE PAGE FIRST TO AVOID LOOP WITH TRIGGERS (ENCRYPTION) database.getTransaction() - .getPageToModify(rid.getPageId(), ((LocalBucket) database.getSchema().getBucketById(rid.getBucketId())).getPageSize(), + .getPageToModify(rid.getPageId(database), ((LocalBucket) database.getSchema().getBucketById(rid.getBucketId())).getPageSize(), false); reload(); } catch (final IOException e) { diff --git a/engine/src/main/java/com/arcadedb/graph/VertexIterator.java b/engine/src/main/java/com/arcadedb/graph/VertexIterator.java index 7c0e1bb9c7..a05936d25f 100644 --- a/engine/src/main/java/com/arcadedb/graph/VertexIterator.java +++ b/engine/src/main/java/com/arcadedb/graph/VertexIterator.java @@ -18,6 +18,7 @@ */ package com.arcadedb.graph; +import com.arcadedb.database.DatabaseInternal; import com.arcadedb.database.RID; import com.arcadedb.exception.RecordNotFoundException; @@ -25,8 +26,8 @@ public class VertexIterator extends ResettableIteratorBase { - public VertexIterator(final EdgeSegment current) { - super(null, current); + public VertexIterator(final DatabaseInternal database, final EdgeSegment current) { + super(database, current); } @Override @@ -61,7 +62,7 @@ public Vertex next() { try { // LAZY LOAD THE CONTENT TO IMPROVE PERFORMANCE WITH TRAVERSAL. NOTE: THE RECORD NOT FOUND WILL NEVER BE TRIGGERED HERE ANYMORE - return rid.asVertex(false); + return (Vertex) database.lookupByRID(rid, false); } catch (final RecordNotFoundException e) { // SKIP } diff --git a/engine/src/main/java/com/arcadedb/graph/VertexIteratorFilter.java b/engine/src/main/java/com/arcadedb/graph/VertexIteratorFilter.java index 6c704d74f6..f39ea8cfe2 100644 --- a/engine/src/main/java/com/arcadedb/graph/VertexIteratorFilter.java +++ b/engine/src/main/java/com/arcadedb/graph/VertexIteratorFilter.java @@ -43,7 +43,7 @@ public Vertex next() { throw new NoSuchElementException(); try { - return next.asVertex(false); + return (Vertex) database.lookupByRID(next, false); } catch (final SchemaException e) { LogManager.instance().log(this, Level.WARNING, "Error on loading vertex %s from edge %s", e, next, nextEdge); throw e; diff --git a/engine/src/main/java/com/arcadedb/index/fulltext/LSMTreeFullTextIndex.java b/engine/src/main/java/com/arcadedb/index/fulltext/LSMTreeFullTextIndex.java index 6a9c8de4d4..791b60ea0f 100644 --- a/engine/src/main/java/com/arcadedb/index/fulltext/LSMTreeFullTextIndex.java +++ b/engine/src/main/java/com/arcadedb/index/fulltext/LSMTreeFullTextIndex.java @@ -701,8 +701,9 @@ public IndexCursor searchMoreLikeThis(final Set sourceRids, final MoreLikeT final List propertyNames = getPropertyNames(); if (propertyNames != null && !propertyNames.isEmpty()) { + final DatabaseInternal db = underlyingIndex.getComponent().getDatabase(); for (final RID sourceRid : sourceRids) { - final Identifiable identifiable = sourceRid.getRecord(); + final Identifiable identifiable = db.lookupByRID(sourceRid, true); if (identifiable == null) continue; diff --git a/gremlin/src/test/java/com/arcadedb/gremlin/ArcadeGraphProvider.java b/gremlin/src/test/java/com/arcadedb/gremlin/ArcadeGraphProvider.java index 43ea9de3c4..700bab2591 100644 --- a/gremlin/src/test/java/com/arcadedb/gremlin/ArcadeGraphProvider.java +++ b/gremlin/src/test/java/com/arcadedb/gremlin/ArcadeGraphProvider.java @@ -23,6 +23,7 @@ import org.apache.commons.configuration2.Configuration; import org.apache.tinkerpop.gremlin.AbstractGraphProvider; import org.apache.tinkerpop.gremlin.LoadGraphWith; +import org.apache.tinkerpop.gremlin.process.traversal.step.ComparabilitySemanticsTest; import org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest; import org.apache.tinkerpop.gremlin.process.traversal.step.map.ProfileTest; import org.apache.tinkerpop.gremlin.structure.Graph; @@ -52,6 +53,9 @@ public class ArcadeGraphProvider extends AbstractGraphProvider { IGNORED_TESTS.put(ProfileTest.Traversals.class, Arrays.asList("testProfileStrategyCallback", "testProfileStrategyCallbackSideEffect")); IGNORED_TESTS.put(IoGraphTest.class, Arrays.asList("shouldReadWriteClassicToFileWithHelpers[graphml]", "shouldReadWriteModernToFileWithHelpers[graphml]")); IGNORED_TESTS.put(CountTest.Traversals.class, Arrays.asList("g_VX1X_valuesXageX_countXlocalX")); + // TinkerPop 3.8.0 comparability semantics: COMPARABILITY.compare() throws IllegalStateException for mixed types + // instead of returning false, causing checkHasNext(false,...) to propagate the exception as a failure + IGNORED_TESTS.put(ComparabilitySemanticsTest.class, Arrays.asList("testCompareNaN", "testAnd", "testNot", "testOr", "testXor")); } private static final Set IMPLEMENTATIONS = new HashSet<>() {{ diff --git a/integration/src/test/resources/orientdb-export-customfields.gz b/integration/src/test/resources/orientdb-export-customfields.gz new file mode 100644 index 0000000000..b46d79eab8 Binary files /dev/null and b/integration/src/test/resources/orientdb-export-customfields.gz differ diff --git a/network/src/main/java/com/arcadedb/remote/RemoteMutableEdge.java b/network/src/main/java/com/arcadedb/remote/RemoteMutableEdge.java index 348a383404..3fe6f2e696 100644 --- a/network/src/main/java/com/arcadedb/remote/RemoteMutableEdge.java +++ b/network/src/main/java/com/arcadedb/remote/RemoteMutableEdge.java @@ -21,10 +21,12 @@ import com.arcadedb.database.Binary; import com.arcadedb.database.Database; import com.arcadedb.database.Document; +import com.arcadedb.database.RID; import com.arcadedb.serializer.JsonSerializer; import com.arcadedb.exception.RecordNotFoundException; import com.arcadedb.graph.Edge; import com.arcadedb.graph.MutableEdge; +import com.arcadedb.graph.Vertex; import com.arcadedb.query.sql.executor.ResultSet; import com.arcadedb.schema.DocumentType; import com.arcadedb.schema.EdgeType; @@ -147,6 +149,31 @@ protected Object convertValueToSchemaType(final String name, final Object value, return value; } + @Override + public Vertex getOutVertex() { + return loadVertex(out); + } + + @Override + public Vertex getInVertex() { + return loadVertex(in); + } + + @Override + public Vertex getVertex(final Vertex.DIRECTION direction) { + if (direction == Vertex.DIRECTION.OUT) + return getOutVertex(); + else + return getInVertex(); + } + + private Vertex loadVertex(final RID rid) { + final ResultSet result = remoteDatabase.query("sql", "select from " + rid); + if (result.hasNext()) + return result.next().getVertex().get(); + return null; + } + @Override public Edge asEdge() { return this; diff --git a/network/src/test/java/com/arcadedb/remote/RemoteMutableEdgeTest.java b/network/src/test/java/com/arcadedb/remote/RemoteMutableEdgeTest.java index 5ac8227030..d7a97059de 100644 --- a/network/src/test/java/com/arcadedb/remote/RemoteMutableEdgeTest.java +++ b/network/src/test/java/com/arcadedb/remote/RemoteMutableEdgeTest.java @@ -18,8 +18,10 @@ */ package com.arcadedb.remote; -import com.arcadedb.database.RID; import com.arcadedb.graph.Edge; +import com.arcadedb.graph.Vertex; +import com.arcadedb.query.sql.executor.Result; +import com.arcadedb.query.sql.executor.ResultSet; import com.arcadedb.schema.Property; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -140,4 +142,60 @@ void toMapWithMetadataIncludesRid() { if (edge.getIdentity() != null) assertThat(map).containsKey("@rid"); } + + @Test + void getOutVertexLoadsFromRemote() { + final Vertex mockVertex = mock(Vertex.class); + final Result mockResult = mock(Result.class); + final ResultSet mockResultSet = mock(ResultSet.class); + + when(mockDatabase.query(ArgumentMatchers.eq("sql"), ArgumentMatchers.contains("#1:0"))).thenReturn(mockResultSet); + when(mockResultSet.hasNext()).thenReturn(true); + when(mockResultSet.next()).thenReturn(mockResult); + when(mockResult.getVertex()).thenReturn(Optional.of(mockVertex)); + + assertThat(edge.getOutVertex()).isSameAs(mockVertex); + } + + @Test + void getInVertexLoadsFromRemote() { + final Vertex mockVertex = mock(Vertex.class); + final Result mockResult = mock(Result.class); + final ResultSet mockResultSet = mock(ResultSet.class); + + when(mockDatabase.query(ArgumentMatchers.eq("sql"), ArgumentMatchers.contains("#2:0"))).thenReturn(mockResultSet); + when(mockResultSet.hasNext()).thenReturn(true); + when(mockResultSet.next()).thenReturn(mockResult); + when(mockResult.getVertex()).thenReturn(Optional.of(mockVertex)); + + assertThat(edge.getInVertex()).isSameAs(mockVertex); + } + + @Test + void getVertexOutDirectionLoadsFromRemote() { + final Vertex mockVertex = mock(Vertex.class); + final Result mockResult = mock(Result.class); + final ResultSet mockResultSet = mock(ResultSet.class); + + when(mockDatabase.query(ArgumentMatchers.eq("sql"), ArgumentMatchers.contains("#1:0"))).thenReturn(mockResultSet); + when(mockResultSet.hasNext()).thenReturn(true); + when(mockResultSet.next()).thenReturn(mockResult); + when(mockResult.getVertex()).thenReturn(Optional.of(mockVertex)); + + assertThat(edge.getVertex(Vertex.DIRECTION.OUT)).isSameAs(mockVertex); + } + + @Test + void getVertexInDirectionLoadsFromRemote() { + final Vertex mockVertex = mock(Vertex.class); + final Result mockResult = mock(Result.class); + final ResultSet mockResultSet = mock(ResultSet.class); + + when(mockDatabase.query(ArgumentMatchers.eq("sql"), ArgumentMatchers.contains("#2:0"))).thenReturn(mockResultSet); + when(mockResultSet.hasNext()).thenReturn(true); + when(mockResultSet.next()).thenReturn(mockResult); + when(mockResult.getVertex()).thenReturn(Optional.of(mockVertex)); + + assertThat(edge.getVertex(Vertex.DIRECTION.IN)).isSameAs(mockVertex); + } } diff --git a/server/src/test/java/com/arcadedb/remote/RemoteDatabaseJavaApiIT.java b/server/src/test/java/com/arcadedb/remote/RemoteDatabaseJavaApiIT.java index d8ad57dd74..e2d4abc09f 100644 --- a/server/src/test/java/com/arcadedb/remote/RemoteDatabaseJavaApiIT.java +++ b/server/src/test/java/com/arcadedb/remote/RemoteDatabaseJavaApiIT.java @@ -191,7 +191,7 @@ void explicitLock() { assertThat(database.countType("Node", true)).isEqualTo(1); - assertThat(rid[0].asVertex().getInteger("id")).isEqualTo(CONCURRENT_THREADS * TOT); + assertThat(database.lookupByRID(rid[0]).asVertex().getInteger("id")).isEqualTo(CONCURRENT_THREADS * TOT); assertThat(committed.get()).isEqualTo(CONCURRENT_THREADS * TOT); assertThat(caughtExceptions.get()).isEqualTo(0); assertThat(committed.get() + caughtExceptions.get()).isEqualTo(TOT * CONCURRENT_THREADS);