diff --git a/README.adoc b/README.adoc
index 5131f55733e..85e0362cd8a 100644
--- a/README.adoc
+++ b/README.adoc
@@ -75,18 +75,24 @@ If you are using Neo4j Desktop you can simply add the Graph Data Science library
|Neo4j 4.3.0 - 4.3.18
|Neo4j 4.4.0 - 4.4.11
-.4+<.^|GDS 2.2.x
+.6+<.^|GDS 2.2.x
|Neo4j 4.3.15 - 4.3.23
-.7+.^|Java 11 & Java 17
+.15+.^|Java 11 & Java 17
|Neo4j 4.4.9 - 4.4.16
|Neo4j 5.1.0
|Neo4j 5.2.0
|Neo4j 5.3.0
-.3+<.^|GDS 2.3.x
-|Neo4j 4.4.9 - 4.4.16
+|Neo4j 5.4.0
+.9+<.^|GDS 2.3.x
+|Neo4j 4.4.9 - 4.4.22
|Neo4j 5.1.0
|Neo4j 5.2.0
|Neo4j 5.3.0
+|Neo4j 5.4.0
+|Neo4j 5.5.0
+|Neo4j 5.6.0
+|Neo4j 5.7.0
+|Neo4j 5.8.0
|===
NOTE: Preview releases are not automatically made available in Neo4j Desktop. They need to be installed manually.
@@ -130,7 +136,7 @@ For the most basic set of features, like graph loading and the graph representat
org.neo4j.gds
core
- 2.2.6
+ 2.3.6
----
@@ -142,21 +148,21 @@ The algorithms are located in the `algo-common`, `algo` and `alpha-algo` modules
org.neo4j.gds
algo-common
- 2.2.6
+ 2.3.8
org.neo4j.gds
algo
- 2.2.6
+ 2.3.8
org.neo4j.gds
alpha-algo
- 2.2.6
+ 2.3.8
----
@@ -168,28 +174,28 @@ The procedures are located in the `proc-common`, `proc` and `alpha-proc` modules
org.neo4j.gds
proc-common
- 2.2.6
+ 2.3.8
org.neo4j.gds
proc
- 2.2.6
+ 2.3.8
org.neo4j.gds
alpha-proc
- 2.2.6
+ 2.3.8
org.neo4j.gds
write-services
- 2.2.6
+ 2.3.8
----
diff --git a/algo-common/src/main/java/org/neo4j/gds/AlgorithmFactory.java b/algo-common/src/main/java/org/neo4j/gds/AlgorithmFactory.java
index 525e868a618..9b1262ce702 100644
--- a/algo-common/src/main/java/org/neo4j/gds/AlgorithmFactory.java
+++ b/algo-common/src/main/java/org/neo4j/gds/AlgorithmFactory.java
@@ -19,6 +19,7 @@
*/
package org.neo4j.gds;
+import org.jetbrains.annotations.NotNull;
import org.neo4j.gds.config.AlgoBaseConfig;
import org.neo4j.gds.core.GraphDimensions;
import org.neo4j.gds.core.utils.mem.MemoryEstimation;
@@ -26,6 +27,7 @@
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
import org.neo4j.gds.core.utils.progress.tasks.Task;
import org.neo4j.gds.core.utils.progress.tasks.TaskProgressTracker;
+import org.neo4j.gds.core.utils.progress.tasks.TaskTreeProgressTracker;
import org.neo4j.gds.core.utils.progress.tasks.Tasks;
import org.neo4j.gds.core.utils.warnings.EmptyUserLogRegistryFactory;
import org.neo4j.gds.core.utils.warnings.UserLogRegistryFactory;
@@ -55,18 +57,47 @@ default ALGO build(
TaskRegistryFactory taskRegistryFactory,
UserLogRegistryFactory userLogRegistryFactory
) {
- var progressTask = progressTask(graphOrGraphStore, configuration);
- var progressTracker = new TaskProgressTracker(
- progressTask,
+ var progressTracker = createProgressTracker(
+ configuration,
log,
- configuration.concurrency(),
- configuration.jobId(),
taskRegistryFactory,
- userLogRegistryFactory
+ userLogRegistryFactory,
+ progressTask(graphOrGraphStore, configuration)
);
return build(graphOrGraphStore, configuration, progressTracker);
}
+ @NotNull
+ private ProgressTracker createProgressTracker(
+ CONFIG configuration,
+ Log log,
+ TaskRegistryFactory taskRegistryFactory,
+ UserLogRegistryFactory userLogRegistryFactory,
+ Task progressTask
+ ) {
+ ProgressTracker progressTracker;
+ if (configuration.logProgress()) {
+ progressTracker = new TaskProgressTracker(
+ progressTask,
+ log,
+ configuration.concurrency(),
+ configuration.jobId(),
+ taskRegistryFactory,
+ userLogRegistryFactory
+ );
+ } else {
+ progressTracker = new TaskTreeProgressTracker(
+ progressTask,
+ log,
+ configuration.concurrency(),
+ configuration.jobId(),
+ taskRegistryFactory,
+ userLogRegistryFactory
+ );
+ }
+ return progressTracker;
+ }
+
ALGO build(
G graphOrGraphStore,
CONFIG configuration,
diff --git a/algo-common/src/main/java/org/neo4j/gds/scaling/ScaleProperties.java b/algo-common/src/main/java/org/neo4j/gds/scaling/ScaleProperties.java
index 1be1d2da185..bd540bedb79 100644
--- a/algo-common/src/main/java/org/neo4j/gds/scaling/ScaleProperties.java
+++ b/algo-common/src/main/java/org/neo4j/gds/scaling/ScaleProperties.java
@@ -225,8 +225,8 @@ public double doubleValue(long nodeId) {
}
@Override
- public long size() {
- return property.size();
+ public long nodeCount() {
+ return property.nodeCount();
}
};
}
@@ -244,8 +244,8 @@ public double doubleValue(long nodeId) {
}
@Override
- public long size() {
- return property.size();
+ public long nodeCount() {
+ return property.nodeCount();
}
};
}
@@ -263,8 +263,8 @@ public double doubleValue(long nodeId) {
}
@Override
- public long size() {
- return property.size();
+ public long nodeCount() {
+ return property.nodeCount();
}
};
}
diff --git a/algo-common/src/main/java/org/neo4j/gds/scaling/ScalePropertiesBaseConfig.java b/algo-common/src/main/java/org/neo4j/gds/scaling/ScalePropertiesBaseConfig.java
index 7c74b650b7f..9bfafb065b8 100644
--- a/algo-common/src/main/java/org/neo4j/gds/scaling/ScalePropertiesBaseConfig.java
+++ b/algo-common/src/main/java/org/neo4j/gds/scaling/ScalePropertiesBaseConfig.java
@@ -27,7 +27,7 @@
import java.util.List;
import java.util.stream.Collectors;
-import static org.neo4j.gds.AbstractPropertyMappings.fromObject;
+import static org.neo4j.gds.PropertyMappings.fromObject;
@ValueClass
@SuppressWarnings("immutables:subtype")
diff --git a/algo-test/src/main/java/org/neo4j/gds/test/TestAlgorithm.java b/algo-test/src/main/java/org/neo4j/gds/test/TestAlgorithm.java
index 64250a7bb8c..f224286a0c1 100644
--- a/algo-test/src/main/java/org/neo4j/gds/test/TestAlgorithm.java
+++ b/algo-test/src/main/java/org/neo4j/gds/test/TestAlgorithm.java
@@ -41,11 +41,12 @@ public TestAlgorithm(
@Override
public TestAlgorithm compute() {
- progressTracker.beginSubTask();
+ progressTracker.beginSubTask(100);
if (throwInCompute) {
throw new IllegalStateException("boo");
}
+ progressTracker.logProgress(50);
relationshipCount = graph.relationshipCount();
progressTracker.endSubTask();
diff --git a/algo/src/main/java/org/neo4j/gds/beta/indexInverse/InverseRelationships.java b/algo/src/main/java/org/neo4j/gds/beta/indexInverse/InverseRelationships.java
index 3062b2f9c24..78a2bf16940 100644
--- a/algo/src/main/java/org/neo4j/gds/beta/indexInverse/InverseRelationships.java
+++ b/algo/src/main/java/org/neo4j/gds/beta/indexInverse/InverseRelationships.java
@@ -79,7 +79,7 @@ public Map compute() {
.propertySchemasFor(fromRelationshipType);
var propertyKeys = propertySchemas.stream().map(PropertySchema::key).collect(Collectors.toList());
- var relationshipsBuilder = initializeRelationshipsBuilder(propertySchemas);
+ var relationshipsBuilder = initializeRelationshipsBuilder(fromRelationshipType, propertySchemas);
var tasks = createTasks(fromRelationshipType, propertyKeys, relationshipsBuilder);
@@ -109,8 +109,9 @@ public Map compute() {
}
@NotNull
- private RelationshipsBuilder initializeRelationshipsBuilder(List propertySchemas) {
+ private RelationshipsBuilder initializeRelationshipsBuilder(RelationshipType relationshipType, List propertySchemas) {
RelationshipsBuilderBuilder relationshipsBuilderBuilder = GraphFactory.initRelationshipsBuilder()
+ .relationshipType(relationshipType)
.concurrency(config.concurrency())
.nodes(graphStore.nodes())
.executorService(executorService)
diff --git a/algo/src/main/java/org/neo4j/gds/beta/undirected/ToUndirected.java b/algo/src/main/java/org/neo4j/gds/beta/undirected/ToUndirected.java
index 6eb1bdc7323..9d102b3d606 100644
--- a/algo/src/main/java/org/neo4j/gds/beta/undirected/ToUndirected.java
+++ b/algo/src/main/java/org/neo4j/gds/beta/undirected/ToUndirected.java
@@ -74,7 +74,10 @@ public SingleTypeRelationships compute() {
.propertySchemasFor(fromRelationshipType);
var propertyKeys = propertySchemas.stream().map(PropertySchema::key).collect(Collectors.toList());
- var relationshipsBuilder = initializeRelationshipsBuilder(propertySchemas);
+ var relationshipsBuilder = initializeRelationshipsBuilder(
+ RelationshipType.of(config.mutateRelationshipType()),
+ propertySchemas
+ );
var tasks = createTasks(fromRelationshipType, propertyKeys, relationshipsBuilder);
@@ -101,8 +104,12 @@ public SingleTypeRelationships compute() {
}
@NotNull
- private RelationshipsBuilder initializeRelationshipsBuilder(List propertySchemas) {
+ private RelationshipsBuilder initializeRelationshipsBuilder(
+ RelationshipType relationshipType,
+ List propertySchemas
+ ) {
RelationshipsBuilderBuilder relationshipsBuilderBuilder = GraphFactory.initRelationshipsBuilder()
+ .relationshipType(relationshipType)
.concurrency(config.concurrency())
.nodes(graphStore.nodes())
.executorService(executorService)
diff --git a/algo/src/main/java/org/neo4j/gds/beta/walking/CollapsePath.java b/algo/src/main/java/org/neo4j/gds/beta/walking/CollapsePath.java
index a2dfe2cecd1..2378f1cbd48 100644
--- a/algo/src/main/java/org/neo4j/gds/beta/walking/CollapsePath.java
+++ b/algo/src/main/java/org/neo4j/gds/beta/walking/CollapsePath.java
@@ -21,6 +21,7 @@
import org.neo4j.gds.Algorithm;
import org.neo4j.gds.Orientation;
+import org.neo4j.gds.RelationshipType;
import org.neo4j.gds.api.Graph;
import org.neo4j.gds.core.Aggregation;
import org.neo4j.gds.core.concurrency.ParallelUtil;
@@ -43,12 +44,14 @@ public class CollapsePath extends Algorithm {
private final List pathTemplates;
private final long nodeCount;
private final boolean allowSelfLoops;
+ private RelationshipType mutateRelationshipType;
private final int concurrency;
private final ExecutorService executorService;
public CollapsePath(
List pathTemplates,
boolean allowSelfLoops,
+ RelationshipType mutateRelationshipType,
int concurrency,
ExecutorService executorService
) {
@@ -56,6 +59,7 @@ public CollapsePath(
this.pathTemplates = pathTemplates;
this.nodeCount = pathTemplates.get(0)[0].nodeCount();
this.allowSelfLoops = allowSelfLoops;
+ this.mutateRelationshipType = mutateRelationshipType;
this.concurrency = concurrency;
this.executorService = executorService;
}
@@ -64,6 +68,7 @@ public CollapsePath(
public SingleTypeRelationships compute() {
RelationshipsBuilder relImporter = GraphFactory.initRelationshipsBuilder()
.nodes(pathTemplates.get(0)[0]) // just need any arbitrary graph
+ .relationshipType(mutateRelationshipType)
.orientation(Orientation.NATURAL)
.aggregation(Aggregation.NONE)
.concurrency(concurrency)
diff --git a/algo/src/main/java/org/neo4j/gds/beta/walking/CollapsePathAlgorithmFactory.java b/algo/src/main/java/org/neo4j/gds/beta/walking/CollapsePathAlgorithmFactory.java
index 1a69af28c0c..9321d6cd310 100644
--- a/algo/src/main/java/org/neo4j/gds/beta/walking/CollapsePathAlgorithmFactory.java
+++ b/algo/src/main/java/org/neo4j/gds/beta/walking/CollapsePathAlgorithmFactory.java
@@ -64,6 +64,7 @@ public CollapsePath build(
return new CollapsePath(
pathTemplatesEncodedAsListsOfSingleRelationshipTypeGraphs,
config.allowSelfLoops(),
+ RelationshipType.of(config.mutateRelationshipType()),
config.concurrency(),
Pools.DEFAULT
);
diff --git a/algo/src/main/java/org/neo4j/gds/embeddings/graphsage/GraphSageHelper.java b/algo/src/main/java/org/neo4j/gds/embeddings/graphsage/GraphSageHelper.java
index 9c80628b99c..25f2f3e6d1d 100644
--- a/algo/src/main/java/org/neo4j/gds/embeddings/graphsage/GraphSageHelper.java
+++ b/algo/src/main/java/org/neo4j/gds/embeddings/graphsage/GraphSageHelper.java
@@ -24,6 +24,7 @@
import org.neo4j.gds.api.Graph;
import org.neo4j.gds.api.IdMap;
import org.neo4j.gds.api.schema.GraphSchema;
+import org.neo4j.gds.api.schema.NodeSchemaEntry;
import org.neo4j.gds.core.utils.mem.MemoryEstimation;
import org.neo4j.gds.core.utils.mem.MemoryEstimations;
import org.neo4j.gds.core.utils.mem.MemoryRange;
@@ -278,7 +279,7 @@ private static Map> propertyKeysPerNodeLabel(GraphSchema
.nodeSchema()
.entries()
.stream()
- .collect(Collectors.toMap(e -> e.identifier, e -> e.properties().keySet()));
+ .collect(Collectors.toMap(NodeSchemaEntry::identifier, e -> e.properties().keySet()));
}
private static Map> filteredPropertyKeysPerNodeLabel(Graph graph, GraphSageTrainConfig config) {
diff --git a/algo/src/main/java/org/neo4j/gds/embeddings/hashgnn/HashGNNFactory.java b/algo/src/main/java/org/neo4j/gds/embeddings/hashgnn/HashGNNFactory.java
index f58ebcca91e..dacde0b0137 100644
--- a/algo/src/main/java/org/neo4j/gds/embeddings/hashgnn/HashGNNFactory.java
+++ b/algo/src/main/java/org/neo4j/gds/embeddings/hashgnn/HashGNNFactory.java
@@ -118,7 +118,7 @@ public MemoryEstimation memoryEstimation(CONFIG config) {
config.heterogeneous() ? dims.relationshipCounts().size() : 1
)));
- int outputDimension = config.outputDimension().orElse(FUDGED_BINARY_DIMENSION);
+ int outputDimension = config.outputDimension().orElse(binaryDimension);
builder.perNode(
"Embeddings output",
n -> HugeObjectArray.memoryEstimation(n, MemoryUsage.sizeOfDoubleArray(outputDimension))
diff --git a/algo/src/main/java/org/neo4j/gds/influenceMaximization/CELF.java b/algo/src/main/java/org/neo4j/gds/influenceMaximization/CELF.java
index 528f89336a5..562f71b9228 100644
--- a/algo/src/main/java/org/neo4j/gds/influenceMaximization/CELF.java
+++ b/algo/src/main/java/org/neo4j/gds/influenceMaximization/CELF.java
@@ -95,15 +95,15 @@ protected boolean lessThan(long a, long b) {
public LongDoubleScatterMap compute() {
//Find the first node with greedy algorithm
progressTracker.beginSubTask();
- greedyPart();
+ var firstSeedNode = greedyPart();
//Find the next k-1 nodes using the list-sorting procedure
- lazyForwardPart();
+ lazyForwardPart(firstSeedNode);
progressTracker.endSubTask();
return seedSetNodes;
}
- private void greedyPart() {
+ private long greedyPart() {
HugeDoubleArray singleSpreadArray = HugeDoubleArray.newArray(graph.nodeCount());
progressTracker.beginSubTask(graph.nodeCount());
var tasks = PartitionUtils.rangePartition(
@@ -136,15 +136,16 @@ private void greedyPart() {
gain = spreads.cost(highestNode);
spreads.pop();
seedSetNodes.put(highestNode, gain);
+ return highestNode;
}
- private void lazyForwardPart() {
+ private void lazyForwardPart(long firstSeedNode) {
var independentCascade = ICLazyForwardMC.create(
graph,
propagationProbability,
monteCarloSimulations,
- seedSetNodes.keys[0],
+ firstSeedNode,
(int) seedSetCount,
concurrency,
executorService,
diff --git a/algo/src/main/java/org/neo4j/gds/leiden/GraphAggregationPhase.java b/algo/src/main/java/org/neo4j/gds/leiden/GraphAggregationPhase.java
index fbd2e4450ae..19a08ab1ad9 100644
--- a/algo/src/main/java/org/neo4j/gds/leiden/GraphAggregationPhase.java
+++ b/algo/src/main/java/org/neo4j/gds/leiden/GraphAggregationPhase.java
@@ -53,22 +53,22 @@ class GraphAggregationPhase {
static MemoryEstimation memoryEstimation() {
return MemoryEstimations.builder(GraphAggregationPhase.class)
- .rangePerGraphDimension("aggregated graph", (rootGraphDimensions, concurrency) -> {
+ .rangePerGraphDimension("aggregated graph", (rootDimensions, concurrency) -> {
// The input graph might have multiple node and relationship properties
// but the aggregated graph will never have more than a single relationship property
// so let's
- var maxDimensions = ImmutableGraphDimensions
- .builder()
- .from(rootGraphDimensions)
- .build();
+
+ // Handle the case where the input graph has only one node
+ var minNodeCount = Math.min(2, rootDimensions.nodeCount());
+ var minRelCount = Math.min(1, rootDimensions.relCountUpperBound());
var minDimensions = ImmutableGraphDimensions
.builder()
- .nodeCount(2)
- .highestPossibleNodeCount(2)
- .relationshipCounts(Map.of(RelationshipType.of("foo"), 1L))
- .relCountUpperBound(1)
- .highestRelationshipId(1)
+ .nodeCount(minNodeCount)
+ .highestPossibleNodeCount(minNodeCount)
+ .relationshipCounts(Map.of(RelationshipType.of("foo"), minRelCount))
+ .relCountUpperBound(minRelCount)
+ .highestRelationshipId(minRelCount)
.build();
var relationshipProjections = ImmutableRelationshipProjections.builder()
@@ -89,7 +89,7 @@ static MemoryEstimation memoryEstimation() {
false
);
var min = memoryEstimation.estimate(minDimensions, concurrency).memoryUsage().min;
- var max = memoryEstimation.estimate(maxDimensions, concurrency).memoryUsage().max;
+ var max = memoryEstimation.estimate(rootDimensions, concurrency).memoryUsage().max;
return MemoryRange.of(min, max);
}).perNode("sorted communities", HugeLongArray::memoryEstimation)
@@ -148,6 +148,7 @@ Graph run() {
IdMap idMap = nodesBuilder.build().idMap();
RelationshipsBuilder relationshipsBuilder = GraphFactory.initRelationshipsBuilder()
.nodes(idMap)
+ .relationshipType(RelationshipType.of("_IGNORED_"))
.orientation(direction.toOrientation())
.addPropertyConfig(GraphFactory.PropertyConfig.builder()
.propertyKey("property")
diff --git a/algo/src/main/java/org/neo4j/gds/leiden/Leiden.java b/algo/src/main/java/org/neo4j/gds/leiden/Leiden.java
index 0244ea21170..784ce0cbf41 100644
--- a/algo/src/main/java/org/neo4j/gds/leiden/Leiden.java
+++ b/algo/src/main/java/org/neo4j/gds/leiden/Leiden.java
@@ -133,9 +133,10 @@ public LeidenResult compute() {
gamma,
concurrency
);
- var communitiesCount = localMovePhase.run();
- boolean localPhaseConverged = communitiesCount == workingGraph.nodeCount() || localMovePhase.swaps == 0;
+ localMovePhase.run();
+ //if you do swaps, no convergence
+ boolean localPhaseConverged = localMovePhase.swaps == 0;
progressTracker.endSubTask("Local Move");
progressTracker.beginSubTask("Modularity Computation");
@@ -244,11 +245,11 @@ public LeidenResult compute() {
@NotNull
private LeidenResult getLeidenResult(boolean didConverge, int iteration) {
- boolean seedIsOptimal = didConverge && seedValues.isPresent() && iteration == 0;
- if (seedIsOptimal) {
+ boolean stoppedAtFirstIteration = didConverge && iteration == 0;
+ if (stoppedAtFirstIteration) {
var modularity = modularities[0];
return LeidenResult.of(
- LeidenUtils.createSeedCommunities(rootGraph.nodeCount(), seedValues.orElse(null)),
+ LeidenUtils.createStartingCommunities(rootGraph.nodeCount(), seedValues.orElse(null)),
1,
didConverge,
null,
@@ -266,8 +267,8 @@ private LeidenResult getLeidenResult(boolean didConverge, int iteration) {
);
}
}
-
- private boolean updateModularity(
+
+ private void updateModularity(
Graph workingGraph,
HugeLongArray localMoveCommunities,
HugeDoubleArray localMoveCommunityVolumes,
@@ -276,8 +277,10 @@ private boolean updateModularity(
boolean localPhaseConverged,
int iteration
) {
- boolean seedIsOptimal = localPhaseConverged && seedValues.isPresent() && iteration == 0;
- boolean shouldCalculateModularity = !localPhaseConverged || seedIsOptimal;
+ // Will calculate modularity only if:
+ // - the local phase has not converged (i.e., no swaps done)
+ // - or we terminate in the first iteration (i.e., given seeding is optimal, graph is empty, etc)
+ boolean shouldCalculateModularity = !localPhaseConverged || iteration == 0;
if (shouldCalculateModularity) {
modularities[iteration] = ModularityComputer.compute(
@@ -291,7 +294,6 @@ private boolean updateModularity(
progressTracker
);
}
- return seedIsOptimal;
}
private double initVolumes(
@@ -327,7 +329,7 @@ private double initVolumes(
rootGraph.forEachNode(nodeId -> {
long communityId = initialCommunities.get(nodeId);
progressTracker.logProgress();
- communityVolumes.addTo(communityId, rootGraph.degree(nodeId));
+ communityVolumes.addTo(communityId, nodeVolumes.get(nodeId));
return true;
});
progressTracker.endSubTask("Initialization");
diff --git a/algo/src/main/java/org/neo4j/gds/leiden/LocalMovePhase.java b/algo/src/main/java/org/neo4j/gds/leiden/LocalMovePhase.java
index ad2d577bbb8..9f7a25afe5b 100644
--- a/algo/src/main/java/org/neo4j/gds/leiden/LocalMovePhase.java
+++ b/algo/src/main/java/org/neo4j/gds/leiden/LocalMovePhase.java
@@ -19,7 +19,6 @@
*/
package org.neo4j.gds.leiden;
-import org.apache.commons.lang3.mutable.MutableDouble;
import org.neo4j.gds.api.Graph;
import org.neo4j.gds.core.concurrency.RunWithConcurrency;
import org.neo4j.gds.core.utils.mem.MemoryEstimation;
@@ -72,9 +71,7 @@ static LocalMovePhase create(
double gamma,
int concurrency
) {
- var encounteredCommunitiesWeights = HugeDoubleArray.newArray(graph.nodeCount());
- encounteredCommunitiesWeights.setAll(c -> -1L);
-
+
return new LocalMovePhase(
graph,
seedCommunities,
@@ -103,10 +100,9 @@ private LocalMovePhase(
}
/**
- *
* @return The new community count.
*/
- public long run() {
+ public void run() {
HugeAtomicDoubleArray atomicCommunityVolumes = HugeAtomicDoubleArray.newArray(graph.nodeCount());
graph.forEachNode(v -> {
atomicCommunityVolumes.set(v, communityVolumes.get(v));
@@ -147,15 +143,11 @@ public long run() {
swaps += task.swaps;
}
- MutableDouble aliveCommunities = new MutableDouble(graph.nodeCount());
graph.forEachNode(v -> {
communityVolumes.set(v, atomicCommunityVolumes.get(v));
- if (Double.compare(communityVolumes.get(v), 0.0) == 0) {
- aliveCommunities.decrement();
- }
return true;
});
- return aliveCommunities.longValue();
+
}
-
+
}
diff --git a/algo/src/main/java/org/neo4j/gds/leiden/LocalMoveTask.java b/algo/src/main/java/org/neo4j/gds/leiden/LocalMoveTask.java
index 0b6f42624cd..58f37752ab0 100644
--- a/algo/src/main/java/org/neo4j/gds/leiden/LocalMoveTask.java
+++ b/algo/src/main/java/org/neo4j/gds/leiden/LocalMoveTask.java
@@ -129,8 +129,11 @@ private void moveNodeToNewCommunity(
long oldCommunityId = currentCommunities.get(nodeId);
currentCommunities.set(nodeId, newCommunityId);
communityVolumes.getAndAdd(newCommunityId, currentNodeVolume);
- communityVolumes.getAndAdd(oldCommunityId, -currentNodeVolume);
-
+ //do a atomic update to never go into negatives etc
+ communityVolumes.update(oldCommunityId, (oldValue) -> {
+ var diff = oldValue - currentNodeVolume;
+ return Math.max(diff, 0.0);
+ });
swaps++;
}
diff --git a/algo/src/main/java/org/neo4j/gds/louvain/Louvain.java b/algo/src/main/java/org/neo4j/gds/louvain/Louvain.java
index 62b15fcad94..2754623084e 100644
--- a/algo/src/main/java/org/neo4j/gds/louvain/Louvain.java
+++ b/algo/src/main/java/org/neo4j/gds/louvain/Louvain.java
@@ -20,6 +20,7 @@
package org.neo4j.gds.louvain;
import org.neo4j.gds.Algorithm;
+import org.neo4j.gds.RelationshipType;
import org.neo4j.gds.api.Graph;
import org.neo4j.gds.api.IdMap;
import org.neo4j.gds.api.RelationshipIterator;
@@ -229,13 +230,11 @@ private Graph summarizeGraph(
});
terminationFlag.assertRunning();
- double scaleCoefficient = 1.0;
- if (workingGraph.schema().isUndirected()) {
- scaleCoefficient /= 2.0;
- }
+
IdMap idMap = nodesBuilder.build().idMap();
RelationshipsBuilder relationshipsBuilder = GraphFactory.initRelationshipsBuilder()
.nodes(idMap)
+ .relationshipType(RelationshipType.of("IGNORED"))
.orientation(rootGraph.schema().direction().toOrientation())
.addPropertyConfig(GraphFactory.PropertyConfig.builder()
.propertyKey("property")
@@ -244,7 +243,6 @@ private Graph summarizeGraph(
.executorService(executorService)
.build();
- double finalScaleCoefficient = scaleCoefficient;
var relationshipCreators = PartitionUtils.rangePartition(
concurrency,
workingGraph.nodeCount(),
@@ -253,7 +251,6 @@ private Graph summarizeGraph(
relationshipsBuilder,
modularityOptimization,
workingGraph.concurrentCopy(),
- finalScaleCoefficient,
partition
),
Optional.empty()
@@ -315,21 +312,16 @@ static final class RelationshipCreator implements Runnable {
private final Partition partition;
- private final double scaleCoefficient;
-
-
private RelationshipCreator(
RelationshipsBuilder relationshipsBuilder,
ModularityOptimization modularityOptimization,
RelationshipIterator relationshipIterator,
- double scaleCoefficient,
Partition partition
) {
this.relationshipsBuilder = relationshipsBuilder;
this.modularityOptimization = modularityOptimization;
this.relationshipIterator = relationshipIterator;
this.partition = partition;
- this.scaleCoefficient = scaleCoefficient;
}
@Override
@@ -337,18 +329,13 @@ public void run() {
partition.consume(nodeId -> {
long communityId = modularityOptimization.getCommunityId(nodeId);
relationshipIterator.forEachRelationship(nodeId, 1.0, (source, target, property) -> {
- // In the case of undirected graphs, we need scaling as otherwise we'd have double the value in these edges
- // see GraphAggregationPhase.java for the equivalent in Leiden; and the corresponding test
- if (source == target) {
- relationshipsBuilder.add(communityId, modularityOptimization.getCommunityId(target), property);
- } else {
+ //ignore scaling alltogether
relationshipsBuilder.add(
communityId,
modularityOptimization.getCommunityId(target),
- property * scaleCoefficient
+ property
);
- }
return true;
});
diff --git a/algo/src/main/java/org/neo4j/gds/modularity/ModularityCalculator.java b/algo/src/main/java/org/neo4j/gds/modularity/ModularityCalculator.java
index b9e6ed11e2f..5daf9e58aae 100644
--- a/algo/src/main/java/org/neo4j/gds/modularity/ModularityCalculator.java
+++ b/algo/src/main/java/org/neo4j/gds/modularity/ModularityCalculator.java
@@ -19,13 +19,13 @@
*/
package org.neo4j.gds.modularity;
+import com.carrotsearch.hppc.cursors.LongLongCursor;
import org.apache.commons.lang3.mutable.MutableDouble;
-import org.apache.commons.lang3.mutable.MutableLong;
import org.neo4j.gds.Algorithm;
import org.neo4j.gds.api.Graph;
import org.neo4j.gds.core.concurrency.RunWithConcurrency;
-import org.neo4j.gds.core.utils.paged.HugeAtomicBitSet;
import org.neo4j.gds.core.utils.paged.HugeAtomicDoubleArray;
+import org.neo4j.gds.core.utils.paged.HugeLongLongMap;
import org.neo4j.gds.core.utils.paged.HugeObjectArray;
import org.neo4j.gds.core.utils.partition.PartitionUtils;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
@@ -38,17 +38,32 @@ public class ModularityCalculator extends Algorithm {
private final Graph graph;
private final LongUnaryOperator communityIdProvider;
+ private final HugeLongLongMap communityMapper;
private final int concurrency;
+ public static ModularityCalculator create(
+ Graph graph,
+ LongUnaryOperator seedCommunityIdProvider,
+ int concurrency
+ ) {
+ var communityMapper = createMapping(graph.nodeCount(), seedCommunityIdProvider);
+ LongUnaryOperator communityIdProvider = nodeId -> communityMapper.getOrDefault(
+ seedCommunityIdProvider.applyAsLong(nodeId),
+ -1
+ );
+ return new ModularityCalculator(graph, communityIdProvider, communityMapper, concurrency);
+ }
- protected ModularityCalculator(
+ private ModularityCalculator(
Graph graph,
LongUnaryOperator communityIdProvider,
+ HugeLongLongMap communityMapper,
int concurrency
) {
super(ProgressTracker.NULL_TRACKER);
this.graph = graph;
this.communityIdProvider = communityIdProvider;
+ this.communityMapper = communityMapper;
this.concurrency = concurrency;
}
@@ -56,9 +71,9 @@ protected ModularityCalculator(
public ModularityResult compute() {
var nodeCount = graph.nodeCount();
- var insideRelationships = HugeAtomicDoubleArray.newArray(nodeCount);
- var totalCommunityRelationships = HugeAtomicDoubleArray.newArray(nodeCount);
- var communityTracker = HugeAtomicBitSet.create(nodeCount);
+ var communityCount = communityMapper.size();
+ var insideRelationships = HugeAtomicDoubleArray.newArray(communityCount);
+ var totalCommunityRelationships = HugeAtomicDoubleArray.newArray(communityCount);
var totalRelationshipWeight = new DoubleAdder();
var tasks = PartitionUtils.rangePartition(
@@ -69,7 +84,6 @@ public ModularityResult compute() {
graph,
insideRelationships,
totalCommunityRelationships,
- communityTracker,
communityIdProvider,
totalRelationshipWeight
), Optional.empty()
@@ -80,25 +94,38 @@ public ModularityResult compute() {
.tasks(tasks)
.run();
- var communityCount = communityTracker.cardinality();
var communityModularities = HugeObjectArray.newArray(
CommunityModularity.class,
communityCount
);
var totalRelWeight = totalRelationshipWeight.doubleValue();
- var resultTracker = new MutableLong();
var totalModularity = new MutableDouble();
- communityTracker.forEachSetBit(communityId -> {
- var ec = insideRelationships.get(communityId);
- var Kc = totalCommunityRelationships.get(communityId);
+ long resultIndex = 0;
+ for (LongLongCursor cursor : communityMapper) {
+ long communityId = cursor.key;
+ long mappedCommunityId = cursor.value;
+ var ec = insideRelationships.get(mappedCommunityId);
+ var Kc = totalCommunityRelationships.get(mappedCommunityId);
var modularity = (ec - Kc * Kc * (1.0 / totalRelWeight)) / totalRelWeight;
totalModularity.add(modularity);
- communityModularities.set(resultTracker.getAndIncrement(), CommunityModularity.of(communityId, modularity));
- });
+ communityModularities.set(resultIndex++, CommunityModularity.of(communityId, modularity));
+ }
return ModularityResult.of(totalModularity.doubleValue(), communityCount, communityModularities);
}
@Override
public void release() {}
+ static HugeLongLongMap createMapping(long nodeCount, LongUnaryOperator seedCommunityId) {
+
+ var seedMap = new HugeLongLongMap(nodeCount);
+ long seedId = 0;
+ for (long nodeId = 0; nodeId < nodeCount; ++nodeId) {
+ long communityId = seedCommunityId.applyAsLong(nodeId);
+ if (!seedMap.containsKey(communityId)) {
+ seedMap.put(communityId, seedId++);
+ }
+ }
+ return seedMap;
+ }
}
diff --git a/algo/src/main/java/org/neo4j/gds/modularity/ModularityCalculatorFactory.java b/algo/src/main/java/org/neo4j/gds/modularity/ModularityCalculatorFactory.java
index 7797c171606..494c9cf3ac2 100644
--- a/algo/src/main/java/org/neo4j/gds/modularity/ModularityCalculatorFactory.java
+++ b/algo/src/main/java/org/neo4j/gds/modularity/ModularityCalculatorFactory.java
@@ -30,7 +30,7 @@ public ModularityCalculator build(
CONFIG configuration,
ProgressTracker progressTracker
) {
- return new ModularityCalculator(
+ return ModularityCalculator.create(
graph,
graph.nodeProperties(configuration.communityProperty())::longValue,
configuration.concurrency()
diff --git a/algo/src/main/java/org/neo4j/gds/modularity/RelationshipCountCollector.java b/algo/src/main/java/org/neo4j/gds/modularity/RelationshipCountCollector.java
index 016d2d078d1..2483f5ba9fe 100644
--- a/algo/src/main/java/org/neo4j/gds/modularity/RelationshipCountCollector.java
+++ b/algo/src/main/java/org/neo4j/gds/modularity/RelationshipCountCollector.java
@@ -20,7 +20,6 @@
package org.neo4j.gds.modularity;
import org.neo4j.gds.api.Graph;
-import org.neo4j.gds.core.utils.paged.HugeAtomicBitSet;
import org.neo4j.gds.core.utils.paged.HugeAtomicDoubleArray;
import org.neo4j.gds.core.utils.partition.Partition;
@@ -32,7 +31,6 @@ class RelationshipCountCollector implements Runnable {
private final Graph localGraph;
private final HugeAtomicDoubleArray insideRelationships;
private final HugeAtomicDoubleArray totalCommunityRelationships;
- private final HugeAtomicBitSet communityTracker;
private final LongUnaryOperator communityIdProvider;
private final DoubleAdder totalRelationshipWeight;
@@ -42,19 +40,15 @@ class RelationshipCountCollector implements Runnable {
Graph graph,
HugeAtomicDoubleArray insideRelationships,
HugeAtomicDoubleArray totalCommunityRelationships,
- HugeAtomicBitSet communityTracker,
LongUnaryOperator communityIdProvider,
DoubleAdder totalRelationshipWeight
) {
this.totalRelationshipWeight = totalRelationshipWeight;
- assert insideRelationships.size() == totalCommunityRelationships.size()
- && insideRelationships.size() == communityTracker.size();
-
+ assert insideRelationships.size() == totalCommunityRelationships.size();
this.partition = partition;
this.localGraph = graph.concurrentCopy();
this.insideRelationships = insideRelationships;
this.totalCommunityRelationships = totalCommunityRelationships;
- this.communityTracker = communityTracker;
this.communityIdProvider = communityIdProvider;
}
@@ -66,7 +60,6 @@ public void run() {
long communityId = communityIdProvider.applyAsLong(nodeId);
localGraph.forEachRelationship(nodeId, 1.0, (s, t, w) -> {
long tCommunityId = communityIdProvider.applyAsLong(t);
- communityTracker.set(communityId);
if (tCommunityId == communityId) {
insideRelationships.getAndAdd(communityId, w);
}
diff --git a/algo/src/main/java/org/neo4j/gds/modularityoptimization/ModularityOptimization.java b/algo/src/main/java/org/neo4j/gds/modularityoptimization/ModularityOptimization.java
index 686206e2346..546b16818ee 100644
--- a/algo/src/main/java/org/neo4j/gds/modularityoptimization/ModularityOptimization.java
+++ b/algo/src/main/java/org/neo4j/gds/modularityoptimization/ModularityOptimization.java
@@ -414,10 +414,9 @@ public LongNodePropertyValues asNodeProperties() {
public long longValue(long nodeId) {
return getCommunityId(nodeId);
}
-
- @Override
- public long size() {
- return currentCommunities.size();
+ @Override
+ public long nodeCount() {
+ return graph.nodeCount();
}
};
}
diff --git a/algo/src/main/java/org/neo4j/gds/pagerank/EigenvectorComputation.java b/algo/src/main/java/org/neo4j/gds/pagerank/EigenvectorComputation.java
index 60cfced65c3..be38380605f 100644
--- a/algo/src/main/java/org/neo4j/gds/pagerank/EigenvectorComputation.java
+++ b/algo/src/main/java/org/neo4j/gds/pagerank/EigenvectorComputation.java
@@ -115,7 +115,7 @@ public boolean masterCompute(MasterComputeContext context) {
var properties = new DoubleNodePropertyValues() {
@Override
- public long size() {
+ public long nodeCount() {
return context.nodeCount();
}
diff --git a/algo/src/main/java/org/neo4j/gds/paths/delta/DeltaStepping.java b/algo/src/main/java/org/neo4j/gds/paths/delta/DeltaStepping.java
index 23a38e6feac..7bf85bac8df 100644
--- a/algo/src/main/java/org/neo4j/gds/paths/delta/DeltaStepping.java
+++ b/algo/src/main/java/org/neo4j/gds/paths/delta/DeltaStepping.java
@@ -58,6 +58,8 @@ public final class DeltaStepping extends Algorithm {
private static final int NO_BIN = Integer.MAX_VALUE;
private static final int BIN_SIZE_THRESHOLD = 1000;
+ private static final int BATCH_SIZE = 64;
+
private final Graph graph;
private final long startNode;
@@ -281,8 +283,8 @@ int minNonEmptyBin() {
private void relaxGlobalBin() {
long offset;
- while ((offset = frontierIndex.getAndAdd(64)) < frontierLength) {
- long limit = Math.min(offset + 64, frontierLength);
+ while ((offset = frontierIndex.getAndAdd(BATCH_SIZE)) < frontierLength) {
+ long limit = Math.min(offset + BATCH_SIZE, frontierLength);
for (long idx = offset; idx < limit; idx++) {
var nodeId = frontier.get(idx);
@@ -326,7 +328,8 @@ private void relaxNode(long nodeId) {
break;
}
// CAX failed, retry
- oldDist = witness;
+ //we need to fetch the most recent value from distances
+ oldDist = distances.distance(targetNodeId);
}
return true;
diff --git a/algo/src/main/java/org/neo4j/gds/paths/delta/TentativeDistances.java b/algo/src/main/java/org/neo4j/gds/paths/delta/TentativeDistances.java
index de82b067f5c..3870a66d40c 100644
--- a/algo/src/main/java/org/neo4j/gds/paths/delta/TentativeDistances.java
+++ b/algo/src/main/java/org/neo4j/gds/paths/delta/TentativeDistances.java
@@ -168,24 +168,38 @@ public double compareAndExchange(long nodeId, double expectedDistance, double ne
// locked by another thread
if (currentPredecessor < 0) {
- return distances.get(nodeId);
+ //we should signal failure
+ // for that we must be sure not to return the 'expectedDistance' by accident!
+ //we hence return its negation (or -1 if ==0)
+ return (Double.compare(expectedDistance, 0.0) == 0) ? -1.0 : -expectedDistance;
}
var witness = predecessors.compareAndExchange(nodeId, currentPredecessor, -predecessor - 1);
// CAX failed
if (witness != currentPredecessor) {
- return distances.get(nodeId);
+ //we should signal failure
+ // for that we must be sure not to return the 'expectedDistance' by accident!
+ return (Double.compare(expectedDistance, 0.0) == 0) ? -1.0 : -expectedDistance;
}
- // we have the look
- distances.set(nodeId, newDistance);
+ // we have the lock; no-one else can write on nodeId at the moment.
+ // Let us do a check if it makes sense to update
- // unlock
- predecessors.set(nodeId, predecessor);
+ double oldDistance = distances.get(nodeId);
+
+ if (oldDistance > newDistance) {
+ distances.set(nodeId, newDistance);
+ // unlock
+ predecessors.set(nodeId, predecessor);
+ // return previous distance to signal successful CAX
- // return previous distance to signal successful CAX
- return expectedDistance;
+ return expectedDistance;
+ }
+ predecessors.set(nodeId, currentPredecessor);
+ //signal unsuccesful update
+ //note that this unsuccesful update will be the last attempt
+ return (Double.compare(expectedDistance, 0.0) == 0.0) ? -1.0 : -expectedDistance;
}
}
}
diff --git a/algo/src/main/java/org/neo4j/gds/paths/dijkstra/Dijkstra.java b/algo/src/main/java/org/neo4j/gds/paths/dijkstra/Dijkstra.java
index 203721de43d..223d9c166e8 100644
--- a/algo/src/main/java/org/neo4j/gds/paths/dijkstra/Dijkstra.java
+++ b/algo/src/main/java/org/neo4j/gds/paths/dijkstra/Dijkstra.java
@@ -217,6 +217,7 @@ private PathResult next(TraversalPredicate traversalPredicate, ImmutablePathResu
return pathResult(node, pathResultBuilder);
}
}
+
return PathResult.EMPTY;
}
diff --git a/algo/src/main/java/org/neo4j/gds/paths/yens/MutablePathResult.java b/algo/src/main/java/org/neo4j/gds/paths/yens/MutablePathResult.java
index 27efd1e401c..8d5e43eb081 100644
--- a/algo/src/main/java/org/neo4j/gds/paths/yens/MutablePathResult.java
+++ b/algo/src/main/java/org/neo4j/gds/paths/yens/MutablePathResult.java
@@ -31,6 +31,7 @@
*/
final class MutablePathResult {
+ private final long[] EMPTY_ARRAY = new long[0];
private long index;
private final long sourceNode;
@@ -131,10 +132,9 @@ boolean matches(MutablePathResult path, int index) {
*/
boolean matchesExactly(MutablePathResult path, int index) {
- if (relationshipIds == null || path.relationshipIds == null) {
+ if (relationshipIds.length == 0 || path.relationshipIds.length == 0) {
return matches(path, index);
}
-
for (int i = 0; i < index; i++) {
if (nodeIds[i] != path.nodeIds[i]) {
return false;
@@ -157,7 +157,8 @@ boolean matchesExactly(MutablePathResult path, int index) {
* The cost value associated with the last value in this path, is added to
* the costs for each node in the second path.
*/
- void append(MutablePathResult path) {
+
+ private void append(MutablePathResult path, long[] relationships) {
// spur node is end of first and beginning of second path
assert nodeIds[nodeIds.length - 1] == path.nodeIds[0];
@@ -166,15 +167,10 @@ void append(MutablePathResult path) {
var newNodeIds = new long[oldLength + path.nodeIds.length - 1];
var newCosts = new double[oldLength + path.nodeIds.length - 1];
- var oldRelationshipIdsLength = relationshipIds.length;
- var newRelationshipIds = new long[oldRelationshipIdsLength + path.relationshipIds.length];
-
// copy node ids
System.arraycopy(this.nodeIds, 0, newNodeIds, 0, oldLength);
System.arraycopy(path.nodeIds, 1, newNodeIds, oldLength, path.nodeIds.length - 1);
- // copy relationship ids
- System.arraycopy(this.relationshipIds, 0, newRelationshipIds, 0, oldRelationshipIdsLength);
- System.arraycopy(path.relationshipIds, 0, newRelationshipIds, oldRelationshipIdsLength, path.relationshipIds.length);
+
// copy costs
System.arraycopy(this.costs, 0, newCosts, 0, oldLength);
System.arraycopy(path.costs, 1, newCosts, oldLength, path.costs.length - 1);
@@ -186,10 +182,41 @@ void append(MutablePathResult path) {
}
this.nodeIds = newNodeIds;
- this.relationshipIds = newRelationshipIds;
+ this.relationshipIds = relationships;
this.costs = newCosts;
}
+ void append(MutablePathResult path) {
+
+ var oldRelationshipIdsLength = relationshipIds.length;
+ var newRelationshipIds = new long[oldRelationshipIdsLength + path.relationshipIds.length];
+ // copy relationship ids
+ System.arraycopy(this.relationshipIds, 0, newRelationshipIds, 0, oldRelationshipIdsLength);
+ System.arraycopy(
+ path.relationshipIds,
+ 0,
+ newRelationshipIds,
+ oldRelationshipIdsLength,
+ path.relationshipIds.length
+ );
+
+ append(path, newRelationshipIds);
+ }
+
+ /**
+ * Appends the given path to this path without creating an explicit relationship array.
+ *
+ * The last node in this path, must match the first node in the given path.
+ * This node will only appear once in the resulting path.
+ * The cost value associated with the last value in this path, is added to
+ * the costs for each node in the second path.
+ */
+ void appendWithoutRelationshipIds(MutablePathResult path) {
+ // spur node is end of first and beginning of second path
+ append(path, EMPTY_ARRAY);
+ }
+
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/algo/src/main/java/org/neo4j/gds/paths/yens/Yens.java b/algo/src/main/java/org/neo4j/gds/paths/yens/Yens.java
index 2bd70bbb9a6..8ecc2700b17 100644
--- a/algo/src/main/java/org/neo4j/gds/paths/yens/Yens.java
+++ b/algo/src/main/java/org/neo4j/gds/paths/yens/Yens.java
@@ -39,6 +39,8 @@
import java.util.Comparator;
import java.util.Optional;
import java.util.PriorityQueue;
+import java.util.function.BiConsumer;
+import java.util.function.ToLongBiFunction;
import java.util.stream.Stream;
import static org.neo4j.gds.utils.StringFormatting.formatWithLocale;
@@ -50,9 +52,12 @@ public final class Yens extends Algorithm {
private final Graph graph;
private final ShortestPathYensBaseConfig config;
private final Dijkstra dijkstra;
+ private final LongScatterSet nodeAvoidList;
+ private final LongObjectScatterMap relationshipAvoidList;
+ private final ToLongBiFunction
+ relationshipAvoidMapper;
+ private final BiConsumer pathAppender;
- private final LongScatterSet nodeBlackList;
- private final LongObjectScatterMap relationshipBlackList;
/**
* Configure Yens to compute at most one source-target shortest path.
@@ -63,12 +68,15 @@ public static Yens sourceTarget(
ProgressTracker progressTracker
) {
// If the input graph is a multi-graph, we need to track
- // parallel relationships. This is necessary since shortest
+ // parallel relationships ids. This is necessary since shortest
// paths can visit the same nodes via different relationships.
+ //If not, we need to track which is the next neighbor.
+
+ boolean shouldTrackRelationships = graph.isMultiGraph();
var newConfig = ImmutableShortestPathYensBaseConfig
.builder()
.from(config)
- .trackRelationships(graph.isMultiGraph())
+ .trackRelationships(shouldTrackRelationships)
.build();
// Init dijkstra algorithm for computing shortest paths
var dijkstra = Dijkstra.sourceTarget(graph, newConfig, Optional.empty(), progressTracker);
@@ -95,16 +103,37 @@ private Yens(Graph graph, Dijkstra dijkstra, ShortestPathYensBaseConfig config,
this.config = config;
// Track nodes and relationships that are skipped in a single iteration.
// The content of these data structures is reset after each of k iterations.
- this.nodeBlackList = new LongScatterSet();
- this.relationshipBlackList = new LongObjectScatterMap<>();
- // set filter in Dijkstra to respect our blacklists
+ this.nodeAvoidList = new LongScatterSet();
+ this.relationshipAvoidList = new LongObjectScatterMap<>();
+ // set filter in Dijkstra to respect our list of relationships to avoid
this.dijkstra = dijkstra;
+
+ if (config.trackRelationships()) {
+ // if we are in a multi-graph, we must store the relationships ids as they are
+ //since two nodes may be connected by multiple relationships and we must know which to avoid
+ relationshipAvoidMapper = (path, position) -> path.relationship(position);
+ pathAppender = (rootPath, spurPath) -> rootPath.append(MutablePathResult.of(spurPath));
+ } else {
+ //otherwise the graph has surely no parallel edges, we do not need to explicitly store relationship ids
+ //we can just store endpoints, so that we know which nodes a node should avoid
+ relationshipAvoidMapper = (path, position) -> path.node(position + 1);
+ pathAppender = (rootPath, spurPath) -> rootPath.appendWithoutRelationshipIds(MutablePathResult.of(spurPath));
+ }
dijkstra.withRelationshipFilter((source, target, relationshipId) ->
- !nodeBlackList.contains(target) &&
- !(relationshipBlackList.getOrDefault(source, EMPTY_SET).contains(relationshipId))
+ !nodeAvoidList.contains(target)
+ && !shouldAvoidRelationship(source, target, relationshipId)
+
);
}
+ private boolean shouldAvoidRelationship(long source, long target, long relationshipId) {
+ long forbidden = config.trackRelationships()
+ ? relationshipId
+ : target;
+ return relationshipAvoidList.getOrDefault(source, EMPTY_SET).contains(forbidden);
+
+ }
+
@Override
public DijkstraResult compute() {
progressTracker.beginSubTask();
@@ -139,13 +168,13 @@ public DijkstraResult compute() {
// Filter relationships that are part of the previous
// shortest paths which share the same root path.
if (rootPath.matchesExactly(path, n + 1)) {
- var relationshipId = path.relationship(n);
+ var relationshipId = relationshipAvoidMapper.applyAsLong(path, n);
- var neighbors = relationshipBlackList.get(spurNode);
+ var neighbors = relationshipAvoidList.get(spurNode);
if (neighbors == null) {
neighbors = new LongHashSet();
- relationshipBlackList.put(spurNode, neighbors);
+ relationshipAvoidList.put(spurNode, neighbors);
}
neighbors.add(relationshipId);
}
@@ -153,7 +182,7 @@ public DijkstraResult compute() {
// Filter nodes from root path to avoid cyclic path searches.
for (int j = 0; j < n; j++) {
- nodeBlackList.add(rootPath.node(j));
+ nodeAvoidList.add(rootPath.node(j));
}
// Calculate the spur path from the spur node to the sink.
@@ -162,8 +191,8 @@ public DijkstraResult compute() {
var spurPath = computeDijkstra(graph.toOriginalNodeId(spurNode));
// Clear filters for next spur node
- nodeBlackList.clear();
- relationshipBlackList.clear();
+ nodeAvoidList.clear();
+ relationshipAvoidList.clear();
// No new candidate from this spur node, continue with next node.
if (spurPath.isEmpty()) {
@@ -171,7 +200,8 @@ public DijkstraResult compute() {
}
// Entire path is made up of the root path and spur path.
- rootPath.append(MutablePathResult.of(spurPath.get()));
+ pathAppender.accept(rootPath, spurPath.get());
+
// Add the potential k-shortest path to the heap.
if (!candidates.contains(rootPath)) {
candidates.add(rootPath);
@@ -203,8 +233,8 @@ private PriorityQueue initCandidatesQueue() {
@Override
public void release() {
dijkstra.release();
- nodeBlackList.release();
- relationshipBlackList.release();
+ nodeAvoidList.release();
+ relationshipAvoidList.release();
}
private Optional computeDijkstra(long sourceNode) {
diff --git a/algo/src/main/java/org/neo4j/gds/similarity/SimilarityGraphBuilder.java b/algo/src/main/java/org/neo4j/gds/similarity/SimilarityGraphBuilder.java
index 9eddd7ab32c..241e0468d5b 100644
--- a/algo/src/main/java/org/neo4j/gds/similarity/SimilarityGraphBuilder.java
+++ b/algo/src/main/java/org/neo4j/gds/similarity/SimilarityGraphBuilder.java
@@ -20,6 +20,7 @@
package org.neo4j.gds.similarity;
import org.neo4j.gds.Orientation;
+import org.neo4j.gds.RelationshipType;
import org.neo4j.gds.api.Graph;
import org.neo4j.gds.api.IdMap;
import org.neo4j.gds.core.compress.AdjacencyListBehavior;
@@ -84,6 +85,7 @@ public SimilarityGraphBuilder(
public Graph build(Stream stream) {
var relationshipsBuilder = GraphFactory.initRelationshipsBuilder()
.nodes(idMap.rootIdMap())
+ .relationshipType(RelationshipType.of("REL"))
.orientation(Orientation.NATURAL)
.addPropertyConfig(GraphFactory.PropertyConfig.of("property"))
.concurrency(concurrency)
diff --git a/algo/src/main/java/org/neo4j/gds/similarity/filterednodesim/FilteredNodeSimilarityFactory.java b/algo/src/main/java/org/neo4j/gds/similarity/filterednodesim/FilteredNodeSimilarityFactory.java
index c4a5441d076..9e4940c9143 100644
--- a/algo/src/main/java/org/neo4j/gds/similarity/filterednodesim/FilteredNodeSimilarityFactory.java
+++ b/algo/src/main/java/org/neo4j/gds/similarity/filterednodesim/FilteredNodeSimilarityFactory.java
@@ -106,7 +106,11 @@ public MemoryEstimation memoryEstimation(CONFIG config) {
);
}
if (config.hasTopN()) {
- builder.add("topN list", TopNList.memoryEstimation(topN));
+ builder.add(
+ "topN list",
+ MemoryEstimations.setup("", (dimensions, concurrency) ->
+ TopNList.memoryEstimation(dimensions.nodeCount(), topN))
+ );
}
return builder.build();
}
diff --git a/algo/src/main/java/org/neo4j/gds/similarity/knn/metrics/LongArrayPropertySimilarityComputer.java b/algo/src/main/java/org/neo4j/gds/similarity/knn/metrics/LongArrayPropertySimilarityComputer.java
index 4f79cba6924..5295efeab76 100644
--- a/algo/src/main/java/org/neo4j/gds/similarity/knn/metrics/LongArrayPropertySimilarityComputer.java
+++ b/algo/src/main/java/org/neo4j/gds/similarity/knn/metrics/LongArrayPropertySimilarityComputer.java
@@ -55,16 +55,22 @@ static final class SortedLongArrayPropertyValues implements LongArrayNodePropert
private final HugeObjectArray properties;
SortedLongArrayPropertyValues(NodePropertyValues nodePropertyValues) {
- this.properties = HugeObjectArray.newArray(long[].class, nodePropertyValues.size());
+ this.properties = HugeObjectArray.newArray(long[].class, nodePropertyValues.nodeCount());
this.properties.setAll(i -> {
- var value = nodePropertyValues.longArrayValue(i).clone();
+ long[] input = nodePropertyValues.longArrayValue(i);
+
+ if (input == null) {
+ return null;
+ }
+
+ var value = input.clone();
Arrays.parallelSort(value);
return value;
});
}
@Override
- public long size() {
+ public long nodeCount() {
return properties.size();
}
diff --git a/algo/src/main/java/org/neo4j/gds/similarity/knn/metrics/NullCheckingNodePropertyValues.java b/algo/src/main/java/org/neo4j/gds/similarity/knn/metrics/NullCheckingNodePropertyValues.java
index 2142b87109e..c1e37ddd4e7 100644
--- a/algo/src/main/java/org/neo4j/gds/similarity/knn/metrics/NullCheckingNodePropertyValues.java
+++ b/algo/src/main/java/org/neo4j/gds/similarity/knn/metrics/NullCheckingNodePropertyValues.java
@@ -86,8 +86,8 @@ public Value value(long nodeId) {
}
@Override
- public long size() {
- return properties.size();
+ public long nodeCount() {
+ return properties.nodeCount();
}
private void check(long nodeId, @Nullable Object value) {
diff --git a/algo/src/main/java/org/neo4j/gds/similarity/nodesim/NodeSimilarity.java b/algo/src/main/java/org/neo4j/gds/similarity/nodesim/NodeSimilarity.java
index dad85ebf44a..00d7bcf40a6 100644
--- a/algo/src/main/java/org/neo4j/gds/similarity/nodesim/NodeSimilarity.java
+++ b/algo/src/main/java/org/neo4j/gds/similarity/nodesim/NodeSimilarity.java
@@ -33,7 +33,6 @@
import org.neo4j.gds.similarity.SimilarityResult;
import org.neo4j.gds.similarity.filtering.NodeFilter;
-import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;
@@ -57,7 +56,6 @@ public class NodeSimilarity extends Algorithm {
private final MetricSimilarityComputer similarityComputer;
private HugeObjectArray vectors;
private HugeObjectArray weights;
- private long nodesToCompare;
private final boolean weighted;
@@ -188,7 +186,7 @@ public SimilarityGraphResult computeToGraph() {
executorService
).build(similarities);
}
- return new SimilarityGraphResult(similarityGraph, nodesToCompare, isTopKGraph);
+ return new SimilarityGraphResult(similarityGraph, sourceNodes.cardinality(), isTopKGraph);
}
private void prepare() {
@@ -218,19 +216,19 @@ private void prepare() {
// TODO: we don't need to do the rest of the prepare for a node that isn't going to be used in the computation
progressTracker.logProgress(graph.degree(node));
vectorComputer.forEachRelationship(node);
+
+ if (sortVectors) {
+ vectorComputer.sortTargetIds();
+ }
if (weighted) {
weights.set(node, vectorComputer.getWeights());
}
- if (sortVectors) {
- Arrays.sort(vectorComputer.targetIds.buffer);
- }
return vectorComputer.targetIds.buffer;
}
progressTracker.logProgress(graph.degree(node));
return null;
});
- nodesToCompare = sourceNodes.cardinality();
progressTracker.endSubTask();
}
@@ -437,8 +435,14 @@ private LongStream checkProgress(LongStream stream) {
}
private long calculateWorkload() {
- long workload = nodesToCompare * nodesToCompare;
- if (concurrency == 1) {
+ //for each source node, examine all their target nodes
+ //if no filter then sourceNodes == targetNodes
+ long workload = sourceNodes.cardinality() * targetNodes.cardinality();
+
+ //when on concurrency of 1 on not-filtered similarity, we only compare nodeId with greater indexed nodes
+ // so work is halved. This does not hold for filtered similarity, since the targetNodes might be lesser indexed.
+ boolean isNotFiltered = sourceNodeFilter.equals(NodeFilter.noOp) && targetNodeFilter.equals(NodeFilter.noOp);
+ if (concurrency == 1 && isNotFiltered) {
workload = workload / 2;
}
return workload;
diff --git a/algo/src/main/java/org/neo4j/gds/similarity/nodesim/NodeSimilarityFactory.java b/algo/src/main/java/org/neo4j/gds/similarity/nodesim/NodeSimilarityFactory.java
index 2f7e5c273d0..4da718731e9 100644
--- a/algo/src/main/java/org/neo4j/gds/similarity/nodesim/NodeSimilarityFactory.java
+++ b/algo/src/main/java/org/neo4j/gds/similarity/nodesim/NodeSimilarityFactory.java
@@ -101,7 +101,11 @@ public MemoryEstimation memoryEstimation(CONFIG config) {
);
}
if (config.hasTopN()) {
- builder.add("topN list", TopNList.memoryEstimation(topN));
+ builder.add(
+ "topN list",
+ MemoryEstimations.setup("", (dimensions, concurrency) ->
+ TopNList.memoryEstimation(dimensions.nodeCount(), topN))
+ );
}
return builder.build();
}
diff --git a/algo/src/main/java/org/neo4j/gds/similarity/nodesim/TopKMap.java b/algo/src/main/java/org/neo4j/gds/similarity/nodesim/TopKMap.java
index 4bfde4aa305..335d66e044e 100644
--- a/algo/src/main/java/org/neo4j/gds/similarity/nodesim/TopKMap.java
+++ b/algo/src/main/java/org/neo4j/gds/similarity/nodesim/TopKMap.java
@@ -39,10 +39,11 @@ public class TopKMap {
private final BitSet sourceNodes;
public static MemoryEstimation memoryEstimation(long nodes, int topK) {
+ int actualTopK = Math.toIntExact(Math.min(topK, nodes));
return MemoryEstimations.builder(TopKMap.class)
.add("topK lists",
MemoryEstimations.builder("topK lists", TopKList.class)
- .add("queues", BoundedLongPriorityQueue.memoryEstimation(topK))
+ .add("queues", BoundedLongPriorityQueue.memoryEstimation(actualTopK))
.build()
.times(nodes)
)
diff --git a/algo/src/main/java/org/neo4j/gds/similarity/nodesim/TopNList.java b/algo/src/main/java/org/neo4j/gds/similarity/nodesim/TopNList.java
index ed3f7810d4e..f1cff556e7b 100644
--- a/algo/src/main/java/org/neo4j/gds/similarity/nodesim/TopNList.java
+++ b/algo/src/main/java/org/neo4j/gds/similarity/nodesim/TopNList.java
@@ -31,9 +31,14 @@
public class TopNList {
- public static MemoryEstimation memoryEstimation(int topN) {
+ public static MemoryEstimation memoryEstimation(long nodeCount, int topN) {
+ int actualTopN = topN;
+ if (topN > nodeCount) { //avoid overflows for nodeCount * nodeCount (when nodeCount is >2^31)
+ long normalizedMaximum = nodeCount * nodeCount;
+ actualTopN = Math.toIntExact(Math.min(normalizedMaximum, topN));
+ }
return MemoryEstimations.builder(TopNList.class)
- .add("queue", BoundedLongLongPriorityQueue.memoryEstimation(topN))
+ .add("queue", BoundedLongLongPriorityQueue.memoryEstimation(actualTopN))
.build();
}
diff --git a/algo/src/main/java/org/neo4j/gds/similarity/nodesim/VectorComputer.java b/algo/src/main/java/org/neo4j/gds/similarity/nodesim/VectorComputer.java
index bd48940112f..8c75e130e6c 100644
--- a/algo/src/main/java/org/neo4j/gds/similarity/nodesim/VectorComputer.java
+++ b/algo/src/main/java/org/neo4j/gds/similarity/nodesim/VectorComputer.java
@@ -25,9 +25,11 @@
import org.neo4j.gds.api.Graph;
import org.neo4j.gds.api.RelationshipConsumer;
import org.neo4j.gds.api.RelationshipWithPropertyConsumer;
+import org.neo4j.gds.core.utils.TwoArraysSort;
-import static org.neo4j.gds.utils.StringFormatting.formatWithLocale;
+import java.util.Arrays;
+import static org.neo4j.gds.utils.StringFormatting.formatWithLocale;
abstract class VectorComputer {
final Graph graph;
@@ -69,6 +71,8 @@ static VectorComputer of(
: new UnweightedVectorComputer(graph);
}
+ abstract void sortTargetIds();
+
static final class UnweightedVectorComputer extends VectorComputer implements RelationshipConsumer {
UnweightedVectorComputer(Graph graph) {
@@ -91,6 +95,11 @@ public double[] getWeights() {
));
}
+ @Override
+ void sortTargetIds() {
+ Arrays.sort(targetIds.buffer);
+ }
+
@Override
void forEachRelationship(long node) {
graph.forEachRelationship(node, this);
@@ -129,5 +138,11 @@ void reset(int degree) {
super.reset(degree);
weights = new DoubleArrayList(degree, ARRAY_SIZING_STRATEGY);
}
+
+ @Override
+ void sortTargetIds() {
+ int length = weights.buffer.length;
+ TwoArraysSort.sortDoubleArrayByLongValues(targetIds.buffer, weights.buffer, length);
+ }
}
}
diff --git a/algo/src/main/java/org/neo4j/gds/spanningtree/SpanningTree.java b/algo/src/main/java/org/neo4j/gds/spanningtree/SpanningTree.java
index 47781805f74..3a1716cc8aa 100644
--- a/algo/src/main/java/org/neo4j/gds/spanningtree/SpanningTree.java
+++ b/algo/src/main/java/org/neo4j/gds/spanningtree/SpanningTree.java
@@ -20,7 +20,7 @@
package org.neo4j.gds.spanningtree;
import org.apache.commons.lang3.builder.EqualsBuilder;
-import org.neo4j.gds.api.RelationshipConsumer;
+import org.neo4j.gds.api.RelationshipWithPropertyConsumer;
import org.neo4j.gds.core.utils.paged.HugeDoubleArray;
import org.neo4j.gds.core.utils.paged.HugeLongArray;
@@ -62,7 +62,9 @@ public double totalWeight() {
return totalWeight;
}
- public HugeLongArray parentArray() {return parent;}
+ public HugeLongArray parentArray() {
+ return parent;
+ }
public long parent(long nodeId) {return parent.get(nodeId);}
@@ -70,13 +72,14 @@ public double costToParent(long nodeId) {
return costToParent.get(nodeId);
}
- public void forEach(RelationshipConsumer consumer) {
+ public void forEach(RelationshipWithPropertyConsumer consumer) {
for (int i = 0; i < nodeCount; i++) {
- final long parent = this.parent.get(i);
+ long parent = this.parent.get(i);
+ double cost = this.costToParent(i);
if (parent == -1) {
continue;
}
- if (!consumer.accept(parent, i)) {
+ if (!consumer.accept(parent, i, cost)) {
return;
}
}
diff --git a/algo/src/main/java/org/neo4j/gds/steiner/SteinerBasedDeltaTask.java b/algo/src/main/java/org/neo4j/gds/steiner/SteinerBasedDeltaTask.java
index eebbe85cd17..de059b4a2a0 100644
--- a/algo/src/main/java/org/neo4j/gds/steiner/SteinerBasedDeltaTask.java
+++ b/algo/src/main/java/org/neo4j/gds/steiner/SteinerBasedDeltaTask.java
@@ -150,6 +150,7 @@ private void relaxNode(long nodeId) {
private void tryToUpdate(long sourceNodeId, long targetNodeId, double weight) {
var oldDist = distances.distance(targetNodeId);
var newDist = distances.distance(sourceNodeId) + weight;
+
while (Double.compare(newDist, oldDist) < 0) {
var witness = distances.compareAndExchange(targetNodeId, oldDist, newDist, sourceNodeId);
@@ -167,7 +168,7 @@ private void tryToUpdate(long sourceNodeId, long targetNodeId, double weight) {
break;
}
// CAX failed, retry
- oldDist = witness;
+ oldDist = distances.distance(targetNodeId);
}
smallestConsideredDistance = Math.min(newDist, smallestConsideredDistance);
diff --git a/algo/src/main/java/org/neo4j/gds/traversal/RandomWalk.java b/algo/src/main/java/org/neo4j/gds/traversal/RandomWalk.java
index 3f07c4b0993..179210d398a 100644
--- a/algo/src/main/java/org/neo4j/gds/traversal/RandomWalk.java
+++ b/algo/src/main/java/org/neo4j/gds/traversal/RandomWalk.java
@@ -38,6 +38,7 @@
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
@@ -96,7 +97,7 @@ public Stream compute() {
? new NextNodeSupplier.GraphNodeSupplier(graph.nodeCount())
: NextNodeSupplier.ListNodeSupplier.of(config, graph);
- var terminationFlag = new ExternalTerminationFlag(this.terminationFlag);
+ var terminationFlag = new ExternalTerminationFlag(this);
BlockingQueue walks = new ArrayBlockingQueue<>(config.walkBufferSize());
long[] TOMB = new long[0];
@@ -178,7 +179,10 @@ private void tasksRunner(
progressTracker.endSubTask("create walks");
try {
- walks.put(tombstone);
+ boolean finished = false;
+ while (!finished && terminationFlag.running()) {
+ finished = walks.offer(tombstone, 100, TimeUnit.MILLISECONDS);
+ }
} catch (InterruptedException exception) {
Thread.currentThread().interrupt();
}
@@ -198,15 +202,15 @@ private Stream walksQueueConsumer(
private static final class ExternalTerminationFlag implements TerminationFlag {
private volatile boolean running = true;
- private final TerminationFlag inner;
+ private final Algorithm> algo;
- ExternalTerminationFlag(TerminationFlag inner) {
- this.inner = inner;
+ ExternalTerminationFlag(Algorithm> algo) {
+ this.algo = algo;
}
@Override
public boolean running() {
- return this.running && this.inner.running();
+ return this.running && this.algo.getTerminationFlag().running();
}
void stop() {
@@ -326,9 +330,13 @@ public void run() {
private boolean flushBuffer(int bufferLength) {
bufferLength = Math.min(bufferLength, this.buffer.length);
- for (int i = 0; i < bufferLength && terminationFlag.running(); i++) {
+ int i = 0;
+ while (i < bufferLength && terminationFlag.running()) {
try {
- walks.put(this.buffer[i]);
+ // allow termination to occur if queue is full
+ if (walks.offer(this.buffer[i], 100, TimeUnit.MILLISECONDS)) {
+ i++;
+ }
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
diff --git a/algo/src/main/java/org/neo4j/gds/wcc/WccStreamConfig.java b/algo/src/main/java/org/neo4j/gds/wcc/WccStreamConfig.java
index 340d05d437d..51fda2ba8a3 100644
--- a/algo/src/main/java/org/neo4j/gds/wcc/WccStreamConfig.java
+++ b/algo/src/main/java/org/neo4j/gds/wcc/WccStreamConfig.java
@@ -21,12 +21,19 @@
import org.neo4j.gds.annotation.Configuration;
import org.neo4j.gds.annotation.ValueClass;
+import org.neo4j.gds.config.CommunitySizeConfig;
import org.neo4j.gds.core.CypherMapWrapper;
+import java.util.Optional;
+
@ValueClass
@Configuration
@SuppressWarnings("immutables:subtype")
-public interface WccStreamConfig extends WccBaseConfig {
+public interface WccStreamConfig extends WccBaseConfig, CommunitySizeConfig {
+
+ @Override
+ @Configuration.Key("minComponentSize")
+ Optional minCommunitySize();
static WccStreamConfig of(CypherMapWrapper userInput) {
WccStreamConfigImpl wccStreamConfig = new WccStreamConfigImpl(userInput);
diff --git a/algo/src/main/java/org/neo4j/gds/wcc/WccWriteConfig.java b/algo/src/main/java/org/neo4j/gds/wcc/WccWriteConfig.java
index 42e8ec9945c..b8eba23f7ee 100644
--- a/algo/src/main/java/org/neo4j/gds/wcc/WccWriteConfig.java
+++ b/algo/src/main/java/org/neo4j/gds/wcc/WccWriteConfig.java
@@ -21,14 +21,20 @@
import org.neo4j.gds.annotation.Configuration;
import org.neo4j.gds.annotation.ValueClass;
-import org.neo4j.gds.config.ComponentSizeConfig;
+import org.neo4j.gds.config.CommunitySizeConfig;
import org.neo4j.gds.config.WritePropertyConfig;
import org.neo4j.gds.core.CypherMapWrapper;
+import java.util.Optional;
+
@ValueClass
@Configuration
@SuppressWarnings("immutables:subtype")
-public interface WccWriteConfig extends WccBaseConfig, WritePropertyConfig, ComponentSizeConfig {
+public interface WccWriteConfig extends WccBaseConfig, WritePropertyConfig, CommunitySizeConfig {
+
+ @Override
+ @Configuration.Key("minComponentSize")
+ Optional minCommunitySize();
static WccWriteConfig of(CypherMapWrapper userInput) {
return new WccWriteConfigImpl(userInput);
diff --git a/algo/src/test/java/org/neo4j/gds/beta/indexInverse/InverseRelationshipsTest.java b/algo/src/test/java/org/neo4j/gds/beta/indexInverse/InverseRelationshipsTest.java
index 1c75624deef..88ec03e6aac 100644
--- a/algo/src/test/java/org/neo4j/gds/beta/indexInverse/InverseRelationshipsTest.java
+++ b/algo/src/test/java/org/neo4j/gds/beta/indexInverse/InverseRelationshipsTest.java
@@ -86,7 +86,7 @@ void shouldCreateIndexedRelationships(int concurrency) {
// we need to use the same name for assertGraphEquals to work
graphStore.deleteRelationships(relationshipType);
- graphStore.addRelationshipType(relationshipType, inverseRelationshipsPerType.get(relationshipType));
+ graphStore.addRelationshipType(inverseRelationshipsPerType.get(relationshipType));
for (String relationshipPropertyKey : inverseGraphStore.relationshipPropertyKeys()) {
assertGraphEquals(
@@ -118,7 +118,7 @@ void shouldIndexMultipleTypes(Object relTypes) {
internalTypes.forEach(internalType -> {
// we need to use the same name for assertGraphEquals to work
graphStore.deleteRelationships(internalType);
- graphStore.addRelationshipType(internalType, inverseRelationshipsPerType.get(internalType));
+ graphStore.addRelationshipType(inverseRelationshipsPerType.get(internalType));
});
for (String relationshipPropertyKey : inverseGraphStore.relationshipPropertyKeys()) {
diff --git a/algo/src/test/java/org/neo4j/gds/beta/k1coloring/K1ColoringTest.java b/algo/src/test/java/org/neo4j/gds/beta/k1coloring/K1ColoringTest.java
index a3c233122c3..295eb7a5fb1 100644
--- a/algo/src/test/java/org/neo4j/gds/beta/k1coloring/K1ColoringTest.java
+++ b/algo/src/test/java/org/neo4j/gds/beta/k1coloring/K1ColoringTest.java
@@ -136,8 +136,8 @@ void testParallelK1Coloring() {
.isLessThan(20L);
assertThat(usedColors.size())
- .as("Used colors should be less than or equal to 20")
- .isLessThanOrEqualTo(20);
+ .as("Used colors should be less than or equal to 21")
+ .isLessThanOrEqualTo(21);
}
diff --git a/algo/src/test/java/org/neo4j/gds/beta/undirected/ToUndirectedTest.java b/algo/src/test/java/org/neo4j/gds/beta/undirected/ToUndirectedTest.java
index 7066237bd4d..43a57663caf 100644
--- a/algo/src/test/java/org/neo4j/gds/beta/undirected/ToUndirectedTest.java
+++ b/algo/src/test/java/org/neo4j/gds/beta/undirected/ToUndirectedTest.java
@@ -82,7 +82,7 @@ void shouldCreateUndirectedRelationships(int concurrency) {
Pools.DEFAULT
).compute();
- directedGraphStore.addRelationshipType(RelationshipType.of(config.mutateRelationshipType()), undirectedRelationships);
+ directedGraphStore.addRelationshipType(undirectedRelationships);
for (String relationshipPropertyKey : undirectedGraphStore.relationshipPropertyKeys()) {
assertGraphEquals(
@@ -129,7 +129,7 @@ void shouldCreateUndirectedRelationshipsWithSingleRelationshipProperty(int concu
Pools.DEFAULT
).compute();
- singleDirectedGraphStore.addRelationshipType(RelationshipType.of(config.mutateRelationshipType()), undirectedRelationships);
+ singleDirectedGraphStore.addRelationshipType(undirectedRelationships);
assertGraphEquals(
singleUndirectedGraphStore.getGraph(RelationshipType.of("T2"), Optional.of("prop1")),
@@ -174,7 +174,7 @@ void shouldCreateUndirectedRelationshipsWithNoRelationshipProperty(int concurren
Pools.DEFAULT
).compute();
- noPropertyDirectedGraphStore.addRelationshipType(RelationshipType.of(config.mutateRelationshipType()), undirectedRelationships);
+ noPropertyDirectedGraphStore.addRelationshipType(undirectedRelationships);
assertGraphEquals(
noPropertyUndirectedGraphStore.getGraph(RelationshipType.of("T2")),
diff --git a/algo/src/test/java/org/neo4j/gds/beta/walking/CollapseMultiPathsTest.java b/algo/src/test/java/org/neo4j/gds/beta/walking/CollapseMultiPathsTest.java
index fea823762b4..ae8d4f754ef 100644
--- a/algo/src/test/java/org/neo4j/gds/beta/walking/CollapseMultiPathsTest.java
+++ b/algo/src/test/java/org/neo4j/gds/beta/walking/CollapseMultiPathsTest.java
@@ -108,6 +108,7 @@ void shouldFollowRoutesFromMovies() {
var path = new CollapsePath(
pathTemplates,
false,
+ RelationshipType.of("REL"),
2,
Pools.DEFAULT
@@ -170,6 +171,7 @@ void shouldTurnEveryRouteIntoRelationship() {
new Graph[]{plane}
),
false,
+ RelationshipType.of("REL"),
2,
Pools.DEFAULT
diff --git a/algo/src/test/java/org/neo4j/gds/beta/walking/CollapsePathTest.java b/algo/src/test/java/org/neo4j/gds/beta/walking/CollapsePathTest.java
index 3e7b0da30cb..c7499f19ff0 100644
--- a/algo/src/test/java/org/neo4j/gds/beta/walking/CollapsePathTest.java
+++ b/algo/src/test/java/org/neo4j/gds/beta/walking/CollapsePathTest.java
@@ -119,6 +119,7 @@ void testCreatingRelationships() {
var relationships = new CollapsePath(
Collections.singletonList(new Graph[]{tookRel, tookRel}),
false,
+ RelationshipType.of("SAME_DRUG"),
2,
Pools.DEFAULT
@@ -134,6 +135,7 @@ void testAllowCreatingSelfLoops() {
var relationships = new CollapsePath(
Collections.singletonList(new Graph[]{tookRel, tookRel}),
true,
+ RelationshipType.of("SAME_DRUG"),
2,
Pools.DEFAULT
@@ -147,6 +149,7 @@ void runWithDifferentRelationshipTypes() {
var relationships = new CollapsePath(
Collections.singletonList(new Graph[]{tookGraph, takenByGraph}),
false,
+ RelationshipType.of("SAME_DRUG"),
2,
Pools.DEFAULT
).compute();
@@ -155,7 +158,7 @@ void runWithDifferentRelationshipTypes() {
}
private void assertResultGraph(GraphStore graphStore, SingleTypeRelationships relationships, String expected) {
- graphStore.addRelationshipType(RelationshipType.of("SAME_DRUG"), relationships);
+ graphStore.addRelationshipType(relationships);
assertGraphEquals(
fromGdl(expected),
@@ -194,7 +197,7 @@ void shouldComputeForAllNodesWithoutNodeLabelsSpecified() {
var relationships = new CollapsePathAlgorithmFactory()
.build(graphStore, config, ProgressTracker.NULL_TRACKER)
.compute();
- graphStore.addRelationshipType(mutateRelType, relationships);
+ graphStore.addRelationshipType(relationships);
var resultGraph = graphStore.getGraph(mutateRelType);
// then two relationships should be created
@@ -219,7 +222,7 @@ void shouldComputeForSubsetOfNodesWithNodeLabelsSpecified() {
var relationships = new CollapsePathAlgorithmFactory()
.build(graphStore, config, ProgressTracker.NULL_TRACKER)
.compute();
- graphStore.addRelationshipType(mutateRelType, relationships);
+ graphStore.addRelationshipType(relationships);
var resultGraph = graphStore.getGraph(mutateRelType);
// a single relationship is created (there is no Dog)
diff --git a/algo/src/test/java/org/neo4j/gds/embeddings/fastrp/FastRPTest.java b/algo/src/test/java/org/neo4j/gds/embeddings/fastrp/FastRPTest.java
index ea8976c75e4..6c089f07b73 100644
--- a/algo/src/test/java/org/neo4j/gds/embeddings/fastrp/FastRPTest.java
+++ b/algo/src/test/java/org/neo4j/gds/embeddings/fastrp/FastRPTest.java
@@ -42,13 +42,13 @@
import org.neo4j.gds.core.utils.progress.EmptyTaskRegistryFactory;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
import org.neo4j.gds.core.utils.progress.tasks.TaskProgressTracker;
+import org.neo4j.gds.core.utils.shuffle.ShuffleUtil;
import org.neo4j.gds.extension.GdlExtension;
import org.neo4j.gds.extension.GdlGraph;
import org.neo4j.gds.extension.IdFunction;
import org.neo4j.gds.extension.Inject;
import org.neo4j.gds.ml.core.features.FeatureExtraction;
import org.neo4j.gds.ml.core.features.FeatureExtractor;
-import org.neo4j.gds.core.utils.shuffle.ShuffleUtil;
import java.util.List;
import java.util.Optional;
@@ -624,6 +624,7 @@ void shouldBeDeterministicGivenSameOriginalIds() {
);
RelationshipsBuilder firstRelationshipsBuilder = GraphFactory.initRelationshipsBuilder()
.nodes(firstIdMap)
+ .relationshipType(RelationshipType.of("REL"))
.orientation(Orientation.UNDIRECTED)
.executorService(Pools.DEFAULT)
.build();
@@ -647,6 +648,7 @@ void shouldBeDeterministicGivenSameOriginalIds() {
);
RelationshipsBuilder secondRelationshipsBuilder = GraphFactory.initRelationshipsBuilder()
.nodes(secondIdMap)
+ .relationshipType(RelationshipType.of("REL"))
.orientation(Orientation.UNDIRECTED)
.executorService(Pools.DEFAULT)
.build();
diff --git a/algo/src/test/java/org/neo4j/gds/embeddings/graphsage/GraphSageModelTrainerTest.java b/algo/src/test/java/org/neo4j/gds/embeddings/graphsage/GraphSageModelTrainerTest.java
index afb6bbd3373..145972a1572 100644
--- a/algo/src/test/java/org/neo4j/gds/embeddings/graphsage/GraphSageModelTrainerTest.java
+++ b/algo/src/test/java/org/neo4j/gds/embeddings/graphsage/GraphSageModelTrainerTest.java
@@ -26,10 +26,14 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
+import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.neo4j.gds.Orientation;
import org.neo4j.gds.api.Graph;
import org.neo4j.gds.api.GraphStore;
+import org.neo4j.gds.beta.generator.PropertyProducer;
+import org.neo4j.gds.beta.generator.RandomGraphGenerator;
+import org.neo4j.gds.beta.generator.RelationshipDistribution;
import org.neo4j.gds.core.concurrency.Pools;
import org.neo4j.gds.core.utils.paged.HugeObjectArray;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
@@ -77,6 +81,7 @@ class GraphSageModelTrainerTest {
private Graph unweightedGraph;
@Inject
private Graph arrayGraph;
+
private HugeObjectArray features;
private GraphSageTrainConfigImpl.Builder configBuilder;
@@ -97,6 +102,53 @@ void setUp() {
.embeddingDimension(EMBEDDING_DIMENSION);
}
+ // This reproduced bug in https://trello.com/c/BQ3e12K3/7826-250-graphsage-returns-nan-when-using-relationship-weights
+ // https://github.com/neo4j/graph-data-science/issues/250
+ @ParameterizedTest
+ @EnumSource(AggregatorType.class)
+ void trainsWithRelationshipWeight(AggregatorType aggregatorType) {
+
+ var config = GraphSageTrainConfigImpl.builder()
+ .randomSeed(42L)
+ .batchSize(100)
+ .relationshipWeightProperty("p")
+ .embeddingDimension(2)
+ .aggregator(aggregatorType)
+ .activationFunction(ActivationFunction.SIGMOID)
+ .featureProperties(List.of("features"))
+ .modelName("model")
+ .modelUser("")
+ .build();
+
+ var trainModel = new GraphSageModelTrainer(config, Pools.DEFAULT, ProgressTracker.NULL_TRACKER);
+
+ int nodeCount = 5_000;
+ var bigGraph = RandomGraphGenerator
+ .builder()
+ .nodeCount(nodeCount)
+ .averageDegree(1)
+ .relationshipDistribution(RelationshipDistribution.UNIFORM)
+ .nodePropertyProducer(PropertyProducer.randomEmbedding("features", 1, -100, 100))
+ .relationshipPropertyProducer(PropertyProducer.fixedDouble("p", 0.5))
+ .seed(42L)
+ .build()
+ .generate();
+
+ features = HugeObjectArray.newArray(double[].class, nodeCount);
+
+ LongStream.range(0, nodeCount).forEach(n -> features.set(n, bigGraph.nodeProperties().get("features").doubleArrayValue(n)));
+
+ GraphSageModelTrainer.ModelTrainResult result = trainModel.train(
+ bigGraph,
+ features
+ );
+
+ assertThat(result.layers())
+ .allSatisfy(layer -> assertThat(layer.weights())
+ .noneMatch(weights -> TensorTestUtils.containsNaN(weights.data()))
+ );
+ }
+
@ParameterizedTest
@ValueSource(booleans = {false, true})
void trainsWithMeanAggregator(boolean useRelationshipWeight) {
@@ -236,18 +288,7 @@ void testLosses() {
assertThat(metrics.ranIterationsPerEpoch()).containsExactly(100, 100, 100, 100, 100, 100, 100, 100, 100, 100);
assertThat(metrics.epochLosses().stream().mapToDouble(Double::doubleValue).toArray())
- .contains(new double[]{
- 18.25,
- 16.31,
- 16.41,
- 16.21,
- 14.96,
- 14.97,
- 14.31,
- 16.17,
- 14.90,
- 15.58
- }, Offset.offset(0.05)
+ .contains(new double[]{19.55, 21.24, 19.90, 19.42, 17.87, 17.03, 17.04, 20.42, 15.86, 20.56}, Offset.offset(0.05)
);
}
@@ -280,18 +321,7 @@ void testLossesWithPoolAggregator() {
assertThat(metrics.ranIterationsPerEpoch()).containsOnly(10);
assertThat(metrics.epochLosses().stream().mapToDouble(Double::doubleValue).toArray())
- .contains(new double[]{
- 23.41,
- 19.94,
- 19.70,
- 21.62,
- 19.06,
- 24.11,
- 19.72,
- 16.47,
- 19.74,
- 20.97
- }, Offset.offset(0.05)
+ .contains(new double[]{19.73, 21.25, 20.81, 23.13, 19.70, 25.34, 20.65, 17.10, 20.48, 21.51}, Offset.offset(0.05)
);
}
diff --git a/algo/src/test/java/org/neo4j/gds/embeddings/graphsage/GraphSageTest.java b/algo/src/test/java/org/neo4j/gds/embeddings/graphsage/GraphSageTest.java
index a15ed0fa801..35eb90754a2 100644
--- a/algo/src/test/java/org/neo4j/gds/embeddings/graphsage/GraphSageTest.java
+++ b/algo/src/test/java/org/neo4j/gds/embeddings/graphsage/GraphSageTest.java
@@ -117,7 +117,6 @@ void setUp() {
graphStore = CSRGraphStoreUtil.createFromGraph(
DatabaseId.random(),
randomGraph,
- "REL",
Optional.of("weight"),
4
);
diff --git a/algo/src/test/java/org/neo4j/gds/embeddings/graphsage/algo/GraphSageTrainAlgorithmFactoryTest.java b/algo/src/test/java/org/neo4j/gds/embeddings/graphsage/algo/GraphSageTrainAlgorithmFactoryTest.java
index 82ac56df6b1..b455e4f9512 100644
--- a/algo/src/test/java/org/neo4j/gds/embeddings/graphsage/algo/GraphSageTrainAlgorithmFactoryTest.java
+++ b/algo/src/test/java/org/neo4j/gds/embeddings/graphsage/algo/GraphSageTrainAlgorithmFactoryTest.java
@@ -499,21 +499,21 @@ void testLogging() {
"GraphSageTrain :: Train model :: Start",
"GraphSageTrain :: Train model :: Epoch 1 of 2 :: Start",
"GraphSageTrain :: Train model :: Epoch 1 of 2 :: Iteration 1 of 2 :: Start",
- "GraphSageTrain :: Train model :: Epoch 1 of 2 :: Iteration 1 of 2 :: Average loss per node: 26.49",
+ "GraphSageTrain :: Train model :: Epoch 1 of 2 :: Iteration 1 of 2 :: Average loss per node: 25.58",
"GraphSageTrain :: Train model :: Epoch 1 of 2 :: Iteration 1 of 2 100%",
"GraphSageTrain :: Train model :: Epoch 1 of 2 :: Iteration 1 of 2 :: Finished",
"GraphSageTrain :: Train model :: Epoch 1 of 2 :: Iteration 2 of 2 :: Start",
- "GraphSageTrain :: Train model :: Epoch 1 of 2 :: Iteration 2 of 2 :: Average loss per node: 25.58",
+ "GraphSageTrain :: Train model :: Epoch 1 of 2 :: Iteration 2 of 2 :: Average loss per node: 26.69",
"GraphSageTrain :: Train model :: Epoch 1 of 2 :: Iteration 2 of 2 100%",
"GraphSageTrain :: Train model :: Epoch 1 of 2 :: Iteration 2 of 2 :: Finished",
"GraphSageTrain :: Train model :: Epoch 1 of 2 :: Finished",
"GraphSageTrain :: Train model :: Epoch 2 of 2 :: Start",
"GraphSageTrain :: Train model :: Epoch 2 of 2 :: Iteration 1 of 2 :: Start",
- "GraphSageTrain :: Train model :: Epoch 2 of 2 :: Iteration 1 of 2 :: Average loss per node: 25.28",
+ "GraphSageTrain :: Train model :: Epoch 2 of 2 :: Iteration 1 of 2 :: Average loss per node: 23.29",
"GraphSageTrain :: Train model :: Epoch 2 of 2 :: Iteration 1 of 2 100%",
"GraphSageTrain :: Train model :: Epoch 2 of 2 :: Iteration 1 of 2 :: Finished",
"GraphSageTrain :: Train model :: Epoch 2 of 2 :: Iteration 2 of 2 :: Start",
- "GraphSageTrain :: Train model :: Epoch 2 of 2 :: Iteration 2 of 2 :: Average loss per node: 25.23",
+ "GraphSageTrain :: Train model :: Epoch 2 of 2 :: Iteration 2 of 2 :: Average loss per node: 22.88",
"GraphSageTrain :: Train model :: Epoch 2 of 2 :: Iteration 2 of 2 100%",
"GraphSageTrain :: Train model :: Epoch 2 of 2 :: Iteration 2 of 2 :: Finished",
"GraphSageTrain :: Train model :: Epoch 2 of 2 :: Finished",
diff --git a/algo/src/test/java/org/neo4j/gds/embeddings/hashgnn/HashGNNTest.java b/algo/src/test/java/org/neo4j/gds/embeddings/hashgnn/HashGNNTest.java
index 97686dd1295..32d8e88339f 100644
--- a/algo/src/test/java/org/neo4j/gds/embeddings/hashgnn/HashGNNTest.java
+++ b/algo/src/test/java/org/neo4j/gds/embeddings/hashgnn/HashGNNTest.java
@@ -28,12 +28,14 @@
import org.junit.jupiter.params.provider.MethodSource;
import org.neo4j.gds.NodeLabel;
import org.neo4j.gds.Orientation;
+import org.neo4j.gds.RelationshipType;
import org.neo4j.gds.ResourceUtil;
import org.neo4j.gds.TestSupport;
import org.neo4j.gds.api.Graph;
import org.neo4j.gds.collections.HugeSparseLongArray;
import org.neo4j.gds.compat.Neo4jProxy;
import org.neo4j.gds.compat.TestLog;
+import org.neo4j.gds.core.GraphDimensions;
import org.neo4j.gds.core.concurrency.Pools;
import org.neo4j.gds.core.loading.ArrayIdMap;
import org.neo4j.gds.core.loading.LabelInformationBuilders;
@@ -45,11 +47,11 @@
import org.neo4j.gds.core.utils.progress.EmptyTaskRegistryFactory;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
import org.neo4j.gds.core.utils.progress.tasks.TaskProgressTracker;
+import org.neo4j.gds.core.utils.shuffle.ShuffleUtil;
import org.neo4j.gds.extension.GdlExtension;
import org.neo4j.gds.extension.GdlGraph;
import org.neo4j.gds.extension.IdFunction;
import org.neo4j.gds.extension.Inject;
-import org.neo4j.gds.core.utils.shuffle.ShuffleUtil;
import java.util.List;
import java.util.Map;
@@ -311,6 +313,37 @@ void shouldEstimateMemory(
);
}
+ @Test
+ void estimationShouldUseGeneratedDimensionIfOutputIsMissing() {
+ var inputDimension = 1000L;
+ var inputRatio = 0.1;
+ var graphDims = GraphDimensions.of((long) 1e6);
+ var concurrency = 4;
+
+ var bigEstimation = new HashGNNFactory<>()
+ .memoryEstimation(HashGNNStreamConfigImpl
+ .builder()
+ .generateFeatures(Map.of("dimension", inputDimension, "densityLevel", 1))
+ .iterations(3)
+ .embeddingDensity(100)
+ .build())
+ .estimate(graphDims, concurrency)
+ .memoryUsage();
+
+ var smallEstimation = new HashGNNFactory<>()
+ .memoryEstimation(HashGNNStreamConfigImpl
+ .builder()
+ .generateFeatures(Map.of("dimension", (long) (inputRatio * inputDimension), "densityLevel", 1))
+ .iterations(3)
+ .embeddingDensity(100)
+ .build())
+ .estimate(graphDims, concurrency)
+ .memoryUsage();
+
+ var outputRatio = (double) smallEstimation.min / bigEstimation.min;
+ assertThat(outputRatio).isCloseTo(inputRatio, Offset.offset(0.1));
+ }
+
@ParameterizedTest
@CsvSource(value = {"true", "false"})
void shouldLogProgress(boolean dense) {
@@ -372,6 +405,7 @@ void shouldBeDeterministicGivenSameOriginalIds() {
);
RelationshipsBuilder firstRelationshipsBuilder = GraphFactory.initRelationshipsBuilder()
.nodes(firstIdMap)
+ .relationshipType(RelationshipType.of("REL"))
.orientation(Orientation.UNDIRECTED)
.executorService(Pools.DEFAULT)
.build();
@@ -395,6 +429,7 @@ void shouldBeDeterministicGivenSameOriginalIds() {
);
RelationshipsBuilder secondRelationshipsBuilder = GraphFactory.initRelationshipsBuilder()
.nodes(secondIdMap)
+ .relationshipType(RelationshipType.of("REL"))
.orientation(Orientation.UNDIRECTED)
.executorService(Pools.DEFAULT)
.build();
diff --git a/algo/src/test/java/org/neo4j/gds/embeddings/node2vec/Node2VecTest.java b/algo/src/test/java/org/neo4j/gds/embeddings/node2vec/Node2VecTest.java
index 6e98729e9fb..06712e2221c 100644
--- a/algo/src/test/java/org/neo4j/gds/embeddings/node2vec/Node2VecTest.java
+++ b/algo/src/test/java/org/neo4j/gds/embeddings/node2vec/Node2VecTest.java
@@ -36,6 +36,7 @@
import org.neo4j.gds.NodeLabel;
import org.neo4j.gds.Orientation;
import org.neo4j.gds.PropertyMapping;
+import org.neo4j.gds.RelationshipType;
import org.neo4j.gds.StoreLoaderBuilder;
import org.neo4j.gds.TestProgressTracker;
import org.neo4j.gds.api.Graph;
@@ -53,10 +54,10 @@
import org.neo4j.gds.core.utils.paged.HugeObjectArray;
import org.neo4j.gds.core.utils.progress.EmptyTaskRegistryFactory;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
+import org.neo4j.gds.core.utils.shuffle.ShuffleUtil;
import org.neo4j.gds.embeddings.node2vec.Node2VecBaseConfig.EmbeddingInitializer;
import org.neo4j.gds.gdl.GdlFactory;
import org.neo4j.gds.ml.core.tensor.FloatVector;
-import org.neo4j.gds.core.utils.shuffle.ShuffleUtil;
import java.util.List;
import java.util.Optional;
@@ -277,6 +278,7 @@ void shouldBeFairlyConsistentUnderOriginalIds(EmbeddingInitializer embeddingInit
);
RelationshipsBuilder firstRelationshipsBuilder = GraphFactory.initRelationshipsBuilder()
.nodes(firstIdMap)
+ .relationshipType(RelationshipType.of("REL"))
.orientation(Orientation.UNDIRECTED)
.executorService(Pools.DEFAULT)
.build();
@@ -298,6 +300,7 @@ void shouldBeFairlyConsistentUnderOriginalIds(EmbeddingInitializer embeddingInit
);
RelationshipsBuilder secondRelationshipsBuilder = GraphFactory.initRelationshipsBuilder()
.nodes(secondIdMap)
+ .relationshipType(RelationshipType.of("REL"))
.orientation(Orientation.UNDIRECTED)
.executorService(Pools.DEFAULT)
.build();
diff --git a/algo/src/test/java/org/neo4j/gds/leiden/GraphAggregationPhaseTest.java b/algo/src/test/java/org/neo4j/gds/leiden/GraphAggregationPhaseTest.java
index 3d38d71303a..317fc5cbe52 100644
--- a/algo/src/test/java/org/neo4j/gds/leiden/GraphAggregationPhaseTest.java
+++ b/algo/src/test/java/org/neo4j/gds/leiden/GraphAggregationPhaseTest.java
@@ -101,8 +101,8 @@ void testGraphAggregation() {
assertGraphEquals(
fromGdl(
"(c1), (c2), " +
- "(c1)-[:REL {w: 4.0}]->(c2), " +
- "(c2)-[:REL {w: 4.0}]->(c1)"
+ "(c1)-[:_IGNORED_ {w: 4.0}]->(c2), " +
+ "(c2)-[:_IGNORED_ {w: 4.0}]->(c1)"
),
aggregatedGraph
);
diff --git a/algo/src/test/java/org/neo4j/gds/leiden/LeidenTest.java b/algo/src/test/java/org/neo4j/gds/leiden/LeidenTest.java
index 193a1674ea1..7e040ee894e 100644
--- a/algo/src/test/java/org/neo4j/gds/leiden/LeidenTest.java
+++ b/algo/src/test/java/org/neo4j/gds/leiden/LeidenTest.java
@@ -41,6 +41,7 @@
import org.neo4j.gds.extension.IdFunction;
import org.neo4j.gds.extension.Inject;
import org.neo4j.gds.extension.TestGraph;
+import org.neo4j.gds.gdl.GdlFactory;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
@@ -154,6 +155,8 @@ void shouldWorkWithBestSeed() {
community -> assertThat(community).containsExactlyInAnyOrder("a1", "a5", "a6", "a7")
);
assertThat(communitiesMap.keySet()).containsExactly(4000L, 5000L);
+
+ assertThat(leidenResult.modularity()).isGreaterThan(0);
}
@Test
@@ -364,4 +367,29 @@ void shouldLogProgress() {
"Leiden :: Finished"
);
}
+
+ @Test
+ void shouldWorkIfStopAtFirstIteration() {
+
+ var query = " CREATE\n" +
+ " (c:C {id: 0}) \n" +
+ " , (d:D {id: 1}) ";
+ var empty = GdlFactory.of(query).build().getUnion();
+ var result = new Leiden(
+ empty,
+ 5,
+ 1,
+ 0.01,
+ false,
+ 42,
+ null,
+ 0.1,
+ 1,
+ ProgressTracker.NULL_TRACKER
+ ).compute();
+ var communities = result.communities();
+ assertThat(communities.toArray()).isEqualTo(new long[]{0, 1});
+
+
+ }
}
diff --git a/algo/src/test/java/org/neo4j/gds/leiden/LeidenWeightedCliqueTest.java b/algo/src/test/java/org/neo4j/gds/leiden/LeidenWeightedCliqueTest.java
new file mode 100644
index 00000000000..96d93d6be1d
--- /dev/null
+++ b/algo/src/test/java/org/neo4j/gds/leiden/LeidenWeightedCliqueTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.leiden;
+
+import org.junit.jupiter.api.Test;
+import org.neo4j.gds.Orientation;
+import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
+import org.neo4j.gds.extension.GdlExtension;
+import org.neo4j.gds.extension.GdlGraph;
+import org.neo4j.gds.extension.IdFunction;
+import org.neo4j.gds.extension.Inject;
+import org.neo4j.gds.extension.TestGraph;
+
+import java.util.Arrays;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.neo4j.gds.core.ProcedureConstants.TOLERANCE_DEFAULT;
+
+@GdlExtension
+class LeidenWeightedCliqueTest {
+
+ @GdlGraph(orientation = Orientation.UNDIRECTED)
+ private static final String DB_CYPHER =
+ "CREATE" +
+ "(a : Node), "+
+ "(b : Node), "+
+ "(c : Node), "+
+ "(d : Node), "+
+ "(e : Node), "+
+
+ "(a)-[:R {w: 0.1}]->(b),"+
+ "(a)-[:R {w: 0.1}]->(c),"+
+ "(a)-[:R {w: 0.1}]->(d),"+
+ "(a)-[:R {w: 0.1}]->(e),"+
+
+ "(b)-[:R {w: 0.1}]->(c),"+
+ "(b)-[:R {w: 0.1}]->(d),"+
+ "(b)-[:R {w: 0.1}]->(e),"+
+
+ "(c)-[:R {w: 0.1}]->(d),"+
+ "(c)-[:R {w: 0.1}]->(e),"+
+
+ "(d)-[:R {w: 0.1}]->(e)";
+
+ @Inject
+ private TestGraph graph;
+
+ @Inject
+ private IdFunction idFunction;
+
+ @Test
+ void weightedLeiden() {
+ var maxLevels = 10;
+
+ Leiden leiden = new Leiden(
+ graph,
+ maxLevels,
+ 1.0,
+ 0.01,
+ true,
+ 19L,
+ null,
+ TOLERANCE_DEFAULT,
+ 4,
+ ProgressTracker.NULL_TRACKER
+
+ );
+ var leidenResult = leiden.compute();
+ assertThat(Arrays.stream(leidenResult.communities().toArray()).distinct().count()).isEqualTo(1);
+
+ }
+
+}
diff --git a/algo/src/test/java/org/neo4j/gds/louvain/LouvainTest.java b/algo/src/test/java/org/neo4j/gds/louvain/LouvainTest.java
index 9bb46d93aa6..00e04bd2630 100644
--- a/algo/src/test/java/org/neo4j/gds/louvain/LouvainTest.java
+++ b/algo/src/test/java/org/neo4j/gds/louvain/LouvainTest.java
@@ -19,6 +19,7 @@
*/
package org.neo4j.gds.louvain;
+import org.assertj.core.data.Offset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@@ -27,9 +28,12 @@
import org.neo4j.gds.Orientation;
import org.neo4j.gds.RelationshipType;
import org.neo4j.gds.api.GraphStore;
+import org.neo4j.gds.api.schema.Direction;
import org.neo4j.gds.beta.generator.RandomGraphGenerator;
import org.neo4j.gds.beta.generator.RelationshipDistribution;
import org.neo4j.gds.compat.Neo4jProxy;
+import org.neo4j.gds.config.RandomGraphGeneratorConfig;
+import org.neo4j.gds.core.Aggregation;
import org.neo4j.gds.core.GraphDimensions;
import org.neo4j.gds.core.ImmutableGraphDimensions;
import org.neo4j.gds.core.concurrency.Pools;
@@ -45,11 +49,14 @@
import org.neo4j.gds.extension.GdlGraph;
import org.neo4j.gds.extension.IdFunction;
import org.neo4j.gds.extension.Inject;
+import org.neo4j.gds.modularity.ModularityCalculator;
import java.util.Map;
import java.util.Optional;
+import java.util.function.LongUnaryOperator;
import java.util.stream.Stream;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -516,6 +523,39 @@ void shouldThrowOnNegativeSeed() {
assertThatThrownBy(algorithm::compute).hasMessageContaining("non-negative");
+ }
+
+ @Test
+ void shouldGiveSameResultWithCalculator() {
+ var myGraph = RandomGraphGenerator
+ .builder()
+ .nodeCount(1_000)
+ .averageDegree(10)
+ .relationshipDistribution(RelationshipDistribution.UNIFORM)
+ .direction(Direction.UNDIRECTED)
+ .allowSelfLoops(RandomGraphGeneratorConfig.AllowSelfLoops.YES)
+ .aggregation(Aggregation.SINGLE)
+ .seed(42)
+ .build()
+ .generate();
+
+ var louvain = new Louvain(
+ myGraph,
+ ImmutableLouvainStreamConfig.builder().build(),
+ false,
+ 10,
+ 10,
+ TOLERANCE_DEFAULT,
+ 4,
+ ProgressTracker.NULL_TRACKER,
+ Pools.DEFAULT
+ );
+ var result = louvain.compute();
+ assertThat(louvain.levels()).isGreaterThan(1);
+ LongUnaryOperator vToCommunity = v -> result.getCommunity(v);
+ var modularityCalculator = ModularityCalculator.create(myGraph, vToCommunity, 4);
+ double calculatedModularity = modularityCalculator.compute().totalModularity();
+ assertThat(result.modularities()[louvain.levels() - 1]).isCloseTo(calculatedModularity, Offset.offset(1e-5));
}
}
diff --git a/algo/src/test/java/org/neo4j/gds/modularity/ModularityCalculatorTest.java b/algo/src/test/java/org/neo4j/gds/modularity/ModularityCalculatorTest.java
index 1ab530f984b..eeefba3ecbc 100644
--- a/algo/src/test/java/org/neo4j/gds/modularity/ModularityCalculatorTest.java
+++ b/algo/src/test/java/org/neo4j/gds/modularity/ModularityCalculatorTest.java
@@ -19,7 +19,8 @@
*/
package org.neo4j.gds.modularity;
-import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
import org.neo4j.gds.Orientation;
import org.neo4j.gds.api.GraphStore;
import org.neo4j.gds.extension.GdlExtension;
@@ -35,12 +36,12 @@ class ModularityCalculatorTest {
@GdlGraph(orientation = Orientation.UNDIRECTED)
static final String GRAPH =
"CREATE " +
- " (a1: Node { communityId: 0 })," +
- " (a2: Node { communityId: 0 })," +
- " (a3: Node { communityId: 5 })," +
- " (a4: Node { communityId: 0 })," +
- " (a5: Node { communityId: 5 })," +
- " (a6: Node { communityId: 5 })," +
+ " (a1: Node { communityId: 0, communityId2: 200 })," +
+ " (a2: Node { communityId: 0, communityId2: 200 })," +
+ " (a3: Node { communityId: 5, communityId2: 205 })," +
+ " (a4: Node { communityId: 0, communityId2: 200 })," +
+ " (a5: Node { communityId: 5, communityId2: 205 })," +
+ " (a6: Node { communityId: 5, communityId2: 205 })," +
" (a1)-[:R]->(a2)," +
" (a1)-[:R]->(a4)," +
@@ -56,11 +57,12 @@ class ModularityCalculatorTest {
@Inject
private GraphStore graphStore;
- @Test
- void compute() {
- var modularityCalculator = new ModularityCalculator(
+ @ParameterizedTest
+ @CsvSource({"communityId,0", "communityId2,200"})
+ void compute(String communityId, int startingId) {
+ var modularityCalculator = ModularityCalculator.create(
graph,
- graphStore.nodeProperty("communityId").values()::longValue,
+ graphStore.nodeProperty(communityId).values()::longValue,
4
);
@@ -74,8 +76,9 @@ void compute() {
assertThat(modularities)
.containsExactlyInAnyOrder(
- CommunityModularity.of(0L, community_0_score),
- CommunityModularity.of(5L, community_5_score)
+ CommunityModularity.of(startingId, community_0_score),
+ CommunityModularity.of(startingId + 5L, community_5_score)
);
}
+
}
diff --git a/algo/src/test/java/org/neo4j/gds/modularity/RelationshipCountCollectorTest.java b/algo/src/test/java/org/neo4j/gds/modularity/RelationshipCountCollectorTest.java
index aeda650ca7b..ca4bf49c28b 100644
--- a/algo/src/test/java/org/neo4j/gds/modularity/RelationshipCountCollectorTest.java
+++ b/algo/src/test/java/org/neo4j/gds/modularity/RelationshipCountCollectorTest.java
@@ -21,7 +21,6 @@
import org.junit.jupiter.api.Test;
import org.neo4j.gds.Orientation;
-import org.neo4j.gds.core.utils.paged.HugeAtomicBitSet;
import org.neo4j.gds.core.utils.paged.HugeAtomicDoubleArray;
import org.neo4j.gds.core.utils.paged.HugeLongArray;
import org.neo4j.gds.core.utils.partition.Partition;
@@ -63,7 +62,6 @@ class RelationshipCountCollectorTest {
void collect() {
var insideRelationships = HugeAtomicDoubleArray.newArray(graph.nodeCount());
var totalCommunityRelationships = HugeAtomicDoubleArray.newArray(graph.nodeCount());
- var communityTracker = HugeAtomicBitSet.create(graph.nodeCount());
var communities = HugeLongArray.of(0, 0, 5, 0, 5, 5);
var totalRelationshipWeight = new DoubleAdder();
@@ -72,7 +70,6 @@ void collect() {
graph,
insideRelationships,
totalCommunityRelationships,
- communityTracker,
communities::get,
totalRelationshipWeight
).run();
diff --git a/algo/src/test/java/org/neo4j/gds/paths/delta/DeltaSteppingTest.java b/algo/src/test/java/org/neo4j/gds/paths/delta/DeltaSteppingTest.java
index 0b7b3569cb0..169ffa7cc43 100644
--- a/algo/src/test/java/org/neo4j/gds/paths/delta/DeltaSteppingTest.java
+++ b/algo/src/test/java/org/neo4j/gds/paths/delta/DeltaSteppingTest.java
@@ -19,6 +19,7 @@
*/
package org.neo4j.gds.paths.delta;
+import org.assertj.core.data.Offset;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
@@ -29,6 +30,10 @@
import org.neo4j.gds.TestProgressTracker;
import org.neo4j.gds.TestSupport;
import org.neo4j.gds.api.Graph;
+import org.neo4j.gds.api.schema.Direction;
+import org.neo4j.gds.beta.generator.PropertyProducer;
+import org.neo4j.gds.beta.generator.RandomGraphGeneratorBuilder;
+import org.neo4j.gds.beta.generator.RelationshipDistribution;
import org.neo4j.gds.compat.Neo4jProxy;
import org.neo4j.gds.core.GraphDimensions;
import org.neo4j.gds.core.concurrency.Pools;
@@ -39,8 +44,10 @@
import org.neo4j.gds.extension.IdFunction;
import org.neo4j.gds.extension.Inject;
import org.neo4j.gds.paths.delta.config.ImmutableAllShortestPathsDeltaStreamConfig;
+import org.neo4j.gds.paths.dijkstra.Dijkstra;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.DoubleStream;
@@ -337,4 +344,60 @@ void singleSource(double delta, int concurrency, long idOffset) {
}
private DeltaSteppingTest() {}
+
+ @Test
+ void shouldGiveSameResultsAsDijkstra() {
+ int nodeCount = 3_000;
+ long seed = 42L;
+ long start = 42;
+ int concurrency = 4;
+ var newGraph = new RandomGraphGeneratorBuilder()
+ .direction(Direction.DIRECTED)
+ .averageDegree(10)
+ .relationshipDistribution(RelationshipDistribution.POWER_LAW)
+ .relationshipPropertyProducer(PropertyProducer.randomDouble("foo", 1, 10))
+ .nodeCount(nodeCount)
+ .seed(seed)
+ .build()
+ .generate();
+
+ var config = ImmutableAllShortestPathsDeltaStreamConfig.builder()
+ .concurrency(concurrency)
+ .sourceNode(start)
+ .trackRelationships(true)
+ .build();
+ var deltaStepping = DeltaStepping.of(
+ newGraph,
+ config,
+ Pools.DEFAULT,
+ ProgressTracker.NULL_TRACKER
+ ).compute();
+
+ var dijkstraAlgo = Dijkstra
+ .singleSource(newGraph, config, Optional.empty(), ProgressTracker.NULL_TRACKER)
+ .compute();
+
+ double[] delta = new double[nodeCount];
+ double[] djikstra = new double[nodeCount];
+
+ double deltaSum = 0;
+ double dijkstraSum = 0;
+
+ for (var path : deltaStepping.pathSet()) {
+ delta[(int) path.targetNode()] = path.totalCost();
+ }
+
+ for (var path : dijkstraAlgo.pathSet()) {
+ djikstra[(int) path.targetNode()] = path.totalCost();
+ }
+ for (int i = 0; i < nodeCount; ++i) {
+ deltaSum += delta[i];
+ dijkstraSum += djikstra[i];
+ assertThat(djikstra[i]).isCloseTo(delta[i], Offset.offset(1e-5));
+
+ }
+ assertThat(deltaSum).isCloseTo(dijkstraSum, Offset.offset(1e-5));
+
+ }
+
}
diff --git a/algo/src/test/java/org/neo4j/gds/paths/yens/YensTest.java b/algo/src/test/java/org/neo4j/gds/paths/yens/YensTest.java
index c3af4a3b569..6275662143e 100644
--- a/algo/src/test/java/org/neo4j/gds/paths/yens/YensTest.java
+++ b/algo/src/test/java/org/neo4j/gds/paths/yens/YensTest.java
@@ -32,6 +32,7 @@
import org.neo4j.gds.api.Graph;
import org.neo4j.gds.compat.Neo4jProxy;
import org.neo4j.gds.compat.TestLog;
+import org.neo4j.gds.core.Aggregation;
import org.neo4j.gds.core.utils.mem.MemoryRange;
import org.neo4j.gds.core.utils.progress.EmptyTaskRegistryFactory;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
@@ -85,7 +86,7 @@ void shouldComputeMemoryEstimation(int nodeCount, long expectedBytes) {
}
// https://en.wikipedia.org/wiki/Yen%27s_algorithm#/media/File:Yen's_K-Shortest_Path_Algorithm,_K=3,_A_to_F.gif
- @GdlGraph
+ @GdlGraph(aggregation = Aggregation.SINGLE)
private static final String DB_CYPHER =
"CREATE" +
" (c:C {id: 0})" +
@@ -165,7 +166,7 @@ static Stream> pathInput() {
@ParameterizedTest
@MethodSource("pathInput")
void compute(Collection expectedPaths) {
- assertResult(graph, idFunction, expectedPaths);
+ assertResult(graph, idFunction, expectedPaths, false);
}
@Test
@@ -238,8 +239,13 @@ void shouldCloseProgressTasksOnEmptyResult() {
);
}
- private static void assertResult(Graph graph, IdFunction idFunction, Collection expectedPaths) {
- var expectedPathResults = expectedPathResults(idFunction, expectedPaths);
+ private static void assertResult(
+ Graph graph,
+ IdFunction idFunction,
+ Collection expectedPaths,
+ boolean trackRelationships
+ ) {
+ var expectedPathResults = expectedPathResults(idFunction, expectedPaths, trackRelationships);
var firstResult = expectedPathResults
.stream()
@@ -267,7 +273,11 @@ private static void assertResult(Graph graph, IdFunction idFunction, Collection<
}
@NotNull
- private static Set expectedPathResults(IdFunction idFunction, Collection expectedPaths) {
+ private static Set expectedPathResults(
+ IdFunction idFunction,
+ Collection expectedPaths,
+ boolean trackRelationships
+ ) {
var index = new MutableInt(0);
return expectedPaths.stream()
.map(expectedPath -> new GDLHandler.Builder()
@@ -312,7 +322,7 @@ private static Set expectedPathResults(IdFunction idFunction, Collec
.sourceNode(sourceNode.getId())
.targetNode(targetNode.getId())
.nodeIds(nodeIds)
- .relationshipIds(relationshipIds)
+ .relationshipIds(trackRelationships ? relationshipIds : new long[0])
.costs(costs)
.build();
})
@@ -375,7 +385,7 @@ Stream> pathInput() {
@ParameterizedTest
@MethodSource("pathInput")
void compute(Collection expectedPaths) {
- assertResult(graph, idFunction, expectedPaths);
+ assertResult(graph, idFunction, expectedPaths, true);
}
}
}
diff --git a/algo/src/test/java/org/neo4j/gds/similarity/filterednodesim/FilteredNodeSimilarityTest.java b/algo/src/test/java/org/neo4j/gds/similarity/filterednodesim/FilteredNodeSimilarityTest.java
index 83d5eb62b87..093f5b7e549 100644
--- a/algo/src/test/java/org/neo4j/gds/similarity/filterednodesim/FilteredNodeSimilarityTest.java
+++ b/algo/src/test/java/org/neo4j/gds/similarity/filterednodesim/FilteredNodeSimilarityTest.java
@@ -20,6 +20,12 @@
package org.neo4j.gds.similarity.filterednodesim;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.neo4j.gds.TestProgressTracker;
+import org.neo4j.gds.compat.Neo4jProxy;
+import org.neo4j.gds.compat.TestLog;
+import org.neo4j.gds.core.utils.progress.EmptyTaskRegistryFactory;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
import org.neo4j.gds.extension.GdlExtension;
import org.neo4j.gds.extension.GdlGraph;
@@ -30,6 +36,8 @@
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.neo4j.gds.assertj.Extractors.removingThreadId;
+import static org.neo4j.gds.compat.TestLog.INFO;
@GdlExtension
class FilteredNodeSimilarityTest {
@@ -154,4 +162,55 @@ void shouldSurviveIoannisFurtherObjections() {
nodeSimilarity.release();
}
+
+ @ParameterizedTest
+ @ValueSource(ints = {1, 2})
+ void shouldLogProgressAccurately(int concurrency) {
+ var sourceNodeFilter = List.of(2L, 3L);
+
+ var config = ImmutableFilteredNodeSimilarityStreamConfig.builder()
+ .sourceNodeFilter(NodeFilterSpecFactory.create(sourceNodeFilter))
+ .concurrency(concurrency)
+ .topK(0)
+ .topN(10)
+ .build();
+ var progressTask = new FilteredNodeSimilarityFactory<>().progressTask(graph, config);
+ TestLog log = Neo4jProxy.testLog();
+ var progressTracker = new TestProgressTracker(
+ progressTask,
+ log,
+ concurrency,
+ EmptyTaskRegistryFactory.INSTANCE
+ );
+
+
+ new FilteredNodeSimilarityFactory<>().build(
+ graph,
+ config,
+ progressTracker
+ ).compute();
+
+
+ assertThat(log.getMessages(INFO))
+ .extracting(removingThreadId())
+ .containsExactly(
+ "FilteredNodeSimilarity :: Start",
+ "FilteredNodeSimilarity :: prepare :: Start",
+ "FilteredNodeSimilarity :: prepare 33%",
+ "FilteredNodeSimilarity :: prepare 55%",
+ "FilteredNodeSimilarity :: prepare 66%",
+ "FilteredNodeSimilarity :: prepare 100%",
+ "FilteredNodeSimilarity :: prepare :: Finished",
+ "FilteredNodeSimilarity :: compare node pairs :: Start",
+ "FilteredNodeSimilarity :: compare node pairs 12%",
+ "FilteredNodeSimilarity :: compare node pairs 25%",
+ "FilteredNodeSimilarity :: compare node pairs 37%",
+ "FilteredNodeSimilarity :: compare node pairs 50%",
+ "FilteredNodeSimilarity :: compare node pairs 62%",
+ "FilteredNodeSimilarity :: compare node pairs 75%",
+ "FilteredNodeSimilarity :: compare node pairs 100%",
+ "FilteredNodeSimilarity :: compare node pairs :: Finished",
+ "FilteredNodeSimilarity :: Finished"
+ );
+ }
}
diff --git a/algo/src/test/java/org/neo4j/gds/similarity/knn/GenerateRandomNeighborsTest.java b/algo/src/test/java/org/neo4j/gds/similarity/knn/GenerateRandomNeighborsTest.java
index 1b2e4b4b638..656c25f8541 100644
--- a/algo/src/test/java/org/neo4j/gds/similarity/knn/GenerateRandomNeighborsTest.java
+++ b/algo/src/test/java/org/neo4j/gds/similarity/knn/GenerateRandomNeighborsTest.java
@@ -23,8 +23,8 @@
import net.jqwik.api.From;
import net.jqwik.api.Property;
import org.eclipse.collections.api.tuple.primitive.IntIntPair;
-import org.neo4j.gds.api.properties.nodes.LongNodePropertyValues;
import org.neo4j.gds.core.huge.DirectIdMap;
+import org.neo4j.gds.core.utils.IdentityPropertyValues;
import org.neo4j.gds.core.utils.paged.HugeObjectArray;
import org.neo4j.gds.core.utils.partition.Partition;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
@@ -51,17 +51,7 @@ void neighborsForKEqualsNMinus1startWithEachOtherAsNeighbors(
nodeCount
);
- var nodeProperties = new LongNodePropertyValues() {
- @Override
- public long longValue(long nodeId) {
- return nodeId;
- }
-
- @Override
- public long size() {
- return nodeCount;
- }
- };
+ var nodeProperties = new IdentityPropertyValues(nodeCount);
var similarityComputer = SimilarityComputer.ofProperty(
idMap,
diff --git a/algo/src/test/java/org/neo4j/gds/similarity/knn/metrics/LongArrayPropertySimilarityComputerTest.java b/algo/src/test/java/org/neo4j/gds/similarity/knn/metrics/LongArrayPropertySimilarityComputerTest.java
new file mode 100644
index 00000000000..4c2bfe93969
--- /dev/null
+++ b/algo/src/test/java/org/neo4j/gds/similarity/knn/metrics/LongArrayPropertySimilarityComputerTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.similarity.knn.metrics;
+
+import org.junit.jupiter.api.Test;
+import org.neo4j.gds.api.properties.nodes.LongArrayNodePropertyValues;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class LongArrayPropertySimilarityComputerTest {
+
+ @Test
+ void usingSparseProperties() {
+ long[][] inputValues = {{42, 24}, null, {84, 48}};
+
+ var sparseProperties = new LongArrayNodePropertyValues() {
+ @Override
+ public long[] longArrayValue(long nodeId) {
+ return inputValues[(int) nodeId];
+ }
+
+ @Override
+ public long nodeCount() {
+ return inputValues.length;
+ }
+ };
+
+ var sortedValues = new LongArrayPropertySimilarityComputer.SortedLongArrayPropertyValues(sparseProperties);
+
+ assertThat(sortedValues.longArrayValue(0)).containsExactly(24, 42);
+ assertThat(sortedValues.longArrayValue(1)).isNull();
+ assertThat(sortedValues.longArrayValue(2)).containsExactly(48, 84);
+ }
+
+}
diff --git a/algo/src/test/java/org/neo4j/gds/similarity/knn/metrics/SimilarityComputerTest.java b/algo/src/test/java/org/neo4j/gds/similarity/knn/metrics/SimilarityComputerTest.java
index 7bec193e48a..33359d3a7a6 100644
--- a/algo/src/test/java/org/neo4j/gds/similarity/knn/metrics/SimilarityComputerTest.java
+++ b/algo/src/test/java/org/neo4j/gds/similarity/knn/metrics/SimilarityComputerTest.java
@@ -265,7 +265,7 @@ public long[] longArrayValue(long nodeId) {
}
@Override
- public long size() {
+ public long nodeCount() {
return nodeCount;
}
};
diff --git a/algo/src/test/java/org/neo4j/gds/similarity/nodesim/NodeSimilarityTerminationTest.java b/algo/src/test/java/org/neo4j/gds/similarity/nodesim/NodeSimilarityTerminationTest.java
index 079603b904c..a07f3c10711 100644
--- a/algo/src/test/java/org/neo4j/gds/similarity/nodesim/NodeSimilarityTerminationTest.java
+++ b/algo/src/test/java/org/neo4j/gds/similarity/nodesim/NodeSimilarityTerminationTest.java
@@ -65,7 +65,7 @@ void shouldTerminate() {
db,
nodeSimilarity,
nhs -> nodeSimilarity.computeToStream(),
- 100
+ 200
);
}
diff --git a/algo/src/test/java/org/neo4j/gds/similarity/nodesim/NodeSimilarityTest.java b/algo/src/test/java/org/neo4j/gds/similarity/nodesim/NodeSimilarityTest.java
index f3c965233aa..7bdfea09fbb 100644
--- a/algo/src/test/java/org/neo4j/gds/similarity/nodesim/NodeSimilarityTest.java
+++ b/algo/src/test/java/org/neo4j/gds/similarity/nodesim/NodeSimilarityTest.java
@@ -773,6 +773,29 @@ void shouldComputeMemrecWithTop(int topK) {
assertEquals(expected.memoryUsage(), actual.memoryUsage());
}
+ @Test
+ void shouldComputeMemrecWithTopKAndTopNGreaterThanNodeCount() {
+ GraphDimensions dimensions = ImmutableGraphDimensions.builder()
+ .nodeCount(100)
+ .relCountUpperBound(20_000)
+ .build();
+
+ NodeSimilarityWriteConfig config = ImmutableNodeSimilarityWriteConfig
+ .builder()
+ .similarityCutoff(0.0)
+ .topK(Integer.MAX_VALUE)
+ .topN(Integer.MAX_VALUE)
+ .writeProperty("writeProperty")
+ .writeRelationshipType("writeRelationshipType")
+ .build();
+
+ MemoryTree actual = new NodeSimilarityFactory<>().memoryEstimation(config).estimate(dimensions, 1);
+
+ assertEquals(570592, actual.memoryUsage().min);
+ assertEquals(732192, actual.memoryUsage().max);
+
+ }
+
@ParameterizedTest(name = "topK = {0}, concurrency = {1}")
@MethodSource("topKAndConcurrencies")
void shouldLogMessages(int topK, int concurrency) {
@@ -799,6 +822,35 @@ void shouldLogMessages(int topK, int concurrency) {
);
}
+ @ParameterizedTest(name = "topK = {0}, concurrency = {1}")
+ @MethodSource("topKAndConcurrencies")
+ void shouldNotLogMessagesWhenLoggingIsDisabled(int topK, int concurrency) {
+ var graph = naturalGraph;
+ var config = configBuilder().topN(100).topK(topK).concurrency(concurrency).logProgress(false).build();
+
+ var progressLog = Neo4jProxy.testLog();
+ var nodeSimilarity = new NodeSimilarityFactory<>().build(
+ graph,
+ config,
+ progressLog,
+ EmptyTaskRegistryFactory.INSTANCE
+ );
+
+ nodeSimilarity.compute();
+
+ assertThat(progressLog.getMessages(INFO))
+ .as("When progress logging is disabled we only log `start` and `finished`.")
+ .extracting(removingThreadId())
+ .containsExactly(
+ "NodeSimilarity :: Start",
+ "NodeSimilarity :: prepare :: Start",
+ "NodeSimilarity :: prepare :: Finished",
+ "NodeSimilarity :: compare node pairs :: Start",
+ "NodeSimilarity :: compare node pairs :: Finished",
+ "NodeSimilarity :: Finished"
+ );
+ }
+
@ParameterizedTest(name = "concurrency = {0}")
@ValueSource(ints = {1,2})
void shouldLogProgress(int concurrency) {
diff --git a/algo/src/test/java/org/neo4j/gds/similarity/nodesim/UnionGraphWeightedNodeSimilarityTest.java b/algo/src/test/java/org/neo4j/gds/similarity/nodesim/UnionGraphWeightedNodeSimilarityTest.java
new file mode 100644
index 00000000000..0c4ab250cad
--- /dev/null
+++ b/algo/src/test/java/org/neo4j/gds/similarity/nodesim/UnionGraphWeightedNodeSimilarityTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.similarity.nodesim;
+
+import org.assertj.core.data.Offset;
+import org.junit.jupiter.api.Test;
+import org.neo4j.gds.api.Graph;
+import org.neo4j.gds.core.concurrency.Pools;
+import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
+import org.neo4j.gds.extension.GdlExtension;
+import org.neo4j.gds.extension.GdlGraph;
+import org.neo4j.gds.extension.Inject;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@GdlExtension
+ class UnionGraphWeightedNodeSimilarityTest {
+
+ @GdlGraph
+ private static final String DB_CYPHER =
+ "CREATE " +
+ " (a1: A)," +
+ " (a2: A)," +
+ " (b1: B)," +
+ " (b2: B)," +
+ " (b3: B)," +
+ "(a1)-[:R1 {w: 5}]->(b1),"+
+ "(a1)-[:R2 {w: 10}]->(b2),"+
+ "(a2)-[:R2 {w: 10}]->(b2),"+
+ "(a2)-[:R1 {w: 8}]->(b3)";
+
+ @Inject
+ private Graph graph;
+
+ @Test
+ void shouldWorkWithUnionGraph(){
+
+ var config = NodeSimilarityStreamConfigImpl.builder()
+ .relationshipWeightProperty("w")
+ .concurrency(1)
+ .topN(1)
+ .build();
+
+ var nodeSimilarity = NodeSimilarity.create(graph, config, Pools.DEFAULT, ProgressTracker.NULL_TRACKER);
+ var result = nodeSimilarity.compute().streamResult().findFirst().get();
+
+ //input should be (0 + 10 + 0)/ (5 + 10 + 8) = 10/23
+ assertThat(result.similarity).isCloseTo((10) / (23.0), Offset.offset(1E-5));
+ }
+}
diff --git a/algo/src/test/java/org/neo4j/gds/traversal/RandomWalkTest.java b/algo/src/test/java/org/neo4j/gds/traversal/RandomWalkTest.java
index 47ad8d3fda9..8bf408cc79c 100644
--- a/algo/src/test/java/org/neo4j/gds/traversal/RandomWalkTest.java
+++ b/algo/src/test/java/org/neo4j/gds/traversal/RandomWalkTest.java
@@ -36,6 +36,7 @@
import org.neo4j.gds.compat.Neo4jProxy;
import org.neo4j.gds.compat.TestLog;
import org.neo4j.gds.core.concurrency.Pools;
+import org.neo4j.gds.core.utils.TerminationFlag;
import org.neo4j.gds.core.utils.progress.GlobalTaskStore;
import org.neo4j.gds.core.utils.progress.TaskRegistryFactory;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
@@ -411,6 +412,37 @@ void testWithConfiguredOffsetStartNodes() {
.anyMatch(walk -> walk[0] == bInternalId);
}
+ /**
+ * Ensure that when termination flag is set externally, we terminate the walk
+ */
+ @Test
+ void testSetTerminationFlagAndMultipleRuns() {
+ for (int i = 0; i < 3; i++) {
+ Node2VecStreamConfig config = ImmutableNode2VecStreamConfig.builder()
+ .walkBufferSize(1)
+ .build();
+
+ var randomWalk = RandomWalk.create(
+ graph,
+ config,
+ ProgressTracker.NULL_TRACKER,
+ Pools.DEFAULT
+ );
+
+ var stream = randomWalk.compute();
+ long count = stream.limit(10).count();
+
+ randomWalk.setTerminationFlag(new TerminationFlag() {
+ @Override
+ public boolean running() {
+ return false;
+ }
+ });
+
+ assertEquals(10, count);
+ }
+ }
+
@Nested
class ProgressTracking {
diff --git a/alpha/alpha-algo/src/main/java/org/neo4j/gds/impl/spanningtree/KSpanningTree.java b/alpha/alpha-algo/src/main/java/org/neo4j/gds/impl/spanningtree/KSpanningTree.java
index b74a506f5f9..59f1eb06e62 100644
--- a/alpha/alpha-algo/src/main/java/org/neo4j/gds/impl/spanningtree/KSpanningTree.java
+++ b/alpha/alpha-algo/src/main/java/org/neo4j/gds/impl/spanningtree/KSpanningTree.java
@@ -19,8 +19,11 @@
*/
package org.neo4j.gds.impl.spanningtree;
+import com.carrotsearch.hppc.BitSet;
+import org.jetbrains.annotations.NotNull;
import org.neo4j.gds.Algorithm;
import org.neo4j.gds.api.Graph;
+import org.neo4j.gds.core.utils.paged.HugeDoubleArray;
import org.neo4j.gds.core.utils.paged.HugeLongArray;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
import org.neo4j.gds.core.utils.queue.HugeLongPriorityQueue;
@@ -45,8 +48,6 @@ public class KSpanningTree extends Algorithm {
private final long startNodeId;
private final long k;
- private SpanningTree spanningTree;
-
public KSpanningTree(
Graph graph,
DoubleUnaryOperator minMax,
@@ -71,38 +72,248 @@ public SpanningTree compute() {
startNodeId,
progressTracker
);
+
prim.setTerminationFlag(getTerminationFlag());
SpanningTree spanningTree = prim.compute();
- HugeLongArray parent = spanningTree.parentArray();
- long parentSize = parent.size();
- HugeLongPriorityQueue priorityQueue = minMax == Prim.MAX_OPERATOR ? HugeLongPriorityQueue.min(parentSize) : HugeLongPriorityQueue.max(
- parentSize);
- progressTracker.beginSubTask(parentSize);
- for (long i = 0; i < parentSize && terminationFlag.running(); i++) {
- long p = parent.get(i);
- if (p == -1) {
- continue;
- }
- priorityQueue.add(i, graph.relationshipProperty(p, i, 0.0D));
- progressTracker.logProgress();
- }
+
+ var outputTree = growApproach(spanningTree);
progressTracker.endSubTask();
- progressTracker.beginSubTask(k - 1);
- // remove k-1 relationships
- for (long i = 0; i < k - 1 && terminationFlag.running(); i++) {
- long cutNode = priorityQueue.pop();
- parent.set(cutNode, -1);
- progressTracker.logProgress();
+ return outputTree;
+ }
+
+ @NotNull
+ private HugeLongPriorityQueue createPriorityQueue(long parentSize, boolean pruning) {
+ boolean minQueue = minMax == Prim.MIN_OPERATOR;
+ //if pruning, we remove the worst (max if it's a minimization problem)
+ //therefore we flip the priority queue
+ if (pruning) {
+ minQueue = !minQueue;
}
- progressTracker.endSubTask();
- this.spanningTree = prim.getSpanningTree();
- progressTracker.endSubTask();
- return this.spanningTree;
+ HugeLongPriorityQueue priorityQueue = minQueue
+ ? HugeLongPriorityQueue.min(parentSize)
+ : HugeLongPriorityQueue.max(parentSize);
+ return priorityQueue;
}
@Override
public void release() {
graph = null;
- spanningTree = null;
}
+
+ private double init(HugeLongArray parent, HugeDoubleArray costToParent, SpanningTree spanningTree) {
+ graph.forEachNode((nodeId) -> {
+ parent.set(nodeId, spanningTree.parent(nodeId));
+ costToParent.set(nodeId, spanningTree.costToParent(nodeId));
+ return true;
+ });
+ return spanningTree.totalWeight();
+ }
+
+
+ private SpanningTree growApproach(SpanningTree spanningTree) {
+
+ //this approach grows gradually the MST found in the previous step
+ //when it is about to get larger than K, we crop the current worst leaf if the new value to be added
+ // is actually better
+ if (spanningTree.effectiveNodeCount() < k)
+ return spanningTree;
+
+ HugeLongArray outDegree = HugeLongArray.newArray(graph.nodeCount());
+
+ HugeLongArray parent = HugeLongArray.newArray(graph.nodeCount());
+ HugeDoubleArray costToParent = HugeDoubleArray.newArray(graph.nodeCount());
+
+ init(parent, costToParent, spanningTree);
+ double totalCost = 0;
+ var priorityQueue = createPriorityQueue(graph.nodeCount(), false);
+ var toTrim = createPriorityQueue(graph.nodeCount(), true);
+
+ //priority-queue does not have a remove method
+ // so we need something to know if a node is still a leaf or not
+ BitSet exterior = new BitSet(graph.nodeCount());
+ //at any point, the tree has a root we mark its neighbors in this bitset to avoid looping to find them
+ BitSet rootNodeAdjacent = new BitSet(graph.nodeCount());
+ //we just save which nodes are in the final output and not (just to do clean-up; probably can be avoided)
+ BitSet included = new BitSet(graph.nodeCount());
+
+ priorityQueue.add(startNodeId, 0);
+ long root = startNodeId; //current root is startNodeId
+ long nodesInTree = 0;
+ progressTracker.beginSubTask(graph.nodeCount());
+ while (!priorityQueue.isEmpty()) {
+ long node = priorityQueue.top();
+ progressTracker.logProgress();
+ double associatedCost = priorityQueue.cost(node);
+ priorityQueue.pop();
+ long nodeParent = parent.get(node);
+
+ boolean nodeAdded = false;
+ if (nodesInTree < k) { //if we are smaller, we can just add it no problemo
+ nodesInTree++;
+ nodeAdded = true;
+ } else {
+ var nodeToTrim = findNextValidLeaf(toTrim, exterior); //a leaf node with currently theworst cost
+ if (parent.get(node) == nodeToTrim) {
+ //we cannot add it, if we're supposed to remove its parent
+ //TODO: should be totally feasible to consider the 2nd worst then.
+ continue;
+ }
+
+ boolean shouldMove = moveMakesSense(associatedCost, toTrim.cost(nodeToTrim), minMax);
+
+ if (shouldMove) {
+ nodeAdded = true;
+
+ double value = toTrim.cost(nodeToTrim);
+ toTrim.pop();
+
+ long parentOfTrimmed = parent.get(nodeToTrim);
+ included.clear(nodeToTrim); //nodeToTrim is removed from the answer
+ clearNode(nodeToTrim, parent, costToParent);
+ totalCost -= value; //as well as its cost from the solution
+
+ if (root != nodeToTrim) { //we are not removing the actual root
+ //reduce degree of parent
+ outDegree.set(parentOfTrimmed, outDegree.get(parentOfTrimmed) - 1);
+ long affectedNode = -1;
+ double affectedCost = -1;
+ long parentOutDegree = outDegree.get(parentOfTrimmed);
+ if (parentOfTrimmed == root) { //if its parent is the root
+ rootNodeAdjacent.clear(nodeToTrim); //remove the trimmed child
+ if (parentOutDegree == 1) { //root becomes a leaf
+ assert rootNodeAdjacent.cardinality() == 1;
+ //get the single sole child of root
+ var rootChild = rootNodeAdjacent.nextSetBit(0);
+ affectedNode = root;
+ affectedCost = costToParent.get(rootChild);
+ }
+ } else {
+ if (parentOutDegree == 0) { //if parent becomes a leaf
+ affectedNode = parentOfTrimmed;
+ affectedCost = costToParent.get(parentOfTrimmed);
+ }
+ }
+ if (affectedNode != -1) { //if a node has been converted to a leaf
+ updateExterior(affectedNode, affectedCost, toTrim, exterior);
+ }
+ } else {
+ //the root is removed, long live the new root!
+ assert rootNodeAdjacent.cardinality() == 1;
+ //the new root is the single sole child of old root
+ var newRoot = rootNodeAdjacent.nextSetBit(0);
+ rootNodeAdjacent.clear(); //empty everything
+ //find the children of the new root (this can happen once per node)
+
+ fillChildren(newRoot, rootNodeAdjacent, parent, included);
+
+ root = newRoot;
+ //set it as root
+ clearNode(root, parent, costToParent);
+ //check if root is a degree-1 to add to exterior
+ if (outDegree.get(root) == 1) {
+ //get single child
+ var rootChild = rootNodeAdjacent.nextSetBit(0);
+ priorityQueue.add(root, costToParent.get(rootChild));
+ exterior.set(root);
+ }
+ }
+ }
+ }
+ if (nodeAdded) {
+ included.set(node); // include it in the solution (for now!)
+ totalCost += associatedCost; //add its associated cost to the weight of tree
+ if (nodeParent == root) { //if it's parent is the root, update the bitset
+ rootNodeAdjacent.set(node);
+ }
+ if (node != root) { //this only happens for startNode to be fair
+ //the node's parent gets an update in degree
+ outDegree.set(nodeParent, outDegree.get(nodeParent) + 1);
+ exterior.clear(nodeParent); //and remoed from exterior if included
+ }
+ //then the node (being a leaf) is added to the trimming priority queu
+ toTrim.add(node, associatedCost);
+ exterior.set(node); //and the exterior
+ relaxNode(node, priorityQueue, parent, spanningTree);
+
+ } else {
+ clearNode(node, parent, costToParent);
+ }
+ }
+ //post-processing step: anything not touched is reset to -1
+ pruneUntouchedNodes(parent, costToParent, included);
+ progressTracker.endSubTask();
+ return new SpanningTree(root, graph.nodeCount(), k, parent, costToParent, totalCost);
+
+ }
+
+ private void pruneUntouchedNodes(HugeLongArray parent, HugeDoubleArray costToParent, BitSet included) {
+ graph.forEachNode(nodeId -> {
+ if (!included.get(nodeId)) {
+ clearNode(nodeId, parent, costToParent);
+ }
+ return true;
+ });
+ }
+
+ private void clearNode(long node, HugeLongArray parent, HugeDoubleArray costToParent) {
+ parent.set(node, -1);
+ costToParent.set(node, -1);
+ }
+
+ private boolean moveMakesSense(double cost1, double cost2, DoubleUnaryOperator minMax) {
+ if (minMax == Prim.MAX_OPERATOR) {
+ return cost1 > cost2;
+ } else {
+ return cost1 < cost2;
+ }
+ }
+
+ private void updateExterior(long affectedNode, double affectedCost, HugeLongPriorityQueue toTrim, BitSet exterior) {
+ if (!toTrim.containsElement(affectedNode)) {
+ toTrim.add(affectedNode, affectedCost); //add it to pq
+ } else {
+ //it is still in the queue, but it is not a leaf anymore, so it's value is obsolete
+ toTrim.set(affectedNode, affectedCost);
+ }
+ exterior.set(affectedNode); //and mark it in the exterior
+ }
+
+ private long findNextValidLeaf(HugeLongPriorityQueue toTrim, BitSet exterior) {
+ while (!exterior.get(toTrim.top())) { //not valid frontier nodes anymore, just ignore
+ toTrim.pop(); //as we said, pq does not have a direct remove method
+ }
+ return toTrim.top();
+ }
+
+ private void fillChildren(long newRoot, BitSet rootNodeAdjacent, HugeLongArray parent, BitSet included) {
+ graph.forEachRelationship(newRoot, (s, t) -> {
+ //relevant are only those nodes which are currently
+ //in the k-tree
+ if (parent.get(t) == s && included.get(t)) {
+ rootNodeAdjacent.set(t);
+ }
+ return true;
+ });
+ }
+
+ private void relaxNode(
+ long node,
+ HugeLongPriorityQueue priorityQueue,
+ HugeLongArray parent,
+ SpanningTree spanningTree
+ ) {
+ graph.forEachRelationship(node, (s, t) -> {
+ if (parent.get(t) == s) {
+ //TODO: work's only on mst edges for now (should be doable to re-find an k-MST from whole graph)
+ if (!priorityQueue.containsElement(t)) {
+ priorityQueue.add(t, spanningTree.costToParent(t));
+ }
+
+ }
+ return true;
+ });
+ }
+
}
+
+
diff --git a/alpha/alpha-algo/src/main/java/org/neo4j/gds/impl/spanningtree/KSpanningTreeAlgorithmFactory.java b/alpha/alpha-algo/src/main/java/org/neo4j/gds/impl/spanningtree/KSpanningTreeAlgorithmFactory.java
index e0e3b7bba99..70c2451b297 100644
--- a/alpha/alpha-algo/src/main/java/org/neo4j/gds/impl/spanningtree/KSpanningTreeAlgorithmFactory.java
+++ b/alpha/alpha-algo/src/main/java/org/neo4j/gds/impl/spanningtree/KSpanningTreeAlgorithmFactory.java
@@ -52,8 +52,7 @@ public Task progressTask(
) {
return Tasks.task(
taskName(),
- Tasks.leaf("SpanningTree", graph.nodeCount()),
- Tasks.leaf("Add relationship weights"),
+ Tasks.leaf("SpanningTree", graph.relationshipCount()),
Tasks.leaf("Remove relationships")
);
}
diff --git a/alpha/alpha-algo/src/test/java/org/neo4j/gds/impl/influenceMaximization/CelfTest.java b/alpha/alpha-algo/src/test/java/org/neo4j/gds/impl/influenceMaximization/CelfTest.java
new file mode 100644
index 00000000000..f35c40dadce
--- /dev/null
+++ b/alpha/alpha-algo/src/test/java/org/neo4j/gds/impl/influenceMaximization/CelfTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.impl.influenceMaximization;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.neo4j.gds.api.schema.Direction;
+import org.neo4j.gds.beta.generator.RandomGraphGenerator;
+import org.neo4j.gds.beta.generator.RelationshipDistribution;
+import org.neo4j.gds.core.concurrency.Pools;
+import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
+import org.neo4j.gds.influenceMaximization.CELF;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+ class CelfTest {
+
+ @ParameterizedTest
+ @ValueSource(ints = {2, 7})
+ void shouldNotReturnNegativeGains(int seedSize) {
+ var graph = RandomGraphGenerator
+ .builder()
+ .averageDegree(5)
+ .relationshipDistribution(RelationshipDistribution.POWER_LAW)
+ .direction(Direction.DIRECTED)
+ .nodeCount(60)
+ .seed(42)
+ .build()
+ .generate();
+
+ var celf = new CELF(graph, seedSize, 0.1, 3, Pools.DEFAULT, 1, 10, 5, ProgressTracker.NULL_TRACKER).compute();
+ for (var a : celf) {
+ assertThat(a.value).isNotNegative();
+ }
+ }
+}
diff --git a/alpha/alpha-algo/src/test/java/org/neo4j/gds/impl/spanningtree/KSpanningTreeTest.java b/alpha/alpha-algo/src/test/java/org/neo4j/gds/impl/spanningtree/KSpanningTreeTest.java
index 4b1c2abee89..f0ecfa60b06 100644
--- a/alpha/alpha-algo/src/test/java/org/neo4j/gds/impl/spanningtree/KSpanningTreeTest.java
+++ b/alpha/alpha-algo/src/test/java/org/neo4j/gds/impl/spanningtree/KSpanningTreeTest.java
@@ -19,28 +19,37 @@
*/
package org.neo4j.gds.impl.spanningtree;
+import org.apache.commons.lang3.mutable.MutableLong;
import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
import org.neo4j.gds.Orientation;
+import org.neo4j.gds.TestProgressTracker;
import org.neo4j.gds.api.Graph;
+import org.neo4j.gds.compat.Neo4jProxy;
+import org.neo4j.gds.compat.TestLog;
+import org.neo4j.gds.core.utils.progress.EmptyTaskRegistryFactory;
import org.neo4j.gds.core.utils.progress.tasks.ProgressTracker;
import org.neo4j.gds.extension.GdlExtension;
import org.neo4j.gds.extension.GdlGraph;
import org.neo4j.gds.extension.IdFunction;
import org.neo4j.gds.extension.Inject;
+import org.neo4j.gds.gdl.GdlFactory;
import org.neo4j.gds.spanningtree.Prim;
-import org.neo4j.gds.spanningtree.SpanningTree;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import java.util.HashSet;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.neo4j.gds.assertj.Extractors.removingThreadId;
+import static org.neo4j.gds.assertj.Extractors.replaceTimings;
/**
- * 1
- * (x) >(a)---(d) (x) (a) (d)
- * /3 \2 /3 => / /
- * (b)---(c) (b) (c)
- * 1
+ * 1
+ * (x), (a)---(d) (x) (a) (d)
+ * /3 \2 /3 => / /
+ * (b)---(c) (b) (c)
+ * 1
*/
@GdlExtension
class KSpanningTreeTest {
@@ -66,6 +75,14 @@ class KSpanningTreeTest {
@Inject
private IdFunction idFunction;
+ private static final int OFFSET = 5;
+
+ @GdlGraph(idOffset = OFFSET, orientation = Orientation.UNDIRECTED, graphNamePrefix = "offset")
+ private static final String DB_CYPHER_WITH_OFFSET = DB_CYPHER;
+
+ @Inject
+ private Graph offsetGraph;
+
private int a, b, c, d, x;
@BeforeEach
@@ -79,49 +96,216 @@ void setUp() {
@Test
void testMaximumKSpanningTree() {
- final SpanningTree spanningTree = new KSpanningTree(
- graph,
- Prim.MAX_OPERATOR,
- a,
- 2,
- ProgressTracker.NULL_TRACKER
- )
+ var spanningTree = new KSpanningTree(graph, Prim.MAX_OPERATOR, a, 2, ProgressTracker.NULL_TRACKER)
.compute();
- assertEquals(spanningTree.head(a), spanningTree.head(b));
- assertEquals(spanningTree.head(c), spanningTree.head(d));
- assertNotEquals(spanningTree.head(a), spanningTree.head(c));
- assertNotEquals(spanningTree.head(a), spanningTree.head(x));
- assertNotEquals(spanningTree.head(c), spanningTree.head(x));
+ assertThat(spanningTree).matches(tree -> tree.head(a) == tree.head(b) ^ tree.head(c) == tree.head(d));
+ assertThat(spanningTree.head(a)).isNotEqualTo(spanningTree.head(c));
+ assertThat(spanningTree.head(a)).isNotEqualTo(spanningTree.head(x));
+ assertThat(spanningTree.head(c)).isNotEqualTo(spanningTree.head(x));
}
@Test
void testMinimumKSpanningTree() {
- final SpanningTree spanningTree = new KSpanningTree(
+ var spanningTree = new KSpanningTree(graph, Prim.MIN_OPERATOR, a, 2, ProgressTracker.NULL_TRACKER)
+ .compute();
+
+ assertThat(spanningTree).matches(tree -> tree.head(a) == tree.head(d) ^ tree.head(b) == tree.head(c));
+ assertThat(spanningTree.head(a)).isNotEqualTo(spanningTree.head(b));
+ assertThat(spanningTree.head(a)).isNotEqualTo(spanningTree.head(x));
+ assertThat(spanningTree.head(b)).isNotEqualTo(spanningTree.head(x));
+ }
+
+ @Test
+ void testNeoIdsWithOffset() {
+ var spanningTree = new KSpanningTree(graph, Prim.MIN_OPERATOR, 0, 2, ProgressTracker.NULL_TRACKER).compute();
+ var otherSpanningTree = new KSpanningTree(offsetGraph, Prim.MIN_OPERATOR, OFFSET, 2, ProgressTracker.NULL_TRACKER).compute();
+
+ assertThat(spanningTree.parentArray().toArray())
+ .containsExactly(otherSpanningTree.parentArray().toArray());
+ }
+
+ @Test
+ void shouldProduceSingleConnectedTree() {
+ var factory = GdlFactory.of("CREATE" +
+ " (a:Node)" +
+ ", (b:Node)" +
+ ", (c:Node)" +
+ ", (d:Node)" +
+ ", (e:Node)" +
+ ", (b)-[:TYPE {cost: 1.0}]->(a)" +
+ ", (c)-[:TYPE {cost: 20.0}]->(b)" +
+ ", (d)-[:TYPE {cost: 30.0}]->(c)" +
+ ", (d)-[:TYPE {cost: 1.0}]->(e)"
+ );
+ var graph = factory.build().getUnion();
+ var startNode = factory.nodeId("d");
+
+ var k = 3;
+ var spanningTree = new KSpanningTree(
graph,
Prim.MIN_OPERATOR,
- a,
- 2,
+ startNode,
+ k,
ProgressTracker.NULL_TRACKER
- )
- .compute();
+ ).compute();
+
+ // if there are more than k nodes then there is more than one root
+ // meaning there is more than one tree (or the tree is broken)
+ var nodesInTree = new HashSet();
+ spanningTree.forEach((s, t, __) -> {
+ nodesInTree.add(s);
+ nodesInTree.add(t);
+ return true;
+ });
- assertEquals(spanningTree.head(a), spanningTree.head(d));
- assertEquals(spanningTree.head(b), spanningTree.head(c));
- assertNotEquals(spanningTree.head(a), spanningTree.head(b));
- assertNotEquals(spanningTree.head(a), spanningTree.head(x));
- assertNotEquals(spanningTree.head(b), spanningTree.head(x));
+ assertThat(nodesInTree.size()).isEqualTo(k);
}
+ @ParameterizedTest
+ @CsvSource({"2,1.0", "3,2.0"})
+ void shouldProduceSingleTreeWithKMinusOneEdges(int k, double expected) {
+ var factory = GdlFactory.of("CREATE" +
+ " (a:Node)" +
+ ", (b:Node)" +
+ ", (c:Node)" +
+ ", (d:Node)" +
+ ", (e:Node)" +
+ ", (f:Node)" +
+ ", (b)-[:TYPE {cost: 1.0}]->(a)" +
+ ", (c)-[:TYPE {cost: 20.0}]->(b)" +
+ ", (d)-[:TYPE {cost: 30.0}]->(c)" +
+ ", (d)-[:TYPE {cost: 1.0}]->(e)" +
+ ", (e)-[:TYPE {cost: 1.0}]->(f)"
+ );
+ var graph = factory.build().getUnion();
+ var startNode = factory.nodeId("d");
+
+
+ var spanningTree = new KSpanningTree(
+ graph,
+ Prim.MIN_OPERATOR,
+ startNode,
+ k,
+ ProgressTracker.NULL_TRACKER
+ ).compute();
+
+ var counter = new MutableLong(0);
+ spanningTree.forEach((__, ___, ____) -> {
+ counter.add(1);
+ return true;
+ });
+
+ assertThat(counter.getValue()).isEqualTo(k - 1);
+
+ assertThat(spanningTree.totalWeight()).isEqualTo(expected);
+ }
+
+
@Test
- @Disabled("Need to extend GdlGraph to generate offset node IDs and fix the test")
- void testNeoIdsWithOffset() {
- SpanningTree spanningTree = new KSpanningTree(graph, Prim.MIN_OPERATOR, 0, 2, ProgressTracker.NULL_TRACKER)
- .compute();
+ void worstCaseForPruningLeaves() {
+ var factory = GdlFactory.of("CREATE" +
+ " (a:Node)" +
+ ", (b:Node)" +
+ ", (c:Node)" +
+ ", (d:Node)" +
+ ", (e:Node)" +
+ ", (f:Node)" +
+ ", (g:Node)" +
+ ", (a)-[:TYPE {cost: 9.0}]->(b)" +
+ ", (b)-[:TYPE {cost: 0.0}]->(c)" +
+ ", (c)-[:TYPE {cost: 0.0}]->(d)" +
+ ", (a)-[:TYPE {cost: 1.0}]->(e)" +
+ ", (e)-[:TYPE {cost: 1.0}]->(f)" +
+ ", (f)-[:TYPE {cost: 1.0}]->(g)"
+
+ );
+ var graph = factory.build().getUnion();
+ var startNode = factory.nodeId("a");
- SpanningTree otherSpanningTree = new KSpanningTree(graph, Prim.MIN_OPERATOR, 5, 2, ProgressTracker.NULL_TRACKER)
- .compute();
- assertEquals(spanningTree, otherSpanningTree);
+ var spanningTree = new KSpanningTree(
+ graph,
+ Prim.MIN_OPERATOR,
+ startNode,
+ 4,
+ ProgressTracker.NULL_TRACKER
+ ).compute();
+
+ var counter = new MutableLong(0);
+ spanningTree.forEach((__, ___, ____) -> {
+ counter.add(1);
+ return true;
+ });
+
+ assertThat(counter.getValue()).isEqualTo(4 - 1);
+ assertThat(spanningTree.totalWeight()).isEqualTo(3.0);
+ //here a bad case for pruning just leaves
+ /// edge weight should be eliminated for the final solution but it is not because
+ //its leaves are not good.
+
}
+
+ @Test
+ void shouldWorkForComponentSmallerThanK() {
+ var factory = GdlFactory.of("CREATE" +
+ " (a:Node)" +
+ ", (b:Node)" +
+ ", (c:Node)" +
+ ", (d:Node)" +
+ ", (e:Node)" +
+ ", (f:Node)" +
+ ", (g:Node)" +
+ ", (a)-[:TYPE {cost: 1.0}]->(b)" +
+ ", (b)-[:TYPE {cost: 1.0}]->(c)" +
+ ", (c)-[:TYPE {cost: 1.0}]->(d)");
+
+ var graph = factory.build().getUnion();
+ var startNode = factory.nodeId("a");
+
+ var spanningTree = new KSpanningTree(
+ graph,
+ Prim.MIN_OPERATOR,
+ startNode,
+ 5,
+ ProgressTracker.NULL_TRACKER
+ ).compute();
+
+ assertThat(spanningTree.effectiveNodeCount()).isEqualTo(4);
+
+ }
+
+ @Test
+ void shouldLogProgress() {
+ var config = KSpanningTreeBaseConfigImpl.builder().sourceNode(idFunction.of("a")).k(2).build();
+ var factory = new KSpanningTreeAlgorithmFactory<>();
+ var log = Neo4jProxy.testLog();
+ var progressTracker = new TestProgressTracker(
+ factory.progressTask(graph, config),
+ log,
+ 1,
+ EmptyTaskRegistryFactory.INSTANCE
+ );
+ factory.build(graph, config, progressTracker).compute();
+ assertThat(log.getMessages(TestLog.INFO))
+ .extracting(removingThreadId())
+ .extracting(replaceTimings())
+ .containsExactly(
+ "KSpanningTree :: Start",
+ "KSpanningTree :: SpanningTree :: Start",
+ "KSpanningTree :: SpanningTree 30%",
+ "KSpanningTree :: SpanningTree 50%",
+ "KSpanningTree :: SpanningTree 80%",
+ "KSpanningTree :: SpanningTree 100%",
+ "KSpanningTree :: SpanningTree :: Finished",
+ "KSpanningTree :: Remove relationships :: Start",
+ "KSpanningTree :: Remove relationships 20%",
+ "KSpanningTree :: Remove relationships 40%",
+ "KSpanningTree :: Remove relationships 60%",
+ "KSpanningTree :: Remove relationships 100%",
+ "KSpanningTree :: Remove relationships :: Finished",
+ "KSpanningTree :: Finished"
+ );
+ }
+
}
diff --git a/alpha/alpha-proc/src/main/java/org/neo4j/gds/centrality/HarmonicCentralityWriteProc.java b/alpha/alpha-proc/src/main/java/org/neo4j/gds/centrality/HarmonicCentralityWriteProc.java
index f572ba0c373..dc8ec4e7680 100644
--- a/alpha/alpha-proc/src/main/java/org/neo4j/gds/centrality/HarmonicCentralityWriteProc.java
+++ b/alpha/alpha-proc/src/main/java/org/neo4j/gds/centrality/HarmonicCentralityWriteProc.java
@@ -92,7 +92,7 @@ public ComputationResultConsumer.
+ */
+package org.neo4j.gds.cypher;
+
+import org.apache.commons.lang3.mutable.MutableLong;
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.dbms.api.DatabaseNotFoundException;
+import org.neo4j.gds.BaseProc;
+import org.neo4j.gds.ProcPreconditions;
+import org.neo4j.gds.compat.GraphDatabaseApiProxy;
+import org.neo4j.gds.core.utils.ProgressTimer;
+import org.neo4j.gds.storageengine.InMemoryDatabaseCreationCatalog;
+import org.neo4j.procedure.Description;
+import org.neo4j.procedure.Name;
+import org.neo4j.procedure.Procedure;
+
+import java.util.stream.Stream;
+
+import static org.neo4j.gds.utils.StringFormatting.formatWithLocale;
+import static org.neo4j.procedure.Mode.WRITE;
+
+public class DropCypherDbProc extends BaseProc {
+
+ private static final String DESCRIPTION = "Drop a database backed by an in-memory graph";
+
+ @Procedure(name = "gds.alpha.drop.cypherdb", mode = WRITE)
+ @Description(DESCRIPTION)
+ public Stream dropDb(
+ @Name(value = "dbName") String dbName
+ ) {
+ ProcPreconditions.check();
+
+ DropCypherDbResult result = runWithExceptionLogging(
+ "Drop in-memory Cypher database failed",
+ () -> {
+ GraphCreateCypherDbProc.validateNeo4jEnterpriseEdition(databaseService);
+ var dbms = GraphDatabaseApiProxy.resolveDependency(databaseService, DatabaseManagementService.class);
+ validateDatabaseName(dbName, dbms);
+ var dropMillis = new MutableLong(0);
+ try (var ignored = ProgressTimer.start(dropMillis::setValue)) {
+ dbms.dropDatabase(dbName);
+ }
+ return new DropCypherDbResult(dbName, dropMillis.getValue());
+ }
+ );
+
+ return Stream.of(result);
+ }
+
+ private static void validateDatabaseName(String dbName, DatabaseManagementService dbms) {
+ if (!dbms.listDatabases().contains(dbName)) {
+ throw new DatabaseNotFoundException(formatWithLocale("A database with name `%s` does not exist", dbName));
+ }
+
+ var graphName = InMemoryDatabaseCreationCatalog.getRegisteredDbCreationGraphName(dbName);
+ if (graphName == null) {
+ throw new IllegalArgumentException(formatWithLocale(
+ "Database with name `%s` is not an in-memory database",
+ dbName
+ ));
+ }
+ }
+
+ public static class DropCypherDbResult {
+ public final String dbName;
+ public final long dropMillis;
+
+ public DropCypherDbResult(String dbName, long dropMillis) {
+ this.dbName = dbName;
+ this.dropMillis = dropMillis;
+ }
+ }
+}
diff --git a/alpha/alpha-proc/src/main/java/org/neo4j/gds/cypher/GraphCreateCypherDbProc.java b/alpha/alpha-proc/src/main/java/org/neo4j/gds/cypher/GraphCreateCypherDbProc.java
index 6402f4d0607..aff8e66825f 100644
--- a/alpha/alpha-proc/src/main/java/org/neo4j/gds/cypher/GraphCreateCypherDbProc.java
+++ b/alpha/alpha-proc/src/main/java/org/neo4j/gds/cypher/GraphCreateCypherDbProc.java
@@ -27,6 +27,7 @@
import org.neo4j.gds.compat.StorageEngineProxy;
import org.neo4j.gds.core.utils.ProgressTimer;
import org.neo4j.gds.storageengine.InMemoryDatabaseCreator;
+import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.Procedure;
@@ -52,7 +53,7 @@ public Stream createDb(
CreateCypherDbResult result = runWithExceptionLogging(
"In-memory Cypher database creation failed",
() -> {
- validateNeo4jEnterpriseEdition();
+ validateNeo4jEnterpriseEdition(databaseService);
MutableLong createMillis = new MutableLong(0);
try (ProgressTimer ignored = ProgressTimer.start(createMillis::setValue)) {
InMemoryDatabaseCreator.createDatabase(databaseService, username(), graphName, dbName);
@@ -78,7 +79,7 @@ public CreateCypherDbResult(String dbName, String graphName, long createMillis)
}
}
- private void validateNeo4jEnterpriseEdition() {
+ static void validateNeo4jEnterpriseEdition(GraphDatabaseService databaseService) {
var edition = StorageEngineProxy.dbmsEdition(databaseService);
if (!(edition == Edition.ENTERPRISE)) {
throw new DatabaseManagementException(formatWithLocale(
diff --git a/alpha/alpha-proc/src/main/java/org/neo4j/gds/influenceMaximization/CelfNodeProperties.java b/alpha/alpha-proc/src/main/java/org/neo4j/gds/influenceMaximization/CelfNodeProperties.java
index 1b39237b442..8e4036196ef 100644
--- a/alpha/alpha-proc/src/main/java/org/neo4j/gds/influenceMaximization/CelfNodeProperties.java
+++ b/alpha/alpha-proc/src/main/java/org/neo4j/gds/influenceMaximization/CelfNodeProperties.java
@@ -33,7 +33,7 @@ class CelfNodeProperties implements DoubleNodePropertyValues {
}
@Override
- public long size() {
+ public long nodeCount() {
return totalGraphNodeCount;
}
diff --git a/alpha/alpha-proc/src/main/java/org/neo4j/gds/scaling/ScalePropertiesProc.java b/alpha/alpha-proc/src/main/java/org/neo4j/gds/scaling/ScalePropertiesProc.java
index 0b6163ca5f9..6b2ebe2bfe3 100644
--- a/alpha/alpha-proc/src/main/java/org/neo4j/gds/scaling/ScalePropertiesProc.java
+++ b/alpha/alpha-proc/src/main/java/org/neo4j/gds/scaling/ScalePropertiesProc.java
@@ -23,7 +23,9 @@
import org.neo4j.gds.api.properties.nodes.NodePropertyValues;
import org.neo4j.gds.executor.ComputationResult;
-public class ScalePropertiesProc {
+public final class ScalePropertiesProc {
+
+ private ScalePropertiesProc() {}
static NodePropertyValues nodeProperties(ComputationResult computationResult) {
var size = computationResult.graph().nodeCount();
@@ -31,7 +33,7 @@ static NodePropertyValues nodeProperties(ComputationResult computationResult,
ExecutionContext executionContext
) {
- computationResult.graphStore().addRelationshipType(
- RelationshipType.of(computationResult.config().mutateRelationshipType()),
- computationResult.result()
- );
+ computationResult.graphStore().addRelationshipType(computationResult.result());
resultBuilder.withRelationshipsWritten(computationResult.result().topology().elementCount());
}
diff --git a/alpha/alpha-proc/src/test/java/org/neo4j/gds/AllShortestPathsDocTest.java b/alpha/alpha-proc/src/test/java/org/neo4j/gds/AllShortestPathsDocTest.java
deleted file mode 100644
index fa3fa7c61c5..00000000000
--- a/alpha/alpha-proc/src/test/java/org/neo4j/gds/AllShortestPathsDocTest.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (c) "Neo4j"
- * Neo4j Sweden AB [http://neo4j.com]
- *
- * This file is part of Neo4j.
- *
- * Neo4j is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package org.neo4j.gds;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.neo4j.gds.catalog.GraphProjectProc;
-import org.neo4j.gds.functions.IsFiniteFunc;
-import org.neo4j.gds.shortestpaths.AllShortestPathsProc;
-import org.neo4j.graphdb.Result;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-public class AllShortestPathsDocTest extends BaseProcTest {
- private static final String NL = System.lineSeparator();
- public static final String DB_CYPHER = "CREATE " +
- " (a:Loc {name:'A'})," +
- " (b:Loc {name:'B'})," +
- " (c:Loc {name:'C'})," +
- " (d:Loc {name:'D'})," +
- " (e:Loc {name:'E'})," +
- " (f:Loc {name:'F'})," +
- " (a)-[:ROAD {cost:50}]->(b)," +
- " (a)-[:ROAD {cost:50}]->(c)," +
- " (a)-[:ROAD {cost:100}]->(d)," +
- " (b)-[:ROAD {cost:40}]->(d)," +
- " (c)-[:ROAD {cost:40}]->(d)," +
- " (c)-[:ROAD {cost:80}]->(e)," +
- " (d)-[:ROAD {cost:30}]->(e)," +
- " (d)-[:ROAD {cost:80}]->(f)," +
- " (e)-[:ROAD {cost:40}]->(f);";
-
- @BeforeEach
- void setupGraph() throws Exception {
- registerProcedures(AllShortestPathsProc.class, GraphProjectProc.class);
- registerFunctions(IsFiniteFunc.class);
- runQuery(DB_CYPHER);
- }
-
- @Test
- void shouldStream() {
- var createQuery = "CALL gds.graph.project(" +
- " 'nativeGraph', " +
- " 'Loc', " +
- " {" +
- " ROAD: {" +
- " type: 'ROAD'," +
- " properties: 'cost'" +
- " }" +
- " }" +
- ")" +
- "YIELD graphName";
- runQuery(createQuery);
- String query = " CALL gds.alpha.allShortestPaths.stream('nativeGraph', {" +
- " relationshipWeightProperty: 'cost'" +
- " })" +
- " YIELD sourceNodeId, targetNodeId, distance" +
- " WITH sourceNodeId, targetNodeId, distance" +
- " WHERE gds.util.isFinite(distance) = true" +
-
- " MATCH (source:Loc) WHERE id(source) = sourceNodeId" +
- " MATCH (target:Loc) WHERE id(target) = targetNodeId" +
- " WITH source, target, distance WHERE source <> target" +
-
- " RETURN source.name AS source, target.name AS target, distance" +
- " ORDER BY distance DESC, source ASC, target ASC" +
- " LIMIT 10";
-
- String actual = runQuery(query, Result::resultAsString);
- String expected = "+----------------------------+" + NL +
- "| source | target | distance |" + NL +
- "+----------------------------+" + NL +
- "| \"A\" | \"F\" | 160.0 |" + NL +
- "| \"A\" | \"E\" | 120.0 |" + NL +
- "| \"B\" | \"F\" | 110.0 |" + NL +
- "| \"C\" | \"F\" | 110.0 |" + NL +
- "| \"A\" | \"D\" | 90.0 |" + NL +
- "| \"B\" | \"E\" | 70.0 |" + NL +
- "| \"C\" | \"E\" | 70.0 |" + NL +
- "| \"D\" | \"F\" | 70.0 |" + NL +
- "| \"A\" | \"B\" | 50.0 |" + NL +
- "| \"A\" | \"C\" | 50.0 |" + NL +
- "+----------------------------+" + NL +
- "10 rows" + NL;
-
- assertEquals(expected, actual);
- }
-
- @Test
- void shouldStreamWithCypherProjection() {
- var createQuery = " CALL gds.graph.project.cypher(" +
- " 'cypherGraph'," +
- " 'MATCH (n:Loc) RETURN id(n) AS id', " +
- " 'MATCH (n:Loc)-[r:ROAD]-(p:Loc) RETURN id(n) AS source, id(p) AS target, r.cost AS cost'" +
- " ) " +
- " YIELD graphName";
- runQuery(createQuery);
- String query = " CALL gds.alpha.allShortestPaths.stream('cypherGraph', {" +
- " relationshipWeightProperty: 'cost'" +
- " })" +
- " YIELD sourceNodeId, targetNodeId, distance" +
- " WITH sourceNodeId, targetNodeId, distance" +
- " WHERE gds.util.isFinite(distance) = true" +
-
- " MATCH (source:Loc) WHERE id(source) = sourceNodeId" +
- " MATCH (target:Loc) WHERE id(target) = targetNodeId" +
- " WITH source, target, distance WHERE source <> target" +
-
- " RETURN source.name AS source, target.name AS target, distance" +
- " ORDER BY distance DESC, source ASC, target ASC" +
- " LIMIT 10";
-
- String actual = runQuery(query, Result::resultAsString);
- String expected = "+----------------------------+" + NL +
- "| source | target | distance |" + NL +
- "+----------------------------+" + NL +
- "| \"A\" | \"F\" | 160.0 |" + NL +
- "| \"F\" | \"A\" | 160.0 |" + NL +
- "| \"A\" | \"E\" | 120.0 |" + NL +
- "| \"E\" | \"A\" | 120.0 |" + NL +
- "| \"B\" | \"F\" | 110.0 |" + NL +
- "| \"C\" | \"F\" | 110.0 |" + NL +
- "| \"F\" | \"B\" | 110.0 |" + NL +
- "| \"F\" | \"C\" | 110.0 |" + NL +
- "| \"A\" | \"D\" | 90.0 |" + NL +
- "| \"D\" | \"A\" | 90.0 |" + NL +
- "+----------------------------+" + NL +
- "10 rows" + NL;
-
- assertEquals(expected, actual);
- }
-}
diff --git a/alpha/alpha-proc/src/test/java/org/neo4j/gds/modularity/ModularityStatsProcTest.java b/alpha/alpha-proc/src/test/java/org/neo4j/gds/modularity/ModularityStatsProcTest.java
index 618085fcf93..5fac0449ecb 100644
--- a/alpha/alpha-proc/src/test/java/org/neo4j/gds/modularity/ModularityStatsProcTest.java
+++ b/alpha/alpha-proc/src/test/java/org/neo4j/gds/modularity/ModularityStatsProcTest.java
@@ -38,10 +38,10 @@ class ModularityStatsProcTest extends BaseProcTest {
@Neo4jGraph
static final String GRAPH =
"CREATE " +
- " (a1: Node { communityId: 0 })," +
- " (a2: Node { communityId: 0 })," +
+ " (a1: Node { communityId: 10 })," +
+ " (a2: Node { communityId: 10 })," +
" (a3: Node { communityId: 5 })," +
- " (a4: Node { communityId: 0 })," +
+ " (a4: Node { communityId: 10 })," +
" (a5: Node { communityId: 5 })," +
" (a6: Node { communityId: 5 })," +
diff --git a/alpha/alpha-proc/src/test/java/org/neo4j/gds/spanningtree/KSpanningTreeWriteProcTest.java b/alpha/alpha-proc/src/test/java/org/neo4j/gds/spanningtree/KSpanningTreeWriteProcTest.java
index d343b89dab0..e2ad893d7ee 100644
--- a/alpha/alpha-proc/src/test/java/org/neo4j/gds/spanningtree/KSpanningTreeWriteProcTest.java
+++ b/alpha/alpha-proc/src/test/java/org/neo4j/gds/spanningtree/KSpanningTreeWriteProcTest.java
@@ -30,17 +30,16 @@
import org.neo4j.gds.extension.Neo4jGraph;
import java.util.HashMap;
+import java.util.HashSet;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
class KSpanningTreeWriteProcTest extends BaseProcTest {
- private static String GRAPH_NAME = "graph";
+ private static final String GRAPH_NAME = "graph";
@Neo4jGraph
- static final String DB_CYPHER =
+ private static final String DB_CYPHER =
"CREATE (a:Node {name:'a'})\n" +
"CREATE (b:Node {name:'b'})\n" +
"CREATE (c:Node {name:'c'})\n" +
@@ -54,7 +53,7 @@ class KSpanningTreeWriteProcTest extends BaseProcTest {
" (d)-[:TYPE {w:3.0}]->(c)";
@Inject
- IdFunction idFunction;
+ private IdFunction idFunction;
@BeforeEach
void setupGraph() throws Exception {
@@ -80,22 +79,25 @@ void testMax() {
.yields("preProcessingMillis", "computeMillis", "writeMillis");
runQueryWithRowConsumer(query, row -> {
- assertTrue(row.getNumber("preProcessingMillis").longValue() >= 0);
- assertTrue(row.getNumber("writeMillis").longValue() >= 0);
- assertTrue(row.getNumber("computeMillis").longValue() >= 0);
+ assertThat(row.getNumber("preProcessingMillis").longValue() >= 0).isTrue();
+ assertThat(row.getNumber("writeMillis").longValue() >= 0).isTrue();
+ assertThat(row.getNumber("computeMillis").longValue() >= 0).isTrue();
});
- final HashMap communities = new HashMap<>();
+ final HashMap communities = new HashMap<>();
+ final HashSet distinctCommunities = new HashSet<>();
runQueryWithRowConsumer("MATCH (n) WHERE n.partition IS NOT NULL RETURN n.name as name, n.partition as p", row -> {
final String name = row.getString("name");
- final long p = row.getNumber("p").longValue();
+ final int p = row.getNumber("p").intValue();
communities.put(name, p);
+ distinctCommunities.add(p);
+
});
- assertEquals(communities.get("a"), communities.get("b"));
- assertEquals(communities.get("d"), communities.get("c"));
- assertNotEquals(communities.get("a"), communities.get("c"));
+ assertThat(communities).matches(c -> c.get("a").equals(c.get("b")) ^ c.get("c").equals(c.get("d")));
+ assertThat(communities.get("a")).isNotEqualTo(communities.get("c"));
+ assertThat(distinctCommunities.size()).isEqualTo(3);
}
@Test
@@ -109,24 +111,22 @@ void testMin() {
.addParameter("writeProperty", "partition")
.yields("preProcessingMillis", "computeMillis", "writeMillis");
-
runQueryWithRowConsumer(query, row -> {
- assertTrue(row.getNumber("preProcessingMillis").longValue() >= 0);
- assertTrue(row.getNumber("writeMillis").longValue() >= 0);
- assertTrue(row.getNumber("computeMillis").longValue() >= 0);
+ assertThat(row.getNumber("preProcessingMillis").longValue() >= 0).isTrue();
+ assertThat(row.getNumber("writeMillis").longValue() >= 0).isTrue();
+ assertThat(row.getNumber("computeMillis").longValue() >= 0).isTrue();
});
final HashMap communities = new HashMap<>();
-
+ final HashSet distinctCommunities = new HashSet<>();
runQueryWithRowConsumer("MATCH (n) WHERE n.partition IS NOT NULL RETURN n.name as name, n.partition as p", row -> {
final String name = row.getString("name");
final int p = row.getNumber("p").intValue();
communities.put(name, p);
+ distinctCommunities.add(p);
});
- assertEquals(communities.get("a"), communities.get("d"));
- assertEquals(communities.get("b"), communities.get("c"));
- assertNotEquals(communities.get("a"), communities.get("b"));
+ assertThat(communities).matches(c -> c.get("a").equals(c.get("d")) ^ c.get("b").equals(c.get("c")));
+ assertThat(distinctCommunities.size()).isEqualTo(3);
}
-
}
diff --git a/annotations/src/main/java/org/neo4j/gds/annotation/DataClass.java b/annotations/src/main/java/org/neo4j/gds/annotation/DataClass.java
deleted file mode 100644
index 513dbe9689b..00000000000
--- a/annotations/src/main/java/org/neo4j/gds/annotation/DataClass.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) "Neo4j"
- * Neo4j Sweden AB [http://neo4j.com]
- *
- * This file is part of Neo4j.
- *
- * Neo4j is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package org.neo4j.gds.annotation;
-
-import org.immutables.value.Value;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Target({ElementType.PACKAGE, ElementType.TYPE})
-@Retention(RetentionPolicy.CLASS)
-@Value.Style(
- allParameters = true,
- builderVisibility = Value.Style.BuilderVisibility.SAME,
- clearBuilder = true,
- deepImmutablesDetection = true,
- deferCollectionAllocation = true,
- depluralize = true,
- headerComments = true,
- jdkOnly = true,
- optionalAcceptNullable = true,
- typeAbstract = "Abstract*",
- typeImmutable = "*",
- visibility = Value.Style.ImplementationVisibility.SAME
-)
-public @interface DataClass {
-}
diff --git a/annotations/src/main/resources/META-INF/annotations/org.immutables.value.immutable b/annotations/src/main/resources/META-INF/annotations/org.immutables.value.immutable
index b067608b47a..cf5adfc05a9 100644
--- a/annotations/src/main/resources/META-INF/annotations/org.immutables.value.immutable
+++ b/annotations/src/main/resources/META-INF/annotations/org.immutables.value.immutable
@@ -1,2 +1 @@
-org.neo4j.gds.annotation.DataClass
org.neo4j.gds.annotation.ValueClass
diff --git a/build.gradle b/build.gradle
index c767e083418..165c9eec480 100644
--- a/build.gradle
+++ b/build.gradle
@@ -31,11 +31,23 @@ ext {
project(':neo4j-kernel-adapter-4.4'),
project(':neo4j-kernel-adapter-5.1'),
project(':neo4j-kernel-adapter-5.2'),
+ project(':neo4j-kernel-adapter-5.3'),
+ project(':neo4j-kernel-adapter-5.4'),
+ project(':neo4j-kernel-adapter-5.5'),
+ project(':neo4j-kernel-adapter-5.6'),
+ project(':neo4j-kernel-adapter-5.7'),
+ project(':neo4j-kernel-adapter-5.8'),
],
'storage-engine-adapter': [
project(':storage-engine-adapter-4.4'),
project(':storage-engine-adapter-5.1'),
project(':storage-engine-adapter-5.2'),
+ project(':storage-engine-adapter-5.3'),
+ project(':storage-engine-adapter-5.4'),
+ project(':storage-engine-adapter-5.5'),
+ project(':storage-engine-adapter-5.6'),
+ project(':storage-engine-adapter-5.7'),
+ project(':storage-engine-adapter-5.8'),
]
]
}
diff --git a/collections/src/main/java/org/neo4j/gds/mem/HugeArrays.java b/collections/src/main/java/org/neo4j/gds/mem/HugeArrays.java
index a792c21684e..8f2bc3f6706 100644
--- a/collections/src/main/java/org/neo4j/gds/mem/HugeArrays.java
+++ b/collections/src/main/java/org/neo4j/gds/mem/HugeArrays.java
@@ -51,7 +51,7 @@ public static int exclusiveIndexOfPage(long index) {
}
public static long indexFromPageIndexAndIndexInPage(int pageIndex, int indexInPage) {
- return (pageIndex << PAGE_SHIFT) | indexInPage;
+ return ((long) pageIndex << PAGE_SHIFT) | indexInPage;
}
public static int numberOfPages(long capacity) {
diff --git a/compatibility/4.4/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_44/Neo4jProxyImpl.java b/compatibility/4.4/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_44/Neo4jProxyImpl.java
index 8ef9e8ffcbb..64bf9b8c087 100644
--- a/compatibility/4.4/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_44/Neo4jProxyImpl.java
+++ b/compatibility/4.4/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_44/Neo4jProxyImpl.java
@@ -28,6 +28,7 @@
import org.neo4j.configuration.helpers.DatabaseNameValidator;
import org.neo4j.dbms.api.DatabaseManagementService;
import org.neo4j.exceptions.KernelException;
+import org.neo4j.fabric.FabricDatabaseManager;
import org.neo4j.gds.annotation.SuppressForbidden;
import org.neo4j.gds.compat.BoltTransactionRunner;
import org.neo4j.gds.compat.CompatCallableProcedure;
@@ -40,6 +41,7 @@
import org.neo4j.gds.compat.GdsDatabaseLayout;
import org.neo4j.gds.compat.GdsDatabaseManagementServiceBuilder;
import org.neo4j.gds.compat.GdsGraphDatabaseAPI;
+import org.neo4j.gds.compat.GraphDatabaseApiProxy;
import org.neo4j.gds.compat.InputEntityIdVisitor;
import org.neo4j.gds.compat.Neo4jProxyApi;
import org.neo4j.gds.compat.PropertyReference;
@@ -108,7 +110,10 @@
import org.neo4j.kernel.database.NamedDatabaseId;
import org.neo4j.kernel.database.NormalizedDatabaseName;
import org.neo4j.kernel.database.TestDatabaseIdRepository;
+import org.neo4j.kernel.impl.coreapi.InternalTransaction;
import org.neo4j.kernel.impl.index.schema.IndexImporterFactoryImpl;
+import org.neo4j.kernel.impl.query.TransactionalContext;
+import org.neo4j.kernel.impl.query.TransactionalContextFactory;
import org.neo4j.kernel.impl.store.MetaDataStore;
import org.neo4j.kernel.impl.store.RecordStore;
import org.neo4j.kernel.impl.store.format.RecordFormatSelector;
@@ -124,6 +129,7 @@
import org.neo4j.storageengine.api.PropertySelection;
import org.neo4j.values.storable.ValueCategory;
import org.neo4j.values.storable.ValueGroup;
+import org.neo4j.values.virtual.MapValue;
import java.io.IOException;
import java.nio.file.Path;
@@ -183,6 +189,11 @@ public long getHighestPossibleIdInUse(
return recordStore.getHighestPossibleIdInUse(kernelTransaction.cursorContext());
}
+ @Override
+ public long getHighId(RecordStore extends AbstractBaseRecord> recordStore) {
+ return recordStore.getHighId();
+ }
+
@Override
public List> entityCursorScan(
KernelTransaction transaction,
@@ -414,7 +425,7 @@ public Input batchInputFrom(CompatInput compatInput) {
}
@Override
- public InputEntityIdVisitor.Long inputEntityLongIdVisitor(IdType idType) {
+ public InputEntityIdVisitor.Long inputEntityLongIdVisitor(IdType idType, ReadableGroups groups) {
switch (idType) {
case ACTUAL:
return new InputEntityIdVisitor.Long() {
@@ -434,20 +445,22 @@ public void visitTargetId(InputEntityVisitor visitor, long id) {
}
};
case INTEGER:
+ var globalGroup = groups.get(Group.GLOBAL.id());
+
return new InputEntityIdVisitor.Long() {
@Override
public void visitNodeId(InputEntityVisitor visitor, long id) {
- visitor.id(id, Group.GLOBAL);
+ visitor.id(id, globalGroup);
}
@Override
public void visitSourceId(InputEntityVisitor visitor, long id) {
- visitor.startId(id, Group.GLOBAL);
+ visitor.startId(id, globalGroup);
}
@Override
public void visitTargetId(InputEntityVisitor visitor, long id) {
- visitor.endId(id, Group.GLOBAL);
+ visitor.endId(id, globalGroup);
}
};
default:
@@ -456,21 +469,22 @@ public void visitTargetId(InputEntityVisitor visitor, long id) {
}
@Override
- public InputEntityIdVisitor.String inputEntityStringIdVisitor() {
+ public InputEntityIdVisitor.String inputEntityStringIdVisitor(ReadableGroups groups) {
+ var globalGroup = groups.get(Group.GLOBAL.id());
return new InputEntityIdVisitor.String() {
@Override
public void visitNodeId(InputEntityVisitor visitor, String id) {
- visitor.id(id, Group.GLOBAL);
+ visitor.id(id, globalGroup);
}
@Override
public void visitSourceId(InputEntityVisitor visitor, String id) {
- visitor.startId(id, Group.GLOBAL);
+ visitor.startId(id, globalGroup);
}
@Override
public void visitTargetId(InputEntityVisitor visitor, String id) {
- visitor.endId(id, Group.GLOBAL);
+ visitor.endId(id, globalGroup);
}
};
}
@@ -785,5 +799,19 @@ public void reserveNeo4jIds(IdGeneratorFactory generatorFactory, int size, Curso
idGenerator.nextConsecutiveIdRange(size, false, cursorContext);
}
+ @Override
+ public TransactionalContext newQueryContext(
+ TransactionalContextFactory contextFactory,
+ InternalTransaction tx,
+ String queryText,
+ MapValue queryParameters
+ ) {
+ return contextFactory.newContext(tx, queryText, queryParameters);
+ }
+ @Override
+ public boolean isCompositeDatabase(GraphDatabaseService databaseService) {
+ var databaseManager = GraphDatabaseApiProxy.resolveDependency(databaseService, FabricDatabaseManager.class);
+ return databaseManager.isFabricDatabase(GraphDatabaseApiProxy.databaseId(databaseService).name());
+ }
}
diff --git a/compatibility/4.4/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_44/SettingProxyImpl.java b/compatibility/4.4/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_44/SettingProxyImpl.java
index 88226c3dbf0..ac658882bf2 100644
--- a/compatibility/4.4/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_44/SettingProxyImpl.java
+++ b/compatibility/4.4/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_44/SettingProxyImpl.java
@@ -77,4 +77,9 @@ public void setDatabaseMode(Config config, DatabaseMode databaseMode, GraphDatab
}
config.set(GraphDatabaseSettings.mode, mode);
}
+
+ @Override
+ public String secondaryModeName() {
+ return "Read Replica";
+ }
}
diff --git a/cypher/4.4/storage-engine-adapter/build.gradle b/compatibility/4.4/storage-engine-adapter/build.gradle
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/build.gradle
rename to compatibility/4.4/storage-engine-adapter/build.gradle
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryCommandCreationContextImpl.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryCommandCreationContextImpl.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryCommandCreationContextImpl.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryCommandCreationContextImpl.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryCountsStore.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryCountsStore.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryCountsStore.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryCountsStore.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryMetaDataProviderImpl.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryMetaDataProviderImpl.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryMetaDataProviderImpl.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryMetaDataProviderImpl.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryNodeCursor.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryNodeCursor.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryNodeCursor.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryNodeCursor.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryNodePropertyCursor.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryNodePropertyCursor.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryNodePropertyCursor.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryNodePropertyCursor.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryPropertyCursor.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryPropertyCursor.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryPropertyCursor.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryPropertyCursor.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryPropertySelectionImpl.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryPropertySelectionImpl.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryPropertySelectionImpl.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryPropertySelectionImpl.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryRelationshipPropertyCursor.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryRelationshipPropertyCursor.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryRelationshipPropertyCursor.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryRelationshipPropertyCursor.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryRelationshipScanCursor.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryRelationshipScanCursor.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryRelationshipScanCursor.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryRelationshipScanCursor.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryRelationshipTraversalCursor.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryRelationshipTraversalCursor.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryRelationshipTraversalCursor.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryRelationshipTraversalCursor.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStorageEngineFactory.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStorageEngineFactory.java
similarity index 97%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStorageEngineFactory.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStorageEngineFactory.java
index 7ac94920456..ef689ef60ab 100644
--- a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStorageEngineFactory.java
+++ b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStorageEngineFactory.java
@@ -22,6 +22,8 @@
import org.neo4j.annotations.service.ServiceProvider;
import org.neo4j.configuration.Config;
import org.neo4j.dbms.database.readonly.DatabaseReadOnlyChecker;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.StorageEngineProxyApi;
import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector;
import org.neo4j.internal.id.IdController;
import org.neo4j.internal.id.IdGeneratorFactory;
@@ -73,6 +75,10 @@ public class InMemoryStorageEngineFactory implements StorageEngineFactory {
static final String IN_MEMORY_STORAGE_ENGINE_NAME = "in-memory-44";
+ public InMemoryStorageEngineFactory() {
+ StorageEngineProxyApi.requireNeo4jVersion(Neo4jVersion.V_4_4, StorageEngineFactory.class);
+ }
+
private final InMemoryMetaDataProviderImpl metadataProvider = new InMemoryMetaDataProviderImpl();
@Override
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStorageEngineImpl.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStorageEngineImpl.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStorageEngineImpl.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStorageEngineImpl.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStorageLocksImpl.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStorageLocksImpl.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStorageLocksImpl.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStorageLocksImpl.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStoreVersion.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStoreVersion.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStoreVersion.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryStoreVersion.java
diff --git a/cypher/api/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/AbstractTransactionIdStore.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryTransactionIdStoreImpl.java
similarity index 55%
rename from cypher/api/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/AbstractTransactionIdStore.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryTransactionIdStoreImpl.java
index a07942bdb6d..98905edd331 100644
--- a/cypher/api/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/AbstractTransactionIdStore.java
+++ b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryTransactionIdStoreImpl.java
@@ -17,42 +17,29 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.neo4j.internal.recordstorage;
+package org.neo4j.gds.compat._44;
+import org.neo4j.internal.recordstorage.AbstractTransactionIdStore;
import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.kernel.impl.transaction.log.LogPosition;
+import org.neo4j.storageengine.api.ClosedTransactionMetadata;
import org.neo4j.storageengine.api.TransactionId;
-import org.neo4j.storageengine.api.TransactionIdStore;
-import org.neo4j.util.concurrent.ArrayQueueOutOfOrderSequence;
-import org.neo4j.util.concurrent.OutOfOrderSequence;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
+public class InMemoryTransactionIdStoreImpl extends AbstractTransactionIdStore {
-public abstract class AbstractTransactionIdStore implements TransactionIdStore {
- private final AtomicLong committingTransactionId;
- protected final OutOfOrderSequence closedTransactionId;
- private final AtomicReference committedTransactionId;
- private final long previouslyCommittedTxId;
- private final int initialTransactionChecksum;
- private final long previouslyCommittedTxCommitTimestamp;
-
- public AbstractTransactionIdStore() {
- this(1L, -559063315, 0L, 0L, 64L);
+ public ClosedTransactionMetadata getLastClosedTransaction() {
+ long[] metaData = this.closedTransactionId.get();
+ return new ClosedTransactionMetadata(metaData[0], new LogPosition(metaData[1], metaData[2]));
}
- public AbstractTransactionIdStore(
+ @Override
+ protected void initLastCommittedAndClosedTransactionId(
long previouslyCommittedTxId,
int checksum,
long previouslyCommittedTxCommitTimestamp,
- long previouslyCommittedTxLogVersion,
- long previouslyCommittedTxLogByteOffset
+ long previouslyCommittedTxLogByteOffset,
+ long previouslyCommittedTxLogVersion
) {
- this.committingTransactionId = new AtomicLong();
- this.closedTransactionId = new ArrayQueueOutOfOrderSequence(-1L, 100, new long[1]);
- this.committedTransactionId = new AtomicReference<>(new TransactionId(1L, -559063315, 0L));
-
- assert previouslyCommittedTxId >= 1L : "cannot start from a tx id less than BASE_TX_ID";
-
this.setLastCommittedAndClosedTransactionId(
previouslyCommittedTxId,
checksum,
@@ -61,21 +48,9 @@ public AbstractTransactionIdStore(
previouslyCommittedTxLogVersion,
getEmptyCursorContext()
);
- this.previouslyCommittedTxId = previouslyCommittedTxId;
- this.initialTransactionChecksum = checksum;
- this.previouslyCommittedTxCommitTimestamp = previouslyCommittedTxCommitTimestamp;
- }
-
- protected abstract CursorContext getEmptyCursorContext();
-
- public long nextCommittingTransactionId() {
- return this.committingTransactionId.incrementAndGet();
- }
-
- public long committingTransactionId() {
- return this.committingTransactionId.get();
}
+ @Override
public synchronized void transactionCommitted(
long transactionId,
int checksum,
@@ -84,31 +59,11 @@ public synchronized void transactionCommitted(
) {
TransactionId current = this.committedTransactionId.get();
if (current == null || transactionId > current.transactionId()) {
- this.committedTransactionId.set(new TransactionId(transactionId, checksum, commitTimestamp));
+ this.committedTransactionId.set(transactionId(transactionId, checksum, commitTimestamp));
}
-
- }
-
- public long getLastCommittedTransactionId() {
- return this.committedTransactionId.get().transactionId();
- }
-
- public TransactionId getLastCommittedTransaction() {
- return this.committedTransactionId.get();
- }
-
- public TransactionId getUpgradeTransaction() {
- return new TransactionId(
- this.previouslyCommittedTxId,
- this.initialTransactionChecksum,
- this.previouslyCommittedTxCommitTimestamp
- );
- }
-
- public long getLastClosedTransactionId() {
- return this.closedTransactionId.getHighestGapFreeNumber();
}
+ @Override
public void setLastCommittedAndClosedTransactionId(
long transactionId,
int checksum,
@@ -118,14 +73,11 @@ public void setLastCommittedAndClosedTransactionId(
CursorContext cursorContext
) {
this.committingTransactionId.set(transactionId);
- this.committedTransactionId.set(new TransactionId(transactionId, checksum, commitTimestamp));
+ this.committedTransactionId.set(transactionId(transactionId, checksum, commitTimestamp));
this.closedTransactionId.set(transactionId, new long[]{logVersion, byteOffset, checksum, commitTimestamp});
}
- public void transactionClosed(long transactionId, long logVersion, long byteOffset, CursorContext cursorContext) {
- this.closedTransactionId.offer(transactionId, new long[]{logVersion, byteOffset});
- }
-
+ @Override
public void resetLastClosedTransaction(
long transactionId,
long byteOffset,
@@ -136,6 +88,30 @@ public void resetLastClosedTransaction(
this.closedTransactionId.set(transactionId, new long[]{logVersion, byteOffset});
}
+ @Override
+ public TransactionId getUpgradeTransaction() {
+ return transactionId(
+ this.previouslyCommittedTxId,
+ this.initialTransactionChecksum,
+ this.previouslyCommittedTxCommitTimestamp
+ );
+ }
+
+ @Override
+ public void transactionClosed(long transactionId, long logVersion, long byteOffset, CursorContext cursorContext) {
+ this.closedTransactionId.offer(transactionId, new long[]{logVersion, byteOffset});
+ }
+
+ @Override
public void flush(CursorContext cursorContext) {
}
+
+ @Override
+ protected TransactionId transactionId(long transactionId, int checksum, long commitTimestamp) {
+ return new TransactionId(transactionId, checksum, commitTimestamp);
+ }
+
+ private CursorContext getEmptyCursorContext() {
+ return CursorContext.NULL;
+ }
}
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryVersionCheck.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryVersionCheck.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryVersionCheck.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryVersionCheck.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/StorageEngineProxyFactoryImpl.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/StorageEngineProxyFactoryImpl.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/StorageEngineProxyFactoryImpl.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/StorageEngineProxyFactoryImpl.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/StorageEngineProxyImpl.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/StorageEngineProxyImpl.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/StorageEngineProxyImpl.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/StorageEngineProxyImpl.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository44.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository44.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository44.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository44.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory44.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory44.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory44.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory44.java
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/InMemoryStorageReader44.java b/compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/InMemoryStorageReader44.java
similarity index 100%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/InMemoryStorageReader44.java
rename to compatibility/4.4/storage-engine-adapter/src/main/java/org/neo4j/internal/recordstorage/InMemoryStorageReader44.java
diff --git a/compatibility/5.1/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_51/Neo4jProxyImpl.java b/compatibility/5.1/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_51/Neo4jProxyImpl.java
index 5d085755c17..17bfd3e1bf0 100644
--- a/compatibility/5.1/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_51/Neo4jProxyImpl.java
+++ b/compatibility/5.1/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_51/Neo4jProxyImpl.java
@@ -31,6 +31,7 @@
import org.neo4j.configuration.helpers.DatabaseNameValidator;
import org.neo4j.dbms.api.DatabaseManagementService;
import org.neo4j.exceptions.KernelException;
+import org.neo4j.fabric.FabricDatabaseManager;
import org.neo4j.gds.annotation.SuppressForbidden;
import org.neo4j.gds.compat.BoltTransactionRunner;
import org.neo4j.gds.compat.CompatCallableProcedure;
@@ -62,7 +63,6 @@
import org.neo4j.internal.batchimport.InputIterable;
import org.neo4j.internal.batchimport.Monitor;
import org.neo4j.internal.batchimport.input.Collector;
-import org.neo4j.internal.batchimport.input.Group;
import org.neo4j.internal.batchimport.input.IdType;
import org.neo4j.internal.batchimport.input.Input;
import org.neo4j.internal.batchimport.input.InputEntityVisitor;
@@ -116,7 +116,10 @@
import org.neo4j.kernel.database.NamedDatabaseId;
import org.neo4j.kernel.database.NormalizedDatabaseName;
import org.neo4j.kernel.database.TestDatabaseIdRepository;
+import org.neo4j.kernel.impl.coreapi.InternalTransaction;
import org.neo4j.kernel.impl.index.schema.IndexImporterFactoryImpl;
+import org.neo4j.kernel.impl.query.TransactionalContext;
+import org.neo4j.kernel.impl.query.TransactionalContextFactory;
import org.neo4j.kernel.impl.store.RecordStore;
import org.neo4j.kernel.impl.store.format.RecordFormatSelector;
import org.neo4j.kernel.impl.store.format.RecordFormats;
@@ -134,6 +137,7 @@
import org.neo4j.util.Bits;
import org.neo4j.values.storable.ValueCategory;
import org.neo4j.values.storable.Values;
+import org.neo4j.values.virtual.MapValue;
import java.io.IOException;
import java.nio.file.Path;
@@ -198,6 +202,11 @@ public long getHighestPossibleIdInUse(
return recordStore.getHighestPossibleIdInUse(kernelTransaction.cursorContext());
}
+ @Override
+ public long getHighId(RecordStore extends AbstractBaseRecord> recordStore) {
+ return recordStore.getHighId();
+ }
+
@Override
public List> entityCursorScan(
KernelTransaction transaction,
@@ -509,7 +518,7 @@ public Input batchInputFrom(CompatInput compatInput) {
}
@Override
- public InputEntityIdVisitor.Long inputEntityLongIdVisitor(IdType idType) {
+ public InputEntityIdVisitor.Long inputEntityLongIdVisitor(IdType idType, ReadableGroups groups) {
switch (idType) {
case ACTUAL -> {
return new InputEntityIdVisitor.Long() {
@@ -530,7 +539,7 @@ public void visitTargetId(InputEntityVisitor visitor, long id) {
};
}
case INTEGER -> {
- var globalGroup = new Group(0, null, null);
+ var globalGroup = groups.get(null);
return new InputEntityIdVisitor.Long() {
@Override
@@ -554,8 +563,8 @@ public void visitTargetId(InputEntityVisitor visitor, long id) {
}
@Override
- public InputEntityIdVisitor.String inputEntityStringIdVisitor() {
- var globalGroup = new Group(0, null, null);
+ public InputEntityIdVisitor.String inputEntityStringIdVisitor(ReadableGroups groups) {
+ var globalGroup = groups.get(null);
return new InputEntityIdVisitor.String() {
@Override
@@ -908,5 +917,19 @@ public void reserveNeo4jIds(IdGeneratorFactory generatorFactory, int size, Curso
idGenerator.nextConsecutiveIdRange(size, false, cursorContext);
}
+ @Override
+ public TransactionalContext newQueryContext(
+ TransactionalContextFactory contextFactory,
+ InternalTransaction tx,
+ String queryText,
+ MapValue queryParameters
+ ) {
+ return contextFactory.newContext(tx, queryText, queryParameters);
+ }
+ @Override
+ public boolean isCompositeDatabase(GraphDatabaseService databaseService) {
+ var databaseManager = GraphDatabaseApiProxy.resolveDependency(databaseService, FabricDatabaseManager.class);
+ return databaseManager.isFabricDatabase(GraphDatabaseApiProxy.databaseId(databaseService));
+ }
}
diff --git a/compatibility/5.1/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_51/SettingProxyImpl.java b/compatibility/5.1/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_51/SettingProxyImpl.java
index 97dbcff99c8..1412919356e 100644
--- a/compatibility/5.1/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_51/SettingProxyImpl.java
+++ b/compatibility/5.1/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_51/SettingProxyImpl.java
@@ -79,4 +79,9 @@ public void setDatabaseMode(Config config, DatabaseMode databaseMode, GraphDatab
throw new RuntimeException("Could not get the permissions to set the mode field.", e);
}
}
+
+ @Override
+ public String secondaryModeName() {
+ return "Secondary";
+ }
}
diff --git a/cypher/5.1/storage-engine-adapter/build.gradle b/compatibility/5.1/storage-engine-adapter/build.gradle
similarity index 96%
rename from cypher/5.1/storage-engine-adapter/build.gradle
rename to compatibility/5.1/storage-engine-adapter/build.gradle
index ac0d4da3b60..d9dcbe96a38 100644
--- a/cypher/5.1/storage-engine-adapter/build.gradle
+++ b/compatibility/5.1/storage-engine-adapter/build.gradle
@@ -44,6 +44,7 @@ if (ver.'neo4j'.startsWith('5.')) {
dependencies {
annotationProcessor group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
compileOnly group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
+ compileOnly group: 'org.neo4j', name: 'neo4j-kernel-api', version: ver.'neo4j'
implementation project(':neo4j-adapter')
implementation project(':storage-engine-adapter-api')
diff --git a/compatibility/5.1/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_51/InMemoryStorageEngineFactory.java b/compatibility/5.1/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_51/InMemoryStorageEngineFactory.java
new file mode 100644
index 00000000000..c416233af83
--- /dev/null
+++ b/compatibility/5.1/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_51/InMemoryStorageEngineFactory.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._51;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.configuration.Config;
+import org.neo4j.dbms.database.readonly.DatabaseReadOnlyChecker;
+import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector;
+import org.neo4j.internal.id.IdController;
+import org.neo4j.internal.id.IdGeneratorFactory;
+import org.neo4j.internal.schema.IndexConfigCompleter;
+import org.neo4j.internal.schema.SchemaRule;
+import org.neo4j.internal.schema.SchemaState;
+import org.neo4j.io.fs.FileSystemAbstraction;
+import org.neo4j.io.layout.DatabaseLayout;
+import org.neo4j.io.layout.Neo4jLayout;
+import org.neo4j.io.pagecache.PageCache;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.io.pagecache.tracing.PageCacheTracer;
+import org.neo4j.lock.LockService;
+import org.neo4j.logging.LogProvider;
+import org.neo4j.logging.internal.LogService;
+import org.neo4j.memory.MemoryTracker;
+import org.neo4j.monitoring.DatabaseHealth;
+import org.neo4j.scheduler.JobScheduler;
+import org.neo4j.storageengine.api.CommandReaderFactory;
+import org.neo4j.storageengine.api.ConstraintRuleAccessor;
+import org.neo4j.storageengine.api.LogVersionRepository;
+import org.neo4j.storageengine.api.MetadataProvider;
+import org.neo4j.storageengine.api.StorageEngine;
+import org.neo4j.storageengine.api.StorageEngineFactory;
+import org.neo4j.storageengine.api.StorageFilesState;
+import org.neo4j.storageengine.api.StoreId;
+import org.neo4j.storageengine.api.StoreVersion;
+import org.neo4j.storageengine.api.StoreVersionCheck;
+import org.neo4j.storageengine.api.TransactionIdStore;
+import org.neo4j.storageengine.migration.RollingUpgradeCompatibility;
+import org.neo4j.storageengine.migration.SchemaRuleMigrationAccess;
+import org.neo4j.storageengine.migration.StoreMigrationParticipant;
+import org.neo4j.token.TokenHolders;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@ServiceProvider
+public class InMemoryStorageEngineFactory implements StorageEngineFactory {
+
+ @Override
+ public String name() {
+ return "unsupported51";
+ }
+
+ @Override
+ public StoreVersionCheck versionCheck(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ LogService logService,
+ PageCacheTracer pageCacheTracer
+ ) {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreVersion versionInformation(String storeVersion) {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreVersion versionInformation(StoreId storeId) {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public RollingUpgradeCompatibility rollingUpgradeCompatibility() {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public List migrationParticipants(
+ FileSystemAbstraction fs,
+ Config config,
+ PageCache pageCache,
+ JobScheduler jobScheduler,
+ LogService logService,
+ PageCacheTracer cacheTracer,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public StorageEngine instantiate(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ TokenHolders tokenHolders,
+ SchemaState schemaState,
+ ConstraintRuleAccessor constraintSemantics,
+ IndexConfigCompleter indexConfigCompleter,
+ LockService lockService,
+ IdGeneratorFactory idGeneratorFactory,
+ IdController idController,
+ DatabaseHealth databaseHealth,
+ LogProvider internalLogProvider,
+ LogProvider userLogProvider,
+ RecoveryCleanupWorkCollector recoveryCleanupWorkCollector,
+ PageCacheTracer cacheTracer,
+ boolean createStoreIfNotExists,
+ DatabaseReadOnlyChecker readOnlyChecker,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public List listStorageFiles(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout) throws
+ IOException {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public boolean storageExists(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout, PageCache pageCache) {
+ return false;
+ }
+
+ @Override
+ public TransactionIdStore readOnlyTransactionIdStore(
+ FileSystemAbstraction filySystem,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public LogVersionRepository readOnlyLogVersionRepository(
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public MetadataProvider transactionMetaDataStore(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ PageCacheTracer cacheTracer,
+ DatabaseReadOnlyChecker readOnlyChecker
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreId storeId(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public void setStoreId(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext,
+ StoreId storeId,
+ long upgradeTxChecksum,
+ long upgradeTxCommitTimestamp
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public void setExternalStoreUUID(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext,
+ UUID externalStoreId
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public Optional databaseIdUuid(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public SchemaRuleMigrationAccess schemaRuleMigrationAccess(
+ FileSystemAbstraction fs,
+ PageCache pageCache,
+ Config config,
+ DatabaseLayout databaseLayout,
+ LogService logService,
+ String recordFormats,
+ PageCacheTracer cacheTracer,
+ CursorContext cursorContext,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public List loadSchemaRules(
+ FileSystemAbstraction fs,
+ PageCache pageCache,
+ Config config,
+ DatabaseLayout databaseLayout,
+ CursorContext cursorContext
+ ) {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public StorageFilesState checkStoreFileState(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache
+ ) {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public CommandReaderFactory commandReaderFactory() {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+
+ @Override
+ public DatabaseLayout databaseLayout(Neo4jLayout neo4jLayout, String databaseName) {
+ throw new UnsupportedOperationException("5.1 storage engine requires JDK17");
+ }
+}
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_51/StorageEngineProxyFactoryImpl.java b/compatibility/5.1/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_51/StorageEngineProxyFactoryImpl.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_51/StorageEngineProxyFactoryImpl.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_51/StorageEngineProxyFactoryImpl.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryCommandCreationContextImpl.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryCommandCreationContextImpl.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryCommandCreationContextImpl.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryCommandCreationContextImpl.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryCountsStoreImpl.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryCountsStoreImpl.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryCountsStoreImpl.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryCountsStoreImpl.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryMetaDataProviderImpl.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryMetaDataProviderImpl.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryMetaDataProviderImpl.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryMetaDataProviderImpl.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryNodeCursor.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryNodeCursor.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryNodeCursor.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryNodeCursor.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryNodePropertyCursor.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryNodePropertyCursor.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryNodePropertyCursor.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryNodePropertyCursor.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryPropertyCursor.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryPropertyCursor.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryPropertyCursor.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryPropertyCursor.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryPropertySelectionImpl.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryPropertySelectionImpl.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryPropertySelectionImpl.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryPropertySelectionImpl.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryRelationshipPropertyCursor.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryRelationshipPropertyCursor.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryRelationshipPropertyCursor.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryRelationshipPropertyCursor.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryRelationshipScanCursor.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryRelationshipScanCursor.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryRelationshipScanCursor.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryRelationshipScanCursor.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryRelationshipTraversalCursor.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryRelationshipTraversalCursor.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryRelationshipTraversalCursor.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryRelationshipTraversalCursor.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStorageEngineFactory.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStorageEngineFactory.java
similarity index 98%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStorageEngineFactory.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStorageEngineFactory.java
index bdbf2a266d4..14dd53ef3fe 100644
--- a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStorageEngineFactory.java
+++ b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStorageEngineFactory.java
@@ -27,6 +27,8 @@
import org.neo4j.consistency.report.ConsistencySummaryStatistics;
import org.neo4j.dbms.database.readonly.DatabaseReadOnlyChecker;
import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.StorageEngineProxyApi;
import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector;
import org.neo4j.internal.batchimport.AdditionalInitialIds;
import org.neo4j.internal.batchimport.BatchImporter;
@@ -113,6 +115,10 @@ public class InMemoryStorageEngineFactory implements StorageEngineFactory {
static final String IN_MEMORY_STORAGE_ENGINE_NAME = "in-memory-51";
+ public InMemoryStorageEngineFactory() {
+ StorageEngineProxyApi.requireNeo4jVersion(Neo4jVersion.V_5_1, StorageEngineFactory.class);
+ }
+
private final InMemoryMetaDataProviderImpl metadataProvider = new InMemoryMetaDataProviderImpl();
@Override
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStorageEngineImpl.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStorageEngineImpl.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStorageEngineImpl.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStorageEngineImpl.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStorageLocksImpl.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStorageLocksImpl.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStorageLocksImpl.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStorageLocksImpl.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStoreVersion.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStoreVersion.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStoreVersion.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryStoreVersion.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryTransactionIdStoreImpl.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryTransactionIdStoreImpl.java
similarity index 74%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryTransactionIdStoreImpl.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryTransactionIdStoreImpl.java
index 0ff4ec50d7c..f370df22453 100644
--- a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryTransactionIdStoreImpl.java
+++ b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryTransactionIdStoreImpl.java
@@ -20,9 +20,9 @@
package org.neo4j.gds.compat._51;
import org.neo4j.internal.recordstorage.AbstractTransactionIdStore;
-import org.neo4j.io.pagecache.context.CursorContext;
import org.neo4j.kernel.impl.transaction.log.LogPosition;
import org.neo4j.storageengine.api.ClosedTransactionMetadata;
+import org.neo4j.storageengine.api.TransactionId;
public class InMemoryTransactionIdStoreImpl extends AbstractTransactionIdStore {
@@ -36,6 +36,23 @@ public ClosedTransactionMetadata getLastClosedTransaction() {
);
}
+ @Override
+ protected void initLastCommittedAndClosedTransactionId(
+ long previouslyCommittedTxId,
+ int checksum,
+ long previouslyCommittedTxCommitTimestamp,
+ long previouslyCommittedTxLogByteOffset,
+ long previouslyCommittedTxLogVersion
+ ) {
+ this.setLastCommittedAndClosedTransactionId(
+ previouslyCommittedTxId,
+ checksum,
+ previouslyCommittedTxCommitTimestamp,
+ previouslyCommittedTxLogByteOffset,
+ previouslyCommittedTxLogVersion
+ );
+ }
+
@Override
public void transactionClosed(
long transactionId,
@@ -69,7 +86,7 @@ public void setLastCommittedAndClosedTransactionId(
}
@Override
- protected CursorContext getEmptyCursorContext() {
- return CursorContext.NULL_CONTEXT;
+ protected TransactionId transactionId(long transactionId, int checksum, long commitTimestamp) {
+ return new TransactionId(transactionId, checksum, commitTimestamp);
}
}
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryVersionCheck.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryVersionCheck.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryVersionCheck.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/InMemoryVersionCheck.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/StorageEngineProxyFactoryImpl.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/StorageEngineProxyFactoryImpl.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/StorageEngineProxyFactoryImpl.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/StorageEngineProxyFactoryImpl.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/StorageEngineProxyImpl.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/StorageEngineProxyImpl.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/StorageEngineProxyImpl.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_51/StorageEngineProxyImpl.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository51.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository51.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository51.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository51.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory51.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory51.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory51.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory51.java
diff --git a/cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader51.java b/compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader51.java
similarity index 100%
rename from cypher/5.1/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader51.java
rename to compatibility/5.1/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader51.java
diff --git a/compatibility/5.2/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_52/Neo4jProxyImpl.java b/compatibility/5.2/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_52/Neo4jProxyImpl.java
index 2d151a763bd..18178a08351 100644
--- a/compatibility/5.2/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_52/Neo4jProxyImpl.java
+++ b/compatibility/5.2/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_52/Neo4jProxyImpl.java
@@ -31,6 +31,7 @@
import org.neo4j.configuration.helpers.DatabaseNameValidator;
import org.neo4j.dbms.api.DatabaseManagementService;
import org.neo4j.exceptions.KernelException;
+import org.neo4j.fabric.FabricDatabaseManager;
import org.neo4j.gds.annotation.SuppressForbidden;
import org.neo4j.gds.compat.BoltTransactionRunner;
import org.neo4j.gds.compat.CompatCallableProcedure;
@@ -62,7 +63,6 @@
import org.neo4j.internal.batchimport.InputIterable;
import org.neo4j.internal.batchimport.Monitor;
import org.neo4j.internal.batchimport.input.Collector;
-import org.neo4j.internal.batchimport.input.Group;
import org.neo4j.internal.batchimport.input.IdType;
import org.neo4j.internal.batchimport.input.Input;
import org.neo4j.internal.batchimport.input.InputEntityVisitor;
@@ -116,7 +116,10 @@
import org.neo4j.kernel.database.NamedDatabaseId;
import org.neo4j.kernel.database.NormalizedDatabaseName;
import org.neo4j.kernel.database.TestDatabaseIdRepository;
+import org.neo4j.kernel.impl.coreapi.InternalTransaction;
import org.neo4j.kernel.impl.index.schema.IndexImporterFactoryImpl;
+import org.neo4j.kernel.impl.query.TransactionalContext;
+import org.neo4j.kernel.impl.query.TransactionalContextFactory;
import org.neo4j.kernel.impl.store.RecordStore;
import org.neo4j.kernel.impl.store.format.RecordFormatSelector;
import org.neo4j.kernel.impl.store.format.RecordFormats;
@@ -134,6 +137,7 @@
import org.neo4j.util.Bits;
import org.neo4j.values.storable.ValueCategory;
import org.neo4j.values.storable.Values;
+import org.neo4j.values.virtual.MapValue;
import java.io.IOException;
import java.nio.file.Path;
@@ -198,6 +202,11 @@ public long getHighestPossibleIdInUse(
return recordStore.getHighestPossibleIdInUse(kernelTransaction.cursorContext());
}
+ @Override
+ public long getHighId(RecordStore extends AbstractBaseRecord> recordStore) {
+ return recordStore.getHighId();
+ }
+
@Override
public List> entityCursorScan(
KernelTransaction transaction,
@@ -507,7 +516,7 @@ public Input batchInputFrom(CompatInput compatInput) {
}
@Override
- public InputEntityIdVisitor.Long inputEntityLongIdVisitor(IdType idType) {
+ public InputEntityIdVisitor.Long inputEntityLongIdVisitor(IdType idType, ReadableGroups groups) {
switch (idType) {
case ACTUAL -> {
return new InputEntityIdVisitor.Long() {
@@ -528,7 +537,7 @@ public void visitTargetId(InputEntityVisitor visitor, long id) {
};
}
case INTEGER -> {
- var globalGroup = new Group(0, null, null);
+ var globalGroup = groups.get(null);
return new InputEntityIdVisitor.Long() {
@Override
@@ -552,8 +561,8 @@ public void visitTargetId(InputEntityVisitor visitor, long id) {
}
@Override
- public InputEntityIdVisitor.String inputEntityStringIdVisitor() {
- var globalGroup = new Group(0, null, null);
+ public InputEntityIdVisitor.String inputEntityStringIdVisitor(ReadableGroups groups) {
+ var globalGroup = groups.get(null);
return new InputEntityIdVisitor.String() {
@Override
@@ -906,5 +915,19 @@ public void reserveNeo4jIds(IdGeneratorFactory generatorFactory, int size, Curso
idGenerator.nextConsecutiveIdRange(size, false, cursorContext);
}
+ @Override
+ public TransactionalContext newQueryContext(
+ TransactionalContextFactory contextFactory,
+ InternalTransaction tx,
+ String queryText,
+ MapValue queryParameters
+ ) {
+ return contextFactory.newContext(tx, queryText, queryParameters);
+ }
+ @Override
+ public boolean isCompositeDatabase(GraphDatabaseService databaseService) {
+ var databaseManager = GraphDatabaseApiProxy.resolveDependency(databaseService, FabricDatabaseManager.class);
+ return databaseManager.isFabricDatabase(GraphDatabaseApiProxy.databaseId(databaseService));
+ }
}
diff --git a/compatibility/5.2/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_52/SettingProxyImpl.java b/compatibility/5.2/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_52/SettingProxyImpl.java
index ef92ef88db5..7dd4139a1e1 100644
--- a/compatibility/5.2/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_52/SettingProxyImpl.java
+++ b/compatibility/5.2/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_52/SettingProxyImpl.java
@@ -79,4 +79,9 @@ public void setDatabaseMode(Config config, DatabaseMode databaseMode, GraphDatab
throw new RuntimeException("Could not get the permissions to set the mode field.", e);
}
}
+
+ @Override
+ public String secondaryModeName() {
+ return "Secondary";
+ }
}
diff --git a/cypher/5.2/storage-engine-adapter/build.gradle b/compatibility/5.2/storage-engine-adapter/build.gradle
similarity index 96%
rename from cypher/5.2/storage-engine-adapter/build.gradle
rename to compatibility/5.2/storage-engine-adapter/build.gradle
index 8af17934fc0..0bc2a320eba 100644
--- a/cypher/5.2/storage-engine-adapter/build.gradle
+++ b/compatibility/5.2/storage-engine-adapter/build.gradle
@@ -44,6 +44,7 @@ if (ver.'neo4j'.startsWith('5.')) {
dependencies {
annotationProcessor group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
compileOnly group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
+ compileOnly group: 'org.neo4j', name: 'neo4j-kernel-api', version: ver.'neo4j'
implementation project(':neo4j-adapter')
implementation project(':storage-engine-adapter-api')
diff --git a/compatibility/5.2/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_52/InMemoryStorageEngineFactory.java b/compatibility/5.2/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_52/InMemoryStorageEngineFactory.java
new file mode 100644
index 00000000000..499d70a28a4
--- /dev/null
+++ b/compatibility/5.2/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_52/InMemoryStorageEngineFactory.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._52;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.configuration.Config;
+import org.neo4j.dbms.database.readonly.DatabaseReadOnlyChecker;
+import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector;
+import org.neo4j.internal.id.IdController;
+import org.neo4j.internal.id.IdGeneratorFactory;
+import org.neo4j.internal.schema.IndexConfigCompleter;
+import org.neo4j.internal.schema.SchemaRule;
+import org.neo4j.internal.schema.SchemaState;
+import org.neo4j.io.fs.FileSystemAbstraction;
+import org.neo4j.io.layout.DatabaseLayout;
+import org.neo4j.io.layout.Neo4jLayout;
+import org.neo4j.io.pagecache.PageCache;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.io.pagecache.tracing.PageCacheTracer;
+import org.neo4j.lock.LockService;
+import org.neo4j.logging.LogProvider;
+import org.neo4j.logging.internal.LogService;
+import org.neo4j.memory.MemoryTracker;
+import org.neo4j.monitoring.DatabaseHealth;
+import org.neo4j.scheduler.JobScheduler;
+import org.neo4j.storageengine.api.CommandReaderFactory;
+import org.neo4j.storageengine.api.ConstraintRuleAccessor;
+import org.neo4j.storageengine.api.LogVersionRepository;
+import org.neo4j.storageengine.api.MetadataProvider;
+import org.neo4j.storageengine.api.StorageEngine;
+import org.neo4j.storageengine.api.StorageEngineFactory;
+import org.neo4j.storageengine.api.StorageFilesState;
+import org.neo4j.storageengine.api.StoreId;
+import org.neo4j.storageengine.api.StoreVersion;
+import org.neo4j.storageengine.api.StoreVersionCheck;
+import org.neo4j.storageengine.api.TransactionIdStore;
+import org.neo4j.storageengine.migration.RollingUpgradeCompatibility;
+import org.neo4j.storageengine.migration.SchemaRuleMigrationAccess;
+import org.neo4j.storageengine.migration.StoreMigrationParticipant;
+import org.neo4j.token.TokenHolders;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@ServiceProvider
+public class InMemoryStorageEngineFactory implements StorageEngineFactory {
+
+ @Override
+ public String name() {
+ return "unsupported52";
+ }
+
+ @Override
+ public StoreVersionCheck versionCheck(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ LogService logService,
+ PageCacheTracer pageCacheTracer
+ ) {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreVersion versionInformation(String storeVersion) {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreVersion versionInformation(StoreId storeId) {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public RollingUpgradeCompatibility rollingUpgradeCompatibility() {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public List migrationParticipants(
+ FileSystemAbstraction fs,
+ Config config,
+ PageCache pageCache,
+ JobScheduler jobScheduler,
+ LogService logService,
+ PageCacheTracer cacheTracer,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public StorageEngine instantiate(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ TokenHolders tokenHolders,
+ SchemaState schemaState,
+ ConstraintRuleAccessor constraintSemantics,
+ IndexConfigCompleter indexConfigCompleter,
+ LockService lockService,
+ IdGeneratorFactory idGeneratorFactory,
+ IdController idController,
+ DatabaseHealth databaseHealth,
+ LogProvider internalLogProvider,
+ LogProvider userLogProvider,
+ RecoveryCleanupWorkCollector recoveryCleanupWorkCollector,
+ PageCacheTracer cacheTracer,
+ boolean createStoreIfNotExists,
+ DatabaseReadOnlyChecker readOnlyChecker,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public List listStorageFiles(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout) throws
+ IOException {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public boolean storageExists(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout, PageCache pageCache) {
+ return false;
+ }
+
+ @Override
+ public TransactionIdStore readOnlyTransactionIdStore(
+ FileSystemAbstraction filySystem,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public LogVersionRepository readOnlyLogVersionRepository(
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public MetadataProvider transactionMetaDataStore(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ PageCacheTracer cacheTracer,
+ DatabaseReadOnlyChecker readOnlyChecker
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreId storeId(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public void setStoreId(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext,
+ StoreId storeId,
+ long upgradeTxChecksum,
+ long upgradeTxCommitTimestamp
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public void setExternalStoreUUID(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext,
+ UUID externalStoreId
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public Optional databaseIdUuid(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public SchemaRuleMigrationAccess schemaRuleMigrationAccess(
+ FileSystemAbstraction fs,
+ PageCache pageCache,
+ Config config,
+ DatabaseLayout databaseLayout,
+ LogService logService,
+ String recordFormats,
+ PageCacheTracer cacheTracer,
+ CursorContext cursorContext,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public List loadSchemaRules(
+ FileSystemAbstraction fs,
+ PageCache pageCache,
+ Config config,
+ DatabaseLayout databaseLayout,
+ CursorContext cursorContext
+ ) {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public StorageFilesState checkStoreFileState(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache
+ ) {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public CommandReaderFactory commandReaderFactory() {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+
+ @Override
+ public DatabaseLayout databaseLayout(Neo4jLayout neo4jLayout, String databaseName) {
+ throw new UnsupportedOperationException("5.2 storage engine requires JDK17");
+ }
+}
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_52/StorageEngineProxyFactoryImpl.java b/compatibility/5.2/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_52/StorageEngineProxyFactoryImpl.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_52/StorageEngineProxyFactoryImpl.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_52/StorageEngineProxyFactoryImpl.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryCommandCreationContextImpl.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryCommandCreationContextImpl.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryCommandCreationContextImpl.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryCommandCreationContextImpl.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryCountsStoreImpl.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryCountsStoreImpl.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryCountsStoreImpl.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryCountsStoreImpl.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryMetaDataProviderImpl.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryMetaDataProviderImpl.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryMetaDataProviderImpl.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryMetaDataProviderImpl.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryNodeCursor.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryNodeCursor.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryNodeCursor.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryNodeCursor.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryNodePropertyCursor.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryNodePropertyCursor.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryNodePropertyCursor.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryNodePropertyCursor.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryPropertyCursor.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryPropertyCursor.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryPropertyCursor.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryPropertyCursor.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryPropertySelectionImpl.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryPropertySelectionImpl.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryPropertySelectionImpl.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryPropertySelectionImpl.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryRelationshipPropertyCursor.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryRelationshipPropertyCursor.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryRelationshipPropertyCursor.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryRelationshipPropertyCursor.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryRelationshipScanCursor.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryRelationshipScanCursor.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryRelationshipScanCursor.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryRelationshipScanCursor.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryRelationshipTraversalCursor.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryRelationshipTraversalCursor.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryRelationshipTraversalCursor.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryRelationshipTraversalCursor.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStorageEngineFactory.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStorageEngineFactory.java
similarity index 98%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStorageEngineFactory.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStorageEngineFactory.java
index 4c3fa12ad27..04a977b6d07 100644
--- a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStorageEngineFactory.java
+++ b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStorageEngineFactory.java
@@ -27,6 +27,8 @@
import org.neo4j.consistency.report.ConsistencySummaryStatistics;
import org.neo4j.dbms.database.readonly.DatabaseReadOnlyChecker;
import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.StorageEngineProxyApi;
import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector;
import org.neo4j.internal.batchimport.AdditionalInitialIds;
import org.neo4j.internal.batchimport.BatchImporter;
@@ -113,6 +115,10 @@ public class InMemoryStorageEngineFactory implements StorageEngineFactory {
static final String IN_MEMORY_STORAGE_ENGINE_NAME = "in-memory-52";
+ public InMemoryStorageEngineFactory() {
+ StorageEngineProxyApi.requireNeo4jVersion(Neo4jVersion.V_5_2, StorageEngineFactory.class);
+ }
+
private final InMemoryMetaDataProviderImpl metadataProvider = new InMemoryMetaDataProviderImpl();
@Override
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStorageEngineImpl.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStorageEngineImpl.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStorageEngineImpl.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStorageEngineImpl.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStorageLocksImpl.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStorageLocksImpl.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStorageLocksImpl.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStorageLocksImpl.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStoreVersion.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStoreVersion.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStoreVersion.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryStoreVersion.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryTransactionIdStoreImpl.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryTransactionIdStoreImpl.java
similarity index 74%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryTransactionIdStoreImpl.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryTransactionIdStoreImpl.java
index 4932d715975..ea4880044c3 100644
--- a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryTransactionIdStoreImpl.java
+++ b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryTransactionIdStoreImpl.java
@@ -20,12 +20,30 @@
package org.neo4j.gds.compat._52;
import org.neo4j.internal.recordstorage.AbstractTransactionIdStore;
-import org.neo4j.io.pagecache.context.CursorContext;
import org.neo4j.kernel.impl.transaction.log.LogPosition;
import org.neo4j.storageengine.api.ClosedTransactionMetadata;
+import org.neo4j.storageengine.api.TransactionId;
public class InMemoryTransactionIdStoreImpl extends AbstractTransactionIdStore {
+ @Override
+ protected void initLastCommittedAndClosedTransactionId(
+ long previouslyCommittedTxId,
+ int checksum,
+ long previouslyCommittedTxCommitTimestamp,
+ long previouslyCommittedTxLogByteOffset,
+ long previouslyCommittedTxLogVersion
+ ) {
+ this.setLastCommittedAndClosedTransactionId(
+ previouslyCommittedTxId,
+ checksum,
+ previouslyCommittedTxCommitTimestamp,
+ previouslyCommittedTxLogByteOffset,
+ previouslyCommittedTxLogVersion
+ );
+ }
+
+ @Override
public ClosedTransactionMetadata getLastClosedTransaction() {
long[] metaData = this.closedTransactionId.get();
return new ClosedTransactionMetadata(
@@ -69,7 +87,7 @@ public void setLastCommittedAndClosedTransactionId(
}
@Override
- protected CursorContext getEmptyCursorContext() {
- return CursorContext.NULL_CONTEXT;
+ protected TransactionId transactionId(long transactionId, int checksum, long commitTimestamp) {
+ return new TransactionId(transactionId, checksum, commitTimestamp);
}
}
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryVersionCheck.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryVersionCheck.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryVersionCheck.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/InMemoryVersionCheck.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/StorageEngineProxyFactoryImpl.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/StorageEngineProxyFactoryImpl.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/StorageEngineProxyFactoryImpl.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/StorageEngineProxyFactoryImpl.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/StorageEngineProxyImpl.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/StorageEngineProxyImpl.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/StorageEngineProxyImpl.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_52/StorageEngineProxyImpl.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository52.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository52.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository52.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository52.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory52.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory52.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory52.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory52.java
diff --git a/cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader52.java b/compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader52.java
similarity index 100%
rename from cypher/5.2/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader52.java
rename to compatibility/5.2/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader52.java
diff --git a/compatibility/5.3/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_53/Neo4jProxyImpl.java b/compatibility/5.3/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_53/Neo4jProxyImpl.java
index 49579510ce0..22542193d48 100644
--- a/compatibility/5.3/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_53/Neo4jProxyImpl.java
+++ b/compatibility/5.3/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_53/Neo4jProxyImpl.java
@@ -31,6 +31,7 @@
import org.neo4j.configuration.helpers.DatabaseNameValidator;
import org.neo4j.dbms.api.DatabaseManagementService;
import org.neo4j.exceptions.KernelException;
+import org.neo4j.fabric.FabricDatabaseManager;
import org.neo4j.gds.annotation.SuppressForbidden;
import org.neo4j.gds.compat.BoltTransactionRunner;
import org.neo4j.gds.compat.CompatCallableProcedure;
@@ -62,7 +63,6 @@
import org.neo4j.internal.batchimport.InputIterable;
import org.neo4j.internal.batchimport.Monitor;
import org.neo4j.internal.batchimport.input.Collector;
-import org.neo4j.internal.batchimport.input.Group;
import org.neo4j.internal.batchimport.input.IdType;
import org.neo4j.internal.batchimport.input.Input;
import org.neo4j.internal.batchimport.input.InputEntityVisitor;
@@ -116,7 +116,10 @@
import org.neo4j.kernel.database.NamedDatabaseId;
import org.neo4j.kernel.database.NormalizedDatabaseName;
import org.neo4j.kernel.database.TestDatabaseIdRepository;
+import org.neo4j.kernel.impl.coreapi.InternalTransaction;
import org.neo4j.kernel.impl.index.schema.IndexImporterFactoryImpl;
+import org.neo4j.kernel.impl.query.TransactionalContext;
+import org.neo4j.kernel.impl.query.TransactionalContextFactory;
import org.neo4j.kernel.impl.store.RecordStore;
import org.neo4j.kernel.impl.store.format.RecordFormatSelector;
import org.neo4j.kernel.impl.store.format.RecordFormats;
@@ -134,6 +137,7 @@
import org.neo4j.util.Bits;
import org.neo4j.values.storable.ValueCategory;
import org.neo4j.values.storable.Values;
+import org.neo4j.values.virtual.MapValue;
import java.io.IOException;
import java.nio.file.Path;
@@ -198,6 +202,11 @@ public long getHighestPossibleIdInUse(
return recordStore.getHighestPossibleIdInUse(kernelTransaction.cursorContext());
}
+ @Override
+ public long getHighId(RecordStore extends AbstractBaseRecord> recordStore) {
+ return recordStore.getHighId();
+ }
+
@Override
public List> entityCursorScan(
KernelTransaction transaction,
@@ -507,7 +516,7 @@ public Input batchInputFrom(CompatInput compatInput) {
}
@Override
- public InputEntityIdVisitor.Long inputEntityLongIdVisitor(IdType idType) {
+ public InputEntityIdVisitor.Long inputEntityLongIdVisitor(IdType idType, ReadableGroups groups) {
switch (idType) {
case ACTUAL -> {
return new InputEntityIdVisitor.Long() {
@@ -528,7 +537,7 @@ public void visitTargetId(InputEntityVisitor visitor, long id) {
};
}
case INTEGER -> {
- var globalGroup = new Group(0, null, null);
+ var globalGroup = groups.get(null);
return new InputEntityIdVisitor.Long() {
@Override
@@ -552,8 +561,8 @@ public void visitTargetId(InputEntityVisitor visitor, long id) {
}
@Override
- public InputEntityIdVisitor.String inputEntityStringIdVisitor() {
- var globalGroup = new Group(0, null, null);
+ public InputEntityIdVisitor.String inputEntityStringIdVisitor(ReadableGroups groups) {
+ var globalGroup = groups.get(null);
return new InputEntityIdVisitor.String() {
@Override
@@ -907,4 +916,19 @@ public void reserveNeo4jIds(IdGeneratorFactory generatorFactory, int size, Curso
idGenerator.nextConsecutiveIdRange(size, false, cursorContext);
}
+ @Override
+ public TransactionalContext newQueryContext(
+ TransactionalContextFactory contextFactory,
+ InternalTransaction tx,
+ String queryText,
+ MapValue queryParameters
+ ) {
+ return contextFactory.newContext(tx, queryText, queryParameters);
+ }
+
+ @Override
+ public boolean isCompositeDatabase(GraphDatabaseService databaseService) {
+ var databaseManager = GraphDatabaseApiProxy.resolveDependency(databaseService, FabricDatabaseManager.class);
+ return databaseManager.isFabricDatabase(GraphDatabaseApiProxy.databaseId(databaseService));
+ }
}
diff --git a/compatibility/5.3/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_53/SettingProxyImpl.java b/compatibility/5.3/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_53/SettingProxyImpl.java
index abdaa7629e9..d1c06a9af61 100644
--- a/compatibility/5.3/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_53/SettingProxyImpl.java
+++ b/compatibility/5.3/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_53/SettingProxyImpl.java
@@ -79,4 +79,9 @@ public void setDatabaseMode(Config config, DatabaseMode databaseMode, GraphDatab
throw new RuntimeException("Could not get the permissions to set the mode field.", e);
}
}
+
+ @Override
+ public String secondaryModeName() {
+ return "Secondary";
+ }
}
diff --git a/cypher/5.3/storage-engine-adapter/build.gradle b/compatibility/5.3/storage-engine-adapter/build.gradle
similarity index 96%
rename from cypher/5.3/storage-engine-adapter/build.gradle
rename to compatibility/5.3/storage-engine-adapter/build.gradle
index 805b935c705..6db56beaaba 100644
--- a/cypher/5.3/storage-engine-adapter/build.gradle
+++ b/compatibility/5.3/storage-engine-adapter/build.gradle
@@ -44,6 +44,7 @@ if (ver.'neo4j'.startsWith('5.')) {
dependencies {
annotationProcessor group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
compileOnly group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
+ compileOnly group: 'org.neo4j', name: 'neo4j-kernel-api', version: ver.'neo4j'
implementation project(':neo4j-adapter')
implementation project(':storage-engine-adapter-api')
diff --git a/compatibility/5.3/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_53/InMemoryStorageEngineFactory.java b/compatibility/5.3/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_53/InMemoryStorageEngineFactory.java
new file mode 100644
index 00000000000..eada03549ce
--- /dev/null
+++ b/compatibility/5.3/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_53/InMemoryStorageEngineFactory.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._53;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.configuration.Config;
+import org.neo4j.dbms.database.readonly.DatabaseReadOnlyChecker;
+import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector;
+import org.neo4j.internal.id.IdController;
+import org.neo4j.internal.id.IdGeneratorFactory;
+import org.neo4j.internal.schema.IndexConfigCompleter;
+import org.neo4j.internal.schema.SchemaRule;
+import org.neo4j.internal.schema.SchemaState;
+import org.neo4j.io.fs.FileSystemAbstraction;
+import org.neo4j.io.layout.DatabaseLayout;
+import org.neo4j.io.layout.Neo4jLayout;
+import org.neo4j.io.pagecache.PageCache;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.io.pagecache.tracing.PageCacheTracer;
+import org.neo4j.lock.LockService;
+import org.neo4j.logging.LogProvider;
+import org.neo4j.logging.internal.LogService;
+import org.neo4j.memory.MemoryTracker;
+import org.neo4j.monitoring.DatabaseHealth;
+import org.neo4j.scheduler.JobScheduler;
+import org.neo4j.storageengine.api.CommandReaderFactory;
+import org.neo4j.storageengine.api.ConstraintRuleAccessor;
+import org.neo4j.storageengine.api.LogVersionRepository;
+import org.neo4j.storageengine.api.MetadataProvider;
+import org.neo4j.storageengine.api.StorageEngine;
+import org.neo4j.storageengine.api.StorageEngineFactory;
+import org.neo4j.storageengine.api.StorageFilesState;
+import org.neo4j.storageengine.api.StoreId;
+import org.neo4j.storageengine.api.StoreVersion;
+import org.neo4j.storageengine.api.StoreVersionCheck;
+import org.neo4j.storageengine.api.TransactionIdStore;
+import org.neo4j.storageengine.migration.RollingUpgradeCompatibility;
+import org.neo4j.storageengine.migration.SchemaRuleMigrationAccess;
+import org.neo4j.storageengine.migration.StoreMigrationParticipant;
+import org.neo4j.token.TokenHolders;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@ServiceProvider
+public class InMemoryStorageEngineFactory implements StorageEngineFactory {
+
+ @Override
+ public String name() {
+ return "unsupported53";
+ }
+
+ @Override
+ public StoreVersionCheck versionCheck(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ LogService logService,
+ PageCacheTracer pageCacheTracer
+ ) {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreVersion versionInformation(String storeVersion) {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreVersion versionInformation(StoreId storeId) {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public RollingUpgradeCompatibility rollingUpgradeCompatibility() {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public List migrationParticipants(
+ FileSystemAbstraction fs,
+ Config config,
+ PageCache pageCache,
+ JobScheduler jobScheduler,
+ LogService logService,
+ PageCacheTracer cacheTracer,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public StorageEngine instantiate(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ TokenHolders tokenHolders,
+ SchemaState schemaState,
+ ConstraintRuleAccessor constraintSemantics,
+ IndexConfigCompleter indexConfigCompleter,
+ LockService lockService,
+ IdGeneratorFactory idGeneratorFactory,
+ IdController idController,
+ DatabaseHealth databaseHealth,
+ LogProvider internalLogProvider,
+ LogProvider userLogProvider,
+ RecoveryCleanupWorkCollector recoveryCleanupWorkCollector,
+ PageCacheTracer cacheTracer,
+ boolean createStoreIfNotExists,
+ DatabaseReadOnlyChecker readOnlyChecker,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public List listStorageFiles(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout) throws
+ IOException {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public boolean storageExists(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout, PageCache pageCache) {
+ return false;
+ }
+
+ @Override
+ public TransactionIdStore readOnlyTransactionIdStore(
+ FileSystemAbstraction filySystem,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public LogVersionRepository readOnlyLogVersionRepository(
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public MetadataProvider transactionMetaDataStore(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ PageCacheTracer cacheTracer,
+ DatabaseReadOnlyChecker readOnlyChecker
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreId storeId(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public void setStoreId(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext,
+ StoreId storeId,
+ long upgradeTxChecksum,
+ long upgradeTxCommitTimestamp
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public void setExternalStoreUUID(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext,
+ UUID externalStoreId
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public Optional databaseIdUuid(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public SchemaRuleMigrationAccess schemaRuleMigrationAccess(
+ FileSystemAbstraction fs,
+ PageCache pageCache,
+ Config config,
+ DatabaseLayout databaseLayout,
+ LogService logService,
+ String recordFormats,
+ PageCacheTracer cacheTracer,
+ CursorContext cursorContext,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public List loadSchemaRules(
+ FileSystemAbstraction fs,
+ PageCache pageCache,
+ Config config,
+ DatabaseLayout databaseLayout,
+ CursorContext cursorContext
+ ) {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public StorageFilesState checkStoreFileState(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache
+ ) {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public CommandReaderFactory commandReaderFactory() {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+
+ @Override
+ public DatabaseLayout databaseLayout(Neo4jLayout neo4jLayout, String databaseName) {
+ throw new UnsupportedOperationException("5.3 storage engine requires JDK17");
+ }
+}
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_53/StorageEngineProxyFactoryImpl.java b/compatibility/5.3/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_53/StorageEngineProxyFactoryImpl.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_53/StorageEngineProxyFactoryImpl.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_53/StorageEngineProxyFactoryImpl.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryCommandCreationContextImpl.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryCommandCreationContextImpl.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryCommandCreationContextImpl.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryCommandCreationContextImpl.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryCountsStoreImpl.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryCountsStoreImpl.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryCountsStoreImpl.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryCountsStoreImpl.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryMetaDataProviderImpl.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryMetaDataProviderImpl.java
similarity index 98%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryMetaDataProviderImpl.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryMetaDataProviderImpl.java
index 8a60b24501b..cc688fa63ff 100644
--- a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryMetaDataProviderImpl.java
+++ b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryMetaDataProviderImpl.java
@@ -19,7 +19,7 @@
*/
package org.neo4j.gds.compat._53;
-import org.neo4j.internal.recordstorage.InMemoryLogVersionRepository;
+import org.neo4j.internal.recordstorage.InMemoryLogVersionRepository53;
import org.neo4j.io.pagecache.context.CursorContext;
import org.neo4j.kernel.KernelVersion;
import org.neo4j.storageengine.api.ClosedTransactionMetadata;
@@ -36,11 +36,11 @@
public class InMemoryMetaDataProviderImpl implements MetadataProvider {
private final ExternalStoreId externalStoreId;
- private final InMemoryLogVersionRepository logVersionRepository;
+ private final InMemoryLogVersionRepository53 logVersionRepository;
private final InMemoryTransactionIdStoreImpl transactionIdStore;
InMemoryMetaDataProviderImpl() {
- this.logVersionRepository = new InMemoryLogVersionRepository();
+ this.logVersionRepository = new InMemoryLogVersionRepository53();
this.externalStoreId = new ExternalStoreId(UUID.randomUUID());
this.transactionIdStore = new InMemoryTransactionIdStoreImpl();
}
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryNodeCursor.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryNodeCursor.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryNodeCursor.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryNodeCursor.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryNodePropertyCursor.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryNodePropertyCursor.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryNodePropertyCursor.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryNodePropertyCursor.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryPropertyCursor.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryPropertyCursor.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryPropertyCursor.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryPropertyCursor.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryPropertySelectionImpl.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryPropertySelectionImpl.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryPropertySelectionImpl.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryPropertySelectionImpl.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryRelationshipPropertyCursor.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryRelationshipPropertyCursor.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryRelationshipPropertyCursor.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryRelationshipPropertyCursor.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryRelationshipScanCursor.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryRelationshipScanCursor.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryRelationshipScanCursor.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryRelationshipScanCursor.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryRelationshipTraversalCursor.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryRelationshipTraversalCursor.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryRelationshipTraversalCursor.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryRelationshipTraversalCursor.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStorageEngineFactory.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStorageEngineFactory.java
similarity index 98%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStorageEngineFactory.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStorageEngineFactory.java
index 696ee516c8c..6d57e38525f 100644
--- a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStorageEngineFactory.java
+++ b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStorageEngineFactory.java
@@ -28,6 +28,8 @@
import org.neo4j.dbms.database.readonly.DatabaseReadOnlyChecker;
import org.neo4j.function.ThrowingSupplier;
import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.StorageEngineProxyApi;
import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector;
import org.neo4j.internal.batchimport.AdditionalInitialIds;
import org.neo4j.internal.batchimport.BatchImporter;
@@ -41,8 +43,8 @@
import org.neo4j.internal.batchimport.input.LenientStoreInput;
import org.neo4j.internal.id.IdGeneratorFactory;
import org.neo4j.internal.id.ScanOnOpenReadOnlyIdGeneratorFactory;
-import org.neo4j.internal.recordstorage.InMemoryLogVersionRepository;
-import org.neo4j.internal.recordstorage.InMemoryStorageCommandReaderFactory;
+import org.neo4j.internal.recordstorage.InMemoryLogVersionRepository53;
+import org.neo4j.internal.recordstorage.InMemoryStorageCommandReaderFactory53;
import org.neo4j.internal.recordstorage.StoreTokens;
import org.neo4j.internal.schema.IndexConfigCompleter;
import org.neo4j.internal.schema.SchemaRule;
@@ -112,6 +114,10 @@
@ServiceProvider
public class InMemoryStorageEngineFactory implements StorageEngineFactory {
+ public InMemoryStorageEngineFactory() {
+ StorageEngineProxyApi.requireNeo4jVersion(Neo4jVersion.V_5_3, StorageEngineFactory.class);
+ }
+
static final String IN_MEMORY_STORAGE_ENGINE_NAME = "in-memory-53";
@Override
@@ -429,7 +435,7 @@ private static List unique( List tokens )
@Override
public CommandReaderFactory commandReaderFactory() {
- return InMemoryStorageCommandReaderFactory.INSTANCE;
+ return InMemoryStorageCommandReaderFactory53.INSTANCE;
}
@Override
@@ -472,7 +478,7 @@ public TransactionIdStore readOnlyTransactionIdStore(LogTailMetadata logTailMeta
@Override
public LogVersionRepository readOnlyLogVersionRepository(LogTailMetadata logTailMetadata) {
- return new InMemoryLogVersionRepository();
+ return new InMemoryLogVersionRepository53();
}
@Override
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStorageEngineImpl.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStorageEngineImpl.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStorageEngineImpl.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStorageEngineImpl.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStorageLocksImpl.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStorageLocksImpl.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStorageLocksImpl.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStorageLocksImpl.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStoreVersion.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStoreVersion.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStoreVersion.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryStoreVersion.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryTransactionIdStoreImpl.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryTransactionIdStoreImpl.java
similarity index 74%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryTransactionIdStoreImpl.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryTransactionIdStoreImpl.java
index 73749482bab..845f307c05c 100644
--- a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryTransactionIdStoreImpl.java
+++ b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryTransactionIdStoreImpl.java
@@ -20,12 +20,30 @@
package org.neo4j.gds.compat._53;
import org.neo4j.internal.recordstorage.AbstractTransactionIdStore;
-import org.neo4j.io.pagecache.context.CursorContext;
import org.neo4j.kernel.impl.transaction.log.LogPosition;
import org.neo4j.storageengine.api.ClosedTransactionMetadata;
+import org.neo4j.storageengine.api.TransactionId;
public class InMemoryTransactionIdStoreImpl extends AbstractTransactionIdStore {
+ @Override
+ protected void initLastCommittedAndClosedTransactionId(
+ long previouslyCommittedTxId,
+ int checksum,
+ long previouslyCommittedTxCommitTimestamp,
+ long previouslyCommittedTxLogByteOffset,
+ long previouslyCommittedTxLogVersion
+ ) {
+ this.setLastCommittedAndClosedTransactionId(
+ previouslyCommittedTxId,
+ checksum,
+ previouslyCommittedTxCommitTimestamp,
+ previouslyCommittedTxLogByteOffset,
+ previouslyCommittedTxLogVersion
+ );
+ }
+
+ @Override
public ClosedTransactionMetadata getLastClosedTransaction() {
long[] metaData = this.closedTransactionId.get();
return new ClosedTransactionMetadata(
@@ -69,7 +87,7 @@ public void setLastCommittedAndClosedTransactionId(
}
@Override
- protected CursorContext getEmptyCursorContext() {
- return CursorContext.NULL_CONTEXT;
+ protected TransactionId transactionId(long transactionId, int checksum, long commitTimestamp) {
+ return new TransactionId(transactionId, checksum, commitTimestamp);
}
}
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryVersionCheck.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryVersionCheck.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryVersionCheck.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/InMemoryVersionCheck.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/StorageEngineProxyFactoryImpl.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/StorageEngineProxyFactoryImpl.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/StorageEngineProxyFactoryImpl.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/StorageEngineProxyFactoryImpl.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/StorageEngineProxyImpl.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/StorageEngineProxyImpl.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/StorageEngineProxyImpl.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_53/StorageEngineProxyImpl.java
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository53.java
similarity index 88%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository53.java
index b491fbc7f32..7a711723491 100644
--- a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository.java
+++ b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository53.java
@@ -23,16 +23,16 @@
import java.util.concurrent.atomic.AtomicLong;
-public class InMemoryLogVersionRepository implements LogVersionRepository {
+public class InMemoryLogVersionRepository53 implements LogVersionRepository {
private final AtomicLong logVersion;
private final AtomicLong checkpointLogVersion;
- public InMemoryLogVersionRepository() {
- this(0,0);
+ public InMemoryLogVersionRepository53() {
+ this(0, 0);
}
- private InMemoryLogVersionRepository(long initialLogVersion, long initialCheckpointLogVersion) {
+ private InMemoryLogVersionRepository53(long initialLogVersion, long initialCheckpointLogVersion) {
this.logVersion = new AtomicLong();
this.checkpointLogVersion = new AtomicLong();
this.logVersion.set(initialLogVersion);
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory53.java
similarity index 92%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory53.java
index 620a8aac11c..2804a31e179 100644
--- a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory.java
+++ b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory53.java
@@ -23,9 +23,9 @@
import org.neo4j.storageengine.api.CommandReader;
import org.neo4j.storageengine.api.CommandReaderFactory;
-public class InMemoryStorageCommandReaderFactory implements CommandReaderFactory {
+public class InMemoryStorageCommandReaderFactory53 implements CommandReaderFactory {
- public static final CommandReaderFactory INSTANCE = new InMemoryStorageCommandReaderFactory();
+ public static final CommandReaderFactory INSTANCE = new InMemoryStorageCommandReaderFactory53();
@Override
public CommandReader get(KernelVersion kernelVersion) {
diff --git a/cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader53.java b/compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader53.java
similarity index 100%
rename from cypher/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader53.java
rename to compatibility/5.3/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader53.java
diff --git a/compatibility/5.4/neo4j-kernel-adapter/build.gradle b/compatibility/5.4/neo4j-kernel-adapter/build.gradle
new file mode 100644
index 00000000000..f73ed67ab7f
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/build.gradle
@@ -0,0 +1,63 @@
+apply plugin: 'java-library'
+apply plugin: 'me.champeau.mrjar'
+
+description = 'Neo4j Graph Data Science :: Neo4j Kernel Adapter 5.4'
+
+group = 'org.neo4j.gds'
+
+// for all 5.x versions
+if (ver.'neo4j'.startsWith('5.')) {
+ sourceSets {
+ main {
+ java {
+ srcDirs = ['src/main/java17']
+ }
+ }
+ }
+
+ dependencies {
+ annotationProcessor project(':annotations')
+ annotationProcessor group: 'org.immutables', name: 'value', version: ver.'immutables'
+ annotationProcessor group: 'org.neo4j', name: 'annotations', version: neos.'5.4'
+
+ compileOnly project(':annotations')
+ compileOnly group: 'com.github.spotbugs', name: 'spotbugs-annotations', version: ver.'spotbugsToolVersion'
+ compileOnly group: 'org.immutables', name: 'value-annotations', version: ver.'immutables'
+ compileOnly group: 'org.neo4j', name: 'annotations', version: neos.'5.4'
+ compileOnly group: 'org.neo4j', name: 'neo4j', version: neos.'5.4'
+ compileOnly group: 'org.neo4j', name: 'neo4j-record-storage-engine', version: neos.'5.4'
+ compileOnly group: 'org.neo4j.community', name: 'it-test-support', version: neos.'5.4'
+
+ implementation project(':neo4j-kernel-adapter-api')
+ }
+} else {
+ multiRelease {
+ targetVersions 11, 17
+ }
+
+ if (!project.hasProperty('no-forbidden-apis')) {
+ forbiddenApisJava17 {
+ exclude('**')
+ }
+ }
+
+ dependencies {
+ annotationProcessor group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
+ compileOnly group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
+
+ implementation project(':neo4j-kernel-adapter-api')
+
+ java17AnnotationProcessor project(':annotations')
+ java17AnnotationProcessor group: 'org.immutables', name: 'value', version: ver.'immutables'
+ java17AnnotationProcessor group: 'org.neo4j', name: 'annotations', version: neos.'5.4'
+
+ java17CompileOnly project(':annotations')
+ java17CompileOnly group: 'org.immutables', name: 'value-annotations', version: ver.'immutables'
+ java17CompileOnly group: 'org.neo4j', name: 'neo4j', version: neos.'5.4'
+ java17CompileOnly group: 'org.neo4j', name: 'neo4j-record-storage-engine', version: neos.'5.4'
+ java17CompileOnly group: 'org.neo4j.community', name: 'it-test-support', version: neos.'5.4'
+ java17CompileOnly group: 'com.github.spotbugs', name: 'spotbugs-annotations', version: ver.'spotbugsToolVersion'
+
+ java17Implementation project(':neo4j-kernel-adapter-api')
+ }
+}
diff --git a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryTransactionIdStoreImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_54/Neo4jProxyFactoryImpl.java
similarity index 53%
rename from cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryTransactionIdStoreImpl.java
rename to compatibility/5.4/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_54/Neo4jProxyFactoryImpl.java
index 7f7818f3db6..1e38d77de3c 100644
--- a/cypher/4.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_44/InMemoryTransactionIdStoreImpl.java
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_54/Neo4jProxyFactoryImpl.java
@@ -17,22 +17,28 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.neo4j.gds.compat._44;
+package org.neo4j.gds.compat._54;
-import org.neo4j.internal.recordstorage.AbstractTransactionIdStore;
-import org.neo4j.io.pagecache.context.CursorContext;
-import org.neo4j.kernel.impl.transaction.log.LogPosition;
-import org.neo4j.storageengine.api.ClosedTransactionMetadata;
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jProxyApi;
+import org.neo4j.gds.compat.Neo4jProxyFactory;
+import org.neo4j.gds.compat.Neo4jVersion;
-public class InMemoryTransactionIdStoreImpl extends AbstractTransactionIdStore {
+@ServiceProvider
+public final class Neo4jProxyFactoryImpl implements Neo4jProxyFactory {
- public ClosedTransactionMetadata getLastClosedTransaction() {
- long[] metaData = this.closedTransactionId.get();
- return new ClosedTransactionMetadata(metaData[0], new LogPosition(metaData[1], metaData[2]));
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return false;
+ }
+
+ @Override
+ public Neo4jProxyApi load() {
+ throw new UnsupportedOperationException("5.4 compatibility requires JDK17");
}
@Override
- protected CursorContext getEmptyCursorContext() {
- return CursorContext.NULL;
+ public String description() {
+ return "Neo4j 5.4 (placeholder)";
}
}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_54/SettingProxyFactoryImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_54/SettingProxyFactoryImpl.java
new file mode 100644
index 00000000000..ff39f69c757
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_54/SettingProxyFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.SettingProxyApi;
+import org.neo4j.gds.compat.SettingProxyFactory;
+
+@ServiceProvider
+public final class SettingProxyFactoryImpl implements SettingProxyFactory {
+
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return false;
+ }
+
+ @Override
+ public SettingProxyApi load() {
+ throw new UnsupportedOperationException("5.4 compatibility requires JDK17");
+ }
+
+ @Override
+ public String description() {
+ return "Neo4j Settings 5.4 (placeholder)";
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/BoltTransactionRunnerImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/BoltTransactionRunnerImpl.java
new file mode 100644
index 00000000000..aa4c808530f
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/BoltTransactionRunnerImpl.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.bolt.dbapi.BoltGraphDatabaseServiceSPI;
+import org.neo4j.bolt.dbapi.BoltTransaction;
+import org.neo4j.bolt.protocol.common.bookmark.Bookmark;
+import org.neo4j.bolt.protocol.common.message.AccessMode;
+import org.neo4j.bolt.protocol.common.transaction.result.AdaptingBoltQuerySubscriber;
+import org.neo4j.bolt.protocol.v41.message.request.RoutingContext;
+import org.neo4j.exceptions.KernelException;
+import org.neo4j.gds.compat.BoltQuerySubscriber;
+import org.neo4j.gds.compat.BoltTransactionRunner;
+import org.neo4j.graphdb.QueryStatistics;
+import org.neo4j.internal.kernel.api.connectioninfo.ClientConnectionInfo;
+import org.neo4j.internal.kernel.api.security.LoginContext;
+import org.neo4j.kernel.api.KernelTransaction;
+import org.neo4j.kernel.impl.query.QueryExecutionKernelException;
+import org.neo4j.values.virtual.MapValue;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+
+public class BoltTransactionRunnerImpl extends BoltTransactionRunner {
+
+ @Override
+ protected BoltQuerySubscriber boltQuerySubscriber() {
+ var subscriber = new AdaptingBoltQuerySubscriber();
+ return new BoltQuerySubscriber<>() {
+ @Override
+ public void assertSucceeded() throws KernelException {
+ subscriber.assertSucceeded();
+ }
+
+ @Override
+ public QueryStatistics queryStatistics() {
+ return subscriber.queryStatistics();
+ }
+
+ @Override
+ public AdaptingBoltQuerySubscriber innerSubscriber() {
+ return subscriber;
+ }
+ };
+ }
+
+ @Override
+ protected void executeQuery(
+ BoltTransaction boltTransaction,
+ String query,
+ MapValue parameters,
+ AdaptingBoltQuerySubscriber querySubscriber
+ ) throws QueryExecutionKernelException {
+ boltTransaction.executeQuery(query, parameters, true, querySubscriber);
+ }
+
+ @Override
+ protected BoltTransaction beginBoltWriteTransaction(
+ BoltGraphDatabaseServiceSPI fabricDb,
+ LoginContext loginContext,
+ KernelTransaction.Type kernelTransactionType,
+ ClientConnectionInfo clientConnectionInfo,
+ List bookmarks,
+ Duration txTimeout,
+ Map txMetadata
+ ) {
+ return fabricDb.beginTransaction(
+ kernelTransactionType,
+ loginContext,
+ clientConnectionInfo,
+ bookmarks,
+ txTimeout,
+ AccessMode.WRITE,
+ txMetadata,
+ new RoutingContext(true, Map.of())
+ );
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CallableProcedureImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CallableProcedureImpl.java
new file mode 100644
index 00000000000..c73a48027a0
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CallableProcedureImpl.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.collection.RawIterator;
+import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.CompatCallableProcedure;
+import org.neo4j.internal.kernel.api.exceptions.ProcedureException;
+import org.neo4j.internal.kernel.api.procs.ProcedureSignature;
+import org.neo4j.kernel.api.ResourceMonitor;
+import org.neo4j.kernel.api.procedure.CallableProcedure;
+import org.neo4j.kernel.api.procedure.Context;
+import org.neo4j.values.AnyValue;
+
+@SuppressForbidden(reason = "This is the compat API")
+public final class CallableProcedureImpl implements CallableProcedure {
+ private final CompatCallableProcedure procedure;
+
+ CallableProcedureImpl(CompatCallableProcedure procedure) {
+ this.procedure = procedure;
+ }
+
+ @Override
+ public ProcedureSignature signature() {
+ return this.procedure.signature();
+ }
+
+ @Override
+ public RawIterator apply(
+ Context ctx,
+ AnyValue[] input,
+ ResourceMonitor resourceMonitor
+ ) throws ProcedureException {
+ return this.procedure.apply(ctx, input);
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CallableUserAggregationFunctionImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CallableUserAggregationFunctionImpl.java
new file mode 100644
index 00000000000..aa1cabf9039
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CallableUserAggregationFunctionImpl.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.CompatUserAggregationFunction;
+import org.neo4j.gds.compat.CompatUserAggregator;
+import org.neo4j.internal.kernel.api.exceptions.ProcedureException;
+import org.neo4j.internal.kernel.api.procs.UserAggregationReducer;
+import org.neo4j.internal.kernel.api.procs.UserAggregationUpdater;
+import org.neo4j.internal.kernel.api.procs.UserFunctionSignature;
+import org.neo4j.kernel.api.procedure.CallableUserAggregationFunction;
+import org.neo4j.kernel.api.procedure.Context;
+import org.neo4j.values.AnyValue;
+
+@SuppressForbidden(reason = "This is the compat API")
+public final class CallableUserAggregationFunctionImpl implements CallableUserAggregationFunction {
+ private final CompatUserAggregationFunction function;
+
+ CallableUserAggregationFunctionImpl(CompatUserAggregationFunction function) {
+ this.function = function;
+ }
+
+ @Override
+ public UserFunctionSignature signature() {
+ return this.function.signature();
+ }
+
+ @Override
+ public UserAggregationReducer createReducer(Context ctx) throws ProcedureException {
+ return new UserAggregatorImpl(this.function.create(ctx));
+ }
+
+ private static final class UserAggregatorImpl implements UserAggregationReducer, UserAggregationUpdater {
+ private final CompatUserAggregator aggregator;
+
+ private UserAggregatorImpl(CompatUserAggregator aggregator) {
+ this.aggregator = aggregator;
+ }
+
+ @Override
+ public UserAggregationUpdater newUpdater() {
+ return this;
+ }
+
+ @Override
+ public void update(AnyValue[] input) throws ProcedureException {
+ this.aggregator.update(input);
+ }
+
+ @Override
+ public void applyUpdates() {
+ }
+
+ @Override
+ public AnyValue result() throws ProcedureException {
+ return this.aggregator.result();
+ }
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompatAccessModeImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompatAccessModeImpl.java
new file mode 100644
index 00000000000..06dfd97bd2d
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompatAccessModeImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.compat.CompatAccessMode;
+import org.neo4j.gds.compat.CustomAccessMode;
+import org.neo4j.internal.kernel.api.RelTypeSupplier;
+import org.neo4j.internal.kernel.api.TokenSet;
+
+import java.util.function.Supplier;
+
+public final class CompatAccessModeImpl extends CompatAccessMode {
+
+ CompatAccessModeImpl(CustomAccessMode custom) {
+ super(custom);
+ }
+
+ @Override
+ public boolean allowsReadNodeProperty(Supplier labels, int propertyKey) {
+ return custom.allowsReadNodeProperty(propertyKey);
+ }
+
+ @Override
+ public boolean allowsReadRelationshipProperty(RelTypeSupplier relType, int propertyKey) {
+ return custom.allowsReadRelationshipProperty(propertyKey);
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompatGraphDatabaseAPIImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompatGraphDatabaseAPIImpl.java
new file mode 100644
index 00000000000..06cedb1d578
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompatGraphDatabaseAPIImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.dbms.systemgraph.TopologyGraphDbmsModel;
+import org.neo4j.gds.compat.GdsGraphDatabaseAPI;
+
+final class CompatGraphDatabaseAPIImpl extends GdsGraphDatabaseAPI {
+
+ CompatGraphDatabaseAPIImpl(DatabaseManagementService dbms) {
+ super(dbms);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return api.isAvailable();
+ }
+
+ @Override
+ public TopologyGraphDbmsModel.HostedOnMode mode() {
+ // NOTE: This means we can never start clusters locally, which is probably fine since:
+ // 1) We never did this before
+ // 2) We only use this for tests and benchmarks
+ return TopologyGraphDbmsModel.HostedOnMode.SINGLE;
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompatIndexQueryImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompatIndexQueryImpl.java
new file mode 100644
index 00000000000..adcd4483a1d
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompatIndexQueryImpl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.compat.CompatIndexQuery;
+import org.neo4j.internal.kernel.api.PropertyIndexQuery;
+
+final class CompatIndexQueryImpl implements CompatIndexQuery {
+ final PropertyIndexQuery indexQuery;
+
+ CompatIndexQueryImpl(PropertyIndexQuery indexQuery) {
+ this.indexQuery = indexQuery;
+ }
+}
diff --git a/cypher-aggregation/src/main/java/org/neo4j/gds/projection/DatabaseTopologyHelper.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompatUsernameAuthSubjectImpl.java
similarity index 56%
rename from cypher-aggregation/src/main/java/org/neo4j/gds/projection/DatabaseTopologyHelper.java
rename to compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompatUsernameAuthSubjectImpl.java
index 9583ad90f09..974b2cea35d 100644
--- a/cypher-aggregation/src/main/java/org/neo4j/gds/projection/DatabaseTopologyHelper.java
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompatUsernameAuthSubjectImpl.java
@@ -17,18 +17,19 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.neo4j.gds.projection;
+package org.neo4j.gds.compat._54;
-import org.neo4j.fabric.FabricDatabaseManager;
-import org.neo4j.gds.compat.GraphDatabaseApiProxy;
-import org.neo4j.graphdb.GraphDatabaseService;
+import org.neo4j.gds.compat.CompatUsernameAuthSubject;
+import org.neo4j.internal.kernel.api.security.AuthSubject;
-public final class DatabaseTopologyHelper {
+final class CompatUsernameAuthSubjectImpl extends CompatUsernameAuthSubject {
- private DatabaseTopologyHelper() {}
+ CompatUsernameAuthSubjectImpl(String username, AuthSubject authSubject) {
+ super(username, authSubject);
+ }
- public static boolean isCompositeDatabase(GraphDatabaseService graphDatabaseService) {
- var databaseManager = GraphDatabaseApiProxy.resolveDependency(graphDatabaseService, FabricDatabaseManager.class);
- return databaseManager.isFabricDatabase(GraphDatabaseApiProxy.databaseId(graphDatabaseService).name());
+ @Override
+ public String executingUser() {
+ return username;
}
}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompositeNodeCursorImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompositeNodeCursorImpl.java
new file mode 100644
index 00000000000..7fcaba83573
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/CompositeNodeCursorImpl.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.compat.CompositeNodeCursor;
+import org.neo4j.internal.kernel.api.NodeLabelIndexCursor;
+
+import java.util.List;
+
+public final class CompositeNodeCursorImpl extends CompositeNodeCursor {
+
+ CompositeNodeCursorImpl(List cursors, int[] labelIds) {
+ super(cursors, labelIds);
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/GdsDatabaseLayoutImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/GdsDatabaseLayoutImpl.java
new file mode 100644
index 00000000000..9f655fe2412
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/GdsDatabaseLayoutImpl.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.compat.GdsDatabaseLayout;
+import org.neo4j.io.layout.DatabaseLayout;
+
+import java.nio.file.Path;
+
+public class GdsDatabaseLayoutImpl implements GdsDatabaseLayout {
+ private final DatabaseLayout databaseLayout;
+
+ public GdsDatabaseLayoutImpl(DatabaseLayout databaseLayout) {this.databaseLayout = databaseLayout;}
+
+ @Override
+ public Path databaseDirectory() {
+ return databaseLayout.databaseDirectory();
+ }
+
+ @Override
+ public Path getTransactionLogsDirectory() {
+ return databaseLayout.getTransactionLogsDirectory();
+ }
+
+ @Override
+ public Path metadataStore() {
+ return databaseLayout.metadataStore();
+ }
+
+ public DatabaseLayout databaseLayout() {
+ return databaseLayout;
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/GdsDatabaseManagementServiceBuilderImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/GdsDatabaseManagementServiceBuilderImpl.java
new file mode 100644
index 00000000000..fb39c02a57e
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/GdsDatabaseManagementServiceBuilderImpl.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.dbms.api.DatabaseManagementServiceBuilderImplementation;
+import org.neo4j.gds.compat.GdsDatabaseManagementServiceBuilder;
+import org.neo4j.graphdb.config.Setting;
+
+import java.nio.file.Path;
+import java.util.Map;
+
+public class GdsDatabaseManagementServiceBuilderImpl implements GdsDatabaseManagementServiceBuilder {
+
+ private final DatabaseManagementServiceBuilderImplementation dbmsBuilder;
+
+ GdsDatabaseManagementServiceBuilderImpl(Path storeDir) {
+ this.dbmsBuilder = new DatabaseManagementServiceBuilderImplementation(storeDir);
+ }
+
+ @Override
+ public GdsDatabaseManagementServiceBuilder setConfigRaw(Map configMap) {
+ dbmsBuilder.setConfigRaw(configMap);
+ return this;
+ }
+
+ @Override
+ public GdsDatabaseManagementServiceBuilder setConfig(Setting setting, S value) {
+ dbmsBuilder.setConfig(setting, value);
+ return this;
+ }
+
+ @Override
+ public DatabaseManagementService build() {
+ return dbmsBuilder.build();
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/Neo4jProxyFactoryImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/Neo4jProxyFactoryImpl.java
new file mode 100644
index 00000000000..3fe03256e7b
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/Neo4jProxyFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jProxyApi;
+import org.neo4j.gds.compat.Neo4jProxyFactory;
+import org.neo4j.gds.compat.Neo4jVersion;
+
+@ServiceProvider
+public final class Neo4jProxyFactoryImpl implements Neo4jProxyFactory {
+
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return version == Neo4jVersion.V_5_4;
+ }
+
+ @Override
+ public Neo4jProxyApi load() {
+ return new Neo4jProxyImpl();
+ }
+
+ @Override
+ public String description() {
+ return "Neo4j 5.4";
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/Neo4jProxyImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/Neo4jProxyImpl.java
new file mode 100644
index 00000000000..95a20fe74d7
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/Neo4jProxyImpl.java
@@ -0,0 +1,934 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.neo4j.common.DependencyResolver;
+import org.neo4j.common.EntityType;
+import org.neo4j.configuration.BootloaderSettings;
+import org.neo4j.configuration.Config;
+import org.neo4j.configuration.GraphDatabaseSettings;
+import org.neo4j.configuration.SettingValueParsers;
+import org.neo4j.configuration.connectors.ConnectorPortRegister;
+import org.neo4j.configuration.connectors.ConnectorType;
+import org.neo4j.configuration.helpers.DatabaseNameValidator;
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.exceptions.KernelException;
+import org.neo4j.fabric.FabricDatabaseManager;
+import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.BoltTransactionRunner;
+import org.neo4j.gds.compat.CompatCallableProcedure;
+import org.neo4j.gds.compat.CompatExecutionMonitor;
+import org.neo4j.gds.compat.CompatIndexQuery;
+import org.neo4j.gds.compat.CompatInput;
+import org.neo4j.gds.compat.CompatUserAggregationFunction;
+import org.neo4j.gds.compat.CompositeNodeCursor;
+import org.neo4j.gds.compat.CustomAccessMode;
+import org.neo4j.gds.compat.GdsDatabaseLayout;
+import org.neo4j.gds.compat.GdsDatabaseManagementServiceBuilder;
+import org.neo4j.gds.compat.GdsGraphDatabaseAPI;
+import org.neo4j.gds.compat.GraphDatabaseApiProxy;
+import org.neo4j.gds.compat.InputEntityIdVisitor;
+import org.neo4j.gds.compat.Neo4jProxyApi;
+import org.neo4j.gds.compat.PropertyReference;
+import org.neo4j.gds.compat.StoreScan;
+import org.neo4j.gds.compat.TestLog;
+import org.neo4j.graphdb.GraphDatabaseService;
+import org.neo4j.graphdb.Node;
+import org.neo4j.graphdb.Relationship;
+import org.neo4j.graphdb.RelationshipType;
+import org.neo4j.graphdb.config.Setting;
+import org.neo4j.internal.batchimport.AdditionalInitialIds;
+import org.neo4j.internal.batchimport.BatchImporter;
+import org.neo4j.internal.batchimport.BatchImporterFactory;
+import org.neo4j.internal.batchimport.Configuration;
+import org.neo4j.internal.batchimport.IndexConfig;
+import org.neo4j.internal.batchimport.InputIterable;
+import org.neo4j.internal.batchimport.Monitor;
+import org.neo4j.internal.batchimport.input.Collector;
+import org.neo4j.internal.batchimport.input.IdType;
+import org.neo4j.internal.batchimport.input.Input;
+import org.neo4j.internal.batchimport.input.InputEntityVisitor;
+import org.neo4j.internal.batchimport.input.PropertySizeCalculator;
+import org.neo4j.internal.batchimport.input.ReadableGroups;
+import org.neo4j.internal.batchimport.staging.ExecutionMonitor;
+import org.neo4j.internal.batchimport.staging.StageExecution;
+import org.neo4j.internal.helpers.HostnamePort;
+import org.neo4j.internal.id.IdGenerator;
+import org.neo4j.internal.id.IdGeneratorFactory;
+import org.neo4j.internal.kernel.api.Cursor;
+import org.neo4j.internal.kernel.api.IndexQueryConstraints;
+import org.neo4j.internal.kernel.api.IndexReadSession;
+import org.neo4j.internal.kernel.api.NodeCursor;
+import org.neo4j.internal.kernel.api.NodeLabelIndexCursor;
+import org.neo4j.internal.kernel.api.NodeValueIndexCursor;
+import org.neo4j.internal.kernel.api.PropertyCursor;
+import org.neo4j.internal.kernel.api.PropertyIndexQuery;
+import org.neo4j.internal.kernel.api.QueryContext;
+import org.neo4j.internal.kernel.api.Read;
+import org.neo4j.internal.kernel.api.RelationshipScanCursor;
+import org.neo4j.internal.kernel.api.Scan;
+import org.neo4j.internal.kernel.api.TokenPredicate;
+import org.neo4j.internal.kernel.api.connectioninfo.ClientConnectionInfo;
+import org.neo4j.internal.kernel.api.procs.FieldSignature;
+import org.neo4j.internal.kernel.api.procs.Neo4jTypes;
+import org.neo4j.internal.kernel.api.procs.ProcedureSignature;
+import org.neo4j.internal.kernel.api.procs.QualifiedName;
+import org.neo4j.internal.kernel.api.procs.UserFunctionSignature;
+import org.neo4j.internal.kernel.api.security.AccessMode;
+import org.neo4j.internal.kernel.api.security.AuthSubject;
+import org.neo4j.internal.kernel.api.security.SecurityContext;
+import org.neo4j.internal.recordstorage.RecordIdType;
+import org.neo4j.internal.schema.IndexCapability;
+import org.neo4j.internal.schema.IndexDescriptor;
+import org.neo4j.internal.schema.IndexOrder;
+import org.neo4j.internal.schema.SchemaDescriptors;
+import org.neo4j.io.fs.FileSystemAbstraction;
+import org.neo4j.io.layout.DatabaseLayout;
+import org.neo4j.io.layout.Neo4jLayout;
+import org.neo4j.io.layout.recordstorage.RecordDatabaseLayout;
+import org.neo4j.io.pagecache.PageCache;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.io.pagecache.context.CursorContextFactory;
+import org.neo4j.io.pagecache.context.EmptyVersionContextSupplier;
+import org.neo4j.io.pagecache.tracing.PageCacheTracer;
+import org.neo4j.kernel.api.KernelTransaction;
+import org.neo4j.kernel.api.KernelTransactionHandle;
+import org.neo4j.kernel.api.procedure.CallableProcedure;
+import org.neo4j.kernel.api.procedure.CallableUserAggregationFunction;
+import org.neo4j.kernel.database.NamedDatabaseId;
+import org.neo4j.kernel.database.NormalizedDatabaseName;
+import org.neo4j.kernel.database.TestDatabaseIdRepository;
+import org.neo4j.kernel.impl.coreapi.InternalTransaction;
+import org.neo4j.kernel.impl.index.schema.IndexImporterFactoryImpl;
+import org.neo4j.kernel.impl.query.TransactionalContext;
+import org.neo4j.kernel.impl.query.TransactionalContextFactory;
+import org.neo4j.kernel.impl.store.RecordStore;
+import org.neo4j.kernel.impl.store.format.RecordFormatSelector;
+import org.neo4j.kernel.impl.store.format.RecordFormats;
+import org.neo4j.kernel.impl.store.record.AbstractBaseRecord;
+import org.neo4j.kernel.impl.transaction.log.EmptyLogTailMetadata;
+import org.neo4j.kernel.impl.transaction.log.files.TransactionLogInitializer;
+import org.neo4j.logging.Log;
+import org.neo4j.logging.internal.LogService;
+import org.neo4j.memory.EmptyMemoryTracker;
+import org.neo4j.procedure.Mode;
+import org.neo4j.scheduler.JobScheduler;
+import org.neo4j.ssl.config.SslPolicyLoader;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.StorageEngineFactory;
+import org.neo4j.util.Bits;
+import org.neo4j.values.storable.ValueCategory;
+import org.neo4j.values.storable.Values;
+import org.neo4j.values.virtual.MapValue;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static java.lang.String.format;
+import static org.neo4j.gds.compat.InternalReadOps.countByIdGenerator;
+import static org.neo4j.io.pagecache.context.EmptyVersionContextSupplier.EMPTY;
+
+public final class Neo4jProxyImpl implements Neo4jProxyApi {
+
+ @Override
+ public GdsGraphDatabaseAPI newDb(DatabaseManagementService dbms) {
+ return new CompatGraphDatabaseAPIImpl(dbms);
+ }
+
+ @Override
+ public String validateExternalDatabaseName(String databaseName) {
+ var normalizedName = new NormalizedDatabaseName(databaseName);
+ DatabaseNameValidator.validateExternalDatabaseName(normalizedName);
+ return normalizedName.name();
+ }
+
+ @Override
+ public AccessMode accessMode(CustomAccessMode customAccessMode) {
+ return new CompatAccessModeImpl(customAccessMode);
+ }
+
+ @Override
+ public String username(AuthSubject subject) {
+ return subject.executingUser();
+ }
+
+ @Override
+ public SecurityContext securityContext(
+ String username,
+ AuthSubject authSubject,
+ AccessMode mode,
+ String databaseName
+ ) {
+ return new SecurityContext(
+ new CompatUsernameAuthSubjectImpl(username, authSubject),
+ mode,
+ // GDS is always operating from an embedded context
+ ClientConnectionInfo.EMBEDDED_CONNECTION,
+ databaseName
+ );
+ }
+
+ @Override
+ public long getHighestPossibleIdInUse(
+ RecordStore extends AbstractBaseRecord> recordStore,
+ KernelTransaction kernelTransaction
+ ) {
+ return recordStore.getHighestPossibleIdInUse(kernelTransaction.cursorContext());
+ }
+
+ @Override
+ public long getHighId(RecordStore extends AbstractBaseRecord> recordStore) {
+ return recordStore.getHighId();
+ }
+
+ @Override
+ public List> entityCursorScan(
+ KernelTransaction transaction,
+ int[] labelIds,
+ int batchSize,
+ boolean allowPartitionedScan
+ ) {
+ if (allowPartitionedScan) {
+ return partitionedNodeLabelIndexScan(transaction, batchSize, labelIds);
+ } else {
+ var read = transaction.dataRead();
+ return Arrays
+ .stream(labelIds)
+ .mapToObj(read::nodeLabelScan)
+ .map(scan -> scanToStoreScan(scan, batchSize))
+ .collect(Collectors.toList());
+ }
+ }
+
+ @Override
+ public PropertyCursor allocatePropertyCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction
+ .cursors()
+ .allocatePropertyCursor(kernelTransaction.cursorContext(), kernelTransaction.memoryTracker());
+ }
+
+ @Override
+ public PropertyReference propertyReference(NodeCursor nodeCursor) {
+ return ReferencePropertyReference.of(nodeCursor.propertiesReference());
+ }
+
+ @Override
+ public PropertyReference propertyReference(RelationshipScanCursor relationshipScanCursor) {
+ return ReferencePropertyReference.of(relationshipScanCursor.propertiesReference());
+ }
+
+ @Override
+ public PropertyReference noPropertyReference() {
+ return ReferencePropertyReference.empty();
+ }
+
+ @Override
+ public void nodeProperties(
+ KernelTransaction kernelTransaction,
+ long nodeReference,
+ PropertyReference reference,
+ PropertyCursor cursor
+ ) {
+ var neoReference = ((ReferencePropertyReference) reference).reference;
+ kernelTransaction
+ .dataRead()
+ .nodeProperties(nodeReference, neoReference, PropertySelection.ALL_PROPERTIES, cursor);
+ }
+
+ @Override
+ public void relationshipProperties(
+ KernelTransaction kernelTransaction,
+ long relationshipReference,
+ PropertyReference reference,
+ PropertyCursor cursor
+ ) {
+ var neoReference = ((ReferencePropertyReference) reference).reference;
+ kernelTransaction
+ .dataRead()
+ .relationshipProperties(relationshipReference, neoReference, PropertySelection.ALL_PROPERTIES, cursor);
+ }
+
+ @Override
+ public NodeCursor allocateNodeCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction.cursors().allocateNodeCursor(kernelTransaction.cursorContext());
+ }
+
+ @Override
+ public RelationshipScanCursor allocateRelationshipScanCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction.cursors().allocateRelationshipScanCursor(kernelTransaction.cursorContext());
+ }
+
+ @Override
+ public NodeLabelIndexCursor allocateNodeLabelIndexCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction.cursors().allocateNodeLabelIndexCursor(kernelTransaction.cursorContext());
+ }
+
+ @Override
+ public NodeValueIndexCursor allocateNodeValueIndexCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction
+ .cursors()
+ .allocateNodeValueIndexCursor(kernelTransaction.cursorContext(), kernelTransaction.memoryTracker());
+ }
+
+ @Override
+ public boolean hasNodeLabelIndex(KernelTransaction kernelTransaction) {
+ return NodeLabelIndexLookupImpl.hasNodeLabelIndex(kernelTransaction);
+ }
+
+ @Override
+ public StoreScan nodeLabelIndexScan(
+ KernelTransaction transaction,
+ int labelId,
+ int batchSize,
+ boolean allowPartitionedScan
+ ) {
+ if (allowPartitionedScan) {
+ return partitionedNodeLabelIndexScan(transaction, batchSize, labelId).get(0);
+ } else {
+ var read = transaction.dataRead();
+ return scanToStoreScan(read.nodeLabelScan(labelId), batchSize);
+ }
+ }
+
+ @Override
+ public StoreScan scanToStoreScan(Scan scan, int batchSize) {
+ return new ScanBasedStoreScanImpl<>(scan, batchSize);
+ }
+
+ private List> partitionedNodeLabelIndexScan(
+ KernelTransaction transaction,
+ int batchSize,
+ int... labelIds
+ ) {
+ var indexDescriptor = NodeLabelIndexLookupImpl.findUsableMatchingIndex(
+ transaction,
+ SchemaDescriptors.forAnyEntityTokens(EntityType.NODE)
+ );
+
+ if (indexDescriptor == IndexDescriptor.NO_INDEX) {
+ throw new IllegalStateException("There is no index that can back a node label scan.");
+ }
+
+ var read = transaction.dataRead();
+
+ // Our current strategy is to select the token with the highest count
+ // and use that one as the driving partitioned index scan. The partitions
+ // of all other partitioned index scans will be aligned to that one.
+ int maxToken = labelIds[0];
+ long maxCount = read.countsForNodeWithoutTxState(labelIds[0]);
+
+ for (int i = 1; i < labelIds.length; i++) {
+ long count = read.countsForNodeWithoutTxState(labelIds[i]);
+ if (count > maxCount) {
+ maxCount = count;
+ maxToken = labelIds[i];
+ }
+ }
+
+ int numberOfPartitions = PartitionedStoreScan.getNumberOfPartitions(maxCount, batchSize);
+
+ try {
+ var session = read.tokenReadSession(indexDescriptor);
+
+ var partitionedScan = read.nodeLabelScan(
+ session,
+ numberOfPartitions,
+ transaction.cursorContext(),
+ new TokenPredicate(maxToken)
+ );
+
+ var scans = new ArrayList>(labelIds.length);
+ scans.add(new PartitionedStoreScan(partitionedScan));
+
+ // Initialize the remaining index scans with the partitioning of the first scan.
+ for (int labelToken : labelIds) {
+ if (labelToken != maxToken) {
+ var scan = read.nodeLabelScan(session, partitionedScan, new TokenPredicate(labelToken));
+ scans.add(new PartitionedStoreScan(scan));
+ }
+ }
+
+ return scans;
+ } catch (KernelException e) {
+ // should not happen, we check for the index existence and applicability
+ // before reading it
+ throw new RuntimeException("Unexpected error while initialising reading from node label index", e);
+ }
+ }
+
+ @Override
+ public CompatIndexQuery rangeIndexQuery(
+ int propertyKeyId,
+ double from,
+ boolean fromInclusive,
+ double to,
+ boolean toInclusive
+ ) {
+ return new CompatIndexQueryImpl(PropertyIndexQuery.range(propertyKeyId, from, fromInclusive, to, toInclusive));
+ }
+
+ @Override
+ public CompatIndexQuery rangeAllIndexQuery(int propertyKeyId) {
+ var rangePredicate = PropertyIndexQuery.range(
+ propertyKeyId,
+ Values.doubleValue(Double.NEGATIVE_INFINITY),
+ true,
+ Values.doubleValue(Double.POSITIVE_INFINITY),
+ true
+ );
+ return new CompatIndexQueryImpl(rangePredicate);
+ }
+
+ @Override
+ public void nodeIndexSeek(
+ Read dataRead,
+ IndexReadSession index,
+ NodeValueIndexCursor cursor,
+ IndexOrder indexOrder,
+ boolean needsValues,
+ CompatIndexQuery query
+ ) throws KernelException {
+ var indexQueryConstraints = indexOrder == IndexOrder.NONE
+ ? IndexQueryConstraints.unordered(needsValues)
+ : IndexQueryConstraints.constrained(indexOrder, needsValues);
+
+ dataRead.nodeIndexSeek(
+ (QueryContext) dataRead,
+ index,
+ cursor,
+ indexQueryConstraints,
+ ((CompatIndexQueryImpl) query).indexQuery
+ );
+ }
+
+ @Override
+ public CompositeNodeCursor compositeNodeCursor(List cursors, int[] labelIds) {
+ return new CompositeNodeCursorImpl(cursors, labelIds);
+ }
+
+ @Override
+ public Configuration batchImporterConfig(
+ int batchSize,
+ int writeConcurrency,
+ Optional pageCacheMemory,
+ boolean highIO,
+ IndexConfig indexConfig
+ ) {
+ return new org.neo4j.internal.batchimport.Configuration() {
+ @Override
+ public int batchSize() {
+ return batchSize;
+ }
+
+ @Override
+ public int maxNumberOfWorkerThreads() {
+ return writeConcurrency;
+ }
+
+ @Override
+ public long pageCacheMemory() {
+ return pageCacheMemory.orElseGet(Configuration.super::pageCacheMemory);
+ }
+
+ @Override
+ public boolean highIO() {
+ return highIO;
+ }
+
+ @Override
+ public IndexConfig indexConfig() {
+ return indexConfig;
+ }
+ };
+ }
+
+ @Override
+ public int writeConcurrency(Configuration batchImportConfiguration) {
+ return batchImportConfiguration.maxNumberOfWorkerThreads();
+ }
+
+ @Override
+ public BatchImporter instantiateBatchImporter(
+ BatchImporterFactory factory,
+ GdsDatabaseLayout directoryStructure,
+ FileSystemAbstraction fileSystem,
+ PageCacheTracer pageCacheTracer,
+ Configuration configuration,
+ LogService logService,
+ ExecutionMonitor executionMonitor,
+ AdditionalInitialIds additionalInitialIds,
+ Config dbConfig,
+ RecordFormats recordFormats,
+ JobScheduler jobScheduler,
+ Collector badCollector
+ ) {
+ dbConfig.set(GraphDatabaseSettings.db_format, recordFormats.name());
+ var databaseLayout = ((GdsDatabaseLayoutImpl) directoryStructure).databaseLayout();
+ return factory.instantiate(
+ databaseLayout,
+ fileSystem,
+ pageCacheTracer,
+ configuration,
+ logService,
+ executionMonitor,
+ additionalInitialIds,
+ new EmptyLogTailMetadata(),
+ dbConfig,
+ Monitor.NO_MONITOR,
+ jobScheduler,
+ badCollector,
+ TransactionLogInitializer.getLogFilesInitializer(),
+ new IndexImporterFactoryImpl(),
+ EmptyMemoryTracker.INSTANCE,
+ new CursorContextFactory(PageCacheTracer.NULL, EmptyVersionContextSupplier.EMPTY)
+ );
+ }
+
+ @Override
+ public Input batchInputFrom(CompatInput compatInput) {
+ return new InputFromCompatInput(compatInput);
+ }
+
+ @Override
+ public InputEntityIdVisitor.Long inputEntityLongIdVisitor(IdType idType, ReadableGroups groups) {
+ switch (idType) {
+ case ACTUAL -> {
+ return new InputEntityIdVisitor.Long() {
+ @Override
+ public void visitNodeId(InputEntityVisitor visitor, long id) {
+ visitor.id(id);
+ }
+
+ @Override
+ public void visitSourceId(InputEntityVisitor visitor, long id) {
+ visitor.startId(id);
+ }
+
+ @Override
+ public void visitTargetId(InputEntityVisitor visitor, long id) {
+ visitor.endId(id);
+ }
+ };
+ }
+ case INTEGER -> {
+ var globalGroup = groups.get(null);
+
+ return new InputEntityIdVisitor.Long() {
+ @Override
+ public void visitNodeId(InputEntityVisitor visitor, long id) {
+ visitor.id(id, globalGroup);
+ }
+
+ @Override
+ public void visitSourceId(InputEntityVisitor visitor, long id) {
+ visitor.startId(id, globalGroup);
+ }
+
+ @Override
+ public void visitTargetId(InputEntityVisitor visitor, long id) {
+ visitor.endId(id, globalGroup);
+ }
+ };
+ }
+ default -> throw new IllegalStateException("Unexpected value: " + idType);
+ }
+ }
+
+ @Override
+ public InputEntityIdVisitor.String inputEntityStringIdVisitor(ReadableGroups groups) {
+ var globalGroup = groups.get(null);
+
+ return new InputEntityIdVisitor.String() {
+ @Override
+ public void visitNodeId(InputEntityVisitor visitor, String id) {
+ visitor.id(id, globalGroup);
+ }
+
+ @Override
+ public void visitSourceId(InputEntityVisitor visitor, String id) {
+ visitor.startId(id, globalGroup);
+ }
+
+ @Override
+ public void visitTargetId(InputEntityVisitor visitor, String id) {
+ visitor.endId(id, globalGroup);
+ }
+ };
+ }
+
+ @Override
+ public Setting additionalJvm() {
+ return BootloaderSettings.additional_jvm;
+ }
+
+ @Override
+ public Setting pageCacheMemory() {
+ return GraphDatabaseSettings.pagecache_memory;
+ }
+
+ @Override
+ public Long pageCacheMemoryValue(String value) {
+ return SettingValueParsers.BYTES.parse(value);
+ }
+
+ @Override
+ public ExecutionMonitor invisibleExecutionMonitor() {
+ return ExecutionMonitor.INVISIBLE;
+ }
+
+ @Override
+ public ProcedureSignature procedureSignature(
+ QualifiedName name,
+ List inputSignature,
+ List outputSignature,
+ Mode mode,
+ boolean admin,
+ String deprecated,
+ String description,
+ String warning,
+ boolean eager,
+ boolean caseInsensitive,
+ boolean systemProcedure,
+ boolean internal,
+ boolean allowExpiredCredentials
+ ) {
+ return new ProcedureSignature(
+ name,
+ inputSignature,
+ outputSignature,
+ mode,
+ admin,
+ deprecated,
+ description,
+ warning,
+ eager,
+ caseInsensitive,
+ systemProcedure,
+ internal,
+ allowExpiredCredentials
+ );
+ }
+
+ @Override
+ public long getHighestPossibleNodeCount(
+ Read read, IdGeneratorFactory idGeneratorFactory
+ ) {
+ return countByIdGenerator(idGeneratorFactory, RecordIdType.NODE).orElseGet(read::nodesGetCount);
+ }
+
+ @Override
+ public long getHighestPossibleRelationshipCount(
+ Read read, IdGeneratorFactory idGeneratorFactory
+ ) {
+ return countByIdGenerator(idGeneratorFactory, RecordIdType.RELATIONSHIP).orElseGet(read::relationshipsGetCount);
+ }
+
+ @Override
+ public String versionLongToString(long storeVersion) {
+ // copied from org.neo4j.kernel.impl.store.LegacyMetadataHandler.versionLongToString which is private
+ if (storeVersion == -1) {
+ return "Unknown";
+ }
+ Bits bits = Bits.bitsFromLongs(new long[]{storeVersion});
+ int length = bits.getShort(8);
+ if (length == 0 || length > 7) {
+ throw new IllegalArgumentException(format(Locale.ENGLISH, "The read version string length %d is not proper.", length));
+ }
+ char[] result = new char[length];
+ for (int i = 0; i < length; i++) {
+ result[i] = (char) bits.getShort(8);
+ }
+ return new String(result);
+ }
+
+ private static final class InputFromCompatInput implements Input {
+ private final CompatInput delegate;
+
+ private InputFromCompatInput(CompatInput delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public InputIterable nodes(Collector badCollector) {
+ return delegate.nodes(badCollector);
+ }
+
+ @Override
+ public InputIterable relationships(Collector badCollector) {
+ return delegate.relationships(badCollector);
+ }
+
+ @Override
+ public IdType idType() {
+ return delegate.idType();
+ }
+
+ @Override
+ public ReadableGroups groups() {
+ return delegate.groups();
+ }
+
+ @Override
+ public Estimates calculateEstimates(PropertySizeCalculator propertySizeCalculator) throws IOException {
+ return delegate.calculateEstimates((values, kernelTransaction) -> propertySizeCalculator.calculateSize(
+ values,
+ kernelTransaction.cursorContext(),
+ kernelTransaction.memoryTracker()
+ ));
+ }
+ }
+
+ @Override
+ public TestLog testLog() {
+ return new TestLogImpl();
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat specific use")
+ public Log getUserLog(LogService logService, Class> loggingClass) {
+ return logService.getUserLog(loggingClass);
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat specific use")
+ public Log getInternalLog(LogService logService, Class> loggingClass) {
+ return logService.getInternalLog(loggingClass);
+ }
+
+ @Override
+ public Relationship virtualRelationship(long id, Node startNode, Node endNode, RelationshipType type) {
+ return new VirtualRelationshipImpl(id, startNode, endNode, type);
+ }
+
+ @Override
+ public GdsDatabaseManagementServiceBuilder databaseManagementServiceBuilder(Path storeDir) {
+ return new GdsDatabaseManagementServiceBuilderImpl(storeDir);
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat specific use")
+ public RecordFormats selectRecordFormatForStore(
+ DatabaseLayout databaseLayout,
+ FileSystemAbstraction fs,
+ PageCache pageCache,
+ LogService logService,
+ PageCacheTracer pageCacheTracer
+ ) {
+ return RecordFormatSelector.selectForStore(
+ (RecordDatabaseLayout) databaseLayout,
+ fs,
+ pageCache,
+ logService.getInternalLogProvider(),
+ new CursorContextFactory(pageCacheTracer, EMPTY)
+ );
+ }
+
+ @Override
+ public boolean isNotNumericIndex(IndexCapability indexCapability) {
+ return !indexCapability.areValueCategoriesAccepted(ValueCategory.NUMBER);
+ }
+
+ @Override
+ public void setAllowUpgrades(Config.Builder configBuilder, boolean value) {
+ }
+
+ @Override
+ public String defaultRecordFormatSetting() {
+ return GraphDatabaseSettings.db_format.defaultValue();
+ }
+
+ @Override
+ public void configureRecordFormat(Config.Builder configBuilder, String recordFormat) {
+ var databaseRecordFormat = recordFormat.toLowerCase(Locale.ENGLISH);
+ configBuilder.set(GraphDatabaseSettings.db_format, databaseRecordFormat);
+ }
+
+ @Override
+ public GdsDatabaseLayout databaseLayout(Config config, String databaseName) {
+ var storageEngineFactory = StorageEngineFactory.selectStorageEngine(config);
+ var dbLayout = neo4jLayout(config).databaseLayout(databaseName);
+ var databaseLayout = storageEngineFactory.formatSpecificDatabaseLayout(dbLayout);
+ return new GdsDatabaseLayoutImpl(databaseLayout);
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat specific use")
+ public Neo4jLayout neo4jLayout(Config config) {
+ return Neo4jLayout.of(config);
+ }
+
+ @Override
+ public BoltTransactionRunner, ?> boltTransactionRunner() {
+ return new BoltTransactionRunnerImpl();
+ }
+
+ @Override
+ public HostnamePort getLocalBoltAddress(ConnectorPortRegister connectorPortRegister) {
+ return connectorPortRegister.getLocalAddress(ConnectorType.BOLT);
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat specific use")
+ public SslPolicyLoader createSllPolicyLoader(
+ FileSystemAbstraction fileSystem,
+ Config config,
+ LogService logService
+ ) {
+ return SslPolicyLoader.create(fileSystem, config, logService.getInternalLogProvider());
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat specific use")
+ public RecordFormats recordFormatSelector(
+ String databaseName,
+ Config databaseConfig,
+ FileSystemAbstraction fs,
+ LogService logService,
+ GraphDatabaseService databaseService
+ ) {
+ var neo4jLayout = Neo4jLayout.of(databaseConfig);
+ var recordDatabaseLayout = RecordDatabaseLayout.of(neo4jLayout, databaseName);
+ return RecordFormatSelector.selectForStoreOrConfigForNewDbs(
+ databaseConfig,
+ recordDatabaseLayout,
+ fs,
+ GraphDatabaseApiProxy.resolveDependency(databaseService, PageCache.class),
+ logService.getInternalLogProvider(),
+ GraphDatabaseApiProxy.resolveDependency(databaseService, CursorContextFactory.class)
+ );
+ }
+
+ @Override
+ public NamedDatabaseId randomDatabaseId() {
+ return new TestDatabaseIdRepository().getByName(UUID.randomUUID().toString()).get();
+ }
+
+ @Override
+ public ExecutionMonitor executionMonitor(CompatExecutionMonitor compatExecutionMonitor) {
+ return new ExecutionMonitor.Adapter(
+ compatExecutionMonitor.checkIntervalMillis(),
+ TimeUnit.MILLISECONDS
+ ) {
+
+ @Override
+ public void initialize(DependencyResolver dependencyResolver) {
+ compatExecutionMonitor.initialize(dependencyResolver);
+ }
+
+ @Override
+ public void start(StageExecution execution) {
+ compatExecutionMonitor.start(execution);
+ }
+
+ @Override
+ public void end(StageExecution execution, long totalTimeMillis) {
+ compatExecutionMonitor.end(execution, totalTimeMillis);
+ }
+
+ @Override
+ public void done(boolean successful, long totalTimeMillis, String additionalInformation) {
+ compatExecutionMonitor.done(successful, totalTimeMillis, additionalInformation);
+ }
+
+ @Override
+ public void check(StageExecution execution) {
+ compatExecutionMonitor.check(execution);
+ }
+ };
+ }
+
+ @Override
+ @SuppressFBWarnings("NP_LOAD_OF_KNOWN_NULL_VALUE") // We assign nulls because it makes the code more readable
+ public UserFunctionSignature userFunctionSignature(
+ QualifiedName name,
+ List inputSignature,
+ Neo4jTypes.AnyType type,
+ String description,
+ boolean internal,
+ boolean threadSafe
+ ) {
+ String deprecated = null; // no depracation
+ String category = null; // No predefined categpry (like temporal or math)
+ var caseInsensitive = false; // case sensitive name match
+ var isBuiltIn = false; // is built in; never true for GDS
+
+ return new UserFunctionSignature(
+ name,
+ inputSignature,
+ type,
+ deprecated,
+ description,
+ category,
+ caseInsensitive,
+ isBuiltIn,
+ internal,
+ threadSafe
+ );
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat API")
+ public CallableProcedure callableProcedure(CompatCallableProcedure procedure) {
+ return new CallableProcedureImpl(procedure);
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat API")
+ public CallableUserAggregationFunction callableUserAggregationFunction(CompatUserAggregationFunction function) {
+ return new CallableUserAggregationFunctionImpl(function);
+ }
+
+ @Override
+ public long transactionId(KernelTransactionHandle kernelTransactionHandle) {
+ return kernelTransactionHandle.getTransactionSequenceNumber();
+ }
+
+ @Override
+ public void reserveNeo4jIds(IdGeneratorFactory generatorFactory, int size, CursorContext cursorContext) {
+ IdGenerator idGenerator = generatorFactory.get(RecordIdType.NODE);
+
+ idGenerator.nextConsecutiveIdRange(size, false, cursorContext);
+ }
+
+ @Override
+ public TransactionalContext newQueryContext(
+ TransactionalContextFactory contextFactory,
+ InternalTransaction tx,
+ String queryText,
+ MapValue queryParameters
+ ) {
+ return contextFactory.newContext(tx, queryText, queryParameters);
+ }
+
+ @Override
+ public boolean isCompositeDatabase(GraphDatabaseService databaseService) {
+ var databaseManager = GraphDatabaseApiProxy.resolveDependency(databaseService, FabricDatabaseManager.class);
+ return databaseManager.isFabricDatabase(GraphDatabaseApiProxy.databaseId(databaseService));
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/NodeLabelIndexLookupImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/NodeLabelIndexLookupImpl.java
new file mode 100644
index 00000000000..bedfd628c17
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/NodeLabelIndexLookupImpl.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.common.EntityType;
+import org.neo4j.internal.kernel.api.InternalIndexState;
+import org.neo4j.internal.kernel.api.SchemaRead;
+import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotFoundKernelException;
+import org.neo4j.internal.schema.IndexDescriptor;
+import org.neo4j.internal.schema.IndexType;
+import org.neo4j.internal.schema.SchemaDescriptor;
+import org.neo4j.internal.schema.SchemaDescriptors;
+import org.neo4j.kernel.api.KernelTransaction;
+
+final class NodeLabelIndexLookupImpl {
+
+ static boolean hasNodeLabelIndex(KernelTransaction transaction) {
+ return NodeLabelIndexLookupImpl.findUsableMatchingIndex(
+ transaction,
+ SchemaDescriptors.forAnyEntityTokens(EntityType.NODE)
+ ) != IndexDescriptor.NO_INDEX;
+ }
+
+ static IndexDescriptor findUsableMatchingIndex(
+ KernelTransaction transaction,
+ SchemaDescriptor schemaDescriptor
+ ) {
+ var schemaRead = transaction.schemaRead();
+ var iterator = schemaRead.index(schemaDescriptor);
+ while (iterator.hasNext()) {
+ var index = iterator.next();
+ if (index.getIndexType() == IndexType.LOOKUP && indexIsOnline(schemaRead, index)) {
+ return index;
+ }
+ }
+ return IndexDescriptor.NO_INDEX;
+ }
+
+ private static boolean indexIsOnline(SchemaRead schemaRead, IndexDescriptor index) {
+ var state = InternalIndexState.FAILED;
+ try {
+ state = schemaRead.indexGetState(index);
+ } catch (IndexNotFoundKernelException e) {
+ // Well the index should always exist here, but if we didn't find it while checking the state,
+ // then we obviously don't want to use it.
+ }
+ return state == InternalIndexState.ONLINE;
+ }
+
+ private NodeLabelIndexLookupImpl() {}
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/PartitionedStoreScan.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/PartitionedStoreScan.java
new file mode 100644
index 00000000000..6d80192595d
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/PartitionedStoreScan.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.compat.StoreScan;
+import org.neo4j.internal.kernel.api.NodeLabelIndexCursor;
+import org.neo4j.internal.kernel.api.PartitionedScan;
+import org.neo4j.kernel.api.KernelTransaction;
+
+final class PartitionedStoreScan implements StoreScan {
+ private final PartitionedScan scan;
+
+ PartitionedStoreScan(PartitionedScan scan) {
+ this.scan = scan;
+ }
+
+ static int getNumberOfPartitions(long nodeCount, int batchSize) {
+ int numberOfPartitions;
+ if (nodeCount > 0) {
+ // ceil div to try to get enough partitions so a single one does
+ // not include more nodes than batchSize
+ long partitions = ((nodeCount - 1) / batchSize) + 1;
+
+ // value must be positive
+ if (partitions < 1) {
+ partitions = 1;
+ }
+
+ numberOfPartitions = (int) Long.min(Integer.MAX_VALUE, partitions);
+ } else {
+ // we have no partitions to scan, but the value must still be positive
+ numberOfPartitions = 1;
+ }
+ return numberOfPartitions;
+ }
+
+ @Override
+ public boolean reserveBatch(NodeLabelIndexCursor cursor, KernelTransaction ktx) {
+ return scan.reservePartition(cursor, ktx.cursorContext(), ktx.securityContext().mode());
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/ReferencePropertyReference.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/ReferencePropertyReference.java
new file mode 100644
index 00000000000..1ccf92014fa
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/ReferencePropertyReference.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.compat.PropertyReference;
+import org.neo4j.storageengine.api.Reference;
+
+import java.util.Objects;
+
+public final class ReferencePropertyReference implements PropertyReference {
+
+ private static final PropertyReference EMPTY = new ReferencePropertyReference(null);
+
+ public final Reference reference;
+
+ private ReferencePropertyReference(Reference reference) {
+ this.reference = reference;
+ }
+
+ public static PropertyReference of(Reference reference) {
+ return new ReferencePropertyReference(Objects.requireNonNull(reference));
+ }
+
+ public static PropertyReference empty() {
+ return EMPTY;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return reference == null;
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/ScanBasedStoreScanImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/ScanBasedStoreScanImpl.java
new file mode 100644
index 00000000000..2e84d278532
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/ScanBasedStoreScanImpl.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.compat.StoreScan;
+import org.neo4j.internal.kernel.api.Cursor;
+import org.neo4j.internal.kernel.api.Scan;
+import org.neo4j.kernel.api.KernelTransaction;
+
+public final class ScanBasedStoreScanImpl implements StoreScan {
+ private final Scan scan;
+ private final int batchSize;
+
+ public ScanBasedStoreScanImpl(Scan scan, int batchSize) {
+ this.scan = scan;
+ this.batchSize = batchSize;
+ }
+
+ @Override
+ public boolean reserveBatch(C cursor, KernelTransaction ktx) {
+ return scan.reserveBatch(cursor, batchSize, ktx.cursorContext(), ktx.securityContext().mode());
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/SettingProxyFactoryImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/SettingProxyFactoryImpl.java
new file mode 100644
index 00000000000..55d9d22773a
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/SettingProxyFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.SettingProxyApi;
+import org.neo4j.gds.compat.SettingProxyFactory;
+
+@ServiceProvider
+public final class SettingProxyFactoryImpl implements SettingProxyFactory {
+
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return version == Neo4jVersion.V_5_4;
+ }
+
+ @Override
+ public SettingProxyApi load() {
+ return new SettingProxyImpl();
+ }
+
+ @Override
+ public String description() {
+ return "Neo4j Settings 5.4";
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/SettingProxyImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/SettingProxyImpl.java
new file mode 100644
index 00000000000..c854e5a5a80
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/SettingProxyImpl.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.configuration.Config;
+import org.neo4j.configuration.SettingBuilder;
+import org.neo4j.dbms.systemgraph.TopologyGraphDbmsModel;
+import org.neo4j.gds.compat.DatabaseMode;
+import org.neo4j.gds.compat.SettingProxyApi;
+import org.neo4j.graphdb.GraphDatabaseService;
+import org.neo4j.graphdb.config.Setting;
+import org.neo4j.kernel.impl.factory.GraphDatabaseFacade;
+import org.neo4j.kernel.internal.GraphDatabaseAPI;
+
+public class SettingProxyImpl implements SettingProxyApi {
+
+ @Override
+ public Setting setting(org.neo4j.gds.compat.Setting setting) {
+ var builder = SettingBuilder.newBuilder(setting.name(), setting.parser(), setting.defaultValue());
+ if (setting.dynamic()) {
+ builder = builder.dynamic();
+ }
+ if (setting.immutable()) {
+ builder = builder.immutable();
+ }
+ setting.dependency().ifPresent(builder::setDependency);
+ setting.constraints().forEach(builder::addConstraint);
+ return builder.build();
+ }
+
+ @Override
+ public DatabaseMode databaseMode(Config config, GraphDatabaseService databaseService) {
+ return switch (((GraphDatabaseAPI) databaseService).mode()) {
+ case RAFT -> DatabaseMode.CORE;
+ case REPLICA -> DatabaseMode.READ_REPLICA;
+ case SINGLE -> DatabaseMode.SINGLE;
+ case VIRTUAL -> throw new UnsupportedOperationException("What's a virtual database anyway?");
+ };
+ }
+
+ @Override
+ public void setDatabaseMode(Config config, DatabaseMode databaseMode, GraphDatabaseService databaseService) {
+ // super hacky, there is no way to set the mode of a database without restarting it
+ if (!(databaseService instanceof GraphDatabaseFacade db)) {
+ throw new IllegalArgumentException(
+ "Cannot set database mode on a database that is not a GraphDatabaseFacade");
+ }
+ try {
+ var modeField = GraphDatabaseFacade.class.getDeclaredField("mode");
+ modeField.setAccessible(true);
+ modeField.set(db, switch (databaseMode) {
+ case CORE -> TopologyGraphDbmsModel.HostedOnMode.RAFT;
+ case READ_REPLICA -> TopologyGraphDbmsModel.HostedOnMode.REPLICA;
+ case SINGLE -> TopologyGraphDbmsModel.HostedOnMode.SINGLE;
+ });
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException(
+ "Could not set the mode field because it no longer exists. This compat layer needs to be updated.",
+ e
+ );
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Could not get the permissions to set the mode field.", e);
+ }
+ }
+
+ @Override
+ public String secondaryModeName() {
+ return "Secondary";
+ }
+}
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/TestLogImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/TestLogImpl.java
new file mode 100644
index 00000000000..6e75890ee50
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/TestLogImpl.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.TestLog;
+
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
+
+public class TestLogImpl implements TestLog {
+
+ private final ConcurrentMap> messages;
+
+ TestLogImpl() {
+ messages = new ConcurrentHashMap<>(3);
+ }
+
+ @Override
+ public void assertContainsMessage(String level, String fragment) {
+ if (!containsMessage(level, fragment)) {
+ throw new RuntimeException(
+ String.format(
+ Locale.US,
+ "Expected log output to contain `%s` for log level `%s`%nLog messages:%n%s",
+ fragment,
+ level,
+ String.join("\n", messages.get(level))
+ )
+ );
+ }
+ }
+
+ @Override
+ public boolean containsMessage(String level, String fragment) {
+ ConcurrentLinkedQueue messageList = messages.getOrDefault(level, new ConcurrentLinkedQueue<>());
+ return messageList.stream().anyMatch((message) -> message.contains(fragment));
+ }
+
+ @Override
+ public boolean hasMessages(String level) {
+ return !messages.getOrDefault(level, new ConcurrentLinkedQueue<>()).isEmpty();
+ }
+
+ @Override
+ public ArrayList getMessages(String level) {
+ return new ArrayList<>(messages.getOrDefault(level, new ConcurrentLinkedQueue<>()));
+ }
+
+ @SuppressForbidden(reason = "test log can print")
+ public void printMessages() {
+ System.out.println("TestLog Messages: " + messages);
+ }
+
+ @Override
+ public boolean isDebugEnabled() {
+ return true;
+ }
+
+ @Override
+ public void debug(String message) {
+ logMessage(DEBUG, message);
+ }
+
+ @Override
+ public void debug(String message, Throwable throwable) {
+ debug(String.format(Locale.US, "%s - %s", message, throwable.getMessage()));
+ }
+
+ @Override
+ public void debug(String format, Object... arguments) {
+ debug(String.format(Locale.US, format, arguments));
+ }
+
+ @Override
+ public void info(String message) {
+ logMessage(INFO, message);
+ }
+
+ @Override
+ public void info(String message, Throwable throwable) {
+ info(String.format(Locale.US, "%s - %s", message, throwable.getMessage()));
+ }
+
+ @Override
+ public void info(String format, Object... arguments) {
+ info(String.format(Locale.US, format, arguments));
+ }
+
+ @Override
+ public void warn(String message) {
+ logMessage(WARN, message);
+ }
+
+ @Override
+ public void warn(String message, Throwable throwable) {
+ warn(String.format(Locale.US, "%s - %s", message, throwable.getMessage()));
+ }
+
+ @Override
+ public void warn(String format, Object... arguments) {
+ warn(String.format(Locale.US, format, arguments));
+ }
+
+ @Override
+ public void error(String message) {
+ logMessage(ERROR, message);
+ }
+
+ @Override
+ public void error(String message, Throwable throwable) {
+ error(String.format(Locale.US, "%s - %s", message, throwable.getMessage()));
+ }
+
+ @Override
+ public void error(String format, Object... arguments) {
+ error(String.format(Locale.US, format, arguments));
+ }
+
+ private void logMessage(String level, String message) {
+ messages.computeIfAbsent(
+ level,
+ (ignore) -> new ConcurrentLinkedQueue<>()
+ ).add(message);
+ }
+}
+
+
diff --git a/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/VirtualRelationshipImpl.java b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/VirtualRelationshipImpl.java
new file mode 100644
index 00000000000..3d0f8a96dbb
--- /dev/null
+++ b/compatibility/5.4/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_54/VirtualRelationshipImpl.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.compat.VirtualRelationship;
+import org.neo4j.graphdb.Node;
+import org.neo4j.graphdb.RelationshipType;
+
+public class VirtualRelationshipImpl extends VirtualRelationship {
+
+ VirtualRelationshipImpl(
+ long id,
+ Node startNode,
+ Node endNode,
+ RelationshipType type
+ ) {
+ super(id, startNode, endNode, type);
+ }
+
+ @Override
+ public String getElementId() {
+ return Long.toString(getId());
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/build.gradle b/compatibility/5.4/storage-engine-adapter/build.gradle
new file mode 100644
index 00000000000..151d3362ee9
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/build.gradle
@@ -0,0 +1,66 @@
+apply plugin: 'java-library'
+apply plugin: 'me.champeau.mrjar'
+
+description = 'Neo4j Graph Data Science :: Storage Engine Adapter 5.4'
+
+group = 'org.neo4j.gds'
+
+// for all 5.x versions
+if (ver.'neo4j'.startsWith('5.')) {
+ sourceSets {
+ main {
+ java {
+ srcDirs = ['src/main/java17']
+ }
+ }
+ }
+
+ dependencies {
+ annotationProcessor project(':annotations')
+ annotationProcessor group: 'org.immutables', name: 'value', version: ver.'immutables'
+ annotationProcessor group: 'org.neo4j', name: 'annotations', version: neos.'5.4'
+
+ compileOnly project(':annotations')
+ compileOnly group: 'org.immutables', name: 'value-annotations', version: ver.'immutables'
+ compileOnly group: 'org.neo4j', name: 'neo4j', version: neos.'5.4'
+ compileOnly group: 'org.neo4j', name: 'neo4j-record-storage-engine', version: neos.'5.4'
+
+ implementation project(':core')
+ implementation project(':storage-engine-adapter-api')
+ implementation project(':config-api')
+ implementation project(':string-formatting')
+ }
+} else {
+ multiRelease {
+ targetVersions 11, 17
+ }
+
+ if (!project.hasProperty('no-forbidden-apis')) {
+ forbiddenApisJava17 {
+ exclude('**')
+ }
+ }
+
+ dependencies {
+ annotationProcessor group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
+ compileOnly group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
+ compileOnly group: 'org.neo4j', name: 'neo4j-kernel-api', version: ver.'neo4j'
+
+ implementation project(':neo4j-adapter')
+ implementation project(':storage-engine-adapter-api')
+
+ java17AnnotationProcessor project(':annotations')
+ java17AnnotationProcessor group: 'org.immutables', name: 'value', version: ver.'immutables'
+ java17AnnotationProcessor group: 'org.neo4j', name: 'annotations', version: neos.'5.4'
+
+ java17CompileOnly project(':annotations')
+ java17CompileOnly group: 'org.immutables', name: 'value-annotations', version: ver.'immutables'
+ java17CompileOnly group: 'org.neo4j', name: 'neo4j', version: neos.'5.4'
+ java17CompileOnly group: 'org.neo4j', name: 'neo4j-record-storage-engine', version: neos.'5.4'
+
+ java17Implementation project(':core')
+ java17Implementation project(':storage-engine-adapter-api')
+ java17Implementation project(':config-api')
+ java17Implementation project(':string-formatting')
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_54/InMemoryStorageEngineFactory.java b/compatibility/5.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_54/InMemoryStorageEngineFactory.java
new file mode 100644
index 00000000000..23fb737630d
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_54/InMemoryStorageEngineFactory.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.configuration.Config;
+import org.neo4j.dbms.database.readonly.DatabaseReadOnlyChecker;
+import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector;
+import org.neo4j.internal.id.IdController;
+import org.neo4j.internal.id.IdGeneratorFactory;
+import org.neo4j.internal.schema.IndexConfigCompleter;
+import org.neo4j.internal.schema.SchemaRule;
+import org.neo4j.internal.schema.SchemaState;
+import org.neo4j.io.fs.FileSystemAbstraction;
+import org.neo4j.io.layout.DatabaseLayout;
+import org.neo4j.io.layout.Neo4jLayout;
+import org.neo4j.io.pagecache.PageCache;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.io.pagecache.tracing.PageCacheTracer;
+import org.neo4j.lock.LockService;
+import org.neo4j.logging.LogProvider;
+import org.neo4j.logging.internal.LogService;
+import org.neo4j.memory.MemoryTracker;
+import org.neo4j.monitoring.DatabaseHealth;
+import org.neo4j.scheduler.JobScheduler;
+import org.neo4j.storageengine.api.CommandReaderFactory;
+import org.neo4j.storageengine.api.ConstraintRuleAccessor;
+import org.neo4j.storageengine.api.LogVersionRepository;
+import org.neo4j.storageengine.api.MetadataProvider;
+import org.neo4j.storageengine.api.StorageEngine;
+import org.neo4j.storageengine.api.StorageEngineFactory;
+import org.neo4j.storageengine.api.StorageFilesState;
+import org.neo4j.storageengine.api.StoreId;
+import org.neo4j.storageengine.api.StoreVersion;
+import org.neo4j.storageengine.api.StoreVersionCheck;
+import org.neo4j.storageengine.api.TransactionIdStore;
+import org.neo4j.storageengine.migration.RollingUpgradeCompatibility;
+import org.neo4j.storageengine.migration.SchemaRuleMigrationAccess;
+import org.neo4j.storageengine.migration.StoreMigrationParticipant;
+import org.neo4j.token.TokenHolders;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@ServiceProvider
+public class InMemoryStorageEngineFactory implements StorageEngineFactory {
+
+ @Override
+ public String name() {
+ return "unsupported54";
+ }
+
+ @Override
+ public StoreVersionCheck versionCheck(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ LogService logService,
+ PageCacheTracer pageCacheTracer
+ ) {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreVersion versionInformation(String storeVersion) {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreVersion versionInformation(StoreId storeId) {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public RollingUpgradeCompatibility rollingUpgradeCompatibility() {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public List migrationParticipants(
+ FileSystemAbstraction fs,
+ Config config,
+ PageCache pageCache,
+ JobScheduler jobScheduler,
+ LogService logService,
+ PageCacheTracer cacheTracer,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public StorageEngine instantiate(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ TokenHolders tokenHolders,
+ SchemaState schemaState,
+ ConstraintRuleAccessor constraintSemantics,
+ IndexConfigCompleter indexConfigCompleter,
+ LockService lockService,
+ IdGeneratorFactory idGeneratorFactory,
+ IdController idController,
+ DatabaseHealth databaseHealth,
+ LogProvider internalLogProvider,
+ LogProvider userLogProvider,
+ RecoveryCleanupWorkCollector recoveryCleanupWorkCollector,
+ PageCacheTracer cacheTracer,
+ boolean createStoreIfNotExists,
+ DatabaseReadOnlyChecker readOnlyChecker,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public List listStorageFiles(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout) throws
+ IOException {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public boolean storageExists(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout, PageCache pageCache) {
+ return false;
+ }
+
+ @Override
+ public TransactionIdStore readOnlyTransactionIdStore(
+ FileSystemAbstraction filySystem,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public LogVersionRepository readOnlyLogVersionRepository(
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public MetadataProvider transactionMetaDataStore(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ PageCacheTracer cacheTracer,
+ DatabaseReadOnlyChecker readOnlyChecker
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreId storeId(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public void setStoreId(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext,
+ StoreId storeId,
+ long upgradeTxChecksum,
+ long upgradeTxCommitTimestamp
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public void setExternalStoreUUID(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext,
+ UUID externalStoreId
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public Optional databaseIdUuid(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public SchemaRuleMigrationAccess schemaRuleMigrationAccess(
+ FileSystemAbstraction fs,
+ PageCache pageCache,
+ Config config,
+ DatabaseLayout databaseLayout,
+ LogService logService,
+ String recordFormats,
+ PageCacheTracer cacheTracer,
+ CursorContext cursorContext,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public List loadSchemaRules(
+ FileSystemAbstraction fs,
+ PageCache pageCache,
+ Config config,
+ DatabaseLayout databaseLayout,
+ CursorContext cursorContext
+ ) {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public StorageFilesState checkStoreFileState(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache
+ ) {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public CommandReaderFactory commandReaderFactory() {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public DatabaseLayout databaseLayout(Neo4jLayout neo4jLayout, String databaseName) {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_54/StorageEngineProxyFactoryImpl.java b/compatibility/5.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_54/StorageEngineProxyFactoryImpl.java
new file mode 100644
index 00000000000..e997b83e3da
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_54/StorageEngineProxyFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.StorageEngineProxyApi;
+import org.neo4j.gds.compat.StorageEngineProxyFactory;
+
+@ServiceProvider
+public class StorageEngineProxyFactoryImpl implements StorageEngineProxyFactory {
+
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return false;
+ }
+
+ @Override
+ public StorageEngineProxyApi load() {
+ throw new UnsupportedOperationException("5.4 storage engine requires JDK17");
+ }
+
+ @Override
+ public String description() {
+ return "Storage Engine 5.4";
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryCommandCreationContextImpl.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryCommandCreationContextImpl.java
new file mode 100644
index 00000000000..d6825dc4902
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryCommandCreationContextImpl.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.kernel.KernelVersion;
+import org.neo4j.kernel.KernelVersionProvider;
+import org.neo4j.lock.LockTracer;
+import org.neo4j.lock.ResourceLocker;
+import org.neo4j.storageengine.api.CommandCreationContext;
+import org.neo4j.storageengine.api.cursor.StoreCursors;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+
+public class InMemoryCommandCreationContextImpl implements CommandCreationContext {
+
+ private final AtomicLong schemaTokens;
+ private final AtomicInteger propertyTokens;
+ private final AtomicInteger labelTokens;
+ private final AtomicInteger typeTokens;
+
+ InMemoryCommandCreationContextImpl() {
+ this.schemaTokens = new AtomicLong(0);
+ this.propertyTokens = new AtomicInteger(0);
+ this.labelTokens = new AtomicInteger(0);
+ this.typeTokens = new AtomicInteger(0);
+ }
+
+ @Override
+ public long reserveNode() {
+ throw new UnsupportedOperationException("Creating nodes is not supported");
+ }
+
+ @Override
+ public long reserveRelationship(
+ long sourceNode,
+ long targetNode,
+ int relationshipType,
+ boolean sourceNodeAddedInTx,
+ boolean targetNodeAddedInTx
+ ) {
+ throw new UnsupportedOperationException("Creating relationships is not supported");
+ }
+
+ @Override
+ public long reserveSchema() {
+ return schemaTokens.getAndIncrement();
+ }
+
+ @Override
+ public int reserveLabelTokenId() {
+ return labelTokens.getAndIncrement();
+ }
+
+ @Override
+ public int reservePropertyKeyTokenId() {
+ return propertyTokens.getAndIncrement();
+ }
+
+ @Override
+ public int reserveRelationshipTypeTokenId() {
+ return typeTokens.getAndIncrement();
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public void initialize(
+ KernelVersionProvider kernelVersionProvider,
+ CursorContext cursorContext,
+ StoreCursors storeCursors,
+ Supplier oldestActiveTransactionSequenceNumber,
+ ResourceLocker locks,
+ Supplier lockTracer
+ ) {
+
+ }
+
+ @Override
+ public KernelVersion kernelVersion() {
+ // NOTE: Double-check if this is still correct when you copy this into a new compat layer
+ return KernelVersion.LATEST;
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryCountsStoreImpl.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryCountsStoreImpl.java
new file mode 100644
index 00000000000..f3f3546f7c3
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryCountsStoreImpl.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.annotations.documented.ReporterFactory;
+import org.neo4j.counts.CountsAccessor;
+import org.neo4j.counts.CountsStorage;
+import org.neo4j.counts.CountsVisitor;
+import org.neo4j.gds.NodeLabel;
+import org.neo4j.gds.api.GraphStore;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.io.pagecache.context.CursorContextFactory;
+import org.neo4j.io.pagecache.tracing.FileFlushEvent;
+import org.neo4j.memory.MemoryTracker;
+import org.neo4j.storageengine.api.cursor.StoreCursors;
+import org.neo4j.token.TokenHolders;
+import org.neo4j.token.api.TokenNotFoundException;
+
+public class InMemoryCountsStoreImpl implements CountsStorage, CountsAccessor {
+
+ private final GraphStore graphStore;
+ private final TokenHolders tokenHolders;
+
+ public InMemoryCountsStoreImpl(
+ GraphStore graphStore,
+ TokenHolders tokenHolders
+ ) {
+
+ this.graphStore = graphStore;
+ this.tokenHolders = tokenHolders;
+ }
+
+ @Override
+ public void start(
+ CursorContext cursorContext, StoreCursors storeCursors, MemoryTracker memoryTracker
+ ) {
+
+ }
+
+ @Override
+ public void checkpoint(FileFlushEvent fileFlushEvent, CursorContext cursorContext) {
+
+ }
+
+ @Override
+ public long nodeCount(int labelId, CursorContext cursorContext) {
+ if (labelId == -1) {
+ return graphStore.nodeCount();
+ }
+
+ String nodeLabel;
+ try {
+ nodeLabel = tokenHolders.labelTokens().getTokenById(labelId).name();
+ } catch (TokenNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ return graphStore.nodes().nodeCount(NodeLabel.of(nodeLabel));
+ }
+
+ @Override
+ public long relationshipCount(int startLabelId, int typeId, int endLabelId, CursorContext cursorContext) {
+ // TODO: this is quite wrong
+ return graphStore.relationshipCount();
+ }
+
+ @Override
+ public boolean consistencyCheck(
+ ReporterFactory reporterFactory,
+ CursorContextFactory contextFactory,
+ int numThreads
+ ) {
+ return true;
+ }
+
+ @Override
+ public CountsAccessor.Updater apply(long txId, boolean isLast, CursorContext cursorContext) {
+ throw new UnsupportedOperationException("Updates are not supported");
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public void accept(CountsVisitor visitor, CursorContext cursorContext) {
+ tokenHolders.labelTokens().getAllTokens().forEach(labelToken -> {
+ visitor.visitNodeCount(labelToken.id(), nodeCount(labelToken.id(), cursorContext));
+ });
+
+ visitor.visitRelationshipCount(-1, -1, -1, graphStore.relationshipCount());
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryMetaDataProviderImpl.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryMetaDataProviderImpl.java
new file mode 100644
index 00000000000..7fd6339cb89
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryMetaDataProviderImpl.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.internal.recordstorage.InMemoryLogVersionRepository54;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.storageengine.api.ClosedTransactionMetadata;
+import org.neo4j.storageengine.api.ExternalStoreId;
+import org.neo4j.storageengine.api.MetadataProvider;
+import org.neo4j.storageengine.api.StoreId;
+import org.neo4j.storageengine.api.TransactionId;
+import org.neo4j.storageengine.api.TransactionIdStore;
+
+import java.io.IOException;
+import java.util.Optional;
+import java.util.UUID;
+
+public class InMemoryMetaDataProviderImpl implements MetadataProvider {
+
+ private final ExternalStoreId externalStoreId;
+ private final InMemoryLogVersionRepository54 logVersionRepository;
+ private final InMemoryTransactionIdStoreImpl transactionIdStore;
+
+ InMemoryMetaDataProviderImpl() {
+ this.logVersionRepository = new InMemoryLogVersionRepository54();
+ this.externalStoreId = new ExternalStoreId(UUID.randomUUID());
+ this.transactionIdStore = new InMemoryTransactionIdStoreImpl();
+ }
+
+ @Override
+ public ExternalStoreId getExternalStoreId() {
+ return this.externalStoreId;
+ }
+
+ @Override
+ public ClosedTransactionMetadata getLastClosedTransaction() {
+ return this.transactionIdStore.getLastClosedTransaction();
+ }
+
+ @Override
+ public void transactionClosed(
+ long transactionId,
+ long logVersion,
+ long byteOffset,
+ int checksum,
+ long commitTimestamp
+ ) {
+ this.transactionIdStore.transactionClosed(
+ transactionId,
+ logVersion,
+ byteOffset,
+ checksum,
+ commitTimestamp
+ );
+ }
+
+ @Override
+ public void resetLastClosedTransaction(
+ long transactionId,
+ long logVersion,
+ long byteOffset,
+ int checksum,
+ long commitTimestamp
+ ) {
+ this.transactionIdStore.resetLastClosedTransaction(
+ transactionId,
+ logVersion,
+ byteOffset,
+ checksum,
+ commitTimestamp
+ );
+ }
+
+ @Override
+ public void setCurrentLogVersion(long version) {
+ logVersionRepository.setCurrentLogVersion(version);
+ }
+
+ @Override
+ public long incrementAndGetVersion() {
+ return logVersionRepository.incrementAndGetVersion();
+ }
+
+ @Override
+ public void setCheckpointLogVersion(long version) {
+ logVersionRepository.setCheckpointLogVersion(version);
+ }
+
+ @Override
+ public long incrementAndGetCheckpointLogVersion() {
+ return logVersionRepository.incrementAndGetCheckpointLogVersion();
+ }
+
+ @Override
+ public void transactionCommitted(long transactionId, int checksum, long commitTimestamp) {
+ transactionIdStore.transactionCommitted(transactionId, checksum, commitTimestamp);
+ }
+
+ @Override
+ public void setLastCommittedAndClosedTransactionId(
+ long transactionId, int checksum, long commitTimestamp, long byteOffset, long logVersion
+ ) {
+ transactionIdStore.setLastCommittedAndClosedTransactionId(
+ transactionId,
+ checksum,
+ commitTimestamp,
+ byteOffset,
+ logVersion
+ );
+ }
+
+ @Override
+ public void regenerateMetadata(StoreId storeId, UUID externalStoreUUID, CursorContext cursorContext) {
+ }
+
+ @Override
+ public StoreId getStoreId() {
+ return StoreId.UNKNOWN;
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+
+ @Override
+ public long getCurrentLogVersion() {
+ return this.logVersionRepository.getCurrentLogVersion();
+ }
+
+ @Override
+ public long getCheckpointLogVersion() {
+ return this.logVersionRepository.getCheckpointLogVersion();
+ }
+
+ @Override
+ public long nextCommittingTransactionId() {
+ return this.transactionIdStore.nextCommittingTransactionId();
+ }
+
+ @Override
+ public long committingTransactionId() {
+ return this.transactionIdStore.committingTransactionId();
+ }
+
+ @Override
+ public long getLastCommittedTransactionId() {
+ return this.transactionIdStore.getLastCommittedTransactionId();
+ }
+
+ @Override
+ public TransactionId getLastCommittedTransaction() {
+ return this.transactionIdStore.getLastCommittedTransaction();
+ }
+
+ @Override
+ public long getLastClosedTransactionId() {
+ return this.transactionIdStore.getLastClosedTransactionId();
+ }
+
+ @Override
+ public Optional getDatabaseIdUuid(CursorContext cursorTracer) {
+ throw new IllegalStateException("Not supported");
+ }
+
+ @Override
+ public void setDatabaseIdUuid(UUID uuid, CursorContext cursorContext) {
+ throw new IllegalStateException("Not supported");
+ }
+
+ TransactionIdStore transactionIdStore() {
+ return this.transactionIdStore;
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryNodeCursor.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryNodeCursor.java
new file mode 100644
index 00000000000..5cc9cc6e713
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryNodeCursor.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.api.GraphStore;
+import org.neo4j.gds.compat.AbstractInMemoryNodeCursor;
+import org.neo4j.storageengine.api.AllNodeScan;
+import org.neo4j.storageengine.api.Degrees;
+import org.neo4j.storageengine.api.LongReference;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.Reference;
+import org.neo4j.storageengine.api.RelationshipSelection;
+import org.neo4j.storageengine.api.StoragePropertyCursor;
+import org.neo4j.storageengine.api.StorageRelationshipTraversalCursor;
+import org.neo4j.token.TokenHolders;
+
+public class InMemoryNodeCursor extends AbstractInMemoryNodeCursor {
+
+ public InMemoryNodeCursor(GraphStore graphStore, TokenHolders tokenHolders) {
+ super(graphStore, tokenHolders);
+ }
+
+ @Override
+ public boolean hasLabel() {
+ return hasAtLeastOneLabelForCurrentNode();
+ }
+
+ @Override
+ public Reference propertiesReference() {
+ return LongReference.longReference(getId());
+ }
+
+ @Override
+ public void properties(StoragePropertyCursor propertyCursor, PropertySelection selection) {
+ propertyCursor.initNodeProperties(propertiesReference(), selection);
+ }
+
+ @Override
+ public void properties(StoragePropertyCursor propertyCursor) {
+ properties(propertyCursor, PropertySelection.ALL_PROPERTIES);
+ }
+
+ @Override
+ public boolean supportsFastRelationshipsTo() {
+ return false;
+ }
+
+ @Override
+ public void relationshipsTo(
+ StorageRelationshipTraversalCursor storageRelationshipTraversalCursor,
+ RelationshipSelection relationshipSelection,
+ long neighbourNodeReference
+ ) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void degrees(RelationshipSelection selection, Degrees.Mutator mutator) {
+ }
+
+ @Override
+ public boolean scanBatch(AllNodeScan allNodeScan, long sizeHint) {
+ return super.scanBatch(allNodeScan, (int) sizeHint);
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryNodePropertyCursor.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryNodePropertyCursor.java
new file mode 100644
index 00000000000..f956b5a6d53
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryNodePropertyCursor.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.compat.AbstractInMemoryNodePropertyCursor;
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.storageengine.api.LongReference;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.Reference;
+import org.neo4j.token.TokenHolders;
+
+public class InMemoryNodePropertyCursor extends AbstractInMemoryNodePropertyCursor {
+
+ public InMemoryNodePropertyCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) {
+ super(graphStore, tokenHolders);
+ }
+
+ @Override
+ public void initNodeProperties(Reference reference, PropertySelection selection, long ownerReference) {
+ reset();
+ setId(((LongReference) reference).id);
+ setPropertySelection(new InMemoryPropertySelectionImpl(selection));
+ }
+
+ @Override
+ public void initRelationshipProperties(Reference reference, PropertySelection selection, long ownerReference) {
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryPropertyCursor.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryPropertyCursor.java
new file mode 100644
index 00000000000..6420fa80c4c
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryPropertyCursor.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.compat.AbstractInMemoryPropertyCursor;
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.Reference;
+import org.neo4j.storageengine.api.StorageNodeCursor;
+import org.neo4j.storageengine.api.StorageRelationshipCursor;
+import org.neo4j.token.TokenHolders;
+
+public class InMemoryPropertyCursor extends AbstractInMemoryPropertyCursor {
+
+ public InMemoryPropertyCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) {
+ super(graphStore, tokenHolders);
+ }
+
+ @Override
+ public void initNodeProperties(Reference reference, PropertySelection selection, long ownerReference) {
+ if (this.delegate == null || !(this.delegate instanceof InMemoryNodePropertyCursor)) {
+ this.delegate = new InMemoryNodePropertyCursor(graphStore, tokenHolders);
+ }
+
+ ((InMemoryNodePropertyCursor) delegate).initNodeProperties(reference, selection);
+ }
+
+ @Override
+ public void initNodeProperties(StorageNodeCursor nodeCursor, PropertySelection selection) {
+ if (this.delegate == null || !(this.delegate instanceof InMemoryNodePropertyCursor)) {
+ this.delegate = new InMemoryNodePropertyCursor(graphStore, tokenHolders);
+ }
+
+ ((InMemoryNodePropertyCursor) delegate).initNodeProperties(nodeCursor, selection);
+ }
+
+ @Override
+ public void initRelationshipProperties(StorageRelationshipCursor relationshipCursor, PropertySelection selection) {
+ if (this.delegate == null || !(this.delegate instanceof InMemoryRelationshipPropertyCursor)) {
+ this.delegate = new InMemoryRelationshipPropertyCursor(graphStore, tokenHolders);
+ }
+
+ ((InMemoryRelationshipPropertyCursor) delegate).initRelationshipProperties(relationshipCursor, selection);
+ }
+
+ @Override
+ public void initRelationshipProperties(Reference reference, PropertySelection selection, long ownerReference) {
+ if (this.delegate == null || !(this.delegate instanceof InMemoryRelationshipPropertyCursor)) {
+ this.delegate = new InMemoryRelationshipPropertyCursor(graphStore, tokenHolders);
+ }
+
+ ((InMemoryRelationshipPropertyCursor) delegate).initRelationshipProperties(reference, selection);
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryPropertySelectionImpl.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryPropertySelectionImpl.java
new file mode 100644
index 00000000000..6cc18b723c0
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryPropertySelectionImpl.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.compat.InMemoryPropertySelection;
+import org.neo4j.storageengine.api.PropertySelection;
+
+public class InMemoryPropertySelectionImpl implements InMemoryPropertySelection {
+
+ private final PropertySelection propertySelection;
+
+ public InMemoryPropertySelectionImpl(PropertySelection propertySelection) {this.propertySelection = propertySelection;}
+
+ @Override
+ public boolean isLimited() {
+ return propertySelection.isLimited();
+ }
+
+ @Override
+ public int numberOfKeys() {
+ return propertySelection.numberOfKeys();
+ }
+
+ @Override
+ public int key(int index) {
+ return propertySelection.key(index);
+ }
+
+ @Override
+ public boolean test(int key) {
+ return propertySelection.test(key);
+ }
+
+ @Override
+ public boolean isKeysOnly() {
+ return propertySelection.isKeysOnly();
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryRelationshipPropertyCursor.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryRelationshipPropertyCursor.java
new file mode 100644
index 00000000000..0d3efb743c1
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryRelationshipPropertyCursor.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.compat.AbstractInMemoryRelationshipPropertyCursor;
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.gds.storageengine.InMemoryRelationshipCursor;
+import org.neo4j.storageengine.api.LongReference;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.Reference;
+import org.neo4j.storageengine.api.StorageRelationshipCursor;
+import org.neo4j.token.TokenHolders;
+
+public class InMemoryRelationshipPropertyCursor extends AbstractInMemoryRelationshipPropertyCursor {
+
+ InMemoryRelationshipPropertyCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) {
+ super(graphStore, tokenHolders);
+ }
+
+ @Override
+ public void initNodeProperties(
+ Reference reference, PropertySelection propertySelection, long ownerReference
+ ) {
+
+ }
+
+ @Override
+ public void initRelationshipProperties(
+ Reference reference, PropertySelection propertySelection, long ownerReference
+ ) {
+ var relationshipId = ((LongReference) reference).id;
+ var relationshipCursor = new InMemoryRelationshipScanCursor(graphStore, tokenHolders);
+ relationshipCursor.single(relationshipId);
+ relationshipCursor.next();
+ relationshipCursor.properties(this, new InMemoryPropertySelectionImpl(propertySelection));
+ }
+
+ @Override
+ public void initRelationshipProperties(StorageRelationshipCursor relationshipCursor, PropertySelection selection) {
+ var inMemoryRelationshipCursor = (InMemoryRelationshipCursor) relationshipCursor;
+ inMemoryRelationshipCursor.properties(this, selection);
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryRelationshipScanCursor.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryRelationshipScanCursor.java
new file mode 100644
index 00000000000..9473f7cbb49
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryRelationshipScanCursor.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.internal.recordstorage.AbstractInMemoryRelationshipScanCursor;
+import org.neo4j.storageengine.api.AllRelationshipsScan;
+import org.neo4j.storageengine.api.LongReference;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.Reference;
+import org.neo4j.storageengine.api.StoragePropertyCursor;
+import org.neo4j.token.TokenHolders;
+
+public class InMemoryRelationshipScanCursor extends AbstractInMemoryRelationshipScanCursor {
+
+ public InMemoryRelationshipScanCursor(
+ CypherGraphStore graphStore,
+ TokenHolders tokenHolders
+ ) {
+ super(graphStore, tokenHolders);
+ }
+
+ @Override
+ public void single(long reference, long sourceNodeReference, int type, long targetNodeReference) {
+ single(reference);
+ }
+
+ @Override
+ public Reference propertiesReference() {
+ return LongReference.longReference(getId());
+ }
+
+ @Override
+ public void properties(
+ StoragePropertyCursor storagePropertyCursor, PropertySelection propertySelection
+ ) {
+ properties(storagePropertyCursor, new InMemoryPropertySelectionImpl(propertySelection));
+ }
+
+ @Override
+ public boolean scanBatch(AllRelationshipsScan allRelationshipsScan, long sizeHint) {
+ return super.scanBatch(allRelationshipsScan, (int) sizeHint);
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryRelationshipTraversalCursor.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryRelationshipTraversalCursor.java
new file mode 100644
index 00000000000..9abb9283d79
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryRelationshipTraversalCursor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.gds.compat.AbstractInMemoryRelationshipTraversalCursor;
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.storageengine.api.LongReference;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.Reference;
+import org.neo4j.storageengine.api.StoragePropertyCursor;
+import org.neo4j.token.TokenHolders;
+
+public class InMemoryRelationshipTraversalCursor extends AbstractInMemoryRelationshipTraversalCursor {
+
+ public InMemoryRelationshipTraversalCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) {
+ super(graphStore, tokenHolders);
+ }
+
+ @Override
+ public Reference propertiesReference() {
+ return LongReference.longReference(getId());
+ }
+
+ @Override
+ public void properties(
+ StoragePropertyCursor propertyCursor, PropertySelection selection
+ ) {
+ properties(propertyCursor, new InMemoryPropertySelectionImpl(selection));
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryStorageEngineFactory.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryStorageEngineFactory.java
new file mode 100644
index 00000000000..a4986e7f275
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryStorageEngineFactory.java
@@ -0,0 +1,569 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.eclipse.collections.api.factory.Sets;
+import org.eclipse.collections.api.set.ImmutableSet;
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.configuration.Config;
+import org.neo4j.consistency.checking.ConsistencyFlags;
+import org.neo4j.consistency.report.ConsistencySummaryStatistics;
+import org.neo4j.dbms.database.readonly.DatabaseReadOnlyChecker;
+import org.neo4j.function.ThrowingSupplier;
+import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.StorageEngineProxyApi;
+import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector;
+import org.neo4j.internal.batchimport.AdditionalInitialIds;
+import org.neo4j.internal.batchimport.BatchImporter;
+import org.neo4j.internal.batchimport.Configuration;
+import org.neo4j.internal.batchimport.IncrementalBatchImporter;
+import org.neo4j.internal.batchimport.IndexImporterFactory;
+import org.neo4j.internal.batchimport.Monitor;
+import org.neo4j.internal.batchimport.ReadBehaviour;
+import org.neo4j.internal.batchimport.input.Collector;
+import org.neo4j.internal.batchimport.input.Input;
+import org.neo4j.internal.batchimport.input.LenientStoreInput;
+import org.neo4j.internal.id.IdGeneratorFactory;
+import org.neo4j.internal.id.ScanOnOpenReadOnlyIdGeneratorFactory;
+import org.neo4j.internal.recordstorage.InMemoryLogVersionRepository54;
+import org.neo4j.internal.recordstorage.InMemoryStorageCommandReaderFactory54;
+import org.neo4j.internal.recordstorage.StoreTokens;
+import org.neo4j.internal.schema.IndexConfigCompleter;
+import org.neo4j.internal.schema.SchemaRule;
+import org.neo4j.internal.schema.SchemaState;
+import org.neo4j.io.fs.FileSystemAbstraction;
+import org.neo4j.io.layout.DatabaseLayout;
+import org.neo4j.io.layout.Neo4jLayout;
+import org.neo4j.io.layout.recordstorage.RecordDatabaseLayout;
+import org.neo4j.io.pagecache.PageCache;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.io.pagecache.context.CursorContextFactory;
+import org.neo4j.io.pagecache.tracing.PageCacheTracer;
+import org.neo4j.kernel.KernelVersionRepository;
+import org.neo4j.kernel.api.index.IndexProvidersAccess;
+import org.neo4j.kernel.impl.api.index.IndexProviderMap;
+import org.neo4j.kernel.impl.locking.Locks;
+import org.neo4j.kernel.impl.store.MetaDataStore;
+import org.neo4j.kernel.impl.store.NeoStores;
+import org.neo4j.kernel.impl.store.StoreFactory;
+import org.neo4j.kernel.impl.store.StoreType;
+import org.neo4j.kernel.impl.store.cursor.CachedStoreCursors;
+import org.neo4j.kernel.impl.transaction.log.LogTailMetadata;
+import org.neo4j.lock.LockService;
+import org.neo4j.logging.InternalLog;
+import org.neo4j.logging.InternalLogProvider;
+import org.neo4j.logging.NullLogProvider;
+import org.neo4j.logging.internal.LogService;
+import org.neo4j.memory.MemoryTracker;
+import org.neo4j.monitoring.DatabaseHealth;
+import org.neo4j.scheduler.JobScheduler;
+import org.neo4j.storageengine.api.CommandReaderFactory;
+import org.neo4j.storageengine.api.ConstraintRuleAccessor;
+import org.neo4j.storageengine.api.LogFilesInitializer;
+import org.neo4j.storageengine.api.LogVersionRepository;
+import org.neo4j.storageengine.api.MetadataProvider;
+import org.neo4j.storageengine.api.SchemaRule44;
+import org.neo4j.storageengine.api.StorageEngine;
+import org.neo4j.storageengine.api.StorageEngineFactory;
+import org.neo4j.storageengine.api.StorageFilesState;
+import org.neo4j.storageengine.api.StoreId;
+import org.neo4j.storageengine.api.StoreVersion;
+import org.neo4j.storageengine.api.StoreVersionCheck;
+import org.neo4j.storageengine.api.StoreVersionIdentifier;
+import org.neo4j.storageengine.api.TransactionIdStore;
+import org.neo4j.storageengine.migration.StoreMigrationParticipant;
+import org.neo4j.time.SystemNanoClock;
+import org.neo4j.token.DelegatingTokenHolder;
+import org.neo4j.token.ReadOnlyTokenCreator;
+import org.neo4j.token.TokenHolders;
+import org.neo4j.token.api.NamedToken;
+import org.neo4j.token.api.TokenHolder;
+import org.neo4j.token.api.TokensLoader;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.UncheckedIOException;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.time.Clock;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Function;
+
+@ServiceProvider
+public class InMemoryStorageEngineFactory implements StorageEngineFactory {
+
+ static final String IN_MEMORY_STORAGE_ENGINE_NAME = "in-memory-54";
+
+ public InMemoryStorageEngineFactory() {
+ StorageEngineProxyApi.requireNeo4jVersion(Neo4jVersion.V_5_4, StorageEngineFactory.class);
+ }
+
+ // Record storage = 0, Freki = 1
+ // Let's leave some room for future storage engines
+ // This arbitrary seems quite future-proof
+
+ public static final byte ID = 42;
+ @Override
+ public byte id() {
+ return ID;
+ }
+
+ @Override
+ public boolean storageExists(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout) {
+ return false;
+ }
+
+ @Override
+ public StorageEngine instantiate(
+ FileSystemAbstraction fs,
+ Clock clock,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ TokenHolders tokenHolders,
+ SchemaState schemaState,
+ ConstraintRuleAccessor constraintSemantics,
+ IndexConfigCompleter indexConfigCompleter,
+ LockService lockService,
+ IdGeneratorFactory idGeneratorFactory,
+ DatabaseHealth databaseHealth,
+ InternalLogProvider internalLogProvider,
+ InternalLogProvider userLogProvider,
+ RecoveryCleanupWorkCollector recoveryCleanupWorkCollector,
+ LogTailMetadata logTailMetadata,
+ KernelVersionRepository kernelVersionRepository,
+ MemoryTracker memoryTracker,
+ CursorContextFactory cursorContextFactory,
+ PageCacheTracer pageCacheTracer
+ ) {
+ StoreFactory factory = new StoreFactory(
+ databaseLayout,
+ config,
+ idGeneratorFactory,
+ pageCache,
+ pageCacheTracer,
+ fs,
+ internalLogProvider,
+ cursorContextFactory,
+ false,
+ logTailMetadata
+ );
+
+ factory.openNeoStores(StoreType.LABEL_TOKEN).close();
+
+ return new InMemoryStorageEngineImpl(
+ databaseLayout,
+ tokenHolders
+ );
+ }
+
+ @Override
+ public Optional databaseIdUuid(
+ FileSystemAbstraction fs, DatabaseLayout databaseLayout, PageCache pageCache, CursorContext cursorContext
+ ) {
+ var fieldAccess = MetaDataStore.getFieldAccess(
+ pageCache,
+ RecordDatabaseLayout.convert(databaseLayout).metadataStore(),
+ databaseLayout.getDatabaseName(),
+ cursorContext
+ );
+
+ try {
+ return fieldAccess.readDatabaseUUID();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ @Override
+ public List migrationParticipants(
+ FileSystemAbstraction fileSystemAbstraction,
+ Config config,
+ PageCache pageCache,
+ JobScheduler jobScheduler,
+ LogService logService,
+ MemoryTracker memoryTracker,
+ PageCacheTracer pageCacheTracer,
+ CursorContextFactory cursorContextFactory,
+ boolean b
+ ) {
+ return List.of();
+ }
+
+ @Override
+ public DatabaseLayout databaseLayout(
+ Neo4jLayout neo4jLayout, String databaseName
+ ) {
+ return RecordDatabaseLayout.of(neo4jLayout, databaseName);
+ }
+
+ @Override
+ public DatabaseLayout formatSpecificDatabaseLayout(DatabaseLayout plainLayout) {
+ return databaseLayout(plainLayout.getNeo4jLayout(), plainLayout.getDatabaseName());
+ }
+
+ @SuppressForbidden(reason = "This is the compat layer and we don't really need to go through the proxy")
+ @Override
+ public BatchImporter batchImporter(
+ DatabaseLayout databaseLayout,
+ FileSystemAbstraction fileSystemAbstraction,
+ PageCacheTracer pageCacheTracer,
+ Configuration configuration,
+ LogService logService,
+ PrintStream printStream,
+ boolean b,
+ AdditionalInitialIds additionalInitialIds,
+ Config config,
+ Monitor monitor,
+ JobScheduler jobScheduler,
+ Collector collector,
+ LogFilesInitializer logFilesInitializer,
+ IndexImporterFactory indexImporterFactory,
+ MemoryTracker memoryTracker,
+ CursorContextFactory cursorContextFactory
+ ) {
+ throw new UnsupportedOperationException("Batch Import into GDS is not supported");
+ }
+
+ @Override
+ public Input asBatchImporterInput(
+ DatabaseLayout databaseLayout,
+ FileSystemAbstraction fileSystemAbstraction,
+ PageCache pageCache,
+ PageCacheTracer pageCacheTracer,
+ Config config,
+ MemoryTracker memoryTracker,
+ ReadBehaviour readBehaviour,
+ boolean b,
+ CursorContextFactory cursorContextFactory,
+ LogTailMetadata logTailMetadata
+ ) {
+ NeoStores neoStores = (new StoreFactory(
+ databaseLayout,
+ config,
+ new ScanOnOpenReadOnlyIdGeneratorFactory(),
+ pageCache,
+ pageCacheTracer,
+ fileSystemAbstraction,
+ NullLogProvider.getInstance(),
+ cursorContextFactory,
+ false,
+ logTailMetadata
+ )).openAllNeoStores();
+ return new LenientStoreInput(
+ neoStores,
+ readBehaviour.decorateTokenHolders(this.loadReadOnlyTokens(neoStores, true, cursorContextFactory)),
+ true,
+ cursorContextFactory,
+ readBehaviour
+ );
+ }
+
+ @Override
+ public long optimalAvailableConsistencyCheckerMemory(
+ FileSystemAbstraction fileSystemAbstraction,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache
+ ) {
+ return 0;
+ }
+
+ @Override
+ public String name() {
+ return IN_MEMORY_STORAGE_ENGINE_NAME;
+ }
+
+ @Override
+ public Set supportedFormats(boolean includeFormatsUnderDevelopment) {
+ return Set.of(IN_MEMORY_STORAGE_ENGINE_NAME);
+ }
+
+ @Override
+ public boolean supportedFormat(String format, boolean includeFormatsUnderDevelopment) {
+ return format.equals(IN_MEMORY_STORAGE_ENGINE_NAME);
+ }
+
+ @Override
+ public MetadataProvider transactionMetaDataStore(
+ FileSystemAbstraction fileSystemAbstraction,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ DatabaseReadOnlyChecker databaseReadOnlyChecker,
+ CursorContextFactory cursorContextFactory,
+ LogTailMetadata logTailMetadata,
+ PageCacheTracer pageCacheTracer
+ ) {
+ return new InMemoryMetaDataProviderImpl();
+ }
+
+ @Override
+ public StoreVersionCheck versionCheck(
+ FileSystemAbstraction fileSystemAbstraction,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ LogService logService,
+ CursorContextFactory cursorContextFactory
+ ) {
+ return new InMemoryVersionCheck();
+ }
+
+ @Override
+ public List loadSchemaRules(
+ FileSystemAbstraction fileSystemAbstraction,
+ PageCache pageCache,
+ PageCacheTracer pageCacheTracer,
+ Config config,
+ DatabaseLayout databaseLayout,
+ boolean b,
+ Function function,
+ CursorContextFactory cursorContextFactory
+ ) {
+ return List.of();
+ }
+
+ @Override
+ public List load44SchemaRules(
+ FileSystemAbstraction fileSystemAbstraction,
+ PageCache pageCache,
+ PageCacheTracer pageCacheTracer,
+ Config config,
+ DatabaseLayout databaseLayout,
+ CursorContextFactory cursorContextFactory,
+ LogTailMetadata logTailMetadata
+ ) {
+ return List.of();
+ }
+
+ @Override
+ public TokenHolders loadReadOnlyTokens(
+ FileSystemAbstraction fileSystemAbstraction,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ PageCacheTracer pageCacheTracer,
+ boolean lenient,
+ CursorContextFactory cursorContextFactory
+ ) {
+ StoreFactory factory = new StoreFactory(
+ databaseLayout,
+ config,
+ new ScanOnOpenReadOnlyIdGeneratorFactory(),
+ pageCache,
+ pageCacheTracer,
+ fileSystemAbstraction,
+ NullLogProvider.getInstance(),
+ cursorContextFactory,
+ false,
+ LogTailMetadata.EMPTY_LOG_TAIL
+ );
+ try ( NeoStores stores = factory.openNeoStores(
+ StoreType.PROPERTY_KEY_TOKEN, StoreType.PROPERTY_KEY_TOKEN_NAME,
+ StoreType.LABEL_TOKEN, StoreType.LABEL_TOKEN_NAME,
+ StoreType.RELATIONSHIP_TYPE_TOKEN, StoreType.RELATIONSHIP_TYPE_TOKEN_NAME ) )
+ {
+ return loadReadOnlyTokens(stores, lenient, cursorContextFactory);
+ }
+ }
+
+ private TokenHolders loadReadOnlyTokens(
+ NeoStores stores,
+ boolean lenient,
+ CursorContextFactory cursorContextFactory
+ )
+ {
+ try ( var cursorContext = cursorContextFactory.create("loadReadOnlyTokens");
+ var storeCursors = new CachedStoreCursors( stores, cursorContext ) )
+ {
+ stores.start( cursorContext );
+ TokensLoader loader = lenient ? StoreTokens.allReadableTokens( stores ) : StoreTokens.allTokens( stores );
+ TokenHolder propertyKeys = new DelegatingTokenHolder( ReadOnlyTokenCreator.READ_ONLY, TokenHolder.TYPE_PROPERTY_KEY );
+ TokenHolder labels = new DelegatingTokenHolder( ReadOnlyTokenCreator.READ_ONLY, TokenHolder.TYPE_LABEL );
+ TokenHolder relationshipTypes = new DelegatingTokenHolder( ReadOnlyTokenCreator.READ_ONLY, TokenHolder.TYPE_RELATIONSHIP_TYPE );
+
+ propertyKeys.setInitialTokens( lenient ? unique( loader.getPropertyKeyTokens( storeCursors ) ) : loader.getPropertyKeyTokens( storeCursors ) );
+ labels.setInitialTokens( lenient ? unique( loader.getLabelTokens( storeCursors ) ) : loader.getLabelTokens( storeCursors ) );
+ relationshipTypes.setInitialTokens(
+ lenient ? unique( loader.getRelationshipTypeTokens( storeCursors ) ) : loader.getRelationshipTypeTokens( storeCursors ) );
+ return new TokenHolders( propertyKeys, labels, relationshipTypes );
+ }
+ catch ( IOException e )
+ {
+ throw new UncheckedIOException( e );
+ }
+ }
+
+ private static List unique( List tokens )
+ {
+ if ( !tokens.isEmpty() )
+ {
+ Set names = new HashSet<>( tokens.size() );
+ int i = 0;
+ while ( i < tokens.size() )
+ {
+ if ( names.add( tokens.get( i ).name() ) )
+ {
+ i++;
+ }
+ else
+ {
+ // Remove the token at the given index, by replacing it with the last token in the list.
+ // This changes the order of elements, but can be done in constant time instead of linear time.
+ int lastIndex = tokens.size() - 1;
+ NamedToken endToken = tokens.remove( lastIndex );
+ if ( i < lastIndex )
+ {
+ tokens.set( i, endToken );
+ }
+ }
+ }
+ }
+ return tokens;
+ }
+
+ @Override
+ public CommandReaderFactory commandReaderFactory() {
+ return InMemoryStorageCommandReaderFactory54.INSTANCE;
+ }
+
+ @Override
+ public void consistencyCheck(
+ FileSystemAbstraction fileSystem,
+ DatabaseLayout layout,
+ Config config,
+ PageCache pageCache,
+ IndexProviderMap indexProviders,
+ InternalLog log,
+ ConsistencySummaryStatistics summary,
+ int numberOfThreads,
+ long maxOffHeapCachingMemory,
+ OutputStream progressOutput,
+ boolean verbose,
+ ConsistencyFlags flags,
+ CursorContextFactory contextFactory,
+ PageCacheTracer pageCacheTracer,
+ LogTailMetadata logTailMetadata
+ ) {
+ // we can do no-op, since our "database" is _always_ consistent
+ }
+
+ @Override
+ public ImmutableSet getStoreOpenOptions(
+ FileSystemAbstraction fs,
+ PageCache pageCache,
+ DatabaseLayout layout,
+ CursorContextFactory contextFactory
+ ) {
+ // Not sure about this, empty set is returned when the store files are in `little-endian` format
+ // See: `org.neo4j.kernel.impl.store.format.PageCacheOptionsSelector.select`
+ return Sets.immutable.empty();
+ }
+
+ @Override
+ public TransactionIdStore readOnlyTransactionIdStore(LogTailMetadata logTailMetadata) {
+ return new InMemoryMetaDataProviderImpl().transactionIdStore();
+ }
+
+ @Override
+ public LogVersionRepository readOnlyLogVersionRepository(LogTailMetadata logTailMetadata) {
+ return new InMemoryLogVersionRepository54();
+ }
+
+ @Override
+ public StoreId retrieveStoreId(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ return StoreId.retrieveFromStore(fs, databaseLayout, pageCache, cursorContext);
+ }
+
+
+ @Override
+ public Optional versionInformation(StoreVersionIdentifier storeVersionIdentifier) {
+ return Optional.of(new InMemoryStoreVersion());
+ }
+
+ @Override
+ public void resetMetadata(
+ FileSystemAbstraction fileSystemAbstraction,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ CursorContextFactory cursorContextFactory,
+ PageCacheTracer pageCacheTracer,
+ StoreId storeId,
+ UUID externalStoreId
+ ) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public IncrementalBatchImporter incrementalBatchImporter(
+ DatabaseLayout databaseLayout,
+ FileSystemAbstraction fileSystemAbstraction,
+ PageCacheTracer pageCacheTracer,
+ Configuration configuration,
+ LogService logService,
+ PrintStream printStream,
+ boolean b,
+ AdditionalInitialIds additionalInitialIds,
+ ThrowingSupplier throwingSupplier,
+ Config config,
+ Monitor monitor,
+ JobScheduler jobScheduler,
+ Collector collector,
+ IndexImporterFactory indexImporterFactory,
+ MemoryTracker memoryTracker,
+ CursorContextFactory cursorContextFactory,
+ IndexProvidersAccess indexProvidersAccess
+ ) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Locks createLocks(Config config, SystemNanoClock clock) {
+ return Locks.NO_LOCKS;
+ }
+
+ @Override
+ public List listStorageFiles(
+ FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout
+ ) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public StorageFilesState checkStoreFileState(
+ FileSystemAbstraction fs, DatabaseLayout databaseLayout, PageCache pageCache
+ ) {
+ return StorageFilesState.recoveredState();
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryStorageEngineImpl.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryStorageEngineImpl.java
new file mode 100644
index 00000000000..159449bbf2b
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryStorageEngineImpl.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.counts.CountsAccessor;
+import org.neo4j.exceptions.KernelException;
+import org.neo4j.gds.compat.TokenManager;
+import org.neo4j.gds.config.GraphProjectConfig;
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.gds.core.loading.GraphStoreCatalog;
+import org.neo4j.gds.storageengine.InMemoryDatabaseCreationCatalog;
+import org.neo4j.gds.storageengine.InMemoryTransactionStateVisitor;
+import org.neo4j.internal.diagnostics.DiagnosticsLogger;
+import org.neo4j.internal.recordstorage.InMemoryStorageReader54;
+import org.neo4j.internal.schema.StorageEngineIndexingBehaviour;
+import org.neo4j.io.layout.DatabaseLayout;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.io.pagecache.tracing.DatabaseFlushEvent;
+import org.neo4j.kernel.KernelVersion;
+import org.neo4j.kernel.impl.store.stats.StoreEntityCounters;
+import org.neo4j.kernel.lifecycle.Lifecycle;
+import org.neo4j.kernel.lifecycle.LifecycleAdapter;
+import org.neo4j.lock.LockGroup;
+import org.neo4j.lock.LockService;
+import org.neo4j.lock.LockTracer;
+import org.neo4j.lock.ResourceLocker;
+import org.neo4j.logging.InternalLog;
+import org.neo4j.memory.MemoryTracker;
+import org.neo4j.storageengine.api.CommandBatchToApply;
+import org.neo4j.storageengine.api.CommandCreationContext;
+import org.neo4j.storageengine.api.CommandStream;
+import org.neo4j.storageengine.api.IndexUpdateListener;
+import org.neo4j.storageengine.api.MetadataProvider;
+import org.neo4j.storageengine.api.StorageCommand;
+import org.neo4j.storageengine.api.StorageEngine;
+import org.neo4j.storageengine.api.StorageLocks;
+import org.neo4j.storageengine.api.StorageReader;
+import org.neo4j.storageengine.api.StoreFileMetadata;
+import org.neo4j.storageengine.api.StoreId;
+import org.neo4j.storageengine.api.TransactionApplicationMode;
+import org.neo4j.storageengine.api.cursor.StoreCursors;
+import org.neo4j.storageengine.api.txstate.ReadableTransactionState;
+import org.neo4j.storageengine.api.txstate.TxStateVisitor;
+import org.neo4j.token.TokenHolders;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.neo4j.gds.utils.StringFormatting.formatWithLocale;
+
+public final class InMemoryStorageEngineImpl implements StorageEngine {
+
+ private final MetadataProvider metadataProvider;
+ private final CypherGraphStore graphStore;
+ private final DatabaseLayout databaseLayout;
+ private final InMemoryTransactionStateVisitor txStateVisitor;
+
+ private final CommandCreationContext commandCreationContext;
+
+ private final TokenManager tokenManager;
+ private final InMemoryCountsStoreImpl countsStore;
+
+ private final StorageEngineIndexingBehaviour indexingBehaviour = () -> false;
+
+ InMemoryStorageEngineImpl(
+ DatabaseLayout databaseLayout,
+ TokenHolders tokenHolders
+ ) {
+ this.databaseLayout = databaseLayout;
+ this.graphStore = getGraphStoreFromCatalog(databaseLayout.getDatabaseName());
+ this.txStateVisitor = new InMemoryTransactionStateVisitor(graphStore, tokenHolders);
+ this.commandCreationContext = new InMemoryCommandCreationContextImpl();
+ this.tokenManager = new TokenManager(
+ tokenHolders,
+ InMemoryStorageEngineImpl.this.txStateVisitor,
+ InMemoryStorageEngineImpl.this.graphStore,
+ commandCreationContext
+ );
+ InMemoryStorageEngineImpl.this.graphStore.initialize(tokenHolders);
+ this.countsStore = new InMemoryCountsStoreImpl(graphStore, tokenHolders);
+ this.metadataProvider = new InMemoryMetaDataProviderImpl();
+ }
+
+ private static CypherGraphStore getGraphStoreFromCatalog(String databaseName) {
+ var graphName = InMemoryDatabaseCreationCatalog.getRegisteredDbCreationGraphName(databaseName);
+ return (CypherGraphStore) GraphStoreCatalog.getAllGraphStores()
+ .filter(graphStoreWithUserNameAndConfig -> graphStoreWithUserNameAndConfig
+ .config()
+ .graphName()
+ .equals(graphName))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(formatWithLocale(
+ "No graph with name `%s` was found in GraphStoreCatalog. Available graph names are %s",
+ graphName,
+ GraphStoreCatalog.getAllGraphStores()
+ .map(GraphStoreCatalog.GraphStoreWithUserNameAndConfig::config)
+ .map(GraphProjectConfig::graphName)
+ .collect(Collectors.toList())
+ )))
+ .graphStore();
+ }
+
+ @Override
+ public StoreEntityCounters storeEntityCounters() {
+ return new StoreEntityCounters() {
+ @Override
+ public long nodes() {
+ return graphStore.nodeCount();
+ }
+
+ @Override
+ public long relationships() {
+ return graphStore.relationshipCount();
+ }
+
+ @Override
+ public long properties() {
+ return graphStore.nodePropertyKeys().size() + graphStore.relationshipPropertyKeys().size();
+ }
+
+ @Override
+ public long relationshipTypes() {
+ return graphStore.relationshipTypes().size();
+ }
+
+ @Override
+ public long allNodesCountStore(CursorContext cursorContext) {
+ return graphStore.nodeCount();
+ }
+
+ @Override
+ public long allRelationshipsCountStore(CursorContext cursorContext) {
+ return graphStore.relationshipCount();
+ }
+ };
+ }
+
+ @Override
+ public StoreCursors createStorageCursors(CursorContext initialContext) {
+ return StoreCursors.NULL;
+ }
+
+ @Override
+ public StorageLocks createStorageLocks(ResourceLocker locker) {
+ return new InMemoryStorageLocksImpl(locker);
+ }
+
+ @Override
+ public List createCommands(
+ ReadableTransactionState state,
+ StorageReader storageReader,
+ CommandCreationContext creationContext,
+ LockTracer lockTracer,
+ TxStateVisitor.Decorator additionalTxStateVisitor,
+ CursorContext cursorContext,
+ StoreCursors storeCursors,
+ MemoryTracker memoryTracker
+ ) throws KernelException {
+ state.accept(txStateVisitor);
+ return List.of();
+ }
+
+ @Override
+ public void dumpDiagnostics(InternalLog internalLog, DiagnosticsLogger diagnosticsLogger) {
+ }
+
+ @Override
+ public List createUpgradeCommands(
+ KernelVersion versionToUpgradeFrom,
+ KernelVersion versionToUpgradeTo
+ ) {
+ return List.of();
+ }
+
+ @Override
+ public StoreId retrieveStoreId() {
+ return metadataProvider.getStoreId();
+ }
+
+ @Override
+ public StorageEngineIndexingBehaviour indexingBehaviour() {
+ return indexingBehaviour;
+ }
+
+ @Override
+ public StorageReader newReader() {
+ return new InMemoryStorageReader54(graphStore, tokenManager.tokenHolders(), countsStore);
+ }
+
+ @Override
+ public void addIndexUpdateListener(IndexUpdateListener listener) {
+
+ }
+
+ @Override
+ public void apply(CommandBatchToApply batch, TransactionApplicationMode mode) {
+ }
+
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public void start() {
+
+ }
+
+ @Override
+ public void stop() {
+ shutdown();
+ }
+
+ @Override
+ public void shutdown() {
+ InMemoryDatabaseCreationCatalog.removeDatabaseEntry(databaseLayout.getDatabaseName());
+ }
+
+ @Override
+ public void listStorageFiles(
+ Collection atomic, Collection replayable
+ ) {
+
+ }
+
+ @Override
+ public Lifecycle schemaAndTokensLifecycle() {
+ return new LifecycleAdapter() {
+ @Override
+ public void init() {
+
+ }
+ };
+ }
+
+ @Override
+ public CountsAccessor countsAccessor() {
+ return countsStore;
+ }
+
+ @Override
+ public MetadataProvider metadataProvider() {
+ return metadataProvider;
+ }
+
+ @Override
+ public CommandCreationContext newCommandCreationContext() {
+ return commandCreationContext;
+ }
+
+ @Override
+ public void lockRecoveryCommands(
+ CommandStream commands, LockService lockService, LockGroup lockGroup, TransactionApplicationMode mode
+ ) {
+
+ }
+
+ @Override
+ public void rollback(ReadableTransactionState txState, CursorContext cursorContext) {
+ // rollback is not supported but it is also called when we fail for something else
+ // that we do not support, such as removing node properties
+ // TODO: do we want to inspect the txState to infer if rollback was called explicitly or not?
+ }
+
+ @Override
+ public void checkpoint(DatabaseFlushEvent flushEvent, CursorContext cursorContext) {
+ // checkpoint is not supported but it is also called when we fail for something else
+ // that we do not support, such as removing node properties
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryStorageLocksImpl.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryStorageLocksImpl.java
new file mode 100644
index 00000000000..b0f496aeda2
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryStorageLocksImpl.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.lock.LockTracer;
+import org.neo4j.lock.ResourceLocker;
+import org.neo4j.storageengine.api.StorageLocks;
+import org.neo4j.storageengine.api.txstate.ReadableTransactionState;
+
+public class InMemoryStorageLocksImpl implements StorageLocks {
+
+ InMemoryStorageLocksImpl(ResourceLocker locker) {}
+
+ @Override
+ public void acquireExclusiveNodeLock(LockTracer lockTracer, long... ids) {}
+
+ @Override
+ public void releaseExclusiveNodeLock(long... ids) {}
+
+ @Override
+ public void acquireSharedNodeLock(LockTracer lockTracer, long... ids) {}
+
+ @Override
+ public void releaseSharedNodeLock(long... ids) {}
+
+ @Override
+ public void acquireExclusiveRelationshipLock(LockTracer lockTracer, long... ids) {}
+
+ @Override
+ public void releaseExclusiveRelationshipLock(long... ids) {}
+
+ @Override
+ public void acquireSharedRelationshipLock(LockTracer lockTracer, long... ids) {}
+
+ @Override
+ public void releaseSharedRelationshipLock(long... ids) {}
+
+ @Override
+ public void acquireRelationshipCreationLock(
+ LockTracer lockTracer,
+ long sourceNode,
+ long targetNode,
+ boolean sourceNodeAddedInTx,
+ boolean targetNodeAddedInTx
+ ) {
+ }
+
+ @Override
+ public void acquireRelationshipDeletionLock(
+ LockTracer lockTracer,
+ long sourceNode,
+ long targetNode,
+ long relationship,
+ boolean relationshipAddedInTx,
+ boolean sourceNodeAddedInTx,
+ boolean targetNodeAddedInTx
+ ) {
+ }
+
+ @Override
+ public void acquireNodeDeletionLock(
+ ReadableTransactionState readableTransactionState,
+ LockTracer lockTracer,
+ long node
+ ) {}
+
+ @Override
+ public void acquireNodeLabelChangeLock(LockTracer lockTracer, long node, int labelId) {}
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryStoreVersion.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryStoreVersion.java
new file mode 100644
index 00000000000..b5663131022
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryStoreVersion.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.storageengine.api.StoreVersion;
+import org.neo4j.storageengine.api.format.Capability;
+import org.neo4j.storageengine.api.format.CapabilityType;
+
+import java.util.Optional;
+
+public class InMemoryStoreVersion implements StoreVersion {
+
+ public static final String STORE_VERSION = "gds-experimental";
+
+ @Override
+ public String getStoreVersionUserString() {
+ return "Unknown";
+ }
+
+ @Override
+ public Optional successorStoreVersion() {
+ return Optional.empty();
+ }
+
+ @Override
+ public String formatName() {
+ return getClass().getSimpleName();
+ }
+
+ @Override
+ public boolean onlyForMigration() {
+ return false;
+ }
+
+ @Override
+ public boolean hasCapability(Capability capability) {
+ return false;
+ }
+
+ @Override
+ public boolean hasCompatibleCapabilities(
+ StoreVersion otherVersion, CapabilityType type
+ ) {
+ return false;
+ }
+
+ @Override
+ public String introductionNeo4jVersion() {
+ return "foo";
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryTransactionIdStoreImpl.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryTransactionIdStoreImpl.java
new file mode 100644
index 00000000000..3a0ee477755
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryTransactionIdStoreImpl.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.internal.recordstorage.AbstractTransactionIdStore;
+import org.neo4j.kernel.impl.transaction.log.LogPosition;
+import org.neo4j.storageengine.api.ClosedTransactionMetadata;
+import org.neo4j.storageengine.api.TransactionId;
+
+public class InMemoryTransactionIdStoreImpl extends AbstractTransactionIdStore {
+
+ @Override
+ protected void initLastCommittedAndClosedTransactionId(
+ long previouslyCommittedTxId,
+ int checksum,
+ long previouslyCommittedTxCommitTimestamp,
+ long previouslyCommittedTxLogByteOffset,
+ long previouslyCommittedTxLogVersion
+ ) {
+ this.setLastCommittedAndClosedTransactionId(
+ previouslyCommittedTxId,
+ checksum,
+ previouslyCommittedTxCommitTimestamp,
+ previouslyCommittedTxLogByteOffset,
+ previouslyCommittedTxLogVersion
+ );
+ }
+
+ @Override
+ public ClosedTransactionMetadata getLastClosedTransaction() {
+ long[] metaData = this.closedTransactionId.get();
+ return new ClosedTransactionMetadata(
+ metaData[0],
+ new LogPosition(metaData[1], metaData[2]),
+ (int) metaData[3],
+ metaData[4]
+ );
+ }
+
+ @Override
+ public void transactionClosed(
+ long transactionId,
+ long logVersion,
+ long byteOffset,
+ int checksum,
+ long commitTimestamp
+ ) {
+ this.closedTransactionId.offer(transactionId, new long[]{logVersion, byteOffset, checksum, commitTimestamp});
+ }
+
+ @Override
+ public void resetLastClosedTransaction(
+ long transactionId,
+ long logVersion,
+ long byteOffset,
+ int checksum,
+ long commitTimestamp
+ ) {
+ this.closedTransactionId.set(transactionId, new long[]{logVersion, byteOffset, checksum, commitTimestamp});
+ }
+
+ @Override
+ public void transactionCommitted(long transactionId, int checksum, long commitTimestamp) {
+ }
+
+ @Override
+ public void setLastCommittedAndClosedTransactionId(
+ long transactionId, int checksum, long commitTimestamp, long byteOffset, long logVersion
+ ) {
+ }
+
+ @Override
+ protected TransactionId transactionId(long transactionId, int checksum, long commitTimestamp) {
+ return new TransactionId(transactionId, checksum, commitTimestamp);
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryVersionCheck.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryVersionCheck.java
new file mode 100644
index 00000000000..45feb84ad9a
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/InMemoryVersionCheck.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.kernel.impl.store.format.FormatFamily;
+import org.neo4j.storageengine.api.StoreVersionCheck;
+import org.neo4j.storageengine.api.StoreVersionIdentifier;
+
+import static org.neo4j.gds.compat._54.InMemoryStoreVersion.STORE_VERSION;
+
+public class InMemoryVersionCheck implements StoreVersionCheck {
+
+ private static final StoreVersionIdentifier STORE_IDENTIFIER = new StoreVersionIdentifier(
+ STORE_VERSION,
+ FormatFamily.STANDARD.name(),
+ 0,
+ 0
+ );
+
+ @Override
+ public boolean isCurrentStoreVersionFullySupported(CursorContext cursorContext) {
+ return true;
+ }
+
+ @Override
+ public MigrationCheckResult getAndCheckMigrationTargetVersion(String formatFamily, CursorContext cursorContext) {
+ return new StoreVersionCheck.MigrationCheckResult(MigrationOutcome.NO_OP, STORE_IDENTIFIER, null, null);
+ }
+
+ @Override
+ public UpgradeCheckResult getAndCheckUpgradeTargetVersion(CursorContext cursorContext) {
+ return new StoreVersionCheck.UpgradeCheckResult(UpgradeOutcome.NO_OP, STORE_IDENTIFIER, null, null);
+ }
+
+ @Override
+ public String getIntroductionVersionFromVersion(StoreVersionIdentifier storeVersionIdentifier) {
+ return STORE_VERSION;
+ }
+
+ public StoreVersionIdentifier findLatestVersion(String s) {
+ return STORE_IDENTIFIER;
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/StorageEngineProxyFactoryImpl.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/StorageEngineProxyFactoryImpl.java
new file mode 100644
index 00000000000..7f07bf1e11a
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/StorageEngineProxyFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.StorageEngineProxyApi;
+import org.neo4j.gds.compat.StorageEngineProxyFactory;
+
+@ServiceProvider
+public class StorageEngineProxyFactoryImpl implements StorageEngineProxyFactory {
+
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return version == Neo4jVersion.V_5_4;
+ }
+
+ @Override
+ public StorageEngineProxyApi load() {
+ return new StorageEngineProxyImpl();
+ }
+
+ @Override
+ public String description() {
+ return "Storage Engine 5.4";
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/StorageEngineProxyImpl.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/StorageEngineProxyImpl.java
new file mode 100644
index 00000000000..f99cb355377
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_54/StorageEngineProxyImpl.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._54;
+
+import org.neo4j.common.Edition;
+import org.neo4j.configuration.Config;
+import org.neo4j.configuration.GraphDatabaseInternalSettings;
+import org.neo4j.counts.CountsAccessor;
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.gds.compat.AbstractInMemoryNodeCursor;
+import org.neo4j.gds.compat.AbstractInMemoryNodePropertyCursor;
+import org.neo4j.gds.compat.AbstractInMemoryRelationshipPropertyCursor;
+import org.neo4j.gds.compat.AbstractInMemoryRelationshipTraversalCursor;
+import org.neo4j.gds.compat.GdsDatabaseManagementServiceBuilder;
+import org.neo4j.gds.compat.GraphDatabaseApiProxy;
+import org.neo4j.gds.compat.StorageEngineProxyApi;
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.graphdb.Direction;
+import org.neo4j.graphdb.GraphDatabaseService;
+import org.neo4j.internal.recordstorage.AbstractInMemoryRelationshipScanCursor;
+import org.neo4j.internal.recordstorage.InMemoryStorageReader54;
+import org.neo4j.io.layout.DatabaseLayout;
+import org.neo4j.storageengine.api.CommandCreationContext;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.RelationshipSelection;
+import org.neo4j.storageengine.api.StorageEngine;
+import org.neo4j.storageengine.api.StorageEntityCursor;
+import org.neo4j.storageengine.api.StoragePropertyCursor;
+import org.neo4j.storageengine.api.StorageReader;
+import org.neo4j.storageengine.api.StorageRelationshipTraversalCursor;
+import org.neo4j.token.TokenHolders;
+
+import static org.neo4j.configuration.GraphDatabaseSettings.db_format;
+
+public class StorageEngineProxyImpl implements StorageEngineProxyApi {
+
+ @Override
+ public CommandCreationContext inMemoryCommandCreationContext() {
+ return new InMemoryCommandCreationContextImpl();
+ }
+
+ @Override
+ public void initRelationshipTraversalCursorForRelType(
+ StorageRelationshipTraversalCursor cursor,
+ long sourceNodeId,
+ int relTypeToken
+ ) {
+ var relationshipSelection = RelationshipSelection.selection(
+ relTypeToken,
+ Direction.OUTGOING
+ );
+ cursor.init(sourceNodeId, -1, relationshipSelection);
+ }
+
+ @Override
+ public StorageReader inMemoryStorageReader(
+ CypherGraphStore graphStore, TokenHolders tokenHolders, CountsAccessor counts
+ ) {
+ return new InMemoryStorageReader54(graphStore, tokenHolders, counts);
+ }
+
+ @Override
+ public StorageEngine createInMemoryStorageEngine(DatabaseLayout databaseLayout, TokenHolders tokenHolders) {
+ return new InMemoryStorageEngineImpl(databaseLayout, tokenHolders);
+ }
+
+ @Override
+ public void createInMemoryDatabase(
+ DatabaseManagementService dbms,
+ String dbName,
+ Config config
+ ) {
+ config.set(db_format, InMemoryStorageEngineFactory.IN_MEMORY_STORAGE_ENGINE_NAME);
+ dbms.createDatabase(dbName, config);
+ }
+
+ @Override
+ public GraphDatabaseService startAndGetInMemoryDatabase(DatabaseManagementService dbms, String dbName) {
+ dbms.startDatabase(dbName);
+ return dbms.database(dbName);
+ }
+
+ @Override
+ public GdsDatabaseManagementServiceBuilder setSkipDefaultIndexesOnCreationSetting(GdsDatabaseManagementServiceBuilder dbmsBuilder) {
+ return dbmsBuilder.setConfig(GraphDatabaseInternalSettings.skip_default_indexes_on_creation, true);
+ }
+
+ @Override
+ public AbstractInMemoryNodeCursor inMemoryNodeCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) {
+ return new InMemoryNodeCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public AbstractInMemoryNodePropertyCursor inMemoryNodePropertyCursor(
+ CypherGraphStore graphStore,
+ TokenHolders tokenHolders
+ ) {
+ return new InMemoryNodePropertyCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public AbstractInMemoryRelationshipTraversalCursor inMemoryRelationshipTraversalCursor(
+ CypherGraphStore graphStore, TokenHolders tokenHolders
+ ) {
+ return new InMemoryRelationshipTraversalCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public AbstractInMemoryRelationshipScanCursor inMemoryRelationshipScanCursor(
+ CypherGraphStore graphStore, TokenHolders tokenHolders
+ ) {
+ return new InMemoryRelationshipScanCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public AbstractInMemoryRelationshipPropertyCursor inMemoryRelationshipPropertyCursor(
+ CypherGraphStore graphStore, TokenHolders tokenHolders
+ ) {
+ return new InMemoryRelationshipPropertyCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public void properties(
+ StorageEntityCursor storageCursor, StoragePropertyCursor propertyCursor, int[] propertySelection
+ ) {
+ PropertySelection selection;
+ if (propertySelection.length == 0) {
+ selection = PropertySelection.ALL_PROPERTIES;
+ } else {
+ selection = PropertySelection.selection(propertySelection);
+ }
+ storageCursor.properties(propertyCursor, selection);
+ }
+
+ @Override
+ public Edition dbmsEdition(GraphDatabaseService databaseService) {
+ return GraphDatabaseApiProxy.dbmsInfo(databaseService).edition;
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository54.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository54.java
new file mode 100644
index 00000000000..c901f56269d
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository54.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.internal.recordstorage;
+
+import org.neo4j.storageengine.api.LogVersionRepository;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public class InMemoryLogVersionRepository54 implements LogVersionRepository {
+
+ private final AtomicLong logVersion;
+ private final AtomicLong checkpointLogVersion;
+
+ public InMemoryLogVersionRepository54() {
+ this(0, 0);
+ }
+
+ private InMemoryLogVersionRepository54(long initialLogVersion, long initialCheckpointLogVersion) {
+ this.logVersion = new AtomicLong();
+ this.checkpointLogVersion = new AtomicLong();
+ this.logVersion.set(initialLogVersion);
+ this.checkpointLogVersion.set(initialCheckpointLogVersion);
+ }
+
+ @Override
+ public void setCurrentLogVersion(long version) {
+ this.logVersion.set(version);
+ }
+
+ @Override
+ public long incrementAndGetVersion() {
+ return this.logVersion.incrementAndGet();
+ }
+
+ @Override
+ public void setCheckpointLogVersion(long version) {
+ this.checkpointLogVersion.set(version);
+ }
+
+ @Override
+ public long incrementAndGetCheckpointLogVersion() {
+ return this.checkpointLogVersion.incrementAndGet();
+ }
+
+ @Override
+ public long getCurrentLogVersion() {
+ return this.logVersion.get();
+ }
+
+ @Override
+ public long getCheckpointLogVersion() {
+ return this.checkpointLogVersion.get();
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory54.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory54.java
new file mode 100644
index 00000000000..4d84f1e0298
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory54.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.internal.recordstorage;
+
+import org.neo4j.kernel.KernelVersion;
+import org.neo4j.storageengine.api.CommandReader;
+import org.neo4j.storageengine.api.CommandReaderFactory;
+
+public class InMemoryStorageCommandReaderFactory54 implements CommandReaderFactory {
+
+ public static final CommandReaderFactory INSTANCE = new InMemoryStorageCommandReaderFactory54();
+
+ @Override
+ public CommandReader get(KernelVersion kernelVersion) {
+ switch (kernelVersion) {
+ case V4_2:
+ return LogCommandSerializationV4_2.INSTANCE;
+ case V4_3_D4:
+ return LogCommandSerializationV4_3_D3.INSTANCE;
+ case V5_0:
+ return LogCommandSerializationV5_0.INSTANCE;
+ default:
+ throw new IllegalArgumentException("Unsupported kernel version " + kernelVersion);
+ }
+ }
+}
diff --git a/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader54.java b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader54.java
new file mode 100644
index 00000000000..3b2d28616fd
--- /dev/null
+++ b/compatibility/5.4/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader54.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.internal.recordstorage;
+
+import org.neo4j.common.EntityType;
+import org.neo4j.common.TokenNameLookup;
+import org.neo4j.counts.CountsAccessor;
+import org.neo4j.gds.compat._54.InMemoryNodeCursor;
+import org.neo4j.gds.compat._54.InMemoryPropertyCursor;
+import org.neo4j.gds.compat._54.InMemoryRelationshipScanCursor;
+import org.neo4j.gds.compat._54.InMemoryRelationshipTraversalCursor;
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.internal.schema.ConstraintDescriptor;
+import org.neo4j.internal.schema.IndexDescriptor;
+import org.neo4j.internal.schema.IndexType;
+import org.neo4j.internal.schema.SchemaDescriptor;
+import org.neo4j.internal.schema.constraints.IndexBackedConstraintDescriptor;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.memory.MemoryTracker;
+import org.neo4j.storageengine.api.AllNodeScan;
+import org.neo4j.storageengine.api.AllRelationshipsScan;
+import org.neo4j.storageengine.api.StorageNodeCursor;
+import org.neo4j.storageengine.api.StoragePropertyCursor;
+import org.neo4j.storageengine.api.StorageReader;
+import org.neo4j.storageengine.api.StorageRelationshipScanCursor;
+import org.neo4j.storageengine.api.StorageRelationshipTraversalCursor;
+import org.neo4j.storageengine.api.StorageSchemaReader;
+import org.neo4j.storageengine.api.cursor.StoreCursors;
+import org.neo4j.token.TokenHolders;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+
+public class InMemoryStorageReader54 implements StorageReader {
+
+ protected final CypherGraphStore graphStore;
+ protected final TokenHolders tokenHolders;
+ protected final CountsAccessor counts;
+ private final Map, Object> dependantState;
+ private boolean closed;
+
+ public InMemoryStorageReader54(
+ CypherGraphStore graphStore,
+ TokenHolders tokenHolders,
+ CountsAccessor counts
+ ) {
+ this.graphStore = graphStore;
+
+ this.tokenHolders = tokenHolders;
+ this.counts = counts;
+ this.dependantState = new ConcurrentHashMap<>();
+ }
+
+ @Override
+ public Collection uniquenessConstraintsGetRelated(
+ long[] changedLabels,
+ long[] unchangedLabels,
+ int[] propertyKeyIds,
+ boolean propertyKeyListIsComplete,
+ EntityType entityType
+ ) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public long relationshipsGetCount(CursorContext cursorTracer) {
+ return graphStore.relationshipCount();
+ }
+
+ @Override
+ public boolean nodeExists(long id, StoreCursors storeCursors) {
+ var originalId = graphStore.nodes().toOriginalNodeId(id);
+ return graphStore.nodes().contains(originalId);
+ }
+
+ @Override
+ public boolean relationshipExists(long id, StoreCursors storeCursors) {
+ return true;
+ }
+
+ @Override
+ public StorageNodeCursor allocateNodeCursor(
+ CursorContext cursorContext, StoreCursors storeCursors
+ ) {
+ return new InMemoryNodeCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public StoragePropertyCursor allocatePropertyCursor(
+ CursorContext cursorContext, StoreCursors storeCursors, MemoryTracker memoryTracker
+ ) {
+ return new InMemoryPropertyCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public StorageRelationshipTraversalCursor allocateRelationshipTraversalCursor(
+ CursorContext cursorContext, StoreCursors storeCursors
+ ) {
+ return new InMemoryRelationshipTraversalCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public StorageRelationshipScanCursor allocateRelationshipScanCursor(
+ CursorContext cursorContext, StoreCursors storeCursors
+ ) {
+ return new InMemoryRelationshipScanCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public IndexDescriptor indexGetForSchemaAndType(
+ SchemaDescriptor descriptor, IndexType type
+ ) {
+ return null;
+ }
+
+ @Override
+ public AllRelationshipsScan allRelationshipScan() {
+ return new AbstractInMemoryAllRelationshipScan() {
+ @Override
+ boolean scanRange(AbstractInMemoryRelationshipScanCursor cursor, long start, long stopInclusive) {
+ return cursor.scanRange(start, stopInclusive);
+ }
+
+ @Override
+ public boolean scanBatch(long sizeHint, AbstractInMemoryRelationshipScanCursor cursor) {
+ return super.scanBatch(sizeHint, cursor);
+ }
+ };
+ }
+
+ @Override
+ public Iterator indexGetForSchema(SchemaDescriptor descriptor) {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public Iterator indexesGetForLabel(int labelId) {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public Iterator indexesGetForRelationshipType(int relationshipType) {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public IndexDescriptor indexGetForName(String name) {
+ return null;
+ }
+
+ @Override
+ public ConstraintDescriptor constraintGetForName(String name) {
+ return null;
+ }
+
+ @Override
+ public boolean indexExists(IndexDescriptor index) {
+ return false;
+ }
+
+ @Override
+ public Iterator indexesGetAll() {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public Collection valueIndexesGetRelated(
+ long[] tokens, int propertyKeyId, EntityType entityType
+ ) {
+ return valueIndexesGetRelated(tokens, new int[]{propertyKeyId}, entityType);
+ }
+
+ @Override
+ public Collection valueIndexesGetRelated(
+ long[] tokens, int[] propertyKeyIds, EntityType entityType
+ ) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Collection uniquenessConstraintsGetRelated(
+ long[] labels,
+ int propertyKeyId,
+ EntityType entityType
+ ) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Collection uniquenessConstraintsGetRelated(
+ long[] tokens,
+ int[] propertyKeyIds,
+ EntityType entityType
+ ) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean hasRelatedSchema(long[] labels, int propertyKey, EntityType entityType) {
+ return false;
+ }
+
+ @Override
+ public boolean hasRelatedSchema(int label, EntityType entityType) {
+ return false;
+ }
+
+ @Override
+ public Iterator constraintsGetForSchema(SchemaDescriptor descriptor) {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public boolean constraintExists(ConstraintDescriptor descriptor) {
+ return false;
+ }
+
+ @Override
+ public Iterator constraintsGetForLabel(int labelId) {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public Iterator constraintsGetForRelationshipType(int typeId) {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public Iterator constraintsGetAll() {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public Long indexGetOwningUniquenessConstraintId(IndexDescriptor index) {
+ return null;
+ }
+
+ @Override
+ public long countsForNode(int labelId, CursorContext cursorContext) {
+ return counts.nodeCount(labelId, cursorContext);
+ }
+
+ @Override
+ public long countsForRelationship(int startLabelId, int typeId, int endLabelId, CursorContext cursorContext) {
+ return counts.relationshipCount(startLabelId, typeId, endLabelId, cursorContext);
+ }
+
+ @Override
+ public long nodesGetCount(CursorContext cursorContext) {
+ return graphStore.nodeCount();
+ }
+
+ @Override
+ public int labelCount() {
+ return graphStore.nodes().availableNodeLabels().size();
+ }
+
+ @Override
+ public int propertyKeyCount() {
+ int nodePropertyCount = graphStore
+ .schema()
+ .nodeSchema()
+ .allProperties()
+ .size();
+ int relPropertyCount = graphStore
+ .schema()
+ .relationshipSchema()
+ .allProperties()
+ .size();
+ return nodePropertyCount + relPropertyCount;
+ }
+
+ @Override
+ public int relationshipTypeCount() {
+ return graphStore.schema().relationshipSchema().availableTypes().size();
+ }
+
+ @Override
+ public T getOrCreateSchemaDependantState(Class type, Function factory) {
+ return type.cast(dependantState.computeIfAbsent(type, key -> factory.apply(this)));
+ }
+
+ @Override
+ public AllNodeScan allNodeScan() {
+ return new InMemoryNodeScan();
+ }
+
+ @Override
+ public void close() {
+ assert !closed;
+ closed = true;
+ }
+
+ @Override
+ public StorageSchemaReader schemaSnapshot() {
+ return this;
+ }
+
+ @Override
+ public TokenNameLookup tokenNameLookup() {
+ return tokenHolders;
+ }
+
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/build.gradle b/compatibility/5.5/neo4j-kernel-adapter/build.gradle
new file mode 100644
index 00000000000..6eed2158956
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/build.gradle
@@ -0,0 +1,63 @@
+apply plugin: 'java-library'
+apply plugin: 'me.champeau.mrjar'
+
+description = 'Neo4j Graph Data Science :: Neo4j Kernel Adapter 5.5'
+
+group = 'org.neo4j.gds'
+
+// for all 5.x versions
+if (ver.'neo4j'.startsWith('5.')) {
+ sourceSets {
+ main {
+ java {
+ srcDirs = ['src/main/java17']
+ }
+ }
+ }
+
+ dependencies {
+ annotationProcessor project(':annotations')
+ annotationProcessor group: 'org.immutables', name: 'value', version: ver.'immutables'
+ annotationProcessor group: 'org.neo4j', name: 'annotations', version: neos.'5.5'
+
+ compileOnly project(':annotations')
+ compileOnly group: 'com.github.spotbugs', name: 'spotbugs-annotations', version: ver.'spotbugsToolVersion'
+ compileOnly group: 'org.immutables', name: 'value-annotations', version: ver.'immutables'
+ compileOnly group: 'org.neo4j', name: 'annotations', version: neos.'5.5'
+ compileOnly group: 'org.neo4j', name: 'neo4j', version: neos.'5.5'
+ compileOnly group: 'org.neo4j', name: 'neo4j-record-storage-engine', version: neos.'5.5'
+ compileOnly group: 'org.neo4j.community', name: 'it-test-support', version: neos.'5.5'
+
+ implementation project(':neo4j-kernel-adapter-api')
+ }
+} else {
+ multiRelease {
+ targetVersions 11, 17
+ }
+
+ if (!project.hasProperty('no-forbidden-apis')) {
+ forbiddenApisJava17 {
+ exclude('**')
+ }
+ }
+
+ dependencies {
+ annotationProcessor group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
+ compileOnly group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
+
+ implementation project(':neo4j-kernel-adapter-api')
+
+ java17AnnotationProcessor project(':annotations')
+ java17AnnotationProcessor group: 'org.immutables', name: 'value', version: ver.'immutables'
+ java17AnnotationProcessor group: 'org.neo4j', name: 'annotations', version: neos.'5.5'
+
+ java17CompileOnly project(':annotations')
+ java17CompileOnly group: 'org.immutables', name: 'value-annotations', version: ver.'immutables'
+ java17CompileOnly group: 'org.neo4j', name: 'neo4j', version: neos.'5.5'
+ java17CompileOnly group: 'org.neo4j', name: 'neo4j-record-storage-engine', version: neos.'5.5'
+ java17CompileOnly group: 'org.neo4j.community', name: 'it-test-support', version: neos.'5.5'
+ java17CompileOnly group: 'com.github.spotbugs', name: 'spotbugs-annotations', version: ver.'spotbugsToolVersion'
+
+ java17Implementation project(':neo4j-kernel-adapter-api')
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_55/Neo4jProxyFactoryImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_55/Neo4jProxyFactoryImpl.java
new file mode 100644
index 00000000000..931a383b46e
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_55/Neo4jProxyFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jProxyApi;
+import org.neo4j.gds.compat.Neo4jProxyFactory;
+import org.neo4j.gds.compat.Neo4jVersion;
+
+@ServiceProvider
+public final class Neo4jProxyFactoryImpl implements Neo4jProxyFactory {
+
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return false;
+ }
+
+ @Override
+ public Neo4jProxyApi load() {
+ throw new UnsupportedOperationException("5.5 compatibility requires JDK17");
+ }
+
+ @Override
+ public String description() {
+ return "Neo4j 5.5 (placeholder)";
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_55/SettingProxyFactoryImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_55/SettingProxyFactoryImpl.java
new file mode 100644
index 00000000000..78e1b894771
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_55/SettingProxyFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.SettingProxyApi;
+import org.neo4j.gds.compat.SettingProxyFactory;
+
+@ServiceProvider
+public final class SettingProxyFactoryImpl implements SettingProxyFactory {
+
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return false;
+ }
+
+ @Override
+ public SettingProxyApi load() {
+ throw new UnsupportedOperationException("5.5 compatibility requires JDK17");
+ }
+
+ @Override
+ public String description() {
+ return "Neo4j Settings 5.5 (placeholder)";
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/BoltTransactionRunnerImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/BoltTransactionRunnerImpl.java
new file mode 100644
index 00000000000..b96e77e2fe3
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/BoltTransactionRunnerImpl.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.bolt.dbapi.BoltGraphDatabaseServiceSPI;
+import org.neo4j.bolt.dbapi.BoltTransaction;
+import org.neo4j.bolt.protocol.common.bookmark.Bookmark;
+import org.neo4j.bolt.protocol.common.message.AccessMode;
+import org.neo4j.bolt.protocol.common.transaction.result.AdaptingBoltQuerySubscriber;
+import org.neo4j.bolt.protocol.v41.message.request.RoutingContext;
+import org.neo4j.exceptions.KernelException;
+import org.neo4j.gds.compat.BoltQuerySubscriber;
+import org.neo4j.gds.compat.BoltTransactionRunner;
+import org.neo4j.graphdb.QueryStatistics;
+import org.neo4j.internal.kernel.api.connectioninfo.ClientConnectionInfo;
+import org.neo4j.internal.kernel.api.security.LoginContext;
+import org.neo4j.kernel.api.KernelTransaction;
+import org.neo4j.kernel.impl.query.QueryExecutionConfiguration;
+import org.neo4j.kernel.impl.query.QueryExecutionKernelException;
+import org.neo4j.values.virtual.MapValue;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+
+public class BoltTransactionRunnerImpl extends BoltTransactionRunner {
+
+ @Override
+ protected BoltQuerySubscriber boltQuerySubscriber() {
+ var subscriber = new AdaptingBoltQuerySubscriber();
+ return new BoltQuerySubscriber<>() {
+ @Override
+ public void assertSucceeded() throws KernelException {
+ subscriber.assertSucceeded();
+ }
+
+ @Override
+ public QueryStatistics queryStatistics() {
+ return subscriber.queryStatistics();
+ }
+
+ @Override
+ public AdaptingBoltQuerySubscriber innerSubscriber() {
+ return subscriber;
+ }
+ };
+ }
+
+ @Override
+ protected void executeQuery(
+ BoltTransaction boltTransaction,
+ String query,
+ MapValue parameters,
+ AdaptingBoltQuerySubscriber querySubscriber
+ ) throws QueryExecutionKernelException {
+ boltTransaction.executeQuery(query, parameters, true, querySubscriber);
+ }
+
+ @Override
+ protected BoltTransaction beginBoltWriteTransaction(
+ BoltGraphDatabaseServiceSPI fabricDb,
+ LoginContext loginContext,
+ KernelTransaction.Type kernelTransactionType,
+ ClientConnectionInfo clientConnectionInfo,
+ List bookmarks,
+ Duration txTimeout,
+ Map txMetadata
+ ) {
+ return fabricDb.beginTransaction(
+ kernelTransactionType,
+ loginContext,
+ clientConnectionInfo,
+ bookmarks,
+ txTimeout,
+ AccessMode.WRITE,
+ txMetadata,
+ new RoutingContext(true, Map.of()),
+ QueryExecutionConfiguration.DEFAULT_CONFIG
+ );
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CallableProcedureImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CallableProcedureImpl.java
new file mode 100644
index 00000000000..72c0b6545ed
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CallableProcedureImpl.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.collection.RawIterator;
+import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.CompatCallableProcedure;
+import org.neo4j.internal.kernel.api.exceptions.ProcedureException;
+import org.neo4j.internal.kernel.api.procs.ProcedureSignature;
+import org.neo4j.kernel.api.ResourceMonitor;
+import org.neo4j.kernel.api.procedure.CallableProcedure;
+import org.neo4j.kernel.api.procedure.Context;
+import org.neo4j.values.AnyValue;
+
+@SuppressForbidden(reason = "This is the compat API")
+public final class CallableProcedureImpl implements CallableProcedure {
+ private final CompatCallableProcedure procedure;
+
+ CallableProcedureImpl(CompatCallableProcedure procedure) {
+ this.procedure = procedure;
+ }
+
+ @Override
+ public ProcedureSignature signature() {
+ return this.procedure.signature();
+ }
+
+ @Override
+ public RawIterator apply(
+ Context ctx,
+ AnyValue[] input,
+ ResourceMonitor resourceMonitor
+ ) throws ProcedureException {
+ return this.procedure.apply(ctx, input);
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CallableUserAggregationFunctionImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CallableUserAggregationFunctionImpl.java
new file mode 100644
index 00000000000..4156ab4cc4e
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CallableUserAggregationFunctionImpl.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.CompatUserAggregationFunction;
+import org.neo4j.gds.compat.CompatUserAggregator;
+import org.neo4j.internal.kernel.api.exceptions.ProcedureException;
+import org.neo4j.internal.kernel.api.procs.UserAggregationReducer;
+import org.neo4j.internal.kernel.api.procs.UserAggregationUpdater;
+import org.neo4j.internal.kernel.api.procs.UserFunctionSignature;
+import org.neo4j.kernel.api.procedure.CallableUserAggregationFunction;
+import org.neo4j.kernel.api.procedure.Context;
+import org.neo4j.values.AnyValue;
+
+@SuppressForbidden(reason = "This is the compat API")
+public final class CallableUserAggregationFunctionImpl implements CallableUserAggregationFunction {
+ private final CompatUserAggregationFunction function;
+
+ CallableUserAggregationFunctionImpl(CompatUserAggregationFunction function) {
+ this.function = function;
+ }
+
+ @Override
+ public UserFunctionSignature signature() {
+ return this.function.signature();
+ }
+
+ @Override
+ public UserAggregationReducer createReducer(Context ctx) throws ProcedureException {
+ return new UserAggregatorImpl(this.function.create(ctx));
+ }
+
+ private static final class UserAggregatorImpl implements UserAggregationReducer, UserAggregationUpdater {
+ private final CompatUserAggregator aggregator;
+
+ private UserAggregatorImpl(CompatUserAggregator aggregator) {
+ this.aggregator = aggregator;
+ }
+
+ @Override
+ public UserAggregationUpdater newUpdater() {
+ return this;
+ }
+
+ @Override
+ public void update(AnyValue[] input) throws ProcedureException {
+ this.aggregator.update(input);
+ }
+
+ @Override
+ public void applyUpdates() {
+ }
+
+ @Override
+ public AnyValue result() throws ProcedureException {
+ return this.aggregator.result();
+ }
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompatAccessModeImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompatAccessModeImpl.java
new file mode 100644
index 00000000000..ce96cc3a72a
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompatAccessModeImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.compat.CompatAccessMode;
+import org.neo4j.gds.compat.CustomAccessMode;
+import org.neo4j.internal.kernel.api.RelTypeSupplier;
+import org.neo4j.internal.kernel.api.TokenSet;
+
+import java.util.function.Supplier;
+
+public final class CompatAccessModeImpl extends CompatAccessMode {
+
+ CompatAccessModeImpl(CustomAccessMode custom) {
+ super(custom);
+ }
+
+ @Override
+ public boolean allowsReadNodeProperty(Supplier labels, int propertyKey) {
+ return custom.allowsReadNodeProperty(propertyKey);
+ }
+
+ @Override
+ public boolean allowsReadRelationshipProperty(RelTypeSupplier relType, int propertyKey) {
+ return custom.allowsReadRelationshipProperty(propertyKey);
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompatGraphDatabaseAPIImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompatGraphDatabaseAPIImpl.java
new file mode 100644
index 00000000000..0af8537b5a8
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompatGraphDatabaseAPIImpl.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.dbms.systemgraph.TopologyGraphDbmsModel;
+import org.neo4j.gds.compat.GdsGraphDatabaseAPI;
+import org.neo4j.internal.kernel.api.connectioninfo.ClientConnectionInfo;
+import org.neo4j.internal.kernel.api.security.LoginContext;
+import org.neo4j.kernel.api.KernelTransaction;
+import org.neo4j.kernel.api.exceptions.Status;
+import org.neo4j.kernel.impl.coreapi.InternalTransaction;
+import org.neo4j.kernel.impl.coreapi.TransactionExceptionMapper;
+
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+final class CompatGraphDatabaseAPIImpl extends GdsGraphDatabaseAPI {
+
+ CompatGraphDatabaseAPIImpl(DatabaseManagementService dbms) {
+ super(dbms);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return api.isAvailable();
+ }
+
+ @Override
+ public TopologyGraphDbmsModel.HostedOnMode mode() {
+ // NOTE: This means we can never start clusters locally, which is probably fine since:
+ // 1) We never did this before
+ // 2) We only use this for tests and benchmarks
+ return TopologyGraphDbmsModel.HostedOnMode.SINGLE;
+ }
+
+ @Override
+ public InternalTransaction beginTransaction(
+ KernelTransaction.Type type,
+ LoginContext loginContext,
+ ClientConnectionInfo clientInfo,
+ long timeout,
+ TimeUnit unit,
+ Consumer terminationCallback,
+ TransactionExceptionMapper transactionExceptionMapper
+ ) {
+ return api.beginTransaction(
+ type,
+ loginContext,
+ clientInfo,
+ timeout,
+ unit,
+ terminationCallback,
+ transactionExceptionMapper
+ );
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompatIndexQueryImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompatIndexQueryImpl.java
new file mode 100644
index 00000000000..8cad95ee9b5
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompatIndexQueryImpl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.compat.CompatIndexQuery;
+import org.neo4j.internal.kernel.api.PropertyIndexQuery;
+
+final class CompatIndexQueryImpl implements CompatIndexQuery {
+ final PropertyIndexQuery indexQuery;
+
+ CompatIndexQueryImpl(PropertyIndexQuery indexQuery) {
+ this.indexQuery = indexQuery;
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompatUsernameAuthSubjectImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompatUsernameAuthSubjectImpl.java
new file mode 100644
index 00000000000..148fa614366
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompatUsernameAuthSubjectImpl.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.compat.CompatUsernameAuthSubject;
+import org.neo4j.internal.kernel.api.security.AuthSubject;
+
+final class CompatUsernameAuthSubjectImpl extends CompatUsernameAuthSubject {
+
+ CompatUsernameAuthSubjectImpl(String username, AuthSubject authSubject) {
+ super(username, authSubject);
+ }
+
+ @Override
+ public String executingUser() {
+ return username;
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompositeNodeCursorImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompositeNodeCursorImpl.java
new file mode 100644
index 00000000000..8ee3052f38e
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/CompositeNodeCursorImpl.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.compat.CompositeNodeCursor;
+import org.neo4j.internal.kernel.api.NodeLabelIndexCursor;
+
+import java.util.List;
+
+public final class CompositeNodeCursorImpl extends CompositeNodeCursor {
+
+ CompositeNodeCursorImpl(List cursors, int[] labelIds) {
+ super(cursors, labelIds);
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/GdsDatabaseLayoutImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/GdsDatabaseLayoutImpl.java
new file mode 100644
index 00000000000..c1dc37d80ef
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/GdsDatabaseLayoutImpl.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.compat.GdsDatabaseLayout;
+import org.neo4j.io.layout.DatabaseLayout;
+
+import java.nio.file.Path;
+
+public class GdsDatabaseLayoutImpl implements GdsDatabaseLayout {
+ private final DatabaseLayout databaseLayout;
+
+ public GdsDatabaseLayoutImpl(DatabaseLayout databaseLayout) {this.databaseLayout = databaseLayout;}
+
+ @Override
+ public Path databaseDirectory() {
+ return databaseLayout.databaseDirectory();
+ }
+
+ @Override
+ public Path getTransactionLogsDirectory() {
+ return databaseLayout.getTransactionLogsDirectory();
+ }
+
+ @Override
+ public Path metadataStore() {
+ return databaseLayout.metadataStore();
+ }
+
+ public DatabaseLayout databaseLayout() {
+ return databaseLayout;
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/GdsDatabaseManagementServiceBuilderImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/GdsDatabaseManagementServiceBuilderImpl.java
new file mode 100644
index 00000000000..13fdae4cce6
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/GdsDatabaseManagementServiceBuilderImpl.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.dbms.api.DatabaseManagementServiceBuilderImplementation;
+import org.neo4j.gds.compat.GdsDatabaseManagementServiceBuilder;
+import org.neo4j.graphdb.config.Setting;
+
+import java.nio.file.Path;
+import java.util.Map;
+
+public class GdsDatabaseManagementServiceBuilderImpl implements GdsDatabaseManagementServiceBuilder {
+
+ private final DatabaseManagementServiceBuilderImplementation dbmsBuilder;
+
+ GdsDatabaseManagementServiceBuilderImpl(Path storeDir) {
+ this.dbmsBuilder = new DatabaseManagementServiceBuilderImplementation(storeDir);
+ }
+
+ @Override
+ public GdsDatabaseManagementServiceBuilder setConfigRaw(Map configMap) {
+ dbmsBuilder.setConfigRaw(configMap);
+ return this;
+ }
+
+ @Override
+ public GdsDatabaseManagementServiceBuilder setConfig(Setting setting, S value) {
+ dbmsBuilder.setConfig(setting, value);
+ return this;
+ }
+
+ @Override
+ public DatabaseManagementService build() {
+ return dbmsBuilder.build();
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/Neo4jProxyFactoryImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/Neo4jProxyFactoryImpl.java
new file mode 100644
index 00000000000..7f13c7ebe56
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/Neo4jProxyFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jProxyApi;
+import org.neo4j.gds.compat.Neo4jProxyFactory;
+import org.neo4j.gds.compat.Neo4jVersion;
+
+@ServiceProvider
+public final class Neo4jProxyFactoryImpl implements Neo4jProxyFactory {
+
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return version == Neo4jVersion.V_5_5;
+ }
+
+ @Override
+ public Neo4jProxyApi load() {
+ return new Neo4jProxyImpl();
+ }
+
+ @Override
+ public String description() {
+ return "Neo4j 5.5";
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/Neo4jProxyImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/Neo4jProxyImpl.java
new file mode 100644
index 00000000000..099e4b3e576
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/Neo4jProxyImpl.java
@@ -0,0 +1,935 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.neo4j.common.DependencyResolver;
+import org.neo4j.common.EntityType;
+import org.neo4j.configuration.BootloaderSettings;
+import org.neo4j.configuration.Config;
+import org.neo4j.configuration.GraphDatabaseSettings;
+import org.neo4j.configuration.SettingValueParsers;
+import org.neo4j.configuration.connectors.ConnectorPortRegister;
+import org.neo4j.configuration.connectors.ConnectorType;
+import org.neo4j.configuration.helpers.DatabaseNameValidator;
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.exceptions.KernelException;
+import org.neo4j.fabric.FabricDatabaseManager;
+import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.BoltTransactionRunner;
+import org.neo4j.gds.compat.CompatCallableProcedure;
+import org.neo4j.gds.compat.CompatExecutionMonitor;
+import org.neo4j.gds.compat.CompatIndexQuery;
+import org.neo4j.gds.compat.CompatInput;
+import org.neo4j.gds.compat.CompatUserAggregationFunction;
+import org.neo4j.gds.compat.CompositeNodeCursor;
+import org.neo4j.gds.compat.CustomAccessMode;
+import org.neo4j.gds.compat.GdsDatabaseLayout;
+import org.neo4j.gds.compat.GdsDatabaseManagementServiceBuilder;
+import org.neo4j.gds.compat.GdsGraphDatabaseAPI;
+import org.neo4j.gds.compat.GraphDatabaseApiProxy;
+import org.neo4j.gds.compat.InputEntityIdVisitor;
+import org.neo4j.gds.compat.Neo4jProxyApi;
+import org.neo4j.gds.compat.PropertyReference;
+import org.neo4j.gds.compat.StoreScan;
+import org.neo4j.gds.compat.TestLog;
+import org.neo4j.graphdb.GraphDatabaseService;
+import org.neo4j.graphdb.Node;
+import org.neo4j.graphdb.Relationship;
+import org.neo4j.graphdb.RelationshipType;
+import org.neo4j.graphdb.config.Setting;
+import org.neo4j.internal.batchimport.AdditionalInitialIds;
+import org.neo4j.internal.batchimport.BatchImporter;
+import org.neo4j.internal.batchimport.BatchImporterFactory;
+import org.neo4j.internal.batchimport.Configuration;
+import org.neo4j.internal.batchimport.IndexConfig;
+import org.neo4j.internal.batchimport.InputIterable;
+import org.neo4j.internal.batchimport.Monitor;
+import org.neo4j.internal.batchimport.input.Collector;
+import org.neo4j.internal.batchimport.input.IdType;
+import org.neo4j.internal.batchimport.input.Input;
+import org.neo4j.internal.batchimport.input.InputEntityVisitor;
+import org.neo4j.internal.batchimport.input.PropertySizeCalculator;
+import org.neo4j.internal.batchimport.input.ReadableGroups;
+import org.neo4j.internal.batchimport.staging.ExecutionMonitor;
+import org.neo4j.internal.batchimport.staging.StageExecution;
+import org.neo4j.internal.helpers.HostnamePort;
+import org.neo4j.internal.id.IdGenerator;
+import org.neo4j.internal.id.IdGeneratorFactory;
+import org.neo4j.internal.kernel.api.Cursor;
+import org.neo4j.internal.kernel.api.IndexQueryConstraints;
+import org.neo4j.internal.kernel.api.IndexReadSession;
+import org.neo4j.internal.kernel.api.NodeCursor;
+import org.neo4j.internal.kernel.api.NodeLabelIndexCursor;
+import org.neo4j.internal.kernel.api.NodeValueIndexCursor;
+import org.neo4j.internal.kernel.api.PropertyCursor;
+import org.neo4j.internal.kernel.api.PropertyIndexQuery;
+import org.neo4j.internal.kernel.api.QueryContext;
+import org.neo4j.internal.kernel.api.Read;
+import org.neo4j.internal.kernel.api.RelationshipScanCursor;
+import org.neo4j.internal.kernel.api.Scan;
+import org.neo4j.internal.kernel.api.TokenPredicate;
+import org.neo4j.internal.kernel.api.connectioninfo.ClientConnectionInfo;
+import org.neo4j.internal.kernel.api.procs.FieldSignature;
+import org.neo4j.internal.kernel.api.procs.Neo4jTypes;
+import org.neo4j.internal.kernel.api.procs.ProcedureSignature;
+import org.neo4j.internal.kernel.api.procs.QualifiedName;
+import org.neo4j.internal.kernel.api.procs.UserFunctionSignature;
+import org.neo4j.internal.kernel.api.security.AccessMode;
+import org.neo4j.internal.kernel.api.security.AuthSubject;
+import org.neo4j.internal.kernel.api.security.SecurityContext;
+import org.neo4j.internal.recordstorage.RecordIdType;
+import org.neo4j.internal.schema.IndexCapability;
+import org.neo4j.internal.schema.IndexDescriptor;
+import org.neo4j.internal.schema.IndexOrder;
+import org.neo4j.internal.schema.SchemaDescriptors;
+import org.neo4j.io.fs.FileSystemAbstraction;
+import org.neo4j.io.layout.DatabaseLayout;
+import org.neo4j.io.layout.Neo4jLayout;
+import org.neo4j.io.layout.recordstorage.RecordDatabaseLayout;
+import org.neo4j.io.pagecache.PageCache;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.io.pagecache.context.CursorContextFactory;
+import org.neo4j.io.pagecache.context.EmptyVersionContextSupplier;
+import org.neo4j.io.pagecache.tracing.PageCacheTracer;
+import org.neo4j.kernel.api.KernelTransaction;
+import org.neo4j.kernel.api.KernelTransactionHandle;
+import org.neo4j.kernel.api.procedure.CallableProcedure;
+import org.neo4j.kernel.api.procedure.CallableUserAggregationFunction;
+import org.neo4j.kernel.database.NamedDatabaseId;
+import org.neo4j.kernel.database.NormalizedDatabaseName;
+import org.neo4j.kernel.database.TestDatabaseIdRepository;
+import org.neo4j.kernel.impl.coreapi.InternalTransaction;
+import org.neo4j.kernel.impl.index.schema.IndexImporterFactoryImpl;
+import org.neo4j.kernel.impl.query.QueryExecutionConfiguration;
+import org.neo4j.kernel.impl.query.TransactionalContext;
+import org.neo4j.kernel.impl.query.TransactionalContextFactory;
+import org.neo4j.kernel.impl.store.RecordStore;
+import org.neo4j.kernel.impl.store.format.RecordFormatSelector;
+import org.neo4j.kernel.impl.store.format.RecordFormats;
+import org.neo4j.kernel.impl.store.record.AbstractBaseRecord;
+import org.neo4j.kernel.impl.transaction.log.EmptyLogTailMetadata;
+import org.neo4j.kernel.impl.transaction.log.files.TransactionLogInitializer;
+import org.neo4j.logging.Log;
+import org.neo4j.logging.internal.LogService;
+import org.neo4j.memory.EmptyMemoryTracker;
+import org.neo4j.procedure.Mode;
+import org.neo4j.scheduler.JobScheduler;
+import org.neo4j.ssl.config.SslPolicyLoader;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.StorageEngineFactory;
+import org.neo4j.util.Bits;
+import org.neo4j.values.storable.ValueCategory;
+import org.neo4j.values.storable.Values;
+import org.neo4j.values.virtual.MapValue;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static java.lang.String.format;
+import static org.neo4j.gds.compat.InternalReadOps.countByIdGenerator;
+import static org.neo4j.io.pagecache.context.EmptyVersionContextSupplier.EMPTY;
+
+public final class Neo4jProxyImpl implements Neo4jProxyApi {
+
+ @Override
+ public GdsGraphDatabaseAPI newDb(DatabaseManagementService dbms) {
+ return new CompatGraphDatabaseAPIImpl(dbms);
+ }
+
+ @Override
+ public String validateExternalDatabaseName(String databaseName) {
+ var normalizedName = new NormalizedDatabaseName(databaseName);
+ DatabaseNameValidator.validateExternalDatabaseName(normalizedName);
+ return normalizedName.name();
+ }
+
+ @Override
+ public AccessMode accessMode(CustomAccessMode customAccessMode) {
+ return new CompatAccessModeImpl(customAccessMode);
+ }
+
+ @Override
+ public String username(AuthSubject subject) {
+ return subject.executingUser();
+ }
+
+ @Override
+ public SecurityContext securityContext(
+ String username,
+ AuthSubject authSubject,
+ AccessMode mode,
+ String databaseName
+ ) {
+ return new SecurityContext(
+ new CompatUsernameAuthSubjectImpl(username, authSubject),
+ mode,
+ // GDS is always operating from an embedded context
+ ClientConnectionInfo.EMBEDDED_CONNECTION,
+ databaseName
+ );
+ }
+
+ @Override
+ public long getHighestPossibleIdInUse(
+ RecordStore extends AbstractBaseRecord> recordStore,
+ KernelTransaction kernelTransaction
+ ) {
+ return recordStore.getHighestPossibleIdInUse(kernelTransaction.cursorContext());
+ }
+
+ @Override
+ public long getHighId(RecordStore extends AbstractBaseRecord> recordStore) {
+ return recordStore.getHighId();
+ }
+
+ @Override
+ public List> entityCursorScan(
+ KernelTransaction transaction,
+ int[] labelIds,
+ int batchSize,
+ boolean allowPartitionedScan
+ ) {
+ if (allowPartitionedScan) {
+ return partitionedNodeLabelIndexScan(transaction, batchSize, labelIds);
+ } else {
+ var read = transaction.dataRead();
+ return Arrays
+ .stream(labelIds)
+ .mapToObj(read::nodeLabelScan)
+ .map(scan -> scanToStoreScan(scan, batchSize))
+ .collect(Collectors.toList());
+ }
+ }
+
+ @Override
+ public PropertyCursor allocatePropertyCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction
+ .cursors()
+ .allocatePropertyCursor(kernelTransaction.cursorContext(), kernelTransaction.memoryTracker());
+ }
+
+ @Override
+ public PropertyReference propertyReference(NodeCursor nodeCursor) {
+ return ReferencePropertyReference.of(nodeCursor.propertiesReference());
+ }
+
+ @Override
+ public PropertyReference propertyReference(RelationshipScanCursor relationshipScanCursor) {
+ return ReferencePropertyReference.of(relationshipScanCursor.propertiesReference());
+ }
+
+ @Override
+ public PropertyReference noPropertyReference() {
+ return ReferencePropertyReference.empty();
+ }
+
+ @Override
+ public void nodeProperties(
+ KernelTransaction kernelTransaction,
+ long nodeReference,
+ PropertyReference reference,
+ PropertyCursor cursor
+ ) {
+ var neoReference = ((ReferencePropertyReference) reference).reference;
+ kernelTransaction
+ .dataRead()
+ .nodeProperties(nodeReference, neoReference, PropertySelection.ALL_PROPERTIES, cursor);
+ }
+
+ @Override
+ public void relationshipProperties(
+ KernelTransaction kernelTransaction,
+ long relationshipReference,
+ PropertyReference reference,
+ PropertyCursor cursor
+ ) {
+ var neoReference = ((ReferencePropertyReference) reference).reference;
+ kernelTransaction
+ .dataRead()
+ .relationshipProperties(relationshipReference, neoReference, PropertySelection.ALL_PROPERTIES, cursor);
+ }
+
+ @Override
+ public NodeCursor allocateNodeCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction.cursors().allocateNodeCursor(kernelTransaction.cursorContext());
+ }
+
+ @Override
+ public RelationshipScanCursor allocateRelationshipScanCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction.cursors().allocateRelationshipScanCursor(kernelTransaction.cursorContext());
+ }
+
+ @Override
+ public NodeLabelIndexCursor allocateNodeLabelIndexCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction.cursors().allocateNodeLabelIndexCursor(kernelTransaction.cursorContext());
+ }
+
+ @Override
+ public NodeValueIndexCursor allocateNodeValueIndexCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction
+ .cursors()
+ .allocateNodeValueIndexCursor(kernelTransaction.cursorContext(), kernelTransaction.memoryTracker());
+ }
+
+ @Override
+ public boolean hasNodeLabelIndex(KernelTransaction kernelTransaction) {
+ return NodeLabelIndexLookupImpl.hasNodeLabelIndex(kernelTransaction);
+ }
+
+ @Override
+ public StoreScan nodeLabelIndexScan(
+ KernelTransaction transaction,
+ int labelId,
+ int batchSize,
+ boolean allowPartitionedScan
+ ) {
+ if (allowPartitionedScan) {
+ return partitionedNodeLabelIndexScan(transaction, batchSize, labelId).get(0);
+ } else {
+ var read = transaction.dataRead();
+ return scanToStoreScan(read.nodeLabelScan(labelId), batchSize);
+ }
+ }
+
+ @Override
+ public StoreScan scanToStoreScan(Scan scan, int batchSize) {
+ return new ScanBasedStoreScanImpl<>(scan, batchSize);
+ }
+
+ private List> partitionedNodeLabelIndexScan(
+ KernelTransaction transaction,
+ int batchSize,
+ int... labelIds
+ ) {
+ var indexDescriptor = NodeLabelIndexLookupImpl.findUsableMatchingIndex(
+ transaction,
+ SchemaDescriptors.forAnyEntityTokens(EntityType.NODE)
+ );
+
+ if (indexDescriptor == IndexDescriptor.NO_INDEX) {
+ throw new IllegalStateException("There is no index that can back a node label scan.");
+ }
+
+ var read = transaction.dataRead();
+
+ // Our current strategy is to select the token with the highest count
+ // and use that one as the driving partitioned index scan. The partitions
+ // of all other partitioned index scans will be aligned to that one.
+ int maxToken = labelIds[0];
+ long maxCount = read.countsForNodeWithoutTxState(labelIds[0]);
+
+ for (int i = 1; i < labelIds.length; i++) {
+ long count = read.countsForNodeWithoutTxState(labelIds[i]);
+ if (count > maxCount) {
+ maxCount = count;
+ maxToken = labelIds[i];
+ }
+ }
+
+ int numberOfPartitions = PartitionedStoreScan.getNumberOfPartitions(maxCount, batchSize);
+
+ try {
+ var session = read.tokenReadSession(indexDescriptor);
+
+ var partitionedScan = read.nodeLabelScan(
+ session,
+ numberOfPartitions,
+ transaction.cursorContext(),
+ new TokenPredicate(maxToken)
+ );
+
+ var scans = new ArrayList>(labelIds.length);
+ scans.add(new PartitionedStoreScan(partitionedScan));
+
+ // Initialize the remaining index scans with the partitioning of the first scan.
+ for (int labelToken : labelIds) {
+ if (labelToken != maxToken) {
+ var scan = read.nodeLabelScan(session, partitionedScan, new TokenPredicate(labelToken));
+ scans.add(new PartitionedStoreScan(scan));
+ }
+ }
+
+ return scans;
+ } catch (KernelException e) {
+ // should not happen, we check for the index existence and applicability
+ // before reading it
+ throw new RuntimeException("Unexpected error while initialising reading from node label index", e);
+ }
+ }
+
+ @Override
+ public CompatIndexQuery rangeIndexQuery(
+ int propertyKeyId,
+ double from,
+ boolean fromInclusive,
+ double to,
+ boolean toInclusive
+ ) {
+ return new CompatIndexQueryImpl(PropertyIndexQuery.range(propertyKeyId, from, fromInclusive, to, toInclusive));
+ }
+
+ @Override
+ public CompatIndexQuery rangeAllIndexQuery(int propertyKeyId) {
+ var rangePredicate = PropertyIndexQuery.range(
+ propertyKeyId,
+ Values.doubleValue(Double.NEGATIVE_INFINITY),
+ true,
+ Values.doubleValue(Double.POSITIVE_INFINITY),
+ true
+ );
+ return new CompatIndexQueryImpl(rangePredicate);
+ }
+
+ @Override
+ public void nodeIndexSeek(
+ Read dataRead,
+ IndexReadSession index,
+ NodeValueIndexCursor cursor,
+ IndexOrder indexOrder,
+ boolean needsValues,
+ CompatIndexQuery query
+ ) throws KernelException {
+ var indexQueryConstraints = indexOrder == IndexOrder.NONE
+ ? IndexQueryConstraints.unordered(needsValues)
+ : IndexQueryConstraints.constrained(indexOrder, needsValues);
+
+ dataRead.nodeIndexSeek(
+ (QueryContext) dataRead,
+ index,
+ cursor,
+ indexQueryConstraints,
+ ((CompatIndexQueryImpl) query).indexQuery
+ );
+ }
+
+ @Override
+ public CompositeNodeCursor compositeNodeCursor(List cursors, int[] labelIds) {
+ return new CompositeNodeCursorImpl(cursors, labelIds);
+ }
+
+ @Override
+ public Configuration batchImporterConfig(
+ int batchSize,
+ int writeConcurrency,
+ Optional pageCacheMemory,
+ boolean highIO,
+ IndexConfig indexConfig
+ ) {
+ return new org.neo4j.internal.batchimport.Configuration() {
+ @Override
+ public int batchSize() {
+ return batchSize;
+ }
+
+ @Override
+ public int maxNumberOfWorkerThreads() {
+ return writeConcurrency;
+ }
+
+ @Override
+ public long pageCacheMemory() {
+ return pageCacheMemory.orElseGet(Configuration.super::pageCacheMemory);
+ }
+
+ @Override
+ public boolean highIO() {
+ return highIO;
+ }
+
+ @Override
+ public IndexConfig indexConfig() {
+ return indexConfig;
+ }
+ };
+ }
+
+ @Override
+ public int writeConcurrency(Configuration batchImportConfiguration) {
+ return batchImportConfiguration.maxNumberOfWorkerThreads();
+ }
+
+ @Override
+ public BatchImporter instantiateBatchImporter(
+ BatchImporterFactory factory,
+ GdsDatabaseLayout directoryStructure,
+ FileSystemAbstraction fileSystem,
+ PageCacheTracer pageCacheTracer,
+ Configuration configuration,
+ LogService logService,
+ ExecutionMonitor executionMonitor,
+ AdditionalInitialIds additionalInitialIds,
+ Config dbConfig,
+ RecordFormats recordFormats,
+ JobScheduler jobScheduler,
+ Collector badCollector
+ ) {
+ dbConfig.set(GraphDatabaseSettings.db_format, recordFormats.name());
+ var databaseLayout = ((GdsDatabaseLayoutImpl) directoryStructure).databaseLayout();
+ return factory.instantiate(
+ databaseLayout,
+ fileSystem,
+ pageCacheTracer,
+ configuration,
+ logService,
+ executionMonitor,
+ additionalInitialIds,
+ new EmptyLogTailMetadata(),
+ dbConfig,
+ Monitor.NO_MONITOR,
+ jobScheduler,
+ badCollector,
+ TransactionLogInitializer.getLogFilesInitializer(),
+ new IndexImporterFactoryImpl(),
+ EmptyMemoryTracker.INSTANCE,
+ new CursorContextFactory(PageCacheTracer.NULL, EmptyVersionContextSupplier.EMPTY)
+ );
+ }
+
+ @Override
+ public Input batchInputFrom(CompatInput compatInput) {
+ return new InputFromCompatInput(compatInput);
+ }
+
+ @Override
+ public InputEntityIdVisitor.Long inputEntityLongIdVisitor(IdType idType, ReadableGroups groups) {
+ switch (idType) {
+ case ACTUAL -> {
+ return new InputEntityIdVisitor.Long() {
+ @Override
+ public void visitNodeId(InputEntityVisitor visitor, long id) {
+ visitor.id(id);
+ }
+
+ @Override
+ public void visitSourceId(InputEntityVisitor visitor, long id) {
+ visitor.startId(id);
+ }
+
+ @Override
+ public void visitTargetId(InputEntityVisitor visitor, long id) {
+ visitor.endId(id);
+ }
+ };
+ }
+ case INTEGER -> {
+ var globalGroup = groups.get(null);
+
+ return new InputEntityIdVisitor.Long() {
+ @Override
+ public void visitNodeId(InputEntityVisitor visitor, long id) {
+ visitor.id(id, globalGroup);
+ }
+
+ @Override
+ public void visitSourceId(InputEntityVisitor visitor, long id) {
+ visitor.startId(id, globalGroup);
+ }
+
+ @Override
+ public void visitTargetId(InputEntityVisitor visitor, long id) {
+ visitor.endId(id, globalGroup);
+ }
+ };
+ }
+ default -> throw new IllegalStateException("Unexpected value: " + idType);
+ }
+ }
+
+ @Override
+ public InputEntityIdVisitor.String inputEntityStringIdVisitor(ReadableGroups groups) {
+ var globalGroup = groups.get(null);
+
+ return new InputEntityIdVisitor.String() {
+ @Override
+ public void visitNodeId(InputEntityVisitor visitor, String id) {
+ visitor.id(id, globalGroup);
+ }
+
+ @Override
+ public void visitSourceId(InputEntityVisitor visitor, String id) {
+ visitor.startId(id, globalGroup);
+ }
+
+ @Override
+ public void visitTargetId(InputEntityVisitor visitor, String id) {
+ visitor.endId(id, globalGroup);
+ }
+ };
+ }
+
+ @Override
+ public Setting additionalJvm() {
+ return BootloaderSettings.additional_jvm;
+ }
+
+ @Override
+ public Setting pageCacheMemory() {
+ return GraphDatabaseSettings.pagecache_memory;
+ }
+
+ @Override
+ public Long pageCacheMemoryValue(String value) {
+ return SettingValueParsers.BYTES.parse(value);
+ }
+
+ @Override
+ public ExecutionMonitor invisibleExecutionMonitor() {
+ return ExecutionMonitor.INVISIBLE;
+ }
+
+ @Override
+ public ProcedureSignature procedureSignature(
+ QualifiedName name,
+ List inputSignature,
+ List outputSignature,
+ Mode mode,
+ boolean admin,
+ String deprecated,
+ String description,
+ String warning,
+ boolean eager,
+ boolean caseInsensitive,
+ boolean systemProcedure,
+ boolean internal,
+ boolean allowExpiredCredentials
+ ) {
+ return new ProcedureSignature(
+ name,
+ inputSignature,
+ outputSignature,
+ mode,
+ admin,
+ deprecated,
+ description,
+ warning,
+ eager,
+ caseInsensitive,
+ systemProcedure,
+ internal,
+ allowExpiredCredentials
+ );
+ }
+
+ @Override
+ public long getHighestPossibleNodeCount(
+ Read read, IdGeneratorFactory idGeneratorFactory
+ ) {
+ return countByIdGenerator(idGeneratorFactory, RecordIdType.NODE).orElseGet(read::nodesGetCount);
+ }
+
+ @Override
+ public long getHighestPossibleRelationshipCount(
+ Read read, IdGeneratorFactory idGeneratorFactory
+ ) {
+ return countByIdGenerator(idGeneratorFactory, RecordIdType.RELATIONSHIP).orElseGet(read::relationshipsGetCount);
+ }
+
+ @Override
+ public String versionLongToString(long storeVersion) {
+ // copied from org.neo4j.kernel.impl.store.LegacyMetadataHandler.versionLongToString which is private
+ if (storeVersion == -1) {
+ return "Unknown";
+ }
+ Bits bits = Bits.bitsFromLongs(new long[]{storeVersion});
+ int length = bits.getShort(8);
+ if (length == 0 || length > 7) {
+ throw new IllegalArgumentException(format(Locale.ENGLISH, "The read version string length %d is not proper.", length));
+ }
+ char[] result = new char[length];
+ for (int i = 0; i < length; i++) {
+ result[i] = (char) bits.getShort(8);
+ }
+ return new String(result);
+ }
+
+ private static final class InputFromCompatInput implements Input {
+ private final CompatInput delegate;
+
+ private InputFromCompatInput(CompatInput delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public InputIterable nodes(Collector badCollector) {
+ return delegate.nodes(badCollector);
+ }
+
+ @Override
+ public InputIterable relationships(Collector badCollector) {
+ return delegate.relationships(badCollector);
+ }
+
+ @Override
+ public IdType idType() {
+ return delegate.idType();
+ }
+
+ @Override
+ public ReadableGroups groups() {
+ return delegate.groups();
+ }
+
+ @Override
+ public Estimates calculateEstimates(PropertySizeCalculator propertySizeCalculator) throws IOException {
+ return delegate.calculateEstimates((values, kernelTransaction) -> propertySizeCalculator.calculateSize(
+ values,
+ kernelTransaction.cursorContext(),
+ kernelTransaction.memoryTracker()
+ ));
+ }
+ }
+
+ @Override
+ public TestLog testLog() {
+ return new TestLogImpl();
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat specific use")
+ public Log getUserLog(LogService logService, Class> loggingClass) {
+ return logService.getUserLog(loggingClass);
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat specific use")
+ public Log getInternalLog(LogService logService, Class> loggingClass) {
+ return logService.getInternalLog(loggingClass);
+ }
+
+ @Override
+ public Relationship virtualRelationship(long id, Node startNode, Node endNode, RelationshipType type) {
+ return new VirtualRelationshipImpl(id, startNode, endNode, type);
+ }
+
+ @Override
+ public GdsDatabaseManagementServiceBuilder databaseManagementServiceBuilder(Path storeDir) {
+ return new GdsDatabaseManagementServiceBuilderImpl(storeDir);
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat specific use")
+ public RecordFormats selectRecordFormatForStore(
+ DatabaseLayout databaseLayout,
+ FileSystemAbstraction fs,
+ PageCache pageCache,
+ LogService logService,
+ PageCacheTracer pageCacheTracer
+ ) {
+ return RecordFormatSelector.selectForStore(
+ (RecordDatabaseLayout) databaseLayout,
+ fs,
+ pageCache,
+ logService.getInternalLogProvider(),
+ new CursorContextFactory(pageCacheTracer, EMPTY)
+ );
+ }
+
+ @Override
+ public boolean isNotNumericIndex(IndexCapability indexCapability) {
+ return !indexCapability.areValueCategoriesAccepted(ValueCategory.NUMBER);
+ }
+
+ @Override
+ public void setAllowUpgrades(Config.Builder configBuilder, boolean value) {
+ }
+
+ @Override
+ public String defaultRecordFormatSetting() {
+ return GraphDatabaseSettings.db_format.defaultValue();
+ }
+
+ @Override
+ public void configureRecordFormat(Config.Builder configBuilder, String recordFormat) {
+ var databaseRecordFormat = recordFormat.toLowerCase(Locale.ENGLISH);
+ configBuilder.set(GraphDatabaseSettings.db_format, databaseRecordFormat);
+ }
+
+ @Override
+ public GdsDatabaseLayout databaseLayout(Config config, String databaseName) {
+ var storageEngineFactory = StorageEngineFactory.selectStorageEngine(config);
+ var dbLayout = neo4jLayout(config).databaseLayout(databaseName);
+ var databaseLayout = storageEngineFactory.formatSpecificDatabaseLayout(dbLayout);
+ return new GdsDatabaseLayoutImpl(databaseLayout);
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat specific use")
+ public Neo4jLayout neo4jLayout(Config config) {
+ return Neo4jLayout.of(config);
+ }
+
+ @Override
+ public BoltTransactionRunner, ?> boltTransactionRunner() {
+ return new BoltTransactionRunnerImpl();
+ }
+
+ @Override
+ public HostnamePort getLocalBoltAddress(ConnectorPortRegister connectorPortRegister) {
+ return connectorPortRegister.getLocalAddress(ConnectorType.BOLT);
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat specific use")
+ public SslPolicyLoader createSllPolicyLoader(
+ FileSystemAbstraction fileSystem,
+ Config config,
+ LogService logService
+ ) {
+ return SslPolicyLoader.create(fileSystem, config, logService.getInternalLogProvider());
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat specific use")
+ public RecordFormats recordFormatSelector(
+ String databaseName,
+ Config databaseConfig,
+ FileSystemAbstraction fs,
+ LogService logService,
+ GraphDatabaseService databaseService
+ ) {
+ var neo4jLayout = Neo4jLayout.of(databaseConfig);
+ var recordDatabaseLayout = RecordDatabaseLayout.of(neo4jLayout, databaseName);
+ return RecordFormatSelector.selectForStoreOrConfigForNewDbs(
+ databaseConfig,
+ recordDatabaseLayout,
+ fs,
+ GraphDatabaseApiProxy.resolveDependency(databaseService, PageCache.class),
+ logService.getInternalLogProvider(),
+ GraphDatabaseApiProxy.resolveDependency(databaseService, CursorContextFactory.class)
+ );
+ }
+
+ @Override
+ public NamedDatabaseId randomDatabaseId() {
+ return new TestDatabaseIdRepository().getByName(UUID.randomUUID().toString()).get();
+ }
+
+ @Override
+ public ExecutionMonitor executionMonitor(CompatExecutionMonitor compatExecutionMonitor) {
+ return new ExecutionMonitor.Adapter(
+ compatExecutionMonitor.checkIntervalMillis(),
+ TimeUnit.MILLISECONDS
+ ) {
+
+ @Override
+ public void initialize(DependencyResolver dependencyResolver) {
+ compatExecutionMonitor.initialize(dependencyResolver);
+ }
+
+ @Override
+ public void start(StageExecution execution) {
+ compatExecutionMonitor.start(execution);
+ }
+
+ @Override
+ public void end(StageExecution execution, long totalTimeMillis) {
+ compatExecutionMonitor.end(execution, totalTimeMillis);
+ }
+
+ @Override
+ public void done(boolean successful, long totalTimeMillis, String additionalInformation) {
+ compatExecutionMonitor.done(successful, totalTimeMillis, additionalInformation);
+ }
+
+ @Override
+ public void check(StageExecution execution) {
+ compatExecutionMonitor.check(execution);
+ }
+ };
+ }
+
+ @Override
+ @SuppressFBWarnings("NP_LOAD_OF_KNOWN_NULL_VALUE") // We assign nulls because it makes the code more readable
+ public UserFunctionSignature userFunctionSignature(
+ QualifiedName name,
+ List inputSignature,
+ Neo4jTypes.AnyType type,
+ String description,
+ boolean internal,
+ boolean threadSafe
+ ) {
+ String deprecated = null; // no depracation
+ String category = null; // No predefined categpry (like temporal or math)
+ var caseInsensitive = false; // case sensitive name match
+ var isBuiltIn = false; // is built in; never true for GDS
+
+ return new UserFunctionSignature(
+ name,
+ inputSignature,
+ type,
+ deprecated,
+ description,
+ category,
+ caseInsensitive,
+ isBuiltIn,
+ internal,
+ threadSafe
+ );
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat API")
+ public CallableProcedure callableProcedure(CompatCallableProcedure procedure) {
+ return new CallableProcedureImpl(procedure);
+ }
+
+ @Override
+ @SuppressForbidden(reason = "This is the compat API")
+ public CallableUserAggregationFunction callableUserAggregationFunction(CompatUserAggregationFunction function) {
+ return new CallableUserAggregationFunctionImpl(function);
+ }
+
+ @Override
+ public long transactionId(KernelTransactionHandle kernelTransactionHandle) {
+ return kernelTransactionHandle.getTransactionSequenceNumber();
+ }
+
+ @Override
+ public void reserveNeo4jIds(IdGeneratorFactory generatorFactory, int size, CursorContext cursorContext) {
+ IdGenerator idGenerator = generatorFactory.get(RecordIdType.NODE);
+
+ idGenerator.nextConsecutiveIdRange(size, false, cursorContext);
+ }
+
+ @Override
+ public TransactionalContext newQueryContext(
+ TransactionalContextFactory contextFactory,
+ InternalTransaction tx,
+ String queryText,
+ MapValue queryParameters
+ ) {
+ return contextFactory.newContext(tx, queryText, queryParameters, QueryExecutionConfiguration.DEFAULT_CONFIG);
+ }
+
+ @Override
+ public boolean isCompositeDatabase(GraphDatabaseService databaseService) {
+ var databaseManager = GraphDatabaseApiProxy.resolveDependency(databaseService, FabricDatabaseManager.class);
+ return databaseManager.isFabricDatabase(GraphDatabaseApiProxy.databaseId(databaseService));
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/NodeLabelIndexLookupImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/NodeLabelIndexLookupImpl.java
new file mode 100644
index 00000000000..d4b58ae0e40
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/NodeLabelIndexLookupImpl.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.common.EntityType;
+import org.neo4j.internal.kernel.api.InternalIndexState;
+import org.neo4j.internal.kernel.api.SchemaRead;
+import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotFoundKernelException;
+import org.neo4j.internal.schema.IndexDescriptor;
+import org.neo4j.internal.schema.IndexType;
+import org.neo4j.internal.schema.SchemaDescriptor;
+import org.neo4j.internal.schema.SchemaDescriptors;
+import org.neo4j.kernel.api.KernelTransaction;
+
+final class NodeLabelIndexLookupImpl {
+
+ static boolean hasNodeLabelIndex(KernelTransaction transaction) {
+ return NodeLabelIndexLookupImpl.findUsableMatchingIndex(
+ transaction,
+ SchemaDescriptors.forAnyEntityTokens(EntityType.NODE)
+ ) != IndexDescriptor.NO_INDEX;
+ }
+
+ static IndexDescriptor findUsableMatchingIndex(
+ KernelTransaction transaction,
+ SchemaDescriptor schemaDescriptor
+ ) {
+ var schemaRead = transaction.schemaRead();
+ var iterator = schemaRead.index(schemaDescriptor);
+ while (iterator.hasNext()) {
+ var index = iterator.next();
+ if (index.getIndexType() == IndexType.LOOKUP && indexIsOnline(schemaRead, index)) {
+ return index;
+ }
+ }
+ return IndexDescriptor.NO_INDEX;
+ }
+
+ private static boolean indexIsOnline(SchemaRead schemaRead, IndexDescriptor index) {
+ var state = InternalIndexState.FAILED;
+ try {
+ state = schemaRead.indexGetState(index);
+ } catch (IndexNotFoundKernelException e) {
+ // Well the index should always exist here, but if we didn't find it while checking the state,
+ // then we obviously don't want to use it.
+ }
+ return state == InternalIndexState.ONLINE;
+ }
+
+ private NodeLabelIndexLookupImpl() {}
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/PartitionedStoreScan.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/PartitionedStoreScan.java
new file mode 100644
index 00000000000..98463b3150b
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/PartitionedStoreScan.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.compat.StoreScan;
+import org.neo4j.internal.kernel.api.NodeLabelIndexCursor;
+import org.neo4j.internal.kernel.api.PartitionedScan;
+import org.neo4j.kernel.api.KernelTransaction;
+
+final class PartitionedStoreScan implements StoreScan {
+ private final PartitionedScan scan;
+
+ PartitionedStoreScan(PartitionedScan scan) {
+ this.scan = scan;
+ }
+
+ static int getNumberOfPartitions(long nodeCount, int batchSize) {
+ int numberOfPartitions;
+ if (nodeCount > 0) {
+ // ceil div to try to get enough partitions so a single one does
+ // not include more nodes than batchSize
+ long partitions = ((nodeCount - 1) / batchSize) + 1;
+
+ // value must be positive
+ if (partitions < 1) {
+ partitions = 1;
+ }
+
+ numberOfPartitions = (int) Long.min(Integer.MAX_VALUE, partitions);
+ } else {
+ // we have no partitions to scan, but the value must still be positive
+ numberOfPartitions = 1;
+ }
+ return numberOfPartitions;
+ }
+
+ @Override
+ public boolean reserveBatch(NodeLabelIndexCursor cursor, KernelTransaction ktx) {
+ return scan.reservePartition(cursor, ktx.cursorContext(), ktx.securityContext().mode());
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/ReferencePropertyReference.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/ReferencePropertyReference.java
new file mode 100644
index 00000000000..afaaa02f22e
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/ReferencePropertyReference.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.compat.PropertyReference;
+import org.neo4j.storageengine.api.Reference;
+
+import java.util.Objects;
+
+public final class ReferencePropertyReference implements PropertyReference {
+
+ private static final PropertyReference EMPTY = new ReferencePropertyReference(null);
+
+ public final Reference reference;
+
+ private ReferencePropertyReference(Reference reference) {
+ this.reference = reference;
+ }
+
+ public static PropertyReference of(Reference reference) {
+ return new ReferencePropertyReference(Objects.requireNonNull(reference));
+ }
+
+ public static PropertyReference empty() {
+ return EMPTY;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return reference == null;
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/ScanBasedStoreScanImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/ScanBasedStoreScanImpl.java
new file mode 100644
index 00000000000..28784106272
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/ScanBasedStoreScanImpl.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.compat.StoreScan;
+import org.neo4j.internal.kernel.api.Cursor;
+import org.neo4j.internal.kernel.api.Scan;
+import org.neo4j.kernel.api.KernelTransaction;
+
+public final class ScanBasedStoreScanImpl implements StoreScan {
+ private final Scan scan;
+ private final int batchSize;
+
+ public ScanBasedStoreScanImpl(Scan scan, int batchSize) {
+ this.scan = scan;
+ this.batchSize = batchSize;
+ }
+
+ @Override
+ public boolean reserveBatch(C cursor, KernelTransaction ktx) {
+ return scan.reserveBatch(cursor, batchSize, ktx.cursorContext(), ktx.securityContext().mode());
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/SettingProxyFactoryImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/SettingProxyFactoryImpl.java
new file mode 100644
index 00000000000..0b4c48c565c
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/SettingProxyFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.SettingProxyApi;
+import org.neo4j.gds.compat.SettingProxyFactory;
+
+@ServiceProvider
+public final class SettingProxyFactoryImpl implements SettingProxyFactory {
+
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return version == Neo4jVersion.V_5_5;
+ }
+
+ @Override
+ public SettingProxyApi load() {
+ return new SettingProxyImpl();
+ }
+
+ @Override
+ public String description() {
+ return "Neo4j Settings 5.5";
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/SettingProxyImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/SettingProxyImpl.java
new file mode 100644
index 00000000000..62d6597d682
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/SettingProxyImpl.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.configuration.Config;
+import org.neo4j.configuration.SettingBuilder;
+import org.neo4j.dbms.systemgraph.TopologyGraphDbmsModel;
+import org.neo4j.gds.compat.DatabaseMode;
+import org.neo4j.gds.compat.SettingProxyApi;
+import org.neo4j.graphdb.GraphDatabaseService;
+import org.neo4j.graphdb.config.Setting;
+import org.neo4j.kernel.impl.factory.GraphDatabaseFacade;
+import org.neo4j.kernel.internal.GraphDatabaseAPI;
+
+public class SettingProxyImpl implements SettingProxyApi {
+
+ @Override
+ public Setting setting(org.neo4j.gds.compat.Setting setting) {
+ var builder = SettingBuilder.newBuilder(setting.name(), setting.parser(), setting.defaultValue());
+ if (setting.dynamic()) {
+ builder = builder.dynamic();
+ }
+ if (setting.immutable()) {
+ builder = builder.immutable();
+ }
+ setting.dependency().ifPresent(builder::setDependency);
+ setting.constraints().forEach(builder::addConstraint);
+ return builder.build();
+ }
+
+ @Override
+ public DatabaseMode databaseMode(Config config, GraphDatabaseService databaseService) {
+ return switch (((GraphDatabaseAPI) databaseService).mode()) {
+ case RAFT -> DatabaseMode.CORE;
+ case REPLICA -> DatabaseMode.READ_REPLICA;
+ case SINGLE -> DatabaseMode.SINGLE;
+ case VIRTUAL -> throw new UnsupportedOperationException("What's a virtual database anyway?");
+ };
+ }
+
+ @Override
+ public void setDatabaseMode(Config config, DatabaseMode databaseMode, GraphDatabaseService databaseService) {
+ // super hacky, there is no way to set the mode of a database without restarting it
+ if (!(databaseService instanceof GraphDatabaseFacade db)) {
+ throw new IllegalArgumentException(
+ "Cannot set database mode on a database that is not a GraphDatabaseFacade");
+ }
+ try {
+ var modeField = GraphDatabaseFacade.class.getDeclaredField("mode");
+ modeField.setAccessible(true);
+ modeField.set(db, switch (databaseMode) {
+ case CORE -> TopologyGraphDbmsModel.HostedOnMode.RAFT;
+ case READ_REPLICA -> TopologyGraphDbmsModel.HostedOnMode.REPLICA;
+ case SINGLE -> TopologyGraphDbmsModel.HostedOnMode.SINGLE;
+ });
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException(
+ "Could not set the mode field because it no longer exists. This compat layer needs to be updated.",
+ e
+ );
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Could not get the permissions to set the mode field.", e);
+ }
+ }
+
+ @Override
+ public String secondaryModeName() {
+ return "Secondary";
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/TestLogImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/TestLogImpl.java
new file mode 100644
index 00000000000..8cc7dec1f3b
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/TestLogImpl.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.TestLog;
+
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
+
+public class TestLogImpl implements TestLog {
+
+ private final ConcurrentMap> messages;
+
+ TestLogImpl() {
+ messages = new ConcurrentHashMap<>(3);
+ }
+
+ @Override
+ public void assertContainsMessage(String level, String fragment) {
+ if (!containsMessage(level, fragment)) {
+ throw new RuntimeException(
+ String.format(
+ Locale.US,
+ "Expected log output to contain `%s` for log level `%s`%nLog messages:%n%s",
+ fragment,
+ level,
+ String.join("\n", messages.get(level))
+ )
+ );
+ }
+ }
+
+ @Override
+ public boolean containsMessage(String level, String fragment) {
+ ConcurrentLinkedQueue messageList = messages.getOrDefault(level, new ConcurrentLinkedQueue<>());
+ return messageList.stream().anyMatch((message) -> message.contains(fragment));
+ }
+
+ @Override
+ public boolean hasMessages(String level) {
+ return !messages.getOrDefault(level, new ConcurrentLinkedQueue<>()).isEmpty();
+ }
+
+ @Override
+ public ArrayList getMessages(String level) {
+ return new ArrayList<>(messages.getOrDefault(level, new ConcurrentLinkedQueue<>()));
+ }
+
+ @SuppressForbidden(reason = "test log can print")
+ public void printMessages() {
+ System.out.println("TestLog Messages: " + messages);
+ }
+
+ @Override
+ public boolean isDebugEnabled() {
+ return true;
+ }
+
+ @Override
+ public void debug(String message) {
+ logMessage(DEBUG, message);
+ }
+
+ @Override
+ public void debug(String message, Throwable throwable) {
+ debug(String.format(Locale.US, "%s - %s", message, throwable.getMessage()));
+ }
+
+ @Override
+ public void debug(String format, Object... arguments) {
+ debug(String.format(Locale.US, format, arguments));
+ }
+
+ @Override
+ public void info(String message) {
+ logMessage(INFO, message);
+ }
+
+ @Override
+ public void info(String message, Throwable throwable) {
+ info(String.format(Locale.US, "%s - %s", message, throwable.getMessage()));
+ }
+
+ @Override
+ public void info(String format, Object... arguments) {
+ info(String.format(Locale.US, format, arguments));
+ }
+
+ @Override
+ public void warn(String message) {
+ logMessage(WARN, message);
+ }
+
+ @Override
+ public void warn(String message, Throwable throwable) {
+ warn(String.format(Locale.US, "%s - %s", message, throwable.getMessage()));
+ }
+
+ @Override
+ public void warn(String format, Object... arguments) {
+ warn(String.format(Locale.US, format, arguments));
+ }
+
+ @Override
+ public void error(String message) {
+ logMessage(ERROR, message);
+ }
+
+ @Override
+ public void error(String message, Throwable throwable) {
+ error(String.format(Locale.US, "%s - %s", message, throwable.getMessage()));
+ }
+
+ @Override
+ public void error(String format, Object... arguments) {
+ error(String.format(Locale.US, format, arguments));
+ }
+
+ private void logMessage(String level, String message) {
+ messages.computeIfAbsent(
+ level,
+ (ignore) -> new ConcurrentLinkedQueue<>()
+ ).add(message);
+ }
+}
diff --git a/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/VirtualRelationshipImpl.java b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/VirtualRelationshipImpl.java
new file mode 100644
index 00000000000..0e6e2b491f3
--- /dev/null
+++ b/compatibility/5.5/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_55/VirtualRelationshipImpl.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.compat.VirtualRelationship;
+import org.neo4j.graphdb.Node;
+import org.neo4j.graphdb.RelationshipType;
+
+public class VirtualRelationshipImpl extends VirtualRelationship {
+
+ VirtualRelationshipImpl(
+ long id,
+ Node startNode,
+ Node endNode,
+ RelationshipType type
+ ) {
+ super(id, startNode, endNode, type);
+ }
+
+ @Override
+ public String getElementId() {
+ return Long.toString(getId());
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/build.gradle b/compatibility/5.5/storage-engine-adapter/build.gradle
new file mode 100644
index 00000000000..330a0408d2c
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/build.gradle
@@ -0,0 +1,66 @@
+apply plugin: 'java-library'
+apply plugin: 'me.champeau.mrjar'
+
+description = 'Neo4j Graph Data Science :: Storage Engine Adapter 5.5'
+
+group = 'org.neo4j.gds'
+
+// for all 5.x versions
+if (ver.'neo4j'.startsWith('5.')) {
+ sourceSets {
+ main {
+ java {
+ srcDirs = ['src/main/java17']
+ }
+ }
+ }
+
+ dependencies {
+ annotationProcessor project(':annotations')
+ annotationProcessor group: 'org.immutables', name: 'value', version: ver.'immutables'
+ annotationProcessor group: 'org.neo4j', name: 'annotations', version: neos.'5.5'
+
+ compileOnly project(':annotations')
+ compileOnly group: 'org.immutables', name: 'value-annotations', version: ver.'immutables'
+ compileOnly group: 'org.neo4j', name: 'neo4j', version: neos.'5.5'
+ compileOnly group: 'org.neo4j', name: 'neo4j-record-storage-engine', version: neos.'5.5'
+
+ implementation project(':core')
+ implementation project(':storage-engine-adapter-api')
+ implementation project(':config-api')
+ implementation project(':string-formatting')
+ }
+} else {
+ multiRelease {
+ targetVersions 11, 17
+ }
+
+ if (!project.hasProperty('no-forbidden-apis')) {
+ forbiddenApisJava17 {
+ exclude('**')
+ }
+ }
+
+ dependencies {
+ annotationProcessor group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
+ compileOnly group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
+ compileOnly group: 'org.neo4j', name: 'neo4j-kernel-api', version: ver.'neo4j'
+
+ implementation project(':neo4j-adapter')
+ implementation project(':storage-engine-adapter-api')
+
+ java17AnnotationProcessor project(':annotations')
+ java17AnnotationProcessor group: 'org.immutables', name: 'value', version: ver.'immutables'
+ java17AnnotationProcessor group: 'org.neo4j', name: 'annotations', version: neos.'5.5'
+
+ java17CompileOnly project(':annotations')
+ java17CompileOnly group: 'org.immutables', name: 'value-annotations', version: ver.'immutables'
+ java17CompileOnly group: 'org.neo4j', name: 'neo4j', version: neos.'5.5'
+ java17CompileOnly group: 'org.neo4j', name: 'neo4j-record-storage-engine', version: neos.'5.5'
+
+ java17Implementation project(':core')
+ java17Implementation project(':storage-engine-adapter-api')
+ java17Implementation project(':config-api')
+ java17Implementation project(':string-formatting')
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_55/InMemoryStorageEngineFactory.java b/compatibility/5.5/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_55/InMemoryStorageEngineFactory.java
new file mode 100644
index 00000000000..177fe8e896b
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_55/InMemoryStorageEngineFactory.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.configuration.Config;
+import org.neo4j.dbms.database.readonly.DatabaseReadOnlyChecker;
+import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector;
+import org.neo4j.internal.id.IdController;
+import org.neo4j.internal.id.IdGeneratorFactory;
+import org.neo4j.internal.schema.IndexConfigCompleter;
+import org.neo4j.internal.schema.SchemaRule;
+import org.neo4j.internal.schema.SchemaState;
+import org.neo4j.io.fs.FileSystemAbstraction;
+import org.neo4j.io.layout.DatabaseLayout;
+import org.neo4j.io.layout.Neo4jLayout;
+import org.neo4j.io.pagecache.PageCache;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.io.pagecache.tracing.PageCacheTracer;
+import org.neo4j.lock.LockService;
+import org.neo4j.logging.LogProvider;
+import org.neo4j.logging.internal.LogService;
+import org.neo4j.memory.MemoryTracker;
+import org.neo4j.monitoring.DatabaseHealth;
+import org.neo4j.scheduler.JobScheduler;
+import org.neo4j.storageengine.api.CommandReaderFactory;
+import org.neo4j.storageengine.api.ConstraintRuleAccessor;
+import org.neo4j.storageengine.api.LogVersionRepository;
+import org.neo4j.storageengine.api.MetadataProvider;
+import org.neo4j.storageengine.api.StorageEngine;
+import org.neo4j.storageengine.api.StorageEngineFactory;
+import org.neo4j.storageengine.api.StorageFilesState;
+import org.neo4j.storageengine.api.StoreId;
+import org.neo4j.storageengine.api.StoreVersion;
+import org.neo4j.storageengine.api.StoreVersionCheck;
+import org.neo4j.storageengine.api.TransactionIdStore;
+import org.neo4j.storageengine.migration.RollingUpgradeCompatibility;
+import org.neo4j.storageengine.migration.SchemaRuleMigrationAccess;
+import org.neo4j.storageengine.migration.StoreMigrationParticipant;
+import org.neo4j.token.TokenHolders;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@ServiceProvider
+public class InMemoryStorageEngineFactory implements StorageEngineFactory {
+
+ @Override
+ public String name() {
+ return "unsupported55";
+ }
+
+ @Override
+ public StoreVersionCheck versionCheck(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ LogService logService,
+ PageCacheTracer pageCacheTracer
+ ) {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreVersion versionInformation(String storeVersion) {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreVersion versionInformation(StoreId storeId) {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public RollingUpgradeCompatibility rollingUpgradeCompatibility() {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public List migrationParticipants(
+ FileSystemAbstraction fs,
+ Config config,
+ PageCache pageCache,
+ JobScheduler jobScheduler,
+ LogService logService,
+ PageCacheTracer cacheTracer,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public StorageEngine instantiate(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ TokenHolders tokenHolders,
+ SchemaState schemaState,
+ ConstraintRuleAccessor constraintSemantics,
+ IndexConfigCompleter indexConfigCompleter,
+ LockService lockService,
+ IdGeneratorFactory idGeneratorFactory,
+ IdController idController,
+ DatabaseHealth databaseHealth,
+ LogProvider internalLogProvider,
+ LogProvider userLogProvider,
+ RecoveryCleanupWorkCollector recoveryCleanupWorkCollector,
+ PageCacheTracer cacheTracer,
+ boolean createStoreIfNotExists,
+ DatabaseReadOnlyChecker readOnlyChecker,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public List listStorageFiles(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout) throws
+ IOException {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public boolean storageExists(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout, PageCache pageCache) {
+ return false;
+ }
+
+ @Override
+ public TransactionIdStore readOnlyTransactionIdStore(
+ FileSystemAbstraction filySystem,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public LogVersionRepository readOnlyLogVersionRepository(
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public MetadataProvider transactionMetaDataStore(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ PageCacheTracer cacheTracer,
+ DatabaseReadOnlyChecker readOnlyChecker
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public StoreId storeId(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public void setStoreId(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext,
+ StoreId storeId,
+ long upgradeTxChecksum,
+ long upgradeTxCommitTimestamp
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public void setExternalStoreUUID(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext,
+ UUID externalStoreId
+ ) throws IOException {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public Optional databaseIdUuid(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public SchemaRuleMigrationAccess schemaRuleMigrationAccess(
+ FileSystemAbstraction fs,
+ PageCache pageCache,
+ Config config,
+ DatabaseLayout databaseLayout,
+ LogService logService,
+ String recordFormats,
+ PageCacheTracer cacheTracer,
+ CursorContext cursorContext,
+ MemoryTracker memoryTracker
+ ) {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public List loadSchemaRules(
+ FileSystemAbstraction fs,
+ PageCache pageCache,
+ Config config,
+ DatabaseLayout databaseLayout,
+ CursorContext cursorContext
+ ) {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public StorageFilesState checkStoreFileState(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache
+ ) {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public CommandReaderFactory commandReaderFactory() {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public DatabaseLayout databaseLayout(Neo4jLayout neo4jLayout, String databaseName) {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_55/StorageEngineProxyFactoryImpl.java b/compatibility/5.5/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_55/StorageEngineProxyFactoryImpl.java
new file mode 100644
index 00000000000..0c98e19eeb7
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_55/StorageEngineProxyFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.StorageEngineProxyApi;
+import org.neo4j.gds.compat.StorageEngineProxyFactory;
+
+@ServiceProvider
+public class StorageEngineProxyFactoryImpl implements StorageEngineProxyFactory {
+
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return false;
+ }
+
+ @Override
+ public StorageEngineProxyApi load() {
+ throw new UnsupportedOperationException("5.5 storage engine requires JDK17");
+ }
+
+ @Override
+ public String description() {
+ return "Storage Engine 5.5";
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryCommandCreationContextImpl.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryCommandCreationContextImpl.java
new file mode 100644
index 00000000000..ac20edc223e
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryCommandCreationContextImpl.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.kernel.KernelVersion;
+import org.neo4j.kernel.KernelVersionProvider;
+import org.neo4j.lock.LockTracer;
+import org.neo4j.lock.ResourceLocker;
+import org.neo4j.storageengine.api.CommandCreationContext;
+import org.neo4j.storageengine.api.cursor.StoreCursors;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+
+public class InMemoryCommandCreationContextImpl implements CommandCreationContext {
+
+ private final AtomicLong schemaTokens;
+ private final AtomicInteger propertyTokens;
+ private final AtomicInteger labelTokens;
+ private final AtomicInteger typeTokens;
+
+ InMemoryCommandCreationContextImpl() {
+ this.schemaTokens = new AtomicLong(0);
+ this.propertyTokens = new AtomicInteger(0);
+ this.labelTokens = new AtomicInteger(0);
+ this.typeTokens = new AtomicInteger(0);
+ }
+
+ @Override
+ public long reserveNode() {
+ throw new UnsupportedOperationException("Creating nodes is not supported");
+ }
+
+ @Override
+ public long reserveRelationship(
+ long sourceNode,
+ long targetNode,
+ int relationshipType,
+ boolean sourceNodeAddedInTx,
+ boolean targetNodeAddedInTx
+ ) {
+ throw new UnsupportedOperationException("Creating relationships is not supported");
+ }
+
+ @Override
+ public long reserveSchema() {
+ return schemaTokens.getAndIncrement();
+ }
+
+ @Override
+ public int reserveLabelTokenId() {
+ return labelTokens.getAndIncrement();
+ }
+
+ @Override
+ public int reservePropertyKeyTokenId() {
+ return propertyTokens.getAndIncrement();
+ }
+
+ @Override
+ public int reserveRelationshipTypeTokenId() {
+ return typeTokens.getAndIncrement();
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public void initialize(
+ KernelVersionProvider kernelVersionProvider,
+ CursorContext cursorContext,
+ StoreCursors storeCursors,
+ Supplier oldestActiveTransactionSequenceNumber,
+ ResourceLocker locks,
+ Supplier lockTracer
+ ) {
+
+ }
+
+ @Override
+ public KernelVersion kernelVersion() {
+ // NOTE: Double-check if this is still correct when you copy this into a new compat layer
+ return KernelVersion.LATEST;
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryCountsStoreImpl.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryCountsStoreImpl.java
new file mode 100644
index 00000000000..8e6544b2055
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryCountsStoreImpl.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.annotations.documented.ReporterFactory;
+import org.neo4j.counts.CountsAccessor;
+import org.neo4j.counts.CountsStorage;
+import org.neo4j.counts.CountsVisitor;
+import org.neo4j.gds.NodeLabel;
+import org.neo4j.gds.api.GraphStore;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.io.pagecache.context.CursorContextFactory;
+import org.neo4j.io.pagecache.tracing.FileFlushEvent;
+import org.neo4j.memory.MemoryTracker;
+import org.neo4j.storageengine.api.cursor.StoreCursors;
+import org.neo4j.token.TokenHolders;
+import org.neo4j.token.api.TokenNotFoundException;
+
+public class InMemoryCountsStoreImpl implements CountsStorage, CountsAccessor {
+
+ private final GraphStore graphStore;
+ private final TokenHolders tokenHolders;
+
+ public InMemoryCountsStoreImpl(
+ GraphStore graphStore,
+ TokenHolders tokenHolders
+ ) {
+
+ this.graphStore = graphStore;
+ this.tokenHolders = tokenHolders;
+ }
+
+ @Override
+ public void start(
+ CursorContext cursorContext, StoreCursors storeCursors, MemoryTracker memoryTracker
+ ) {
+
+ }
+
+ @Override
+ public void checkpoint(FileFlushEvent fileFlushEvent, CursorContext cursorContext) {
+
+ }
+
+ @Override
+ public long nodeCount(int labelId, CursorContext cursorContext) {
+ if (labelId == -1) {
+ return graphStore.nodeCount();
+ }
+
+ String nodeLabel;
+ try {
+ nodeLabel = tokenHolders.labelTokens().getTokenById(labelId).name();
+ } catch (TokenNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ return graphStore.nodes().nodeCount(NodeLabel.of(nodeLabel));
+ }
+
+ @Override
+ public long relationshipCount(int startLabelId, int typeId, int endLabelId, CursorContext cursorContext) {
+ // TODO: this is quite wrong
+ return graphStore.relationshipCount();
+ }
+
+ @Override
+ public boolean consistencyCheck(
+ ReporterFactory reporterFactory,
+ CursorContextFactory contextFactory,
+ int numThreads
+ ) {
+ return true;
+ }
+
+ @Override
+ public CountsAccessor.Updater apply(long txId, boolean isLast, CursorContext cursorContext) {
+ throw new UnsupportedOperationException("Updates are not supported");
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public void accept(CountsVisitor visitor, CursorContext cursorContext) {
+ tokenHolders.labelTokens().getAllTokens().forEach(labelToken -> {
+ visitor.visitNodeCount(labelToken.id(), nodeCount(labelToken.id(), cursorContext));
+ });
+
+ visitor.visitRelationshipCount(-1, -1, -1, graphStore.relationshipCount());
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryMetaDataProviderImpl.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryMetaDataProviderImpl.java
new file mode 100644
index 00000000000..8dec0e77ad9
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryMetaDataProviderImpl.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.internal.recordstorage.InMemoryLogVersionRepository55;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.storageengine.api.ClosedTransactionMetadata;
+import org.neo4j.storageengine.api.ExternalStoreId;
+import org.neo4j.storageengine.api.MetadataProvider;
+import org.neo4j.storageengine.api.StoreId;
+import org.neo4j.storageengine.api.TransactionId;
+
+import java.io.IOException;
+import java.util.Optional;
+import java.util.UUID;
+
+public class InMemoryMetaDataProviderImpl implements MetadataProvider {
+
+ private final ExternalStoreId externalStoreId;
+ private final InMemoryLogVersionRepository55 logVersionRepository;
+ private final InMemoryTransactionIdStoreImpl transactionIdStore;
+
+ InMemoryMetaDataProviderImpl() {
+ this.logVersionRepository = new InMemoryLogVersionRepository55();
+ this.externalStoreId = new ExternalStoreId(UUID.randomUUID());
+ this.transactionIdStore = new InMemoryTransactionIdStoreImpl();
+ }
+
+ @Override
+ public ExternalStoreId getExternalStoreId() {
+ return this.externalStoreId;
+ }
+
+ @Override
+ public ClosedTransactionMetadata getLastClosedTransaction() {
+ return this.transactionIdStore.getLastClosedTransaction();
+ }
+
+ @Override
+ public void transactionClosed(
+ long transactionId,
+ long logVersion,
+ long byteOffset,
+ int checksum,
+ long commitTimestamp
+ ) {
+ this.transactionIdStore.transactionClosed(
+ transactionId,
+ logVersion,
+ byteOffset,
+ checksum,
+ commitTimestamp
+ );
+ }
+
+ @Override
+ public void resetLastClosedTransaction(
+ long transactionId,
+ long logVersion,
+ long byteOffset,
+ int checksum,
+ long commitTimestamp
+ ) {
+ this.transactionIdStore.resetLastClosedTransaction(
+ transactionId,
+ logVersion,
+ byteOffset,
+ checksum,
+ commitTimestamp
+ );
+ }
+
+ @Override
+ public void setCurrentLogVersion(long version) {
+ logVersionRepository.setCurrentLogVersion(version);
+ }
+
+ @Override
+ public long incrementAndGetVersion() {
+ return logVersionRepository.incrementAndGetVersion();
+ }
+
+ @Override
+ public void setCheckpointLogVersion(long version) {
+ logVersionRepository.setCheckpointLogVersion(version);
+ }
+
+ @Override
+ public long incrementAndGetCheckpointLogVersion() {
+ return logVersionRepository.incrementAndGetCheckpointLogVersion();
+ }
+
+ @Override
+ public void transactionCommitted(long transactionId, int checksum, long commitTimestamp) {
+ transactionIdStore.transactionCommitted(transactionId, checksum, commitTimestamp);
+ }
+
+ @Override
+ public void setLastCommittedAndClosedTransactionId(
+ long transactionId, int checksum, long commitTimestamp, long byteOffset, long logVersion
+ ) {
+ transactionIdStore.setLastCommittedAndClosedTransactionId(
+ transactionId,
+ checksum,
+ commitTimestamp,
+ byteOffset,
+ logVersion
+ );
+ }
+
+ @Override
+ public void regenerateMetadata(StoreId storeId, UUID externalStoreUUID, CursorContext cursorContext) {
+ }
+
+ @Override
+ public StoreId getStoreId() {
+ return StoreId.UNKNOWN;
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+
+ @Override
+ public long getCurrentLogVersion() {
+ return this.logVersionRepository.getCurrentLogVersion();
+ }
+
+ @Override
+ public long getCheckpointLogVersion() {
+ return this.logVersionRepository.getCheckpointLogVersion();
+ }
+
+ @Override
+ public long nextCommittingTransactionId() {
+ return this.transactionIdStore.nextCommittingTransactionId();
+ }
+
+ @Override
+ public long committingTransactionId() {
+ return this.transactionIdStore.committingTransactionId();
+ }
+
+ @Override
+ public long getLastCommittedTransactionId() {
+ return this.transactionIdStore.getLastCommittedTransactionId();
+ }
+
+ @Override
+ public TransactionId getLastCommittedTransaction() {
+ return this.transactionIdStore.getLastCommittedTransaction();
+ }
+
+ @Override
+ public long getLastClosedTransactionId() {
+ return this.transactionIdStore.getLastClosedTransactionId();
+ }
+
+ @Override
+ public Optional getDatabaseIdUuid(CursorContext cursorTracer) {
+ throw new IllegalStateException("Not supported");
+ }
+
+ @Override
+ public void setDatabaseIdUuid(UUID uuid, CursorContext cursorContext) {
+ throw new IllegalStateException("Not supported");
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryNodeCursor.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryNodeCursor.java
new file mode 100644
index 00000000000..d6118867135
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryNodeCursor.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.api.GraphStore;
+import org.neo4j.gds.compat.AbstractInMemoryNodeCursor;
+import org.neo4j.storageengine.api.AllNodeScan;
+import org.neo4j.storageengine.api.Degrees;
+import org.neo4j.storageengine.api.LongReference;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.Reference;
+import org.neo4j.storageengine.api.RelationshipSelection;
+import org.neo4j.storageengine.api.StoragePropertyCursor;
+import org.neo4j.storageengine.api.StorageRelationshipTraversalCursor;
+import org.neo4j.token.TokenHolders;
+
+public class InMemoryNodeCursor extends AbstractInMemoryNodeCursor {
+
+ public InMemoryNodeCursor(GraphStore graphStore, TokenHolders tokenHolders) {
+ super(graphStore, tokenHolders);
+ }
+
+ @Override
+ public boolean hasLabel() {
+ return hasAtLeastOneLabelForCurrentNode();
+ }
+
+ @Override
+ public Reference propertiesReference() {
+ return LongReference.longReference(getId());
+ }
+
+ @Override
+ public void properties(StoragePropertyCursor propertyCursor, PropertySelection selection) {
+ propertyCursor.initNodeProperties(propertiesReference(), selection);
+ }
+
+ @Override
+ public void properties(StoragePropertyCursor propertyCursor) {
+ properties(propertyCursor, PropertySelection.ALL_PROPERTIES);
+ }
+
+ @Override
+ public boolean supportsFastRelationshipsTo() {
+ return false;
+ }
+
+ @Override
+ public void relationshipsTo(
+ StorageRelationshipTraversalCursor storageRelationshipTraversalCursor,
+ RelationshipSelection relationshipSelection,
+ long neighbourNodeReference
+ ) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void degrees(RelationshipSelection selection, Degrees.Mutator mutator) {
+ }
+
+ @Override
+ public boolean scanBatch(AllNodeScan allNodeScan, long sizeHint) {
+ return super.scanBatch(allNodeScan, (int) sizeHint);
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryNodePropertyCursor.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryNodePropertyCursor.java
new file mode 100644
index 00000000000..3718d2ce791
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryNodePropertyCursor.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.compat.AbstractInMemoryNodePropertyCursor;
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.storageengine.api.LongReference;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.Reference;
+import org.neo4j.token.TokenHolders;
+
+public class InMemoryNodePropertyCursor extends AbstractInMemoryNodePropertyCursor {
+
+ public InMemoryNodePropertyCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) {
+ super(graphStore, tokenHolders);
+ }
+
+ @Override
+ public void initNodeProperties(Reference reference, PropertySelection selection, long ownerReference) {
+ reset();
+ setId(((LongReference) reference).id);
+ setPropertySelection(new InMemoryPropertySelectionImpl(selection));
+ }
+
+ @Override
+ public void initRelationshipProperties(Reference reference, PropertySelection selection, long ownerReference) {
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryPropertyCursor.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryPropertyCursor.java
new file mode 100644
index 00000000000..48434b8e0a1
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryPropertyCursor.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.compat.AbstractInMemoryPropertyCursor;
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.Reference;
+import org.neo4j.storageengine.api.StorageNodeCursor;
+import org.neo4j.storageengine.api.StorageRelationshipCursor;
+import org.neo4j.token.TokenHolders;
+
+public class InMemoryPropertyCursor extends AbstractInMemoryPropertyCursor {
+
+ public InMemoryPropertyCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) {
+ super(graphStore, tokenHolders);
+ }
+
+ @Override
+ public void initNodeProperties(Reference reference, PropertySelection selection, long ownerReference) {
+ if (this.delegate == null || !(this.delegate instanceof InMemoryNodePropertyCursor)) {
+ this.delegate = new InMemoryNodePropertyCursor(graphStore, tokenHolders);
+ }
+
+ ((InMemoryNodePropertyCursor) delegate).initNodeProperties(reference, selection);
+ }
+
+ @Override
+ public void initNodeProperties(StorageNodeCursor nodeCursor, PropertySelection selection) {
+ if (this.delegate == null || !(this.delegate instanceof InMemoryNodePropertyCursor)) {
+ this.delegate = new InMemoryNodePropertyCursor(graphStore, tokenHolders);
+ }
+
+ ((InMemoryNodePropertyCursor) delegate).initNodeProperties(nodeCursor, selection);
+ }
+
+ @Override
+ public void initRelationshipProperties(StorageRelationshipCursor relationshipCursor, PropertySelection selection) {
+ if (this.delegate == null || !(this.delegate instanceof InMemoryRelationshipPropertyCursor)) {
+ this.delegate = new InMemoryRelationshipPropertyCursor(graphStore, tokenHolders);
+ }
+
+ ((InMemoryRelationshipPropertyCursor) delegate).initRelationshipProperties(relationshipCursor, selection);
+ }
+
+ @Override
+ public void initRelationshipProperties(Reference reference, PropertySelection selection, long ownerReference) {
+ if (this.delegate == null || !(this.delegate instanceof InMemoryRelationshipPropertyCursor)) {
+ this.delegate = new InMemoryRelationshipPropertyCursor(graphStore, tokenHolders);
+ }
+
+ ((InMemoryRelationshipPropertyCursor) delegate).initRelationshipProperties(reference, selection);
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryPropertySelectionImpl.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryPropertySelectionImpl.java
new file mode 100644
index 00000000000..146d3613887
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryPropertySelectionImpl.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.compat.InMemoryPropertySelection;
+import org.neo4j.storageengine.api.PropertySelection;
+
+public class InMemoryPropertySelectionImpl implements InMemoryPropertySelection {
+
+ private final PropertySelection propertySelection;
+
+ public InMemoryPropertySelectionImpl(PropertySelection propertySelection) {this.propertySelection = propertySelection;}
+
+ @Override
+ public boolean isLimited() {
+ return propertySelection.isLimited();
+ }
+
+ @Override
+ public int numberOfKeys() {
+ return propertySelection.numberOfKeys();
+ }
+
+ @Override
+ public int key(int index) {
+ return propertySelection.key(index);
+ }
+
+ @Override
+ public boolean test(int key) {
+ return propertySelection.test(key);
+ }
+
+ @Override
+ public boolean isKeysOnly() {
+ return propertySelection.isKeysOnly();
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryRelationshipPropertyCursor.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryRelationshipPropertyCursor.java
new file mode 100644
index 00000000000..93fa5f6ba74
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryRelationshipPropertyCursor.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.compat.AbstractInMemoryRelationshipPropertyCursor;
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.gds.storageengine.InMemoryRelationshipCursor;
+import org.neo4j.storageengine.api.LongReference;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.Reference;
+import org.neo4j.storageengine.api.StorageRelationshipCursor;
+import org.neo4j.token.TokenHolders;
+
+public class InMemoryRelationshipPropertyCursor extends AbstractInMemoryRelationshipPropertyCursor {
+
+ InMemoryRelationshipPropertyCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) {
+ super(graphStore, tokenHolders);
+ }
+
+ @Override
+ public void initNodeProperties(
+ Reference reference, PropertySelection propertySelection, long ownerReference
+ ) {
+
+ }
+
+ @Override
+ public void initRelationshipProperties(
+ Reference reference, PropertySelection propertySelection, long ownerReference
+ ) {
+ var relationshipId = ((LongReference) reference).id;
+ var relationshipCursor = new InMemoryRelationshipScanCursor(graphStore, tokenHolders);
+ relationshipCursor.single(relationshipId);
+ relationshipCursor.next();
+ relationshipCursor.properties(this, new InMemoryPropertySelectionImpl(propertySelection));
+ }
+
+ @Override
+ public void initRelationshipProperties(StorageRelationshipCursor relationshipCursor, PropertySelection selection) {
+ var inMemoryRelationshipCursor = (InMemoryRelationshipCursor) relationshipCursor;
+ inMemoryRelationshipCursor.properties(this, selection);
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryRelationshipScanCursor.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryRelationshipScanCursor.java
new file mode 100644
index 00000000000..42b15b04014
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryRelationshipScanCursor.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.internal.recordstorage.AbstractInMemoryRelationshipScanCursor;
+import org.neo4j.storageengine.api.AllRelationshipsScan;
+import org.neo4j.storageengine.api.LongReference;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.Reference;
+import org.neo4j.storageengine.api.StoragePropertyCursor;
+import org.neo4j.token.TokenHolders;
+
+public class InMemoryRelationshipScanCursor extends AbstractInMemoryRelationshipScanCursor {
+
+ public InMemoryRelationshipScanCursor(
+ CypherGraphStore graphStore,
+ TokenHolders tokenHolders
+ ) {
+ super(graphStore, tokenHolders);
+ }
+
+ @Override
+ public void single(long reference, long sourceNodeReference, int type, long targetNodeReference) {
+ single(reference);
+ }
+
+ @Override
+ public Reference propertiesReference() {
+ return LongReference.longReference(getId());
+ }
+
+ @Override
+ public void properties(
+ StoragePropertyCursor storagePropertyCursor, PropertySelection propertySelection
+ ) {
+ properties(storagePropertyCursor, new InMemoryPropertySelectionImpl(propertySelection));
+ }
+
+ @Override
+ public boolean scanBatch(AllRelationshipsScan allRelationshipsScan, long sizeHint) {
+ return super.scanBatch(allRelationshipsScan, (int) sizeHint);
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryRelationshipTraversalCursor.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryRelationshipTraversalCursor.java
new file mode 100644
index 00000000000..66cb205b6f1
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryRelationshipTraversalCursor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.gds.compat.AbstractInMemoryRelationshipTraversalCursor;
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.storageengine.api.LongReference;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.Reference;
+import org.neo4j.storageengine.api.StoragePropertyCursor;
+import org.neo4j.token.TokenHolders;
+
+public class InMemoryRelationshipTraversalCursor extends AbstractInMemoryRelationshipTraversalCursor {
+
+ public InMemoryRelationshipTraversalCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) {
+ super(graphStore, tokenHolders);
+ }
+
+ @Override
+ public Reference propertiesReference() {
+ return LongReference.longReference(getId());
+ }
+
+ @Override
+ public void properties(
+ StoragePropertyCursor propertyCursor, PropertySelection selection
+ ) {
+ properties(propertyCursor, new InMemoryPropertySelectionImpl(selection));
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryStorageEngineFactory.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryStorageEngineFactory.java
new file mode 100644
index 00000000000..7c22c84e89a
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryStorageEngineFactory.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.eclipse.collections.api.factory.Sets;
+import org.eclipse.collections.api.set.ImmutableSet;
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.configuration.Config;
+import org.neo4j.consistency.checking.ConsistencyFlags;
+import org.neo4j.consistency.report.ConsistencySummaryStatistics;
+import org.neo4j.dbms.database.readonly.DatabaseReadOnlyChecker;
+import org.neo4j.function.ThrowingSupplier;
+import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.StorageEngineProxyApi;
+import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector;
+import org.neo4j.internal.batchimport.AdditionalInitialIds;
+import org.neo4j.internal.batchimport.BatchImporter;
+import org.neo4j.internal.batchimport.Configuration;
+import org.neo4j.internal.batchimport.IncrementalBatchImporter;
+import org.neo4j.internal.batchimport.IndexImporterFactory;
+import org.neo4j.internal.batchimport.Monitor;
+import org.neo4j.internal.batchimport.ReadBehaviour;
+import org.neo4j.internal.batchimport.input.Collector;
+import org.neo4j.internal.batchimport.input.Input;
+import org.neo4j.internal.batchimport.input.LenientStoreInput;
+import org.neo4j.internal.id.IdGeneratorFactory;
+import org.neo4j.internal.id.ScanOnOpenReadOnlyIdGeneratorFactory;
+import org.neo4j.internal.recordstorage.InMemoryStorageCommandReaderFactory55;
+import org.neo4j.internal.recordstorage.StoreTokens;
+import org.neo4j.internal.schema.IndexConfigCompleter;
+import org.neo4j.internal.schema.SchemaRule;
+import org.neo4j.internal.schema.SchemaState;
+import org.neo4j.io.fs.FileSystemAbstraction;
+import org.neo4j.io.layout.DatabaseLayout;
+import org.neo4j.io.layout.Neo4jLayout;
+import org.neo4j.io.layout.recordstorage.RecordDatabaseLayout;
+import org.neo4j.io.pagecache.PageCache;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.io.pagecache.context.CursorContextFactory;
+import org.neo4j.io.pagecache.tracing.PageCacheTracer;
+import org.neo4j.kernel.KernelVersionRepository;
+import org.neo4j.kernel.api.index.IndexProvidersAccess;
+import org.neo4j.kernel.impl.api.index.IndexProviderMap;
+import org.neo4j.kernel.impl.locking.Locks;
+import org.neo4j.kernel.impl.store.MetaDataStore;
+import org.neo4j.kernel.impl.store.NeoStores;
+import org.neo4j.kernel.impl.store.StoreFactory;
+import org.neo4j.kernel.impl.store.StoreType;
+import org.neo4j.kernel.impl.store.cursor.CachedStoreCursors;
+import org.neo4j.kernel.impl.transaction.log.LogTailMetadata;
+import org.neo4j.lock.LockService;
+import org.neo4j.logging.InternalLog;
+import org.neo4j.logging.InternalLogProvider;
+import org.neo4j.logging.NullLogProvider;
+import org.neo4j.logging.internal.LogService;
+import org.neo4j.memory.MemoryTracker;
+import org.neo4j.monitoring.DatabaseHealth;
+import org.neo4j.scheduler.JobScheduler;
+import org.neo4j.storageengine.api.CommandReaderFactory;
+import org.neo4j.storageengine.api.ConstraintRuleAccessor;
+import org.neo4j.storageengine.api.LogFilesInitializer;
+import org.neo4j.storageengine.api.MetadataProvider;
+import org.neo4j.storageengine.api.SchemaRule44;
+import org.neo4j.storageengine.api.StorageEngine;
+import org.neo4j.storageengine.api.StorageEngineFactory;
+import org.neo4j.storageengine.api.StorageFilesState;
+import org.neo4j.storageengine.api.StoreId;
+import org.neo4j.storageengine.api.StoreVersion;
+import org.neo4j.storageengine.api.StoreVersionCheck;
+import org.neo4j.storageengine.api.StoreVersionIdentifier;
+import org.neo4j.storageengine.migration.StoreMigrationParticipant;
+import org.neo4j.time.SystemNanoClock;
+import org.neo4j.token.DelegatingTokenHolder;
+import org.neo4j.token.ReadOnlyTokenCreator;
+import org.neo4j.token.TokenHolders;
+import org.neo4j.token.api.NamedToken;
+import org.neo4j.token.api.TokenHolder;
+import org.neo4j.token.api.TokensLoader;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.UncheckedIOException;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.time.Clock;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Function;
+
+@ServiceProvider
+public class InMemoryStorageEngineFactory implements StorageEngineFactory {
+
+ static final String IN_MEMORY_STORAGE_ENGINE_NAME = "in-memory-55";
+
+ public InMemoryStorageEngineFactory() {
+ StorageEngineProxyApi.requireNeo4jVersion(Neo4jVersion.V_5_5, StorageEngineFactory.class);
+ }
+
+ // Record storage = 0, Freki = 1
+ // Let's leave some room for future storage engines
+ // This arbitrary seems quite future-proof
+ public static final byte ID = 42;
+
+ @Override
+ public byte id() {
+ return ID;
+ }
+
+ @Override
+ public boolean storageExists(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout) {
+ return false;
+ }
+
+ @Override
+ public StorageEngine instantiate(
+ FileSystemAbstraction fs,
+ Clock clock,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ TokenHolders tokenHolders,
+ SchemaState schemaState,
+ ConstraintRuleAccessor constraintSemantics,
+ IndexConfigCompleter indexConfigCompleter,
+ LockService lockService,
+ IdGeneratorFactory idGeneratorFactory,
+ DatabaseHealth databaseHealth,
+ InternalLogProvider internalLogProvider,
+ InternalLogProvider userLogProvider,
+ RecoveryCleanupWorkCollector recoveryCleanupWorkCollector,
+ LogTailMetadata logTailMetadata,
+ KernelVersionRepository kernelVersionRepository,
+ MemoryTracker memoryTracker,
+ CursorContextFactory contextFactory,
+ PageCacheTracer pageCacheTracer
+ ) {
+ StoreFactory factory = new StoreFactory(
+ databaseLayout,
+ config,
+ idGeneratorFactory,
+ pageCache,
+ pageCacheTracer,
+ fs,
+ internalLogProvider,
+ contextFactory,
+ false,
+ logTailMetadata
+ );
+
+ factory.openNeoStores(StoreType.LABEL_TOKEN).close();
+
+ return new InMemoryStorageEngineImpl(
+ databaseLayout,
+ tokenHolders
+ );
+ }
+
+ @Override
+ public Optional databaseIdUuid(
+ FileSystemAbstraction fs, DatabaseLayout databaseLayout, PageCache pageCache, CursorContext cursorContext
+ ) {
+ var fieldAccess = MetaDataStore.getFieldAccess(
+ pageCache,
+ RecordDatabaseLayout.convert(databaseLayout).metadataStore(),
+ databaseLayout.getDatabaseName(),
+ cursorContext
+ );
+
+ try {
+ return fieldAccess.readDatabaseUUID();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ @Override
+ public List migrationParticipants(
+ FileSystemAbstraction fileSystemAbstraction,
+ Config config,
+ PageCache pageCache,
+ JobScheduler jobScheduler,
+ LogService logService,
+ MemoryTracker memoryTracker,
+ PageCacheTracer pageCacheTracer,
+ CursorContextFactory cursorContextFactory,
+ boolean b
+ ) {
+ return List.of();
+ }
+
+ @Override
+ public DatabaseLayout databaseLayout(
+ Neo4jLayout neo4jLayout, String databaseName
+ ) {
+ return RecordDatabaseLayout.of(neo4jLayout, databaseName);
+ }
+
+ @Override
+ public DatabaseLayout formatSpecificDatabaseLayout(DatabaseLayout plainLayout) {
+ return databaseLayout(plainLayout.getNeo4jLayout(), plainLayout.getDatabaseName());
+ }
+
+ @SuppressForbidden(reason = "This is the compat layer and we don't really need to go through the proxy")
+ @Override
+ public BatchImporter batchImporter(
+ DatabaseLayout databaseLayout,
+ FileSystemAbstraction fileSystemAbstraction,
+ PageCacheTracer pageCacheTracer,
+ Configuration configuration,
+ LogService logService,
+ PrintStream printStream,
+ boolean b,
+ AdditionalInitialIds additionalInitialIds,
+ Config config,
+ Monitor monitor,
+ JobScheduler jobScheduler,
+ Collector collector,
+ LogFilesInitializer logFilesInitializer,
+ IndexImporterFactory indexImporterFactory,
+ MemoryTracker memoryTracker,
+ CursorContextFactory cursorContextFactory
+ ) {
+ throw new UnsupportedOperationException("Batch Import into GDS is not supported");
+ }
+
+ @Override
+ public Input asBatchImporterInput(
+ DatabaseLayout databaseLayout,
+ FileSystemAbstraction fileSystemAbstraction,
+ PageCache pageCache,
+ PageCacheTracer pageCacheTracer,
+ Config config,
+ MemoryTracker memoryTracker,
+ ReadBehaviour readBehaviour,
+ boolean b,
+ CursorContextFactory cursorContextFactory,
+ LogTailMetadata logTailMetadata
+ ) {
+ NeoStores neoStores = (new StoreFactory(
+ databaseLayout,
+ config,
+ new ScanOnOpenReadOnlyIdGeneratorFactory(),
+ pageCache,
+ pageCacheTracer,
+ fileSystemAbstraction,
+ NullLogProvider.getInstance(),
+ cursorContextFactory,
+ false,
+ logTailMetadata
+ )).openAllNeoStores();
+ return new LenientStoreInput(
+ neoStores,
+ readBehaviour.decorateTokenHolders(this.loadReadOnlyTokens(neoStores, true, cursorContextFactory)),
+ true,
+ cursorContextFactory,
+ readBehaviour
+ );
+ }
+
+ @Override
+ public long optimalAvailableConsistencyCheckerMemory(
+ FileSystemAbstraction fileSystemAbstraction,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache
+ ) {
+ return 0;
+ }
+
+ @Override
+ public String name() {
+ return IN_MEMORY_STORAGE_ENGINE_NAME;
+ }
+
+ @Override
+ public Set supportedFormats(boolean includeFormatsUnderDevelopment) {
+ return Set.of(IN_MEMORY_STORAGE_ENGINE_NAME);
+ }
+
+ @Override
+ public boolean supportedFormat(String format, boolean includeFormatsUnderDevelopment) {
+ return format.equals(IN_MEMORY_STORAGE_ENGINE_NAME);
+ }
+
+ @Override
+ public MetadataProvider transactionMetaDataStore(
+ FileSystemAbstraction fileSystemAbstraction,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ DatabaseReadOnlyChecker databaseReadOnlyChecker,
+ CursorContextFactory cursorContextFactory,
+ LogTailMetadata logTailMetadata,
+ PageCacheTracer pageCacheTracer
+ ) {
+ return new InMemoryMetaDataProviderImpl();
+ }
+
+ @Override
+ public StoreVersionCheck versionCheck(
+ FileSystemAbstraction fileSystemAbstraction,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ LogService logService,
+ CursorContextFactory cursorContextFactory
+ ) {
+ return new InMemoryVersionCheck();
+ }
+
+ @Override
+ public List loadSchemaRules(
+ FileSystemAbstraction fileSystemAbstraction,
+ PageCache pageCache,
+ PageCacheTracer pageCacheTracer,
+ Config config,
+ DatabaseLayout databaseLayout,
+ boolean b,
+ Function function,
+ CursorContextFactory cursorContextFactory
+ ) {
+ return List.of();
+ }
+
+ @Override
+ public List load44SchemaRules(
+ FileSystemAbstraction fileSystemAbstraction,
+ PageCache pageCache,
+ PageCacheTracer pageCacheTracer,
+ Config config,
+ DatabaseLayout databaseLayout,
+ CursorContextFactory cursorContextFactory,
+ LogTailMetadata logTailMetadata
+ ) {
+ return List.of();
+ }
+
+ @Override
+ public TokenHolders loadReadOnlyTokens(
+ FileSystemAbstraction fileSystemAbstraction,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ PageCacheTracer pageCacheTracer,
+ boolean lenient,
+ CursorContextFactory cursorContextFactory
+ ) {
+ StoreFactory factory = new StoreFactory(
+ databaseLayout,
+ config,
+ new ScanOnOpenReadOnlyIdGeneratorFactory(),
+ pageCache,
+ pageCacheTracer,
+ fileSystemAbstraction,
+ NullLogProvider.getInstance(),
+ cursorContextFactory,
+ false,
+ LogTailMetadata.EMPTY_LOG_TAIL
+ );
+ try ( NeoStores stores = factory.openNeoStores(
+ StoreType.PROPERTY_KEY_TOKEN, StoreType.PROPERTY_KEY_TOKEN_NAME,
+ StoreType.LABEL_TOKEN, StoreType.LABEL_TOKEN_NAME,
+ StoreType.RELATIONSHIP_TYPE_TOKEN, StoreType.RELATIONSHIP_TYPE_TOKEN_NAME ) )
+ {
+ return loadReadOnlyTokens(stores, lenient, cursorContextFactory);
+ }
+ }
+
+ private TokenHolders loadReadOnlyTokens(
+ NeoStores stores,
+ boolean lenient,
+ CursorContextFactory cursorContextFactory
+ )
+ {
+ try ( var cursorContext = cursorContextFactory.create("loadReadOnlyTokens");
+ var storeCursors = new CachedStoreCursors( stores, cursorContext ) )
+ {
+ stores.start( cursorContext );
+ TokensLoader loader = lenient ? StoreTokens.allReadableTokens( stores ) : StoreTokens.allTokens( stores );
+ TokenHolder propertyKeys = new DelegatingTokenHolder( ReadOnlyTokenCreator.READ_ONLY, TokenHolder.TYPE_PROPERTY_KEY );
+ TokenHolder labels = new DelegatingTokenHolder( ReadOnlyTokenCreator.READ_ONLY, TokenHolder.TYPE_LABEL );
+ TokenHolder relationshipTypes = new DelegatingTokenHolder( ReadOnlyTokenCreator.READ_ONLY, TokenHolder.TYPE_RELATIONSHIP_TYPE );
+
+ propertyKeys.setInitialTokens( lenient ? unique( loader.getPropertyKeyTokens( storeCursors ) ) : loader.getPropertyKeyTokens( storeCursors ) );
+ labels.setInitialTokens( lenient ? unique( loader.getLabelTokens( storeCursors ) ) : loader.getLabelTokens( storeCursors ) );
+ relationshipTypes.setInitialTokens(
+ lenient ? unique( loader.getRelationshipTypeTokens( storeCursors ) ) : loader.getRelationshipTypeTokens( storeCursors ) );
+ return new TokenHolders( propertyKeys, labels, relationshipTypes );
+ }
+ catch ( IOException e )
+ {
+ throw new UncheckedIOException( e );
+ }
+ }
+
+ private static List unique( List tokens )
+ {
+ if ( !tokens.isEmpty() )
+ {
+ Set names = new HashSet<>( tokens.size() );
+ int i = 0;
+ while ( i < tokens.size() )
+ {
+ if ( names.add( tokens.get( i ).name() ) )
+ {
+ i++;
+ }
+ else
+ {
+ // Remove the token at the given index, by replacing it with the last token in the list.
+ // This changes the order of elements, but can be done in constant time instead of linear time.
+ int lastIndex = tokens.size() - 1;
+ NamedToken endToken = tokens.remove( lastIndex );
+ if ( i < lastIndex )
+ {
+ tokens.set( i, endToken );
+ }
+ }
+ }
+ }
+ return tokens;
+ }
+
+ @Override
+ public CommandReaderFactory commandReaderFactory() {
+ return InMemoryStorageCommandReaderFactory55.INSTANCE;
+ }
+
+ @Override
+ public void consistencyCheck(
+ FileSystemAbstraction fileSystem,
+ DatabaseLayout layout,
+ Config config,
+ PageCache pageCache,
+ IndexProviderMap indexProviders,
+ InternalLog log,
+ ConsistencySummaryStatistics summary,
+ int numberOfThreads,
+ long maxOffHeapCachingMemory,
+ OutputStream progressOutput,
+ boolean verbose,
+ ConsistencyFlags flags,
+ CursorContextFactory contextFactory,
+ PageCacheTracer pageCacheTracer,
+ LogTailMetadata logTailMetadata
+ ) {
+ // we can do no-op, since our "database" is _always_ consistent
+ }
+
+ @Override
+ public ImmutableSet getStoreOpenOptions(
+ FileSystemAbstraction fs,
+ PageCache pageCache,
+ DatabaseLayout layout,
+ CursorContextFactory contextFactory
+ ) {
+ // Not sure about this, empty set is returned when the store files are in `little-endian` format
+ // See: `org.neo4j.kernel.impl.store.format.PageCacheOptionsSelector.select`
+ return Sets.immutable.empty();
+ }
+
+ @Override
+ public StoreId retrieveStoreId(
+ FileSystemAbstraction fs,
+ DatabaseLayout databaseLayout,
+ PageCache pageCache,
+ CursorContext cursorContext
+ ) throws IOException {
+ return StoreId.retrieveFromStore(fs, databaseLayout, pageCache, cursorContext);
+ }
+
+
+ @Override
+ public Optional versionInformation(StoreVersionIdentifier storeVersionIdentifier) {
+ return Optional.of(new InMemoryStoreVersion());
+ }
+
+ @Override
+ public void resetMetadata(
+ FileSystemAbstraction fileSystemAbstraction,
+ DatabaseLayout databaseLayout,
+ Config config,
+ PageCache pageCache,
+ CursorContextFactory cursorContextFactory,
+ PageCacheTracer pageCacheTracer,
+ StoreId storeId,
+ UUID externalStoreId
+ ) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public IncrementalBatchImporter incrementalBatchImporter(
+ DatabaseLayout databaseLayout,
+ FileSystemAbstraction fileSystem,
+ PageCacheTracer pageCacheTracer,
+ Configuration config,
+ LogService logService,
+ PrintStream progressOutput,
+ boolean verboseProgressOutput,
+ AdditionalInitialIds additionalInitialIds,
+ ThrowingSupplier logTailMetadataSupplier,
+ Config dbConfig,
+ Monitor monitor,
+ JobScheduler jobScheduler,
+ Collector badCollector,
+ IndexImporterFactory indexImporterFactory,
+ MemoryTracker memoryTracker,
+ CursorContextFactory contextFactory,
+ IndexProvidersAccess indexProvidersAccess
+ ) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Locks createLocks(Config config, SystemNanoClock clock) {
+ return Locks.NO_LOCKS;
+ }
+
+ @Override
+ public List listStorageFiles(
+ FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout
+ ) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public StorageFilesState checkStoreFileState(
+ FileSystemAbstraction fs, DatabaseLayout databaseLayout, PageCache pageCache
+ ) {
+ return StorageFilesState.recoveredState();
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryStorageEngineImpl.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryStorageEngineImpl.java
new file mode 100644
index 00000000000..1573e72d1d5
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryStorageEngineImpl.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.counts.CountsAccessor;
+import org.neo4j.exceptions.KernelException;
+import org.neo4j.gds.compat.TokenManager;
+import org.neo4j.gds.config.GraphProjectConfig;
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.gds.core.loading.GraphStoreCatalog;
+import org.neo4j.gds.storageengine.InMemoryDatabaseCreationCatalog;
+import org.neo4j.gds.storageengine.InMemoryTransactionStateVisitor;
+import org.neo4j.internal.diagnostics.DiagnosticsLogger;
+import org.neo4j.internal.recordstorage.InMemoryStorageReader55;
+import org.neo4j.internal.schema.StorageEngineIndexingBehaviour;
+import org.neo4j.io.layout.DatabaseLayout;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.io.pagecache.tracing.DatabaseFlushEvent;
+import org.neo4j.kernel.KernelVersion;
+import org.neo4j.kernel.impl.store.stats.StoreEntityCounters;
+import org.neo4j.kernel.lifecycle.Lifecycle;
+import org.neo4j.kernel.lifecycle.LifecycleAdapter;
+import org.neo4j.lock.LockGroup;
+import org.neo4j.lock.LockService;
+import org.neo4j.lock.LockTracer;
+import org.neo4j.lock.ResourceLocker;
+import org.neo4j.logging.InternalLog;
+import org.neo4j.memory.MemoryTracker;
+import org.neo4j.storageengine.api.CommandBatchToApply;
+import org.neo4j.storageengine.api.CommandCreationContext;
+import org.neo4j.storageengine.api.CommandStream;
+import org.neo4j.storageengine.api.IndexUpdateListener;
+import org.neo4j.storageengine.api.MetadataProvider;
+import org.neo4j.storageengine.api.StorageCommand;
+import org.neo4j.storageengine.api.StorageEngine;
+import org.neo4j.storageengine.api.StorageLocks;
+import org.neo4j.storageengine.api.StorageReader;
+import org.neo4j.storageengine.api.StoreFileMetadata;
+import org.neo4j.storageengine.api.StoreId;
+import org.neo4j.storageengine.api.TransactionApplicationMode;
+import org.neo4j.storageengine.api.cursor.StoreCursors;
+import org.neo4j.storageengine.api.txstate.ReadableTransactionState;
+import org.neo4j.storageengine.api.txstate.TxStateVisitor;
+import org.neo4j.token.TokenHolders;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.neo4j.gds.utils.StringFormatting.formatWithLocale;
+
+public final class InMemoryStorageEngineImpl implements StorageEngine {
+
+ private final MetadataProvider metadataProvider;
+ private final CypherGraphStore graphStore;
+ private final DatabaseLayout databaseLayout;
+ private final InMemoryTransactionStateVisitor txStateVisitor;
+
+ private final CommandCreationContext commandCreationContext;
+
+ private final TokenManager tokenManager;
+ private final InMemoryCountsStoreImpl countsStore;
+
+ private final StorageEngineIndexingBehaviour indexingBehaviour = () -> false;
+
+ InMemoryStorageEngineImpl(
+ DatabaseLayout databaseLayout,
+ TokenHolders tokenHolders
+ ) {
+ this.databaseLayout = databaseLayout;
+ this.graphStore = getGraphStoreFromCatalog(databaseLayout.getDatabaseName());
+ this.txStateVisitor = new InMemoryTransactionStateVisitor(graphStore, tokenHolders);
+ this.commandCreationContext = new InMemoryCommandCreationContextImpl();
+ this.tokenManager = new TokenManager(
+ tokenHolders,
+ InMemoryStorageEngineImpl.this.txStateVisitor,
+ InMemoryStorageEngineImpl.this.graphStore,
+ commandCreationContext
+ );
+ InMemoryStorageEngineImpl.this.graphStore.initialize(tokenHolders);
+ this.countsStore = new InMemoryCountsStoreImpl(graphStore, tokenHolders);
+ this.metadataProvider = new InMemoryMetaDataProviderImpl();
+ }
+
+ private static CypherGraphStore getGraphStoreFromCatalog(String databaseName) {
+ var graphName = InMemoryDatabaseCreationCatalog.getRegisteredDbCreationGraphName(databaseName);
+ return (CypherGraphStore) GraphStoreCatalog.getAllGraphStores()
+ .filter(graphStoreWithUserNameAndConfig -> graphStoreWithUserNameAndConfig
+ .config()
+ .graphName()
+ .equals(graphName))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(formatWithLocale(
+ "No graph with name `%s` was found in GraphStoreCatalog. Available graph names are %s",
+ graphName,
+ GraphStoreCatalog.getAllGraphStores()
+ .map(GraphStoreCatalog.GraphStoreWithUserNameAndConfig::config)
+ .map(GraphProjectConfig::graphName)
+ .collect(Collectors.toList())
+ )))
+ .graphStore();
+ }
+
+ @Override
+ public StoreEntityCounters storeEntityCounters() {
+ return new StoreEntityCounters() {
+ @Override
+ public long nodes() {
+ return graphStore.nodeCount();
+ }
+
+ @Override
+ public long relationships() {
+ return graphStore.relationshipCount();
+ }
+
+ @Override
+ public long properties() {
+ return graphStore.nodePropertyKeys().size() + graphStore.relationshipPropertyKeys().size();
+ }
+
+ @Override
+ public long relationshipTypes() {
+ return graphStore.relationshipTypes().size();
+ }
+
+ @Override
+ public long allNodesCountStore(CursorContext cursorContext) {
+ return graphStore.nodeCount();
+ }
+
+ @Override
+ public long allRelationshipsCountStore(CursorContext cursorContext) {
+ return graphStore.relationshipCount();
+ }
+ };
+ }
+
+ @Override
+ public void preAllocateStoreFilesForCommands(
+ CommandBatchToApply commandBatchToApply,
+ TransactionApplicationMode transactionApplicationMode
+ ) {
+ }
+
+ @Override
+ public StoreCursors createStorageCursors(CursorContext initialContext) {
+ return StoreCursors.NULL;
+ }
+
+ @Override
+ public StorageLocks createStorageLocks(ResourceLocker locker) {
+ return new InMemoryStorageLocksImpl(locker);
+ }
+
+ @Override
+ public List createCommands(
+ ReadableTransactionState state,
+ StorageReader storageReader,
+ CommandCreationContext creationContext,
+ LockTracer lockTracer,
+ TxStateVisitor.Decorator additionalTxStateVisitor,
+ CursorContext cursorContext,
+ StoreCursors storeCursors,
+ MemoryTracker memoryTracker
+ ) throws KernelException {
+ state.accept(txStateVisitor);
+ return List.of();
+ }
+
+ @Override
+ public void dumpDiagnostics(InternalLog internalLog, DiagnosticsLogger diagnosticsLogger) {
+ }
+
+ @Override
+ public List createUpgradeCommands(
+ KernelVersion versionToUpgradeFrom,
+ KernelVersion versionToUpgradeTo
+ ) {
+ return List.of();
+ }
+
+ @Override
+ public StoreId retrieveStoreId() {
+ return metadataProvider.getStoreId();
+ }
+
+ @Override
+ public StorageEngineIndexingBehaviour indexingBehaviour() {
+ return indexingBehaviour;
+ }
+
+ @Override
+ public StorageReader newReader() {
+ return new InMemoryStorageReader55(graphStore, tokenManager.tokenHolders(), countsStore);
+ }
+
+ @Override
+ public void addIndexUpdateListener(IndexUpdateListener listener) {
+
+ }
+
+ @Override
+ public void apply(CommandBatchToApply batch, TransactionApplicationMode mode) {
+ }
+
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public void start() {
+
+ }
+
+ @Override
+ public void stop() {
+ shutdown();
+ }
+
+ @Override
+ public void shutdown() {
+ InMemoryDatabaseCreationCatalog.removeDatabaseEntry(databaseLayout.getDatabaseName());
+ }
+
+ @Override
+ public void listStorageFiles(
+ Collection atomic, Collection replayable
+ ) {
+
+ }
+
+ @Override
+ public Lifecycle schemaAndTokensLifecycle() {
+ return new LifecycleAdapter() {
+ @Override
+ public void init() {
+
+ }
+ };
+ }
+
+ @Override
+ public CountsAccessor countsAccessor() {
+ return countsStore;
+ }
+
+ @Override
+ public MetadataProvider metadataProvider() {
+ return metadataProvider;
+ }
+
+ @Override
+ public CommandCreationContext newCommandCreationContext() {
+ return commandCreationContext;
+ }
+
+ @Override
+ public void lockRecoveryCommands(
+ CommandStream commands, LockService lockService, LockGroup lockGroup, TransactionApplicationMode mode
+ ) {
+
+ }
+
+ @Override
+ public void rollback(ReadableTransactionState txState, CursorContext cursorContext) {
+ // rollback is not supported but it is also called when we fail for something else
+ // that we do not support, such as removing node properties
+ // TODO: do we want to inspect the txState to infer if rollback was called explicitly or not?
+ }
+
+ @Override
+ public void checkpoint(DatabaseFlushEvent flushEvent, CursorContext cursorContext) {
+ // checkpoint is not supported but it is also called when we fail for something else
+ // that we do not support, such as removing node properties
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryStorageLocksImpl.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryStorageLocksImpl.java
new file mode 100644
index 00000000000..122e557284b
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryStorageLocksImpl.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.lock.LockTracer;
+import org.neo4j.lock.ResourceLocker;
+import org.neo4j.storageengine.api.StorageLocks;
+import org.neo4j.storageengine.api.txstate.ReadableTransactionState;
+
+public class InMemoryStorageLocksImpl implements StorageLocks {
+
+ InMemoryStorageLocksImpl(ResourceLocker locker) {}
+
+ @Override
+ public void acquireExclusiveNodeLock(LockTracer lockTracer, long... ids) {}
+
+ @Override
+ public void releaseExclusiveNodeLock(long... ids) {}
+
+ @Override
+ public void acquireSharedNodeLock(LockTracer lockTracer, long... ids) {}
+
+ @Override
+ public void releaseSharedNodeLock(long... ids) {}
+
+ @Override
+ public void acquireExclusiveRelationshipLock(LockTracer lockTracer, long... ids) {}
+
+ @Override
+ public void releaseExclusiveRelationshipLock(long... ids) {}
+
+ @Override
+ public void acquireSharedRelationshipLock(LockTracer lockTracer, long... ids) {}
+
+ @Override
+ public void releaseSharedRelationshipLock(long... ids) {}
+
+ @Override
+ public void acquireRelationshipCreationLock(
+ LockTracer lockTracer,
+ long sourceNode,
+ long targetNode,
+ boolean sourceNodeAddedInTx,
+ boolean targetNodeAddedInTx
+ ) {
+ }
+
+ @Override
+ public void acquireRelationshipDeletionLock(
+ LockTracer lockTracer,
+ long sourceNode,
+ long targetNode,
+ long relationship,
+ boolean relationshipAddedInTx,
+ boolean sourceNodeAddedInTx,
+ boolean targetNodeAddedInTx
+ ) {
+ }
+
+ @Override
+ public void acquireNodeDeletionLock(
+ ReadableTransactionState readableTransactionState,
+ LockTracer lockTracer,
+ long node
+ ) {}
+
+ @Override
+ public void acquireNodeLabelChangeLock(LockTracer lockTracer, long node, int labelId) {}
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryStoreVersion.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryStoreVersion.java
new file mode 100644
index 00000000000..000361016cf
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryStoreVersion.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.storageengine.api.StoreVersion;
+import org.neo4j.storageengine.api.format.Capability;
+import org.neo4j.storageengine.api.format.CapabilityType;
+
+import java.util.Optional;
+
+public class InMemoryStoreVersion implements StoreVersion {
+
+ public static final String STORE_VERSION = "gds-experimental";
+
+ @Override
+ public String getStoreVersionUserString() {
+ return "Unknown";
+ }
+
+ @Override
+ public Optional successorStoreVersion() {
+ return Optional.empty();
+ }
+
+ @Override
+ public String formatName() {
+ return getClass().getSimpleName();
+ }
+
+ @Override
+ public boolean onlyForMigration() {
+ return false;
+ }
+
+ @Override
+ public boolean hasCapability(Capability capability) {
+ return false;
+ }
+
+ @Override
+ public boolean hasCompatibleCapabilities(
+ StoreVersion otherVersion, CapabilityType type
+ ) {
+ return false;
+ }
+
+ @Override
+ public String introductionNeo4jVersion() {
+ return "foo";
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryTransactionIdStoreImpl.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryTransactionIdStoreImpl.java
new file mode 100644
index 00000000000..0a6f3e8c889
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryTransactionIdStoreImpl.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.internal.recordstorage.AbstractTransactionIdStore;
+import org.neo4j.kernel.impl.transaction.log.LogPosition;
+import org.neo4j.storageengine.api.ClosedTransactionMetadata;
+import org.neo4j.storageengine.api.TransactionId;
+
+public class InMemoryTransactionIdStoreImpl extends AbstractTransactionIdStore {
+
+ @Override
+ protected void initLastCommittedAndClosedTransactionId(
+ long previouslyCommittedTxId,
+ int checksum,
+ long previouslyCommittedTxCommitTimestamp,
+ long previouslyCommittedTxLogByteOffset,
+ long previouslyCommittedTxLogVersion
+ ) {
+ this.setLastCommittedAndClosedTransactionId(
+ previouslyCommittedTxId,
+ checksum,
+ previouslyCommittedTxCommitTimestamp,
+ previouslyCommittedTxLogByteOffset,
+ previouslyCommittedTxLogVersion
+ );
+ }
+
+ public ClosedTransactionMetadata getLastClosedTransaction() {
+ long[] metaData = this.closedTransactionId.get();
+ return new ClosedTransactionMetadata(
+ metaData[0],
+ new LogPosition(metaData[1], metaData[2]),
+ (int) metaData[3],
+ metaData[4]
+ );
+ }
+
+ @Override
+ public void transactionClosed(
+ long transactionId,
+ long logVersion,
+ long byteOffset,
+ int checksum,
+ long commitTimestamp
+ ) {
+ this.closedTransactionId.offer(transactionId, new long[]{logVersion, byteOffset, checksum, commitTimestamp});
+ }
+
+ @Override
+ public void resetLastClosedTransaction(
+ long transactionId,
+ long logVersion,
+ long byteOffset,
+ int checksum,
+ long commitTimestamp
+ ) {
+ this.closedTransactionId.set(transactionId, new long[]{logVersion, byteOffset, checksum, commitTimestamp});
+ }
+
+ @Override
+ public void transactionCommitted(long transactionId, int checksum, long commitTimestamp) {
+ }
+
+ @Override
+ public void setLastCommittedAndClosedTransactionId(
+ long transactionId, int checksum, long commitTimestamp, long byteOffset, long logVersion
+ ) {
+ }
+
+ @Override
+ protected TransactionId transactionId(long transactionId, int checksum, long commitTimestamp) {
+ return new TransactionId(transactionId, checksum, commitTimestamp);
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryVersionCheck.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryVersionCheck.java
new file mode 100644
index 00000000000..1ae0d5d7f76
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/InMemoryVersionCheck.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.kernel.impl.store.format.FormatFamily;
+import org.neo4j.storageengine.api.StoreVersionCheck;
+import org.neo4j.storageengine.api.StoreVersionIdentifier;
+
+import static org.neo4j.gds.compat._55.InMemoryStoreVersion.STORE_VERSION;
+
+public class InMemoryVersionCheck implements StoreVersionCheck {
+
+ private static final StoreVersionIdentifier STORE_IDENTIFIER = new StoreVersionIdentifier(
+ STORE_VERSION,
+ FormatFamily.STANDARD.name(),
+ 0,
+ 0
+ );
+
+ @Override
+ public boolean isCurrentStoreVersionFullySupported(CursorContext cursorContext) {
+ return true;
+ }
+
+ @Override
+ public MigrationCheckResult getAndCheckMigrationTargetVersion(String formatFamily, CursorContext cursorContext) {
+ return new StoreVersionCheck.MigrationCheckResult(MigrationOutcome.NO_OP, STORE_IDENTIFIER, null, null);
+ }
+
+ @Override
+ public UpgradeCheckResult getAndCheckUpgradeTargetVersion(CursorContext cursorContext) {
+ return new StoreVersionCheck.UpgradeCheckResult(UpgradeOutcome.NO_OP, STORE_IDENTIFIER, null, null);
+ }
+
+ @Override
+ public String getIntroductionVersionFromVersion(StoreVersionIdentifier storeVersionIdentifier) {
+ return STORE_VERSION;
+ }
+
+ public StoreVersionIdentifier findLatestVersion(String s) {
+ return STORE_IDENTIFIER;
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/StorageEngineProxyFactoryImpl.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/StorageEngineProxyFactoryImpl.java
new file mode 100644
index 00000000000..8c9a25a65ac
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/StorageEngineProxyFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.StorageEngineProxyApi;
+import org.neo4j.gds.compat.StorageEngineProxyFactory;
+
+@ServiceProvider
+public class StorageEngineProxyFactoryImpl implements StorageEngineProxyFactory {
+
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return version == Neo4jVersion.V_5_5;
+ }
+
+ @Override
+ public StorageEngineProxyApi load() {
+ return new StorageEngineProxyImpl();
+ }
+
+ @Override
+ public String description() {
+ return "Storage Engine 5.5";
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/StorageEngineProxyImpl.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/StorageEngineProxyImpl.java
new file mode 100644
index 00000000000..7124a0829d1
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_55/StorageEngineProxyImpl.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._55;
+
+import org.neo4j.common.Edition;
+import org.neo4j.configuration.Config;
+import org.neo4j.configuration.GraphDatabaseInternalSettings;
+import org.neo4j.counts.CountsAccessor;
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.gds.compat.AbstractInMemoryNodeCursor;
+import org.neo4j.gds.compat.AbstractInMemoryNodePropertyCursor;
+import org.neo4j.gds.compat.AbstractInMemoryRelationshipPropertyCursor;
+import org.neo4j.gds.compat.AbstractInMemoryRelationshipTraversalCursor;
+import org.neo4j.gds.compat.GdsDatabaseManagementServiceBuilder;
+import org.neo4j.gds.compat.GraphDatabaseApiProxy;
+import org.neo4j.gds.compat.StorageEngineProxyApi;
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.graphdb.Direction;
+import org.neo4j.graphdb.GraphDatabaseService;
+import org.neo4j.internal.recordstorage.AbstractInMemoryRelationshipScanCursor;
+import org.neo4j.internal.recordstorage.InMemoryStorageReader55;
+import org.neo4j.io.layout.DatabaseLayout;
+import org.neo4j.storageengine.api.CommandCreationContext;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.RelationshipSelection;
+import org.neo4j.storageengine.api.StorageEngine;
+import org.neo4j.storageengine.api.StorageEntityCursor;
+import org.neo4j.storageengine.api.StoragePropertyCursor;
+import org.neo4j.storageengine.api.StorageReader;
+import org.neo4j.storageengine.api.StorageRelationshipTraversalCursor;
+import org.neo4j.token.TokenHolders;
+
+import static org.neo4j.configuration.GraphDatabaseSettings.db_format;
+
+public class StorageEngineProxyImpl implements StorageEngineProxyApi {
+
+ @Override
+ public CommandCreationContext inMemoryCommandCreationContext() {
+ return new InMemoryCommandCreationContextImpl();
+ }
+
+ @Override
+ public void initRelationshipTraversalCursorForRelType(
+ StorageRelationshipTraversalCursor cursor,
+ long sourceNodeId,
+ int relTypeToken
+ ) {
+ var relationshipSelection = RelationshipSelection.selection(
+ relTypeToken,
+ Direction.OUTGOING
+ );
+ cursor.init(sourceNodeId, -1, relationshipSelection);
+ }
+
+ @Override
+ public StorageReader inMemoryStorageReader(
+ CypherGraphStore graphStore, TokenHolders tokenHolders, CountsAccessor counts
+ ) {
+ return new InMemoryStorageReader55(graphStore, tokenHolders, counts);
+ }
+
+ @Override
+ public StorageEngine createInMemoryStorageEngine(DatabaseLayout databaseLayout, TokenHolders tokenHolders) {
+ return new InMemoryStorageEngineImpl(databaseLayout, tokenHolders);
+ }
+
+ @Override
+ public void createInMemoryDatabase(
+ DatabaseManagementService dbms,
+ String dbName,
+ Config config
+ ) {
+ config.set(db_format, InMemoryStorageEngineFactory.IN_MEMORY_STORAGE_ENGINE_NAME);
+ dbms.createDatabase(dbName, config);
+ }
+
+ @Override
+ public GraphDatabaseService startAndGetInMemoryDatabase(DatabaseManagementService dbms, String dbName) {
+ dbms.startDatabase(dbName);
+ return dbms.database(dbName);
+ }
+
+ @Override
+ public GdsDatabaseManagementServiceBuilder setSkipDefaultIndexesOnCreationSetting(GdsDatabaseManagementServiceBuilder dbmsBuilder) {
+ return dbmsBuilder.setConfig(GraphDatabaseInternalSettings.skip_default_indexes_on_creation, true);
+ }
+
+ @Override
+ public AbstractInMemoryNodeCursor inMemoryNodeCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) {
+ return new InMemoryNodeCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public AbstractInMemoryNodePropertyCursor inMemoryNodePropertyCursor(
+ CypherGraphStore graphStore,
+ TokenHolders tokenHolders
+ ) {
+ return new InMemoryNodePropertyCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public AbstractInMemoryRelationshipTraversalCursor inMemoryRelationshipTraversalCursor(
+ CypherGraphStore graphStore, TokenHolders tokenHolders
+ ) {
+ return new InMemoryRelationshipTraversalCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public AbstractInMemoryRelationshipScanCursor inMemoryRelationshipScanCursor(
+ CypherGraphStore graphStore, TokenHolders tokenHolders
+ ) {
+ return new InMemoryRelationshipScanCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public AbstractInMemoryRelationshipPropertyCursor inMemoryRelationshipPropertyCursor(
+ CypherGraphStore graphStore, TokenHolders tokenHolders
+ ) {
+ return new InMemoryRelationshipPropertyCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public void properties(
+ StorageEntityCursor storageCursor, StoragePropertyCursor propertyCursor, int[] propertySelection
+ ) {
+ PropertySelection selection;
+ if (propertySelection.length == 0) {
+ selection = PropertySelection.ALL_PROPERTIES;
+ } else {
+ selection = PropertySelection.selection(propertySelection);
+ }
+ storageCursor.properties(propertyCursor, selection);
+ }
+
+ @Override
+ public Edition dbmsEdition(GraphDatabaseService databaseService) {
+ return GraphDatabaseApiProxy.dbmsInfo(databaseService).edition;
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository55.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository55.java
new file mode 100644
index 00000000000..cefa28655c4
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository55.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.internal.recordstorage;
+
+import org.neo4j.storageengine.api.LogVersionRepository;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public class InMemoryLogVersionRepository55 implements LogVersionRepository {
+
+ private final AtomicLong logVersion;
+ private final AtomicLong checkpointLogVersion;
+
+ public InMemoryLogVersionRepository55() {
+ this(0, 0);
+ }
+
+ private InMemoryLogVersionRepository55(long initialLogVersion, long initialCheckpointLogVersion) {
+ this.logVersion = new AtomicLong();
+ this.checkpointLogVersion = new AtomicLong();
+ this.logVersion.set(initialLogVersion);
+ this.checkpointLogVersion.set(initialCheckpointLogVersion);
+ }
+
+ @Override
+ public void setCurrentLogVersion(long version) {
+ this.logVersion.set(version);
+ }
+
+ @Override
+ public long incrementAndGetVersion() {
+ return this.logVersion.incrementAndGet();
+ }
+
+ @Override
+ public void setCheckpointLogVersion(long version) {
+ this.checkpointLogVersion.set(version);
+ }
+
+ @Override
+ public long incrementAndGetCheckpointLogVersion() {
+ return this.checkpointLogVersion.incrementAndGet();
+ }
+
+ @Override
+ public long getCurrentLogVersion() {
+ return this.logVersion.get();
+ }
+
+ @Override
+ public long getCheckpointLogVersion() {
+ return this.checkpointLogVersion.get();
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory55.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory55.java
new file mode 100644
index 00000000000..36530d653b5
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory55.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.internal.recordstorage;
+
+import org.neo4j.kernel.KernelVersion;
+import org.neo4j.storageengine.api.CommandReader;
+import org.neo4j.storageengine.api.CommandReaderFactory;
+
+public class InMemoryStorageCommandReaderFactory55 implements CommandReaderFactory {
+
+ public static final CommandReaderFactory INSTANCE = new InMemoryStorageCommandReaderFactory55();
+
+ @Override
+ public CommandReader get(KernelVersion kernelVersion) {
+ switch (kernelVersion) {
+ case V4_2:
+ return LogCommandSerializationV4_2.INSTANCE;
+ case V4_3_D4:
+ return LogCommandSerializationV4_3_D3.INSTANCE;
+ case V5_0:
+ return LogCommandSerializationV5_0.INSTANCE;
+ default:
+ throw new IllegalArgumentException("Unsupported kernel version " + kernelVersion);
+ }
+ }
+}
diff --git a/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader55.java b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader55.java
new file mode 100644
index 00000000000..67fd5e0c840
--- /dev/null
+++ b/compatibility/5.5/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader55.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.internal.recordstorage;
+
+import org.neo4j.common.EntityType;
+import org.neo4j.common.TokenNameLookup;
+import org.neo4j.counts.CountsAccessor;
+import org.neo4j.gds.compat._55.InMemoryNodeCursor;
+import org.neo4j.gds.compat._55.InMemoryPropertyCursor;
+import org.neo4j.gds.compat._55.InMemoryRelationshipScanCursor;
+import org.neo4j.gds.compat._55.InMemoryRelationshipTraversalCursor;
+import org.neo4j.gds.core.cypher.CypherGraphStore;
+import org.neo4j.internal.schema.ConstraintDescriptor;
+import org.neo4j.internal.schema.IndexDescriptor;
+import org.neo4j.internal.schema.IndexType;
+import org.neo4j.internal.schema.SchemaDescriptor;
+import org.neo4j.internal.schema.constraints.IndexBackedConstraintDescriptor;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.memory.MemoryTracker;
+import org.neo4j.storageengine.api.AllNodeScan;
+import org.neo4j.storageengine.api.AllRelationshipsScan;
+import org.neo4j.storageengine.api.StorageNodeCursor;
+import org.neo4j.storageengine.api.StoragePropertyCursor;
+import org.neo4j.storageengine.api.StorageReader;
+import org.neo4j.storageengine.api.StorageRelationshipScanCursor;
+import org.neo4j.storageengine.api.StorageRelationshipTraversalCursor;
+import org.neo4j.storageengine.api.StorageSchemaReader;
+import org.neo4j.storageengine.api.cursor.StoreCursors;
+import org.neo4j.token.TokenHolders;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+
+public class InMemoryStorageReader55 implements StorageReader {
+
+ protected final CypherGraphStore graphStore;
+ protected final TokenHolders tokenHolders;
+ protected final CountsAccessor counts;
+ private final Map, Object> dependantState;
+ private boolean closed;
+
+ public InMemoryStorageReader55(
+ CypherGraphStore graphStore,
+ TokenHolders tokenHolders,
+ CountsAccessor counts
+ ) {
+ this.graphStore = graphStore;
+
+ this.tokenHolders = tokenHolders;
+ this.counts = counts;
+ this.dependantState = new ConcurrentHashMap<>();
+ }
+
+ @Override
+ public Collection uniquenessConstraintsGetRelated(
+ long[] changedLabels,
+ long[] unchangedLabels,
+ int[] propertyKeyIds,
+ boolean propertyKeyListIsComplete,
+ EntityType entityType
+ ) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public long relationshipsGetCount(CursorContext cursorTracer) {
+ return graphStore.relationshipCount();
+ }
+
+ @Override
+ public boolean nodeExists(long id, StoreCursors storeCursors) {
+ var originalId = graphStore.nodes().toOriginalNodeId(id);
+ return graphStore.nodes().contains(originalId);
+ }
+
+ @Override
+ public boolean relationshipExists(long id, StoreCursors storeCursors) {
+ return true;
+ }
+
+ @Override
+ public StorageNodeCursor allocateNodeCursor(
+ CursorContext cursorContext, StoreCursors storeCursors
+ ) {
+ return new InMemoryNodeCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public StoragePropertyCursor allocatePropertyCursor(
+ CursorContext cursorContext, StoreCursors storeCursors, MemoryTracker memoryTracker
+ ) {
+ return new InMemoryPropertyCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public StorageRelationshipTraversalCursor allocateRelationshipTraversalCursor(
+ CursorContext cursorContext, StoreCursors storeCursors
+ ) {
+ return new InMemoryRelationshipTraversalCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public StorageRelationshipScanCursor allocateRelationshipScanCursor(
+ CursorContext cursorContext, StoreCursors storeCursors
+ ) {
+ return new InMemoryRelationshipScanCursor(graphStore, tokenHolders);
+ }
+
+ @Override
+ public IndexDescriptor indexGetForSchemaAndType(
+ SchemaDescriptor descriptor, IndexType type
+ ) {
+ return null;
+ }
+
+ @Override
+ public AllRelationshipsScan allRelationshipScan() {
+ return new AbstractInMemoryAllRelationshipScan() {
+ @Override
+ boolean scanRange(AbstractInMemoryRelationshipScanCursor cursor, long start, long stopInclusive) {
+ return cursor.scanRange(start, stopInclusive);
+ }
+
+ @Override
+ public boolean scanBatch(long sizeHint, AbstractInMemoryRelationshipScanCursor cursor) {
+ return super.scanBatch(sizeHint, cursor);
+ }
+ };
+ }
+
+ @Override
+ public Iterator indexGetForSchema(SchemaDescriptor descriptor) {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public Iterator indexesGetForLabel(int labelId) {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public Iterator indexesGetForRelationshipType(int relationshipType) {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public IndexDescriptor indexGetForName(String name) {
+ return null;
+ }
+
+ @Override
+ public ConstraintDescriptor constraintGetForName(String name) {
+ return null;
+ }
+
+ @Override
+ public boolean indexExists(IndexDescriptor index) {
+ return false;
+ }
+
+ @Override
+ public Iterator indexesGetAll() {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public Collection valueIndexesGetRelated(
+ long[] tokens, int propertyKeyId, EntityType entityType
+ ) {
+ return valueIndexesGetRelated(tokens, new int[]{propertyKeyId}, entityType);
+ }
+
+ @Override
+ public Collection valueIndexesGetRelated(
+ long[] tokens, int[] propertyKeyIds, EntityType entityType
+ ) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Collection uniquenessConstraintsGetRelated(
+ long[] labels,
+ int propertyKeyId,
+ EntityType entityType
+ ) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Collection uniquenessConstraintsGetRelated(
+ long[] tokens,
+ int[] propertyKeyIds,
+ EntityType entityType
+ ) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean hasRelatedSchema(long[] labels, int propertyKey, EntityType entityType) {
+ return false;
+ }
+
+ @Override
+ public boolean hasRelatedSchema(int label, EntityType entityType) {
+ return false;
+ }
+
+ @Override
+ public Iterator constraintsGetForSchema(SchemaDescriptor descriptor) {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public boolean constraintExists(ConstraintDescriptor descriptor) {
+ return false;
+ }
+
+ @Override
+ public Iterator constraintsGetForLabel(int labelId) {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public Iterator constraintsGetForRelationshipType(int typeId) {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public Iterator constraintsGetAll() {
+ return Collections.emptyIterator();
+ }
+
+ @Override
+ public Long indexGetOwningUniquenessConstraintId(IndexDescriptor index) {
+ return null;
+ }
+
+ @Override
+ public long countsForNode(int labelId, CursorContext cursorContext) {
+ return counts.nodeCount(labelId, cursorContext);
+ }
+
+ @Override
+ public long countsForRelationship(int startLabelId, int typeId, int endLabelId, CursorContext cursorContext) {
+ return counts.relationshipCount(startLabelId, typeId, endLabelId, cursorContext);
+ }
+
+ @Override
+ public long nodesGetCount(CursorContext cursorContext) {
+ return graphStore.nodeCount();
+ }
+
+ @Override
+ public int labelCount() {
+ return graphStore.nodes().availableNodeLabels().size();
+ }
+
+ @Override
+ public int propertyKeyCount() {
+ int nodePropertyCount = graphStore
+ .schema()
+ .nodeSchema()
+ .allProperties()
+ .size();
+ int relPropertyCount = graphStore
+ .schema()
+ .relationshipSchema()
+ .allProperties()
+ .size();
+ return nodePropertyCount + relPropertyCount;
+ }
+
+ @Override
+ public int relationshipTypeCount() {
+ return graphStore.schema().relationshipSchema().availableTypes().size();
+ }
+
+ @Override
+ public T getOrCreateSchemaDependantState(Class type, Function factory) {
+ return type.cast(dependantState.computeIfAbsent(type, key -> factory.apply(this)));
+ }
+
+ @Override
+ public AllNodeScan allNodeScan() {
+ return new InMemoryNodeScan();
+ }
+
+ @Override
+ public void close() {
+ assert !closed;
+ closed = true;
+ }
+
+ @Override
+ public StorageSchemaReader schemaSnapshot() {
+ return this;
+ }
+
+ @Override
+ public TokenNameLookup tokenNameLookup() {
+ return tokenHolders;
+ }
+
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/build.gradle b/compatibility/5.6/neo4j-kernel-adapter/build.gradle
new file mode 100644
index 00000000000..28100243536
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/build.gradle
@@ -0,0 +1,63 @@
+apply plugin: 'java-library'
+apply plugin: 'me.champeau.mrjar'
+
+description = 'Neo4j Graph Data Science :: Neo4j Kernel Adapter 5.6'
+
+group = 'org.neo4j.gds'
+
+// for all 5.x versions
+if (ver.'neo4j'.startsWith('5.')) {
+ sourceSets {
+ main {
+ java {
+ srcDirs = ['src/main/java17']
+ }
+ }
+ }
+
+ dependencies {
+ annotationProcessor project(':annotations')
+ annotationProcessor group: 'org.immutables', name: 'value', version: ver.'immutables'
+ annotationProcessor group: 'org.neo4j', name: 'annotations', version: neos.'5.6'
+
+ compileOnly project(':annotations')
+ compileOnly group: 'com.github.spotbugs', name: 'spotbugs-annotations', version: ver.'spotbugsToolVersion'
+ compileOnly group: 'org.immutables', name: 'value-annotations', version: ver.'immutables'
+ compileOnly group: 'org.neo4j', name: 'annotations', version: neos.'5.6'
+ compileOnly group: 'org.neo4j', name: 'neo4j', version: neos.'5.6'
+ compileOnly group: 'org.neo4j', name: 'neo4j-record-storage-engine', version: neos.'5.6'
+ compileOnly group: 'org.neo4j.community', name: 'it-test-support', version: neos.'5.6'
+
+ implementation project(':neo4j-kernel-adapter-api')
+ }
+} else {
+ multiRelease {
+ targetVersions 11, 17
+ }
+
+ if (!project.hasProperty('no-forbidden-apis')) {
+ forbiddenApisJava17 {
+ exclude('**')
+ }
+ }
+
+ dependencies {
+ annotationProcessor group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
+ compileOnly group: 'org.neo4j', name: 'annotations', version: ver.'neo4j'
+
+ implementation project(':neo4j-kernel-adapter-api')
+
+ java17AnnotationProcessor project(':annotations')
+ java17AnnotationProcessor group: 'org.immutables', name: 'value', version: ver.'immutables'
+ java17AnnotationProcessor group: 'org.neo4j', name: 'annotations', version: neos.'5.6'
+
+ java17CompileOnly project(':annotations')
+ java17CompileOnly group: 'org.immutables', name: 'value-annotations', version: ver.'immutables'
+ java17CompileOnly group: 'org.neo4j', name: 'neo4j', version: neos.'5.6'
+ java17CompileOnly group: 'org.neo4j', name: 'neo4j-record-storage-engine', version: neos.'5.6'
+ java17CompileOnly group: 'org.neo4j.community', name: 'it-test-support', version: neos.'5.6'
+ java17CompileOnly group: 'com.github.spotbugs', name: 'spotbugs-annotations', version: ver.'spotbugsToolVersion'
+
+ java17Implementation project(':neo4j-kernel-adapter-api')
+ }
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_56/Neo4jProxyFactoryImpl.java b/compatibility/5.6/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_56/Neo4jProxyFactoryImpl.java
new file mode 100644
index 00000000000..5f0279f2fc5
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_56/Neo4jProxyFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._56;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jProxyApi;
+import org.neo4j.gds.compat.Neo4jProxyFactory;
+import org.neo4j.gds.compat.Neo4jVersion;
+
+@ServiceProvider
+public final class Neo4jProxyFactoryImpl implements Neo4jProxyFactory {
+
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return false;
+ }
+
+ @Override
+ public Neo4jProxyApi load() {
+ throw new UnsupportedOperationException("5.6 compatibility requires JDK17");
+ }
+
+ @Override
+ public String description() {
+ return "Neo4j 5.6 (placeholder)";
+ }
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_56/SettingProxyFactoryImpl.java b/compatibility/5.6/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_56/SettingProxyFactoryImpl.java
new file mode 100644
index 00000000000..98d6a61dca1
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_56/SettingProxyFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._56;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jVersion;
+import org.neo4j.gds.compat.SettingProxyApi;
+import org.neo4j.gds.compat.SettingProxyFactory;
+
+@ServiceProvider
+public final class SettingProxyFactoryImpl implements SettingProxyFactory {
+
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return false;
+ }
+
+ @Override
+ public SettingProxyApi load() {
+ throw new UnsupportedOperationException("5.6 compatibility requires JDK17");
+ }
+
+ @Override
+ public String description() {
+ return "Neo4j Settings 5.6 (placeholder)";
+ }
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/BoltTransactionRunnerImpl.java b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/BoltTransactionRunnerImpl.java
new file mode 100644
index 00000000000..974956d0662
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/BoltTransactionRunnerImpl.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._56;
+
+import org.neo4j.bolt.dbapi.BoltGraphDatabaseServiceSPI;
+import org.neo4j.bolt.dbapi.BoltTransaction;
+import org.neo4j.bolt.protocol.common.bookmark.Bookmark;
+import org.neo4j.bolt.protocol.common.message.AccessMode;
+import org.neo4j.bolt.protocol.v41.message.request.RoutingContext;
+import org.neo4j.bolt.tx.statement.StatementQuerySubscriber;
+import org.neo4j.exceptions.KernelException;
+import org.neo4j.gds.compat.BoltQuerySubscriber;
+import org.neo4j.gds.compat.BoltTransactionRunner;
+import org.neo4j.graphdb.QueryStatistics;
+import org.neo4j.internal.kernel.api.connectioninfo.ClientConnectionInfo;
+import org.neo4j.internal.kernel.api.security.LoginContext;
+import org.neo4j.kernel.api.KernelTransaction;
+import org.neo4j.kernel.impl.query.QueryExecutionConfiguration;
+import org.neo4j.kernel.impl.query.QueryExecutionKernelException;
+import org.neo4j.values.virtual.MapValue;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+
+public class BoltTransactionRunnerImpl extends BoltTransactionRunner {
+
+ @Override
+ protected BoltQuerySubscriber boltQuerySubscriber() {
+ var subscriber = new StatementQuerySubscriber();
+ return new BoltQuerySubscriber<>() {
+ @Override
+ public void assertSucceeded() throws KernelException {
+ subscriber.assertSuccess();
+ }
+
+ @Override
+ public QueryStatistics queryStatistics() {
+ return subscriber.getStatistics();
+ }
+
+ @Override
+ public StatementQuerySubscriber innerSubscriber() {
+ return subscriber;
+ }
+ };
+ }
+
+ @Override
+ protected void executeQuery(
+ BoltTransaction boltTransaction,
+ String query,
+ MapValue parameters,
+ StatementQuerySubscriber querySubscriber
+ ) throws QueryExecutionKernelException {
+ boltTransaction.executeQuery(query, parameters, true, querySubscriber);
+ }
+
+ @Override
+ protected BoltTransaction beginBoltWriteTransaction(
+ BoltGraphDatabaseServiceSPI fabricDb,
+ LoginContext loginContext,
+ KernelTransaction.Type kernelTransactionType,
+ ClientConnectionInfo clientConnectionInfo,
+ List bookmarks,
+ Duration txTimeout,
+ Map txMetadata
+ ) {
+ return fabricDb.beginTransaction(
+ kernelTransactionType,
+ loginContext,
+ clientConnectionInfo,
+ bookmarks,
+ txTimeout,
+ AccessMode.WRITE,
+ txMetadata,
+ new RoutingContext(true, Map.of()),
+ QueryExecutionConfiguration.DEFAULT_CONFIG
+ );
+ }
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CallableProcedureImpl.java b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CallableProcedureImpl.java
new file mode 100644
index 00000000000..bd0b3c0a07a
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CallableProcedureImpl.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._56;
+
+import org.neo4j.collection.RawIterator;
+import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.CompatCallableProcedure;
+import org.neo4j.internal.kernel.api.exceptions.ProcedureException;
+import org.neo4j.internal.kernel.api.procs.ProcedureSignature;
+import org.neo4j.kernel.api.ResourceMonitor;
+import org.neo4j.kernel.api.procedure.CallableProcedure;
+import org.neo4j.kernel.api.procedure.Context;
+import org.neo4j.values.AnyValue;
+
+@SuppressForbidden(reason = "This is the compat API")
+public final class CallableProcedureImpl implements CallableProcedure {
+ private final CompatCallableProcedure procedure;
+
+ CallableProcedureImpl(CompatCallableProcedure procedure) {
+ this.procedure = procedure;
+ }
+
+ @Override
+ public ProcedureSignature signature() {
+ return this.procedure.signature();
+ }
+
+ @Override
+ public RawIterator apply(
+ Context ctx,
+ AnyValue[] input,
+ ResourceMonitor resourceMonitor
+ ) throws ProcedureException {
+ return this.procedure.apply(ctx, input);
+ }
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CallableUserAggregationFunctionImpl.java b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CallableUserAggregationFunctionImpl.java
new file mode 100644
index 00000000000..b1f07487e92
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CallableUserAggregationFunctionImpl.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._56;
+
+import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.CompatUserAggregationFunction;
+import org.neo4j.gds.compat.CompatUserAggregator;
+import org.neo4j.internal.kernel.api.exceptions.ProcedureException;
+import org.neo4j.internal.kernel.api.procs.UserAggregationReducer;
+import org.neo4j.internal.kernel.api.procs.UserAggregationUpdater;
+import org.neo4j.internal.kernel.api.procs.UserFunctionSignature;
+import org.neo4j.kernel.api.procedure.CallableUserAggregationFunction;
+import org.neo4j.kernel.api.procedure.Context;
+import org.neo4j.values.AnyValue;
+
+@SuppressForbidden(reason = "This is the compat API")
+public final class CallableUserAggregationFunctionImpl implements CallableUserAggregationFunction {
+ private final CompatUserAggregationFunction function;
+
+ CallableUserAggregationFunctionImpl(CompatUserAggregationFunction function) {
+ this.function = function;
+ }
+
+ @Override
+ public UserFunctionSignature signature() {
+ return this.function.signature();
+ }
+
+ @Override
+ public UserAggregationReducer createReducer(Context ctx) throws ProcedureException {
+ return new UserAggregatorImpl(this.function.create(ctx));
+ }
+
+ private static final class UserAggregatorImpl implements UserAggregationReducer, UserAggregationUpdater {
+ private final CompatUserAggregator aggregator;
+
+ private UserAggregatorImpl(CompatUserAggregator aggregator) {
+ this.aggregator = aggregator;
+ }
+
+ @Override
+ public UserAggregationUpdater newUpdater() {
+ return this;
+ }
+
+ @Override
+ public void update(AnyValue[] input) throws ProcedureException {
+ this.aggregator.update(input);
+ }
+
+ @Override
+ public void applyUpdates() {
+ }
+
+ @Override
+ public AnyValue result() throws ProcedureException {
+ return this.aggregator.result();
+ }
+ }
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompatAccessModeImpl.java b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompatAccessModeImpl.java
new file mode 100644
index 00000000000..230ae244460
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompatAccessModeImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._56;
+
+import org.neo4j.gds.compat.CompatAccessMode;
+import org.neo4j.gds.compat.CustomAccessMode;
+import org.neo4j.internal.kernel.api.RelTypeSupplier;
+import org.neo4j.internal.kernel.api.TokenSet;
+
+import java.util.function.Supplier;
+
+public final class CompatAccessModeImpl extends CompatAccessMode {
+
+ CompatAccessModeImpl(CustomAccessMode custom) {
+ super(custom);
+ }
+
+ @Override
+ public boolean allowsReadNodeProperty(Supplier labels, int propertyKey) {
+ return custom.allowsReadNodeProperty(propertyKey);
+ }
+
+ @Override
+ public boolean allowsReadRelationshipProperty(RelTypeSupplier relType, int propertyKey) {
+ return custom.allowsReadRelationshipProperty(propertyKey);
+ }
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompatGraphDatabaseAPIImpl.java b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompatGraphDatabaseAPIImpl.java
new file mode 100644
index 00000000000..b99d39e27c0
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompatGraphDatabaseAPIImpl.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._56;
+
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.dbms.systemgraph.TopologyGraphDbmsModel;
+import org.neo4j.gds.compat.GdsGraphDatabaseAPI;
+import org.neo4j.internal.kernel.api.connectioninfo.ClientConnectionInfo;
+import org.neo4j.internal.kernel.api.security.LoginContext;
+import org.neo4j.kernel.api.KernelTransaction;
+import org.neo4j.kernel.api.exceptions.Status;
+import org.neo4j.kernel.impl.coreapi.InternalTransaction;
+import org.neo4j.kernel.impl.coreapi.TransactionExceptionMapper;
+
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+final class CompatGraphDatabaseAPIImpl extends GdsGraphDatabaseAPI {
+
+ CompatGraphDatabaseAPIImpl(DatabaseManagementService dbms) {
+ super(dbms);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return api.isAvailable();
+ }
+
+ @Override
+ public TopologyGraphDbmsModel.HostedOnMode mode() {
+ // NOTE: This means we can never start clusters locally, which is probably fine since:
+ // 1) We never did this before
+ // 2) We only use this for tests and benchmarks
+ return TopologyGraphDbmsModel.HostedOnMode.SINGLE;
+ }
+
+ @Override
+ public InternalTransaction beginTransaction(
+ KernelTransaction.Type type,
+ LoginContext loginContext,
+ ClientConnectionInfo clientInfo,
+ long timeout,
+ TimeUnit unit,
+ Consumer terminationCallback,
+ TransactionExceptionMapper transactionExceptionMapper
+ ) {
+ return api.beginTransaction(
+ type,
+ loginContext,
+ clientInfo,
+ timeout,
+ unit,
+ terminationCallback,
+ transactionExceptionMapper
+ );
+ }
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompatIndexQueryImpl.java b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompatIndexQueryImpl.java
new file mode 100644
index 00000000000..2e06af57725
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompatIndexQueryImpl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._56;
+
+import org.neo4j.gds.compat.CompatIndexQuery;
+import org.neo4j.internal.kernel.api.PropertyIndexQuery;
+
+final class CompatIndexQueryImpl implements CompatIndexQuery {
+ final PropertyIndexQuery indexQuery;
+
+ CompatIndexQueryImpl(PropertyIndexQuery indexQuery) {
+ this.indexQuery = indexQuery;
+ }
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompatUsernameAuthSubjectImpl.java b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompatUsernameAuthSubjectImpl.java
new file mode 100644
index 00000000000..3397d8d71a0
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompatUsernameAuthSubjectImpl.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._56;
+
+import org.neo4j.gds.compat.CompatUsernameAuthSubject;
+import org.neo4j.internal.kernel.api.security.AuthSubject;
+
+final class CompatUsernameAuthSubjectImpl extends CompatUsernameAuthSubject {
+
+ CompatUsernameAuthSubjectImpl(String username, AuthSubject authSubject) {
+ super(username, authSubject);
+ }
+
+ @Override
+ public String executingUser() {
+ return username;
+ }
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompositeNodeCursorImpl.java b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompositeNodeCursorImpl.java
new file mode 100644
index 00000000000..b4d73fca9f6
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/CompositeNodeCursorImpl.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._56;
+
+import org.neo4j.gds.compat.CompositeNodeCursor;
+import org.neo4j.internal.kernel.api.NodeLabelIndexCursor;
+
+import java.util.List;
+
+public final class CompositeNodeCursorImpl extends CompositeNodeCursor {
+
+ CompositeNodeCursorImpl(List cursors, int[] labelIds) {
+ super(cursors, labelIds);
+ }
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/GdsDatabaseLayoutImpl.java b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/GdsDatabaseLayoutImpl.java
new file mode 100644
index 00000000000..2173de12ab1
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/GdsDatabaseLayoutImpl.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._56;
+
+import org.neo4j.gds.compat.GdsDatabaseLayout;
+import org.neo4j.io.layout.DatabaseLayout;
+
+import java.nio.file.Path;
+
+public class GdsDatabaseLayoutImpl implements GdsDatabaseLayout {
+ private final DatabaseLayout databaseLayout;
+
+ public GdsDatabaseLayoutImpl(DatabaseLayout databaseLayout) {this.databaseLayout = databaseLayout;}
+
+ @Override
+ public Path databaseDirectory() {
+ return databaseLayout.databaseDirectory();
+ }
+
+ @Override
+ public Path getTransactionLogsDirectory() {
+ return databaseLayout.getTransactionLogsDirectory();
+ }
+
+ @Override
+ public Path metadataStore() {
+ return databaseLayout.metadataStore();
+ }
+
+ public DatabaseLayout databaseLayout() {
+ return databaseLayout;
+ }
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/GdsDatabaseManagementServiceBuilderImpl.java b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/GdsDatabaseManagementServiceBuilderImpl.java
new file mode 100644
index 00000000000..65ad6f6a250
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/GdsDatabaseManagementServiceBuilderImpl.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._56;
+
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.dbms.api.DatabaseManagementServiceBuilderImplementation;
+import org.neo4j.gds.compat.GdsDatabaseManagementServiceBuilder;
+import org.neo4j.graphdb.config.Setting;
+
+import java.nio.file.Path;
+import java.util.Map;
+
+public class GdsDatabaseManagementServiceBuilderImpl implements GdsDatabaseManagementServiceBuilder {
+
+ private final DatabaseManagementServiceBuilderImplementation dbmsBuilder;
+
+ GdsDatabaseManagementServiceBuilderImpl(Path storeDir) {
+ this.dbmsBuilder = new DatabaseManagementServiceBuilderImplementation(storeDir);
+ }
+
+ @Override
+ public GdsDatabaseManagementServiceBuilder setConfigRaw(Map configMap) {
+ dbmsBuilder.setConfigRaw(configMap);
+ return this;
+ }
+
+ @Override
+ public GdsDatabaseManagementServiceBuilder setConfig(Setting setting, S value) {
+ dbmsBuilder.setConfig(setting, value);
+ return this;
+ }
+
+ @Override
+ public DatabaseManagementService build() {
+ return dbmsBuilder.build();
+ }
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/Neo4jProxyFactoryImpl.java b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/Neo4jProxyFactoryImpl.java
new file mode 100644
index 00000000000..f064a42e37c
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/Neo4jProxyFactoryImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._56;
+
+import org.neo4j.annotations.service.ServiceProvider;
+import org.neo4j.gds.compat.Neo4jProxyApi;
+import org.neo4j.gds.compat.Neo4jProxyFactory;
+import org.neo4j.gds.compat.Neo4jVersion;
+
+@ServiceProvider
+public final class Neo4jProxyFactoryImpl implements Neo4jProxyFactory {
+
+ @Override
+ public boolean canLoad(Neo4jVersion version) {
+ return version == Neo4jVersion.V_5_6;
+ }
+
+ @Override
+ public Neo4jProxyApi load() {
+ return new Neo4jProxyImpl();
+ }
+
+ @Override
+ public String description() {
+ return "Neo4j 5.6";
+ }
+}
diff --git a/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/Neo4jProxyImpl.java b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/Neo4jProxyImpl.java
new file mode 100644
index 00000000000..1458cdca72a
--- /dev/null
+++ b/compatibility/5.6/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_56/Neo4jProxyImpl.java
@@ -0,0 +1,935 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.gds.compat._56;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.neo4j.common.DependencyResolver;
+import org.neo4j.common.EntityType;
+import org.neo4j.configuration.BootloaderSettings;
+import org.neo4j.configuration.Config;
+import org.neo4j.configuration.GraphDatabaseSettings;
+import org.neo4j.configuration.SettingValueParsers;
+import org.neo4j.configuration.connectors.ConnectorPortRegister;
+import org.neo4j.configuration.connectors.ConnectorType;
+import org.neo4j.configuration.helpers.DatabaseNameValidator;
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.exceptions.KernelException;
+import org.neo4j.fabric.FabricDatabaseManager;
+import org.neo4j.gds.annotation.SuppressForbidden;
+import org.neo4j.gds.compat.BoltTransactionRunner;
+import org.neo4j.gds.compat.CompatCallableProcedure;
+import org.neo4j.gds.compat.CompatExecutionMonitor;
+import org.neo4j.gds.compat.CompatIndexQuery;
+import org.neo4j.gds.compat.CompatInput;
+import org.neo4j.gds.compat.CompatUserAggregationFunction;
+import org.neo4j.gds.compat.CompositeNodeCursor;
+import org.neo4j.gds.compat.CustomAccessMode;
+import org.neo4j.gds.compat.GdsDatabaseLayout;
+import org.neo4j.gds.compat.GdsDatabaseManagementServiceBuilder;
+import org.neo4j.gds.compat.GdsGraphDatabaseAPI;
+import org.neo4j.gds.compat.GraphDatabaseApiProxy;
+import org.neo4j.gds.compat.InputEntityIdVisitor;
+import org.neo4j.gds.compat.Neo4jProxyApi;
+import org.neo4j.gds.compat.PropertyReference;
+import org.neo4j.gds.compat.StoreScan;
+import org.neo4j.gds.compat.TestLog;
+import org.neo4j.graphdb.GraphDatabaseService;
+import org.neo4j.graphdb.Node;
+import org.neo4j.graphdb.Relationship;
+import org.neo4j.graphdb.RelationshipType;
+import org.neo4j.graphdb.config.Setting;
+import org.neo4j.internal.batchimport.AdditionalInitialIds;
+import org.neo4j.internal.batchimport.BatchImporter;
+import org.neo4j.internal.batchimport.BatchImporterFactory;
+import org.neo4j.internal.batchimport.Configuration;
+import org.neo4j.internal.batchimport.IndexConfig;
+import org.neo4j.internal.batchimport.InputIterable;
+import org.neo4j.internal.batchimport.Monitor;
+import org.neo4j.internal.batchimport.input.Collector;
+import org.neo4j.internal.batchimport.input.IdType;
+import org.neo4j.internal.batchimport.input.Input;
+import org.neo4j.internal.batchimport.input.InputEntityVisitor;
+import org.neo4j.internal.batchimport.input.PropertySizeCalculator;
+import org.neo4j.internal.batchimport.input.ReadableGroups;
+import org.neo4j.internal.batchimport.staging.ExecutionMonitor;
+import org.neo4j.internal.batchimport.staging.StageExecution;
+import org.neo4j.internal.helpers.HostnamePort;
+import org.neo4j.internal.id.IdGenerator;
+import org.neo4j.internal.id.IdGeneratorFactory;
+import org.neo4j.internal.kernel.api.Cursor;
+import org.neo4j.internal.kernel.api.IndexQueryConstraints;
+import org.neo4j.internal.kernel.api.IndexReadSession;
+import org.neo4j.internal.kernel.api.NodeCursor;
+import org.neo4j.internal.kernel.api.NodeLabelIndexCursor;
+import org.neo4j.internal.kernel.api.NodeValueIndexCursor;
+import org.neo4j.internal.kernel.api.PropertyCursor;
+import org.neo4j.internal.kernel.api.PropertyIndexQuery;
+import org.neo4j.internal.kernel.api.QueryContext;
+import org.neo4j.internal.kernel.api.Read;
+import org.neo4j.internal.kernel.api.RelationshipScanCursor;
+import org.neo4j.internal.kernel.api.Scan;
+import org.neo4j.internal.kernel.api.TokenPredicate;
+import org.neo4j.internal.kernel.api.connectioninfo.ClientConnectionInfo;
+import org.neo4j.internal.kernel.api.procs.FieldSignature;
+import org.neo4j.internal.kernel.api.procs.Neo4jTypes;
+import org.neo4j.internal.kernel.api.procs.ProcedureSignature;
+import org.neo4j.internal.kernel.api.procs.QualifiedName;
+import org.neo4j.internal.kernel.api.procs.UserFunctionSignature;
+import org.neo4j.internal.kernel.api.security.AccessMode;
+import org.neo4j.internal.kernel.api.security.AuthSubject;
+import org.neo4j.internal.kernel.api.security.SecurityContext;
+import org.neo4j.internal.recordstorage.RecordIdType;
+import org.neo4j.internal.schema.IndexCapability;
+import org.neo4j.internal.schema.IndexDescriptor;
+import org.neo4j.internal.schema.IndexOrder;
+import org.neo4j.internal.schema.SchemaDescriptors;
+import org.neo4j.io.fs.FileSystemAbstraction;
+import org.neo4j.io.layout.DatabaseLayout;
+import org.neo4j.io.layout.Neo4jLayout;
+import org.neo4j.io.layout.recordstorage.RecordDatabaseLayout;
+import org.neo4j.io.pagecache.PageCache;
+import org.neo4j.io.pagecache.context.CursorContext;
+import org.neo4j.io.pagecache.context.CursorContextFactory;
+import org.neo4j.io.pagecache.context.EmptyVersionContextSupplier;
+import org.neo4j.io.pagecache.tracing.PageCacheTracer;
+import org.neo4j.kernel.api.KernelTransaction;
+import org.neo4j.kernel.api.KernelTransactionHandle;
+import org.neo4j.kernel.api.procedure.CallableProcedure;
+import org.neo4j.kernel.api.procedure.CallableUserAggregationFunction;
+import org.neo4j.kernel.database.NamedDatabaseId;
+import org.neo4j.kernel.database.NormalizedDatabaseName;
+import org.neo4j.kernel.database.TestDatabaseIdRepository;
+import org.neo4j.kernel.impl.coreapi.InternalTransaction;
+import org.neo4j.kernel.impl.index.schema.IndexImporterFactoryImpl;
+import org.neo4j.kernel.impl.query.QueryExecutionConfiguration;
+import org.neo4j.kernel.impl.query.TransactionalContext;
+import org.neo4j.kernel.impl.query.TransactionalContextFactory;
+import org.neo4j.kernel.impl.store.RecordStore;
+import org.neo4j.kernel.impl.store.format.RecordFormatSelector;
+import org.neo4j.kernel.impl.store.format.RecordFormats;
+import org.neo4j.kernel.impl.store.record.AbstractBaseRecord;
+import org.neo4j.kernel.impl.transaction.log.EmptyLogTailMetadata;
+import org.neo4j.kernel.impl.transaction.log.files.TransactionLogInitializer;
+import org.neo4j.logging.Log;
+import org.neo4j.logging.internal.LogService;
+import org.neo4j.memory.EmptyMemoryTracker;
+import org.neo4j.procedure.Mode;
+import org.neo4j.scheduler.JobScheduler;
+import org.neo4j.ssl.config.SslPolicyLoader;
+import org.neo4j.storageengine.api.PropertySelection;
+import org.neo4j.storageengine.api.StorageEngineFactory;
+import org.neo4j.util.Bits;
+import org.neo4j.values.storable.ValueCategory;
+import org.neo4j.values.storable.Values;
+import org.neo4j.values.virtual.MapValue;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static java.lang.String.format;
+import static org.neo4j.gds.compat.InternalReadOps.countByIdGenerator;
+import static org.neo4j.io.pagecache.context.EmptyVersionContextSupplier.EMPTY;
+
+public final class Neo4jProxyImpl implements Neo4jProxyApi {
+
+ @Override
+ public GdsGraphDatabaseAPI newDb(DatabaseManagementService dbms) {
+ return new CompatGraphDatabaseAPIImpl(dbms);
+ }
+
+ @Override
+ public String validateExternalDatabaseName(String databaseName) {
+ var normalizedName = new NormalizedDatabaseName(databaseName);
+ DatabaseNameValidator.validateExternalDatabaseName(normalizedName);
+ return normalizedName.name();
+ }
+
+ @Override
+ public AccessMode accessMode(CustomAccessMode customAccessMode) {
+ return new CompatAccessModeImpl(customAccessMode);
+ }
+
+ @Override
+ public String username(AuthSubject subject) {
+ return subject.executingUser();
+ }
+
+ @Override
+ public SecurityContext securityContext(
+ String username,
+ AuthSubject authSubject,
+ AccessMode mode,
+ String databaseName
+ ) {
+ return new SecurityContext(
+ new CompatUsernameAuthSubjectImpl(username, authSubject),
+ mode,
+ // GDS is always operating from an embedded context
+ ClientConnectionInfo.EMBEDDED_CONNECTION,
+ databaseName
+ );
+ }
+
+ @Override
+ public long getHighestPossibleIdInUse(
+ RecordStore extends AbstractBaseRecord> recordStore,
+ KernelTransaction kernelTransaction
+ ) {
+ return recordStore.getHighestPossibleIdInUse(kernelTransaction.cursorContext());
+ }
+
+ @Override
+ public long getHighId(RecordStore extends AbstractBaseRecord> recordStore) {
+ return recordStore.getHighId();
+ }
+
+ @Override
+ public List> entityCursorScan(
+ KernelTransaction transaction,
+ int[] labelIds,
+ int batchSize,
+ boolean allowPartitionedScan
+ ) {
+ if (allowPartitionedScan) {
+ return partitionedNodeLabelIndexScan(transaction, batchSize, labelIds);
+ } else {
+ var read = transaction.dataRead();
+ return Arrays
+ .stream(labelIds)
+ .mapToObj(read::nodeLabelScan)
+ .map(scan -> scanToStoreScan(scan, batchSize))
+ .collect(Collectors.toList());
+ }
+ }
+
+ @Override
+ public PropertyCursor allocatePropertyCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction
+ .cursors()
+ .allocatePropertyCursor(kernelTransaction.cursorContext(), kernelTransaction.memoryTracker());
+ }
+
+ @Override
+ public PropertyReference propertyReference(NodeCursor nodeCursor) {
+ return ReferencePropertyReference.of(nodeCursor.propertiesReference());
+ }
+
+ @Override
+ public PropertyReference propertyReference(RelationshipScanCursor relationshipScanCursor) {
+ return ReferencePropertyReference.of(relationshipScanCursor.propertiesReference());
+ }
+
+ @Override
+ public PropertyReference noPropertyReference() {
+ return ReferencePropertyReference.empty();
+ }
+
+ @Override
+ public void nodeProperties(
+ KernelTransaction kernelTransaction,
+ long nodeReference,
+ PropertyReference reference,
+ PropertyCursor cursor
+ ) {
+ var neoReference = ((ReferencePropertyReference) reference).reference;
+ kernelTransaction
+ .dataRead()
+ .nodeProperties(nodeReference, neoReference, PropertySelection.ALL_PROPERTIES, cursor);
+ }
+
+ @Override
+ public void relationshipProperties(
+ KernelTransaction kernelTransaction,
+ long relationshipReference,
+ PropertyReference reference,
+ PropertyCursor cursor
+ ) {
+ var neoReference = ((ReferencePropertyReference) reference).reference;
+ kernelTransaction
+ .dataRead()
+ .relationshipProperties(relationshipReference, neoReference, PropertySelection.ALL_PROPERTIES, cursor);
+ }
+
+ @Override
+ public NodeCursor allocateNodeCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction.cursors().allocateNodeCursor(kernelTransaction.cursorContext());
+ }
+
+ @Override
+ public RelationshipScanCursor allocateRelationshipScanCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction.cursors().allocateRelationshipScanCursor(kernelTransaction.cursorContext());
+ }
+
+ @Override
+ public NodeLabelIndexCursor allocateNodeLabelIndexCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction.cursors().allocateNodeLabelIndexCursor(kernelTransaction.cursorContext());
+ }
+
+ @Override
+ public NodeValueIndexCursor allocateNodeValueIndexCursor(KernelTransaction kernelTransaction) {
+ return kernelTransaction
+ .cursors()
+ .allocateNodeValueIndexCursor(kernelTransaction.cursorContext(), kernelTransaction.memoryTracker());
+ }
+
+ @Override
+ public boolean hasNodeLabelIndex(KernelTransaction kernelTransaction) {
+ return NodeLabelIndexLookupImpl.hasNodeLabelIndex(kernelTransaction);
+ }
+
+ @Override
+ public StoreScan nodeLabelIndexScan(
+ KernelTransaction transaction,
+ int labelId,
+ int batchSize,
+ boolean allowPartitionedScan
+ ) {
+ if (allowPartitionedScan) {
+ return partitionedNodeLabelIndexScan(transaction, batchSize, labelId).get(0);
+ } else {
+ var read = transaction.dataRead();
+ return scanToStoreScan(read.nodeLabelScan(labelId), batchSize);
+ }
+ }
+
+ @Override
+ public StoreScan scanToStoreScan(Scan scan, int batchSize) {
+ return new ScanBasedStoreScanImpl<>(scan, batchSize);
+ }
+
+ private List> partitionedNodeLabelIndexScan(
+ KernelTransaction transaction,
+ int batchSize,
+ int... labelIds
+ ) {
+ var indexDescriptor = NodeLabelIndexLookupImpl.findUsableMatchingIndex(
+ transaction,
+ SchemaDescriptors.forAnyEntityTokens(EntityType.NODE)
+ );
+
+ if (indexDescriptor == IndexDescriptor.NO_INDEX) {
+ throw new IllegalStateException("There is no index that can back a node label scan.");
+ }
+
+ var read = transaction.dataRead();
+
+ // Our current strategy is to select the token with the highest count
+ // and use that one as the driving partitioned index scan. The partitions
+ // of all other partitioned index scans will be aligned to that one.
+ int maxToken = labelIds[0];
+ long maxCount = read.countsForNodeWithoutTxState(labelIds[0]);
+
+ for (int i = 1; i < labelIds.length; i++) {
+ long count = read.countsForNodeWithoutTxState(labelIds[i]);
+ if (count > maxCount) {
+ maxCount = count;
+ maxToken = labelIds[i];
+ }
+ }
+
+ int numberOfPartitions = PartitionedStoreScan.getNumberOfPartitions(maxCount, batchSize);
+
+ try {
+ var session = read.tokenReadSession(indexDescriptor);
+
+ var partitionedScan = read.nodeLabelScan(
+ session,
+ numberOfPartitions,
+ transaction.cursorContext(),
+ new TokenPredicate(maxToken)
+ );
+
+ var scans = new ArrayList>(labelIds.length);
+ scans.add(new PartitionedStoreScan(partitionedScan));
+
+ // Initialize the remaining index scans with the partitioning of the first scan.
+ for (int labelToken : labelIds) {
+ if (labelToken != maxToken) {
+ var scan = read.nodeLabelScan(session, partitionedScan, new TokenPredicate(labelToken));
+ scans.add(new PartitionedStoreScan(scan));
+ }
+ }
+
+ return scans;
+ } catch (KernelException e) {
+ // should not happen, we check for the index existence and applicability
+ // before reading it
+ throw new RuntimeException("Unexpected error while initialising reading from node label index", e);
+ }
+ }
+
+ @Override
+ public CompatIndexQuery rangeIndexQuery(
+ int propertyKeyId,
+ double from,
+ boolean fromInclusive,
+ double to,
+ boolean toInclusive
+ ) {
+ return new CompatIndexQueryImpl(PropertyIndexQuery.range(propertyKeyId, from, fromInclusive, to, toInclusive));
+ }
+
+ @Override
+ public CompatIndexQuery rangeAllIndexQuery(int propertyKeyId) {
+ var rangePredicate = PropertyIndexQuery.range(
+ propertyKeyId,
+ Values.doubleValue(Double.NEGATIVE_INFINITY),
+ true,
+ Values.doubleValue(Double.POSITIVE_INFINITY),
+ true
+ );
+ return new CompatIndexQueryImpl(rangePredicate);
+ }
+
+ @Override
+ public void nodeIndexSeek(
+ Read dataRead,
+ IndexReadSession index,
+ NodeValueIndexCursor cursor,
+ IndexOrder indexOrder,
+ boolean needsValues,
+ CompatIndexQuery query
+ ) throws KernelException {
+ var indexQueryConstraints = indexOrder == IndexOrder.NONE
+ ? IndexQueryConstraints.unordered(needsValues)
+ : IndexQueryConstraints.constrained(indexOrder, needsValues);
+
+ dataRead.nodeIndexSeek(
+ (QueryContext) dataRead,
+ index,
+ cursor,
+ indexQueryConstraints,
+ ((CompatIndexQueryImpl) query).indexQuery
+ );
+ }
+
+ @Override
+ public CompositeNodeCursor compositeNodeCursor(List cursors, int[] labelIds) {
+ return new CompositeNodeCursorImpl(cursors, labelIds);
+ }
+
+ @Override
+ public Configuration batchImporterConfig(
+ int batchSize,
+ int writeConcurrency,
+ Optional pageCacheMemory,
+ boolean highIO,
+ IndexConfig indexConfig
+ ) {
+ return new org.neo4j.internal.batchimport.Configuration() {
+ @Override
+ public int batchSize() {
+ return batchSize;
+ }
+
+ @Override
+ public int maxNumberOfWorkerThreads() {
+ return writeConcurrency;
+ }
+
+ @Override
+ public long pageCacheMemory() {
+ return pageCacheMemory.orElseGet(Configuration.super::pageCacheMemory);
+ }
+
+ @Override
+ public boolean highIO() {
+ return highIO;
+ }
+
+ @Override
+ public IndexConfig indexConfig() {
+ return indexConfig;
+ }
+ };
+ }
+
+ @Override
+ public int writeConcurrency(Configuration batchImportConfiguration) {
+ return batchImportConfiguration.maxNumberOfWorkerThreads();
+ }
+
+ @Override
+ public BatchImporter instantiateBatchImporter(
+ BatchImporterFactory factory,
+ GdsDatabaseLayout directoryStructure,
+ FileSystemAbstraction fileSystem,
+ PageCacheTracer pageCacheTracer,
+ Configuration configuration,
+ LogService logService,
+ ExecutionMonitor executionMonitor,
+ AdditionalInitialIds additionalInitialIds,
+ Config dbConfig,
+ RecordFormats recordFormats,
+ JobScheduler jobScheduler,
+ Collector badCollector
+ ) {
+ dbConfig.set(GraphDatabaseSettings.db_format, recordFormats.name());
+ var databaseLayout = ((GdsDatabaseLayoutImpl) directoryStructure).databaseLayout();
+ return factory.instantiate(
+ databaseLayout,
+ fileSystem,
+ pageCacheTracer,
+ configuration,
+ logService,
+ executionMonitor,
+ additionalInitialIds,
+ new EmptyLogTailMetadata(dbConfig),
+ dbConfig,
+ Monitor.NO_MONITOR,
+ jobScheduler,
+ badCollector,
+ TransactionLogInitializer.getLogFilesInitializer(),
+ new IndexImporterFactoryImpl(),
+ EmptyMemoryTracker.INSTANCE,
+ new CursorContextFactory(PageCacheTracer.NULL, EmptyVersionContextSupplier.EMPTY)
+ );
+ }
+
+ @Override
+ public Input batchInputFrom(CompatInput compatInput) {
+ return new InputFromCompatInput(compatInput);
+ }
+
+ @Override
+ public InputEntityIdVisitor.Long inputEntityLongIdVisitor(IdType idType, ReadableGroups groups) {
+ switch (idType) {
+ case ACTUAL -> {
+ return new InputEntityIdVisitor.Long() {
+ @Override
+ public void visitNodeId(InputEntityVisitor visitor, long id) {
+ visitor.id(id);
+ }
+
+ @Override
+ public void visitSourceId(InputEntityVisitor visitor, long id) {
+ visitor.startId(id);
+ }
+
+ @Override
+ public void visitTargetId(InputEntityVisitor visitor, long id) {
+ visitor.endId(id);
+ }
+ };
+ }
+ case INTEGER -> {
+ var globalGroup = groups.get(null);
+
+ return new InputEntityIdVisitor.Long() {
+ @Override
+ public void visitNodeId(InputEntityVisitor visitor, long id) {
+ visitor.id(id, globalGroup);
+ }
+
+ @Override
+ public void visitSourceId(InputEntityVisitor visitor, long id) {
+ visitor.startId(id, globalGroup);
+ }
+
+ @Override
+ public void visitTargetId(InputEntityVisitor visitor, long id) {
+ visitor.endId(id, globalGroup);
+ }
+ };
+ }
+ default -> throw new IllegalStateException("Unexpected value: " + idType);
+ }
+ }
+
+ @Override
+ public InputEntityIdVisitor.String inputEntityStringIdVisitor(ReadableGroups groups) {
+ var globalGroup = groups.get(null);
+
+ return new InputEntityIdVisitor.String() {
+ @Override
+ public void visitNodeId(InputEntityVisitor visitor, String id) {
+ visitor.id(id, globalGroup);
+ }
+
+ @Override
+ public void visitSourceId(InputEntityVisitor visitor, String id) {
+ visitor.startId(id, globalGroup);
+ }
+
+ @Override
+ public void visitTargetId(InputEntityVisitor visitor, String id) {
+ visitor.endId(id, globalGroup);
+ }
+ };
+ }
+
+ @Override
+ public Setting