diff --git a/applications/graph-store-catalog/build.gradle b/applications/graph-store-catalog/build.gradle index 0d915c92edd..14f5fd37587 100644 --- a/applications/graph-store-catalog/build.gradle +++ b/applications/graph-store-catalog/build.gradle @@ -34,6 +34,7 @@ dependencies { implementation project(':core-write') implementation project(':executor') implementation project(':graph-projection-api') + implementation project(':graph-sampling') implementation project(':logging') implementation project(':memory-usage') implementation project(':native-projection') diff --git a/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/ConfigurationService.java b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/ConfigurationService.java index c1691fe96f6..58c1475540f 100644 --- a/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/ConfigurationService.java +++ b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/ConfigurationService.java @@ -35,6 +35,7 @@ import org.neo4j.gds.core.CypherMapAccess; import org.neo4j.gds.core.CypherMapWrapper; import org.neo4j.gds.core.loading.GraphStoreWithConfig; +import org.neo4j.gds.graphsampling.config.CommonNeighbourAwareRandomWalkConfig; import org.neo4j.gds.projection.GraphProjectFromStoreConfig; import java.util.Collection; @@ -329,6 +330,12 @@ GraphWriteRelationshipConfig parseGraphWriteRelationshipConfiguration( return configuration; } + CommonNeighbourAwareRandomWalkConfig parseCommonNeighbourAwareRandomWalkConfig(Map rawConfiguration) { + var cypherConfig = CypherMapWrapper.create(rawConfiguration); + + return CommonNeighbourAwareRandomWalkConfig.of(cypherConfig); + } + private void ensureThereAreNoExtraConfigurationKeys(CypherMapAccess cypherConfig, BaseConfig config) { cypherConfig.requireOnlyKeysFrom(config.configKeys()); } diff --git a/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/DefaultGraphStoreCatalogBusinessFacade.java b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/DefaultGraphStoreCatalogBusinessFacade.java index 0c22ca4711a..68392a25637 100644 --- a/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/DefaultGraphStoreCatalogBusinessFacade.java +++ b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/DefaultGraphStoreCatalogBusinessFacade.java @@ -25,6 +25,7 @@ import org.neo4j.gds.api.User; import org.neo4j.gds.config.MutateLabelConfig; import org.neo4j.gds.config.WriteLabelConfig; +import org.neo4j.gds.core.CypherMapWrapper; import org.neo4j.gds.core.loading.CatalogRequest; import org.neo4j.gds.core.loading.GraphDropNodePropertiesResult; import org.neo4j.gds.core.loading.GraphDropRelationshipResult; @@ -35,6 +36,8 @@ import org.neo4j.gds.core.utils.TerminationFlag; import org.neo4j.gds.core.utils.progress.TaskRegistryFactory; import org.neo4j.gds.core.utils.warnings.UserLogRegistryFactory; +import org.neo4j.gds.graphsampling.RandomWalkBasedNodesSampler; +import org.neo4j.gds.graphsampling.config.RandomWalkWithRestartsConfig; import org.neo4j.gds.logging.Log; import org.neo4j.gds.projection.GraphProjectNativeResult; import org.neo4j.gds.results.MemoryEstimateResult; @@ -42,9 +45,15 @@ import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.neo4j.gds.applications.graphstorecatalog.SamplerCompanion.CNARW_CONFIG_PROVIDER; +import static org.neo4j.gds.applications.graphstorecatalog.SamplerCompanion.CNARW_PROVIDER; +import static org.neo4j.gds.applications.graphstorecatalog.SamplerCompanion.RWR_CONFIG_PROVIDER; +import static org.neo4j.gds.applications.graphstorecatalog.SamplerCompanion.RWR_PROVIDER; + /** * This layer is shared between Neo4j and other integrations. It is entry-point agnostic. * "Business facade" to distinguish it from "procedure facade" and similar. @@ -90,6 +99,8 @@ public class DefaultGraphStoreCatalogBusinessFacade implements GraphStoreCatalog private final WriteRelationshipPropertiesApplication writeRelationshipPropertiesApplication; private final WriteNodeLabelApplication writeNodeLabelApplication; private final WriteRelationshipsApplication writeRelationshipsApplication; + private final GraphSamplingApplication graphSamplingApplication; + private final EstimateCommonNeighbourAwareRandomWalkApplication estimateCommonNeighbourAwareRandomWalkApplication; public DefaultGraphStoreCatalogBusinessFacade( Log log, @@ -112,7 +123,9 @@ public DefaultGraphStoreCatalogBusinessFacade( WriteNodePropertiesApplication writeNodePropertiesApplication, WriteRelationshipPropertiesApplication writeRelationshipPropertiesApplication, WriteNodeLabelApplication writeNodeLabelApplication, - WriteRelationshipsApplication writeRelationshipsApplication + WriteRelationshipsApplication writeRelationshipsApplication, + GraphSamplingApplication graphSamplingApplication, + EstimateCommonNeighbourAwareRandomWalkApplication estimateCommonNeighbourAwareRandomWalkApplication ) { this.log = log; @@ -137,6 +150,8 @@ public DefaultGraphStoreCatalogBusinessFacade( this.writeRelationshipPropertiesApplication = writeRelationshipPropertiesApplication; this.writeNodeLabelApplication = writeNodeLabelApplication; this.writeRelationshipsApplication = writeRelationshipsApplication; + this.graphSamplingApplication = graphSamplingApplication; + this.estimateCommonNeighbourAwareRandomWalkApplication = estimateCommonNeighbourAwareRandomWalkApplication; } @Override @@ -753,6 +768,96 @@ public WriteRelationshipResult writeRelationships( ); } + @Override + public RandomWalkSamplingResult sampleRandomWalkWithRestarts( + User user, + DatabaseId databaseId, + TaskRegistryFactory taskRegistryFactory, + UserLogRegistryFactory userLogRegistryFactory, + String graphName, + String originGraphName, + Map configuration + ) { + return sampleRandomWalk( + user, + databaseId, + taskRegistryFactory, + userLogRegistryFactory, + graphName, + originGraphName, + configuration, + RWR_CONFIG_PROVIDER, + RWR_PROVIDER + ); + } + + @Override + public RandomWalkSamplingResult sampleCommonNeighbourAwareRandomWalk( + User user, + DatabaseId databaseId, + TaskRegistryFactory taskRegistryFactory, + UserLogRegistryFactory userLogRegistryFactory, + String graphNameAsString, + String originGraphName, + Map configuration + ) { + return sampleRandomWalk( + user, + databaseId, + taskRegistryFactory, + userLogRegistryFactory, + graphNameAsString, + originGraphName, + configuration, + CNARW_CONFIG_PROVIDER, + CNARW_PROVIDER + ); + } + + @Override + public MemoryEstimateResult estimateCommonNeighbourAwareRandomWalk( + User user, + DatabaseId databaseId, + String graphName, + Map rawConfiguration + ) { + var configuration = configurationService.parseCommonNeighbourAwareRandomWalkConfig(rawConfiguration); + + return estimateCommonNeighbourAwareRandomWalkApplication.estimate(user, databaseId, graphName, configuration); + } + + private RandomWalkSamplingResult sampleRandomWalk( + User user, + DatabaseId databaseId, + TaskRegistryFactory taskRegistryFactory, + UserLogRegistryFactory userLogRegistryFactory, + String graphNameAsString, + String originGraphNameAsString, + Map configuration, + Function samplerConfigProvider, + Function samplerAlgorithmProvider + ) { + var graphName = ensureGraphNameValidAndUnknown(user, databaseId, graphNameAsString); + var originGraphName = GraphName.parse(originGraphNameAsString); + + var graphStoreWithConfig = graphStoreCatalogService.get(CatalogRequest.of(user, databaseId), originGraphName); + var graphStore = graphStoreWithConfig.graphStore(); + var graphProjectConfig = graphStoreWithConfig.config(); + + return graphSamplingApplication.sample( + user, + taskRegistryFactory, + userLogRegistryFactory, + graphStore, + graphProjectConfig, + originGraphName, + graphName, + configuration, + samplerConfigProvider, + samplerAlgorithmProvider + ); + } + private GraphName ensureGraphNameValidAndUnknown(User user, DatabaseId databaseId, String graphNameAsString) { var graphName = graphNameValidationService.validateStrictly(graphNameAsString); diff --git a/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/EstimateCommonNeighbourAwareRandomWalkApplication.java b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/EstimateCommonNeighbourAwareRandomWalkApplication.java new file mode 100644 index 00000000000..0bfb3414614 --- /dev/null +++ b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/EstimateCommonNeighbourAwareRandomWalkApplication.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.applications.graphstorecatalog; + +import org.neo4j.gds.api.DatabaseId; +import org.neo4j.gds.api.User; +import org.neo4j.gds.core.utils.mem.MemoryTreeWithDimensions; +import org.neo4j.gds.executor.GraphStoreFromCatalogLoader; +import org.neo4j.gds.graphsampling.config.CommonNeighbourAwareRandomWalkConfig; +import org.neo4j.gds.graphsampling.samplers.rw.cnarw.CommonNeighbourAwareRandomWalk; +import org.neo4j.gds.results.MemoryEstimateResult; + +public class EstimateCommonNeighbourAwareRandomWalkApplication { + MemoryEstimateResult estimate( + User user, + DatabaseId databaseId, + String graphName, + CommonNeighbourAwareRandomWalkConfig configuration + ) { + var loader = new GraphStoreFromCatalogLoader( + graphName, + configuration, + user.getUsername(), + databaseId, + user.isAdmin() + ); + + var memoryTree = CommonNeighbourAwareRandomWalk + .memoryEstimation(configuration) + .estimate(loader.graphDimensions(), configuration.concurrency()); + + var memoryTreeWithDimensions = new MemoryTreeWithDimensions(memoryTree, loader.graphDimensions()); + + return new MemoryEstimateResult(memoryTreeWithDimensions); + } +} diff --git a/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/GraphSamplingApplication.java b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/GraphSamplingApplication.java new file mode 100644 index 00000000000..defcca6a96a --- /dev/null +++ b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/GraphSamplingApplication.java @@ -0,0 +1,104 @@ +/* + * 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.applications.graphstorecatalog; + +import org.neo4j.gds.api.GraphName; +import org.neo4j.gds.api.GraphStore; +import org.neo4j.gds.api.User; +import org.neo4j.gds.config.GraphProjectConfig; +import org.neo4j.gds.core.CypherMapWrapper; +import org.neo4j.gds.core.loading.GraphStoreCatalogService; +import org.neo4j.gds.core.utils.ProgressTimer; +import org.neo4j.gds.core.utils.progress.TaskRegistryFactory; +import org.neo4j.gds.core.utils.progress.tasks.TaskProgressTracker; +import org.neo4j.gds.core.utils.warnings.UserLogRegistryFactory; +import org.neo4j.gds.graphsampling.GraphSampleConstructor; +import org.neo4j.gds.graphsampling.RandomWalkBasedNodesSampler; +import org.neo4j.gds.graphsampling.config.RandomWalkWithRestartsConfig; +import org.neo4j.gds.logging.Log; + +import java.util.Map; +import java.util.function.Function; + +public final class GraphSamplingApplication { + private final Log log; + private final GraphStoreCatalogService graphStoreCatalogService; + + public GraphSamplingApplication(Log log, GraphStoreCatalogService graphStoreCatalogService) { + this.log = log; + this.graphStoreCatalogService = graphStoreCatalogService; + } + + RandomWalkSamplingResult sample( + User user, + TaskRegistryFactory taskRegistryFactory, + UserLogRegistryFactory userLogRegistryFactory, + GraphStore graphStore, + GraphProjectConfig graphProjectConfig, + GraphName originGraphName, + GraphName graphName, + Map configuration, + Function samplerConfigProvider, + Function samplerAlgorithmProvider + ) { + try (var progressTimer = ProgressTimer.start()) { + var cypherMap = CypherMapWrapper.create(configuration); + var samplerConfig = samplerConfigProvider.apply(cypherMap); + + var samplerAlgorithm = samplerAlgorithmProvider.apply(samplerConfig); + var progressTracker = new TaskProgressTracker( + GraphSampleConstructor.progressTask(graphStore, samplerAlgorithm), + (org.neo4j.logging.Log) log.getNeo4jLog(), + samplerConfig.concurrency(), + samplerConfig.jobId(), + taskRegistryFactory, + userLogRegistryFactory + ); + var graphSampleConstructor = new GraphSampleConstructor( + samplerConfig, + graphStore, + samplerAlgorithm, + progressTracker + ); + var sampledGraphStore = graphSampleConstructor.compute(); + + var rwrProcConfig = RandomWalkWithRestartsConfiguration.of( + user.getUsername(), + graphName.getValue(), + originGraphName.getValue(), + graphProjectConfig, + cypherMap + ); + + graphStoreCatalogService.set(rwrProcConfig, sampledGraphStore); + + var projectMillis = progressTimer.stop().getDuration(); + + return new RandomWalkSamplingResult( + graphName.getValue(), + originGraphName.getValue(), + sampledGraphStore.nodeCount(), + sampledGraphStore.relationshipCount(), + samplerAlgorithm.startNodesCount(), + projectMillis + ); + } + } +} diff --git a/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/GraphStoreCatalogBusinessFacade.java b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/GraphStoreCatalogBusinessFacade.java index aa916fda574..bbf5ed68927 100644 --- a/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/GraphStoreCatalogBusinessFacade.java +++ b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/GraphStoreCatalogBusinessFacade.java @@ -234,4 +234,31 @@ WriteRelationshipResult writeRelationships( String relationshipProperty, Map configuration ); + + RandomWalkSamplingResult sampleRandomWalkWithRestarts( + User user, + DatabaseId databaseId, + TaskRegistryFactory taskRegistryFactory, + UserLogRegistryFactory userLogRegistryFactory, + String graphName, + String originGraphName, + Map configuration + ); + + RandomWalkSamplingResult sampleCommonNeighbourAwareRandomWalk( + User user, + DatabaseId databaseId, + TaskRegistryFactory taskRegistryFactory, + UserLogRegistryFactory userLogRegistryFactory, + String graphName, + String originGraphName, + Map configuration + ); + + MemoryEstimateResult estimateCommonNeighbourAwareRandomWalk( + User user, + DatabaseId databaseId, + String graphName, + Map configuration + ); } diff --git a/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/GraphStoreCatalogBusinessFacadePreConditionsDecorator.java b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/GraphStoreCatalogBusinessFacadePreConditionsDecorator.java index bd619f81d1b..2bb78604047 100644 --- a/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/GraphStoreCatalogBusinessFacadePreConditionsDecorator.java +++ b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/GraphStoreCatalogBusinessFacadePreConditionsDecorator.java @@ -477,6 +477,63 @@ public WriteRelationshipResult writeRelationships( )); } + @Override + public RandomWalkSamplingResult sampleRandomWalkWithRestarts( + User user, + DatabaseId databaseId, + TaskRegistryFactory taskRegistryFactory, + UserLogRegistryFactory userLogRegistryFactory, + String graphName, + String originGraphName, + Map configuration + ) { + return runWithPreconditionsChecked(() -> delegate.sampleRandomWalkWithRestarts( + user, + databaseId, + taskRegistryFactory, + userLogRegistryFactory, + graphName, + originGraphName, + configuration + )); + } + + @Override + public RandomWalkSamplingResult sampleCommonNeighbourAwareRandomWalk( + User user, + DatabaseId databaseId, + TaskRegistryFactory taskRegistryFactory, + UserLogRegistryFactory userLogRegistryFactory, + String graphName, + String originGraphName, + Map configuration + ) { + return runWithPreconditionsChecked(() -> delegate.sampleCommonNeighbourAwareRandomWalk( + user, + databaseId, + taskRegistryFactory, + userLogRegistryFactory, + graphName, + originGraphName, + configuration + )); + } + + @Override + public MemoryEstimateResult estimateCommonNeighbourAwareRandomWalk( + User user, + DatabaseId databaseId, + String graphName, + Map configuration + ) { + return runWithPreconditionsChecked(() -> delegate.estimateCommonNeighbourAwareRandomWalk( + user, + databaseId, + graphName, + configuration + )); + } + private T runWithPreconditionsChecked(Supplier businessLogic) { preconditionsService.checkPreconditions(); diff --git a/proc/catalog/src/main/java/org/neo4j/gds/catalog/RandomWalkSamplingResult.java b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/RandomWalkSamplingResult.java similarity index 96% rename from proc/catalog/src/main/java/org/neo4j/gds/catalog/RandomWalkSamplingResult.java rename to applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/RandomWalkSamplingResult.java index 01cf49aaaa4..4effbcf6812 100644 --- a/proc/catalog/src/main/java/org/neo4j/gds/catalog/RandomWalkSamplingResult.java +++ b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/RandomWalkSamplingResult.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.gds.catalog; +package org.neo4j.gds.applications.graphstorecatalog; import org.neo4j.gds.core.loading.GraphProjectResult; diff --git a/proc/catalog/src/main/java/org/neo4j/gds/config/RandomWalkWithRestartsProcConfig.java b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/RandomWalkWithRestartsConfiguration.java similarity index 79% rename from proc/catalog/src/main/java/org/neo4j/gds/config/RandomWalkWithRestartsProcConfig.java rename to applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/RandomWalkWithRestartsConfiguration.java index 29c9f4e4ad7..fd1814d65ad 100644 --- a/proc/catalog/src/main/java/org/neo4j/gds/config/RandomWalkWithRestartsProcConfig.java +++ b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/RandomWalkWithRestartsConfiguration.java @@ -17,15 +17,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.gds.config; +package org.neo4j.gds.applications.graphstorecatalog; import org.neo4j.gds.annotation.Configuration; +import org.neo4j.gds.config.GraphProjectConfig; +import org.neo4j.gds.config.GraphSampleProcConfig; import org.neo4j.gds.core.CypherMapWrapper; import org.neo4j.gds.graphsampling.config.RandomWalkWithRestartsConfig; @Configuration -public interface RandomWalkWithRestartsProcConfig extends GraphSampleProcConfig { - static RandomWalkWithRestartsProcConfig of( +public interface RandomWalkWithRestartsConfiguration extends GraphSampleProcConfig { + static RandomWalkWithRestartsConfiguration of( String userName, String graphName, String fromGraphName, @@ -33,7 +35,8 @@ static RandomWalkWithRestartsProcConfig of( CypherMapWrapper procedureConfig ) { var rwrConfig = RandomWalkWithRestartsConfig.of(procedureConfig); - return new RandomWalkWithRestartsProcConfigImpl( + + return new RandomWalkWithRestartsConfigurationImpl( originalConfig, fromGraphName, rwrConfig, diff --git a/proc/catalog/src/main/java/org/neo4j/gds/catalog/SamplerCompanion.java b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/SamplerCompanion.java similarity index 97% rename from proc/catalog/src/main/java/org/neo4j/gds/catalog/SamplerCompanion.java rename to applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/SamplerCompanion.java index 899b977606d..3784b8ade75 100644 --- a/proc/catalog/src/main/java/org/neo4j/gds/catalog/SamplerCompanion.java +++ b/applications/graph-store-catalog/src/main/java/org/neo4j/gds/applications/graphstorecatalog/SamplerCompanion.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.gds.catalog; +package org.neo4j.gds.applications.graphstorecatalog; import org.neo4j.gds.core.CypherMapWrapper; import org.neo4j.gds.graphsampling.RandomWalkBasedNodesSampler; @@ -29,7 +29,6 @@ import java.util.function.Function; public class SamplerCompanion { - public static final Function RWR_CONFIG_PROVIDER = (cypherMapWrapper) -> RandomWalkWithRestartsConfig.of(cypherMapWrapper); public static final Function CNARW_CONFIG_PROVIDER = diff --git a/applications/graph-store-catalog/src/test/java/org/neo4j/gds/applications/graphstorecatalog/DefaultGraphStoreCatalogBusinessFacadeTest.java b/applications/graph-store-catalog/src/test/java/org/neo4j/gds/applications/graphstorecatalog/DefaultGraphStoreCatalogBusinessFacadeTest.java index 47d7fe29acf..83a6e4d1c1b 100644 --- a/applications/graph-store-catalog/src/test/java/org/neo4j/gds/applications/graphstorecatalog/DefaultGraphStoreCatalogBusinessFacadeTest.java +++ b/applications/graph-store-catalog/src/test/java/org/neo4j/gds/applications/graphstorecatalog/DefaultGraphStoreCatalogBusinessFacadeTest.java @@ -58,6 +58,8 @@ void shouldDetermineGraphExists() { null, null, null, + null, + null, null ); @@ -95,6 +97,8 @@ void shouldDetermineGraphDoesNotExist() { null, null, null, + null, + null, null ); @@ -136,6 +140,8 @@ void shouldValidateInputGraphName() { null, null, null, + null, + null, null ); @@ -168,6 +174,8 @@ void shouldUseStrictValidationWhenProjecting() { null, null, null, + null, + null, null ); @@ -231,6 +239,8 @@ void shouldHandleNullsInNativeProjectParameters() { null, null, null, + null, + null, null ); @@ -315,6 +325,8 @@ void shouldHandleNullsInCypherProjectParameters() { null, null, null, + null, + null, null ); @@ -397,6 +409,8 @@ void shouldDoExistenceCheckWhenProjecting() { null, null, null, + null, + null, null ); @@ -471,6 +485,8 @@ void shouldDoPositiveExistenceCheckWhenProjectingSubGraph() { null, null, null, + null, + null, null ); diff --git a/proc/catalog/src/test/java/org/neo4j/gds/catalog/SamplerOperatorTest.java b/applications/graph-store-catalog/src/test/java/org/neo4j/gds/applications/graphstorecatalog/GraphSamplingApplicationTest.java similarity index 54% rename from proc/catalog/src/test/java/org/neo4j/gds/catalog/SamplerOperatorTest.java rename to applications/graph-store-catalog/src/test/java/org/neo4j/gds/applications/graphstorecatalog/GraphSamplingApplicationTest.java index 32cfece85e2..c181d9821ce 100644 --- a/proc/catalog/src/test/java/org/neo4j/gds/catalog/SamplerOperatorTest.java +++ b/applications/graph-store-catalog/src/test/java/org/neo4j/gds/applications/graphstorecatalog/GraphSamplingApplicationTest.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.gds.catalog; +package org.neo4j.gds.applications.graphstorecatalog; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -25,22 +25,21 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; -import org.neo4j.gds.api.AlgorithmMetaDataSetter; -import org.neo4j.gds.api.CloseableResourceRegistry; +import org.neo4j.gds.api.GraphName; import org.neo4j.gds.api.GraphStore; -import org.neo4j.gds.api.NodeLookup; -import org.neo4j.gds.api.ProcedureReturnColumns; -import org.neo4j.gds.api.TerminationMonitor; +import org.neo4j.gds.api.User; import org.neo4j.gds.compat.Neo4jProxy; +import org.neo4j.gds.compat.TestLog; import org.neo4j.gds.config.GraphProjectConfig; import org.neo4j.gds.core.loading.GraphStoreCatalog; +import org.neo4j.gds.core.loading.GraphStoreCatalogService; import org.neo4j.gds.core.utils.progress.EmptyTaskRegistryFactory; import org.neo4j.gds.core.utils.warnings.EmptyUserLogRegistryFactory; -import org.neo4j.gds.executor.ImmutableExecutionContext; 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.logging.Log; import java.util.List; import java.util.Map; @@ -48,13 +47,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.params.provider.Arguments.arguments; -import static org.neo4j.gds.catalog.SamplerCompanion.CNARW_CONFIG_PROVIDER; -import static org.neo4j.gds.catalog.SamplerCompanion.CNARW_PROVIDER; -import static org.neo4j.gds.catalog.SamplerCompanion.RWR_CONFIG_PROVIDER; -import static org.neo4j.gds.catalog.SamplerCompanion.RWR_PROVIDER; @GdlExtension -class SamplerOperatorTest { +class GraphSamplingApplicationTest { @GdlGraph(idOffset = 42) private static final String DB_CYPHER = @@ -112,28 +107,36 @@ static Stream samplingParameters() { @ParameterizedTest @MethodSource("samplingParameters") void shouldSampleRWR(Map mapConfiguration, long expectedNodeCount) { + var graphSamplingApplication = new GraphSamplingApplication( + new Neo4jBackedLogForTesting(), + new GraphStoreCatalogService() + ); - var executionContext = executionContextBuilder() - .build(); - - var result = SamplerOperator.performSampling("graph", "sample", + var user = new User("user", false); + var result = graphSamplingApplication.sample( + user, + EmptyTaskRegistryFactory.INSTANCE, + EmptyUserLogRegistryFactory.INSTANCE, + graphStore, + GraphProjectConfig.emptyWithName("user", "graph"), + GraphName.parse("graph"), + GraphName.parse("sample"), mapConfiguration, - RWR_CONFIG_PROVIDER, - RWR_PROVIDER, - executionContext - ).findFirst().get(); + SamplerCompanion.RWR_CONFIG_PROVIDER, + SamplerCompanion.RWR_PROVIDER + ); assertThat(result.nodeCount).isEqualTo(expectedNodeCount); assertThat(GraphStoreCatalog.exists( - executionContext.username(), - executionContext.databaseId().databaseName(), + user.getUsername(), + graphStore.databaseId().databaseName(), "sample" )).isTrue(); var sampledGraphStore = GraphStoreCatalog.get( - executionContext.username(), - executionContext.databaseId().databaseName(), + user.getUsername(), + graphStore.databaseId().databaseName(), "sample" ).graphStore(); assertThat(sampledGraphStore.nodeCount()).isEqualTo(expectedNodeCount); @@ -142,26 +145,35 @@ void shouldSampleRWR(Map mapConfiguration, long expectedNodeCoun @ParameterizedTest @MethodSource("samplingParameters") void shouldSampleCNARW(Map mapConfiguration, long expectedNodeCount) { - var executionContext = executionContextBuilder() - .build(); + var graphSamplingApplication = new GraphSamplingApplication( + new Neo4jBackedLogForTesting(), + new GraphStoreCatalogService() + ); - var result = SamplerOperator.performSampling("graph", "sample", + var user = new User("user", false); + var result = graphSamplingApplication.sample( + user, + EmptyTaskRegistryFactory.INSTANCE, + EmptyUserLogRegistryFactory.INSTANCE, + graphStore, + GraphProjectConfig.emptyWithName("user", "graph"), + GraphName.parse("graph"), + GraphName.parse("sample"), mapConfiguration, - CNARW_CONFIG_PROVIDER, - CNARW_PROVIDER, - executionContext - ).findFirst().get(); + SamplerCompanion.CNARW_CONFIG_PROVIDER, + SamplerCompanion.CNARW_PROVIDER + ); assertThat(result.nodeCount).isEqualTo(expectedNodeCount); assertThat(GraphStoreCatalog.exists( - executionContext.username(), - executionContext.databaseId().databaseName(), + user.getUsername(), + graphStore.databaseId(), "sample" )).isTrue(); var sampledGraphStore = GraphStoreCatalog.get( - executionContext.username(), - executionContext.databaseId().databaseName(), + user.getUsername(), + graphStore.databaseId(), "sample" ).graphStore(); assertThat(sampledGraphStore.nodeCount()).isEqualTo(expectedNodeCount); @@ -170,28 +182,36 @@ void shouldSampleCNARW(Map mapConfiguration, long expectedNodeCo @ParameterizedTest @CsvSource(value = {"0.28,1", "0.35,2"}) void shouldUseSingleStartNodeRWR(double samplingRatio, long expectedStartNodeCount) { - var x = idFunction.of("x"); - - var executionContext = executionContextBuilder() - .build(); + var graphSamplingApplication = new GraphSamplingApplication( + new Neo4jBackedLogForTesting(), + new GraphStoreCatalogService() + ); - var result = SamplerOperator.performSampling("graph", "sample", + var user = new User("user", false); + var x = idFunction.of("x"); + var result = graphSamplingApplication.sample( + user, + EmptyTaskRegistryFactory.INSTANCE, + EmptyUserLogRegistryFactory.INSTANCE, + graphStore, + GraphProjectConfig.emptyWithName("user", "graph"), + GraphName.parse("graph"), + GraphName.parse("sample"), Map.of( "samplingRatio", samplingRatio, "concurrency", 1, "startNodes", List.of(x), - "randomSeed", 42l + "randomSeed", 42L ), - RWR_CONFIG_PROVIDER, - RWR_PROVIDER, - executionContext - ).findFirst().get(); + SamplerCompanion.RWR_CONFIG_PROVIDER, + SamplerCompanion.RWR_PROVIDER + ); assertThat(result.startNodeCount).isEqualTo(expectedStartNodeCount); assertThat(GraphStoreCatalog.exists( - executionContext.username(), - executionContext.databaseId().databaseName(), + user.getUsername(), + graphStore.databaseId(), "sample" )).isTrue(); @@ -200,49 +220,82 @@ void shouldUseSingleStartNodeRWR(double samplingRatio, long expectedStartNodeCou @ParameterizedTest @CsvSource(value = {"0.28,1", "0.35,2"}) void shouldUseSingleStartNodeCNARW(double samplingRatio, long expectedStartNodeCount) { - var x = idFunction.of("x"); - - var executionContext = executionContextBuilder() - .build(); + var graphSamplingApplication = new GraphSamplingApplication( + new Neo4jBackedLogForTesting(), + new GraphStoreCatalogService() + ); - var result = SamplerOperator.performSampling("graph", "sample", + var user = new User("user", false); + var x = idFunction.of("x"); + var result = graphSamplingApplication.sample( + user, + EmptyTaskRegistryFactory.INSTANCE, + EmptyUserLogRegistryFactory.INSTANCE, + graphStore, + GraphProjectConfig.emptyWithName("user", "graph"), + GraphName.parse("graph"), + GraphName.parse("sample"), Map.of( "samplingRatio", samplingRatio, "concurrency", 1, "startNodes", List.of(x), - "randomSeed", 42l + "randomSeed", 42L ), - CNARW_CONFIG_PROVIDER, - CNARW_PROVIDER, - executionContext - ).findFirst().get(); + SamplerCompanion.CNARW_CONFIG_PROVIDER, + SamplerCompanion.CNARW_PROVIDER + ); assertThat(result.startNodeCount).isEqualTo(expectedStartNodeCount); assertThat(GraphStoreCatalog.exists( - executionContext.username(), - executionContext.databaseId().databaseName(), + user.getUsername(), + graphStore.databaseId(), "sample" )).isTrue(); } - - private ImmutableExecutionContext.Builder executionContextBuilder() { - return ImmutableExecutionContext - .builder() - .databaseId(graphStore.databaseId()) - .dependencyResolver(Neo4jProxy.emptyDependencyResolver()) - .returnColumns(ProcedureReturnColumns.EMPTY) - .userLogRegistryFactory(EmptyUserLogRegistryFactory.INSTANCE) - .taskRegistryFactory(EmptyTaskRegistryFactory.INSTANCE) - .username("user") - .terminationMonitor(TerminationMonitor.EMPTY) - .closeableResourceRegistry(CloseableResourceRegistry.EMPTY) - .algorithmMetaDataSetter(AlgorithmMetaDataSetter.EMPTY) - .nodeLookup(NodeLookup.EMPTY) - .log(Neo4jProxy.testLog()) - .isGdsAdmin(false); + /** + * @deprecated We need this just long enough that we can drive out usages of Neo4j's log. + * Therefore, I do not want to build general support for this + */ + @Deprecated + private static class Neo4jBackedLogForTesting implements Log { + private final TestLog neo4jLog = Neo4jProxy.testLog(); + + @Override + public void info(String message) { + throw new UnsupportedOperationException("TODO"); + } + + @Override + public void info(String format, Object... arguments) { + throw new UnsupportedOperationException("TODO"); + } + + @Override + public void warn(String message, Exception e) { + throw new UnsupportedOperationException("TODO"); + } + + @Override + public void warn(String format, Object... arguments) { + throw new UnsupportedOperationException("TODO"); + } + + @Override + public boolean isDebugEnabled() { + throw new UnsupportedOperationException("TODO"); + } + + @Override + public void debug(String format, Object... arguments) { + throw new UnsupportedOperationException("TODO"); + } + + @Override + public Object getNeo4jLog() { + return neo4jLog; + } } - } diff --git a/proc/catalog/src/main/java/org/neo4j/gds/catalog/GraphCatalogProcedureConstants.java b/proc/catalog/src/main/java/org/neo4j/gds/catalog/GraphCatalogProcedureConstants.java index 32c99c805bf..c5bbdec8dec 100644 --- a/proc/catalog/src/main/java/org/neo4j/gds/catalog/GraphCatalogProcedureConstants.java +++ b/proc/catalog/src/main/java/org/neo4j/gds/catalog/GraphCatalogProcedureConstants.java @@ -37,6 +37,9 @@ final class GraphCatalogProcedureConstants { static final String WRITE_NODE_LABEL_DESCRIPTION = "Writes the given node Label to an online Neo4j database."; static final String WRITE_RELATIONSHIP_DESCRIPTION = "Writes the given relationship and an optional relationship property to an online Neo4j database."; static final String WRITE_RELATIONSHIP_PROPERTIES_DESCRIPTION = "Writes the given relationship and a list of relationship properties to an online Neo4j database."; + static final String RWR_DESCRIPTION = "Constructs a random subgraph based on random walks with restarts"; + static final String CNARW_DESCRIPTION = "Constructs a random subgraph based on common neighbour aware random walks"; + static final String ESTIMATE_CNARW_DESCRIPTION = "Estimate memory requirements for sampling graph using CNARW algorithm"; private GraphCatalogProcedureConstants() {} } diff --git a/proc/catalog/src/main/java/org/neo4j/gds/catalog/GraphSampleProc.java b/proc/catalog/src/main/java/org/neo4j/gds/catalog/GraphSampleProc.java index 1cbef1f88ec..560f166798b 100644 --- a/proc/catalog/src/main/java/org/neo4j/gds/catalog/GraphSampleProc.java +++ b/proc/catalog/src/main/java/org/neo4j/gds/catalog/GraphSampleProc.java @@ -19,15 +19,10 @@ */ package org.neo4j.gds.catalog; -import org.neo4j.gds.BaseProc; -import org.neo4j.gds.core.CypherMapWrapper; -import org.neo4j.gds.core.utils.mem.MemoryTree; -import org.neo4j.gds.core.utils.mem.MemoryTreeWithDimensions; -import org.neo4j.gds.executor.GraphStoreFromCatalogLoader; -import org.neo4j.gds.executor.Preconditions; -import org.neo4j.gds.graphsampling.config.CommonNeighbourAwareRandomWalkConfig; -import org.neo4j.gds.graphsampling.samplers.rw.cnarw.CommonNeighbourAwareRandomWalk; +import org.neo4j.gds.applications.graphstorecatalog.RandomWalkSamplingResult; +import org.neo4j.gds.procedures.GraphDataScience; import org.neo4j.gds.results.MemoryEstimateResult; +import org.neo4j.procedure.Context; import org.neo4j.procedure.Description; import org.neo4j.procedure.Internal; import org.neo4j.procedure.Name; @@ -36,49 +31,40 @@ import java.util.Map; import java.util.stream.Stream; -import static org.neo4j.gds.catalog.SamplerCompanion.CNARW_CONFIG_PROVIDER; -import static org.neo4j.gds.catalog.SamplerCompanion.CNARW_PROVIDER; -import static org.neo4j.gds.catalog.SamplerCompanion.RWR_CONFIG_PROVIDER; -import static org.neo4j.gds.catalog.SamplerCompanion.RWR_PROVIDER; +import static org.neo4j.gds.catalog.GraphCatalogProcedureConstants.CNARW_DESCRIPTION; +import static org.neo4j.gds.catalog.GraphCatalogProcedureConstants.ESTIMATE_CNARW_DESCRIPTION; +import static org.neo4j.gds.catalog.GraphCatalogProcedureConstants.RWR_DESCRIPTION; import static org.neo4j.procedure.Mode.READ; -public class GraphSampleProc extends BaseProc { - private static final String RWR_DESCRIPTION = "Constructs a random subgraph based on random walks with restarts"; - private static final String CNARW_DESCRIPTION = "Constructs a random subgraph based on common neighbour aware random walks"; +public class GraphSampleProc { + @Context + public GraphDataScience facade; - @Internal - @Deprecated(forRemoval = true) - @Procedure(name = "gds.alpha.graph.sample.rwr", mode = READ, deprecatedBy = "gds.graph.sample.rwr") + @SuppressWarnings("WeakerAccess") + @Procedure(name = "gds.graph.sample.rwr", mode = READ) @Description(RWR_DESCRIPTION) - public Stream sampleRandomWalkWithRestartsAlpha( + public Stream sampleRandomWalkWithRestarts( @Name(value = "graphName") String graphName, @Name(value = "fromGraphName") String fromGraphName, @Name(value = "configuration", defaultValue = "{}") Map configuration ) { - return sampleRandomWalkWithRestarts(graphName, fromGraphName, configuration); + return facade.catalog().sampleRandomWalkWithRestarts(graphName, fromGraphName, configuration); } - @Procedure(name = "gds.graph.sample.rwr", mode = READ) + @Internal + @Deprecated(forRemoval = true) + @Procedure(name = "gds.alpha.graph.sample.rwr", mode = READ, deprecatedBy = "gds.graph.sample.rwr") @Description(RWR_DESCRIPTION) - public Stream sampleRandomWalkWithRestarts( + public Stream sampleRandomWalkWithRestartsAlpha( @Name(value = "graphName") String graphName, @Name(value = "fromGraphName") String fromGraphName, @Name(value = "configuration", defaultValue = "{}") Map configuration ) { - - Preconditions.check(); - validateGraphNameAndEnsureItDoesNotExist(username(), graphName); - return SamplerOperator.performSampling( - fromGraphName, - graphName, configuration, - RWR_CONFIG_PROVIDER, - RWR_PROVIDER, - executionContext() - ); - + return sampleRandomWalkWithRestarts(graphName, fromGraphName, configuration); } + @SuppressWarnings("unused") @Procedure(name = "gds.graph.sample.cnarw", mode = READ) @Description(CNARW_DESCRIPTION) public Stream sampleCNARW( @@ -86,41 +72,16 @@ public Stream sampleCNARW( @Name(value = "fromGraphName") String fromGraphName, @Name(value = "configuration", defaultValue = "{}") Map configuration ) { - Preconditions.check(); - validateGraphNameAndEnsureItDoesNotExist(username(), graphName); - return SamplerOperator.performSampling( - fromGraphName, - graphName, configuration, - CNARW_CONFIG_PROVIDER, - CNARW_PROVIDER, - executionContext() - ); - + return facade.catalog().sampleCommonNeighbourAwareRandomWalk(graphName, fromGraphName, configuration); } + @SuppressWarnings("unused") @Procedure(name = "gds.graph.sample.cnarw.estimate", mode = READ) - @Description("Estimate memory requirements for sampling graph using CNARW algorithm") + @Description(ESTIMATE_CNARW_DESCRIPTION) public Stream estimateCNARW( @Name(value = "fromGraphName") String fromGraphName, @Name(value = "configuration", defaultValue = "{}") Map configuration ) { - var cypherMap = CypherMapWrapper.create(configuration); - var cnarwConfig = CommonNeighbourAwareRandomWalkConfig.of(cypherMap); - - var loader = new GraphStoreFromCatalogLoader( - fromGraphName, - cnarwConfig, - executionContext().username(), - executionContext().databaseId(), - executionContext().isGdsAdmin() - ); - - MemoryTree memoryTree = CommonNeighbourAwareRandomWalk.memoryEstimation(cnarwConfig) - .estimate(loader.graphDimensions(), cnarwConfig.concurrency()); - - return Stream.of(new MemoryEstimateResult(new MemoryTreeWithDimensions( - memoryTree, - loader.graphDimensions() - ))); + return facade.catalog().estimateCommonNeighbourAwareRandomWalk(fromGraphName, configuration); } } diff --git a/proc/catalog/src/main/java/org/neo4j/gds/catalog/SamplerOperator.java b/proc/catalog/src/main/java/org/neo4j/gds/catalog/SamplerOperator.java deleted file mode 100644 index 98cb317a3f9..00000000000 --- a/proc/catalog/src/main/java/org/neo4j/gds/catalog/SamplerOperator.java +++ /dev/null @@ -1,105 +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.catalog; - -import org.neo4j.gds.config.RandomWalkWithRestartsProcConfig; -import org.neo4j.gds.core.CypherMapWrapper; -import org.neo4j.gds.core.loading.GraphStoreCatalog; -import org.neo4j.gds.core.loading.GraphStoreWithConfig; -import org.neo4j.gds.core.loading.ImmutableCatalogRequest; -import org.neo4j.gds.core.utils.ProgressTimer; -import org.neo4j.gds.core.utils.progress.tasks.TaskProgressTracker; -import org.neo4j.gds.core.utils.warnings.EmptyUserLogRegistryFactory; -import org.neo4j.gds.executor.ExecutionContext; -import org.neo4j.gds.graphsampling.GraphSampleConstructor; -import org.neo4j.gds.graphsampling.RandomWalkBasedNodesSampler; -import org.neo4j.gds.graphsampling.config.RandomWalkWithRestartsConfig; - -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Stream; - -public final class SamplerOperator { - - public static Stream performSampling( - String fromGraphName, - String graphName, - Map configuration, - Function samplerConfigProvider, - Function samplerAlgorithmProvider, - ExecutionContext executionContext - ) { - try (var progressTimer = ProgressTimer.start()) { - - var fromGraphStore = graphStoreFromCatalog(fromGraphName, executionContext); - - var cypherMap = CypherMapWrapper.create(configuration); - var samplerConfig = samplerConfigProvider.apply(cypherMap); - - var samplerAlgorithm = samplerAlgorithmProvider.apply(samplerConfig); - var progressTracker = new TaskProgressTracker( - GraphSampleConstructor.progressTask(fromGraphStore.graphStore(), samplerAlgorithm), - executionContext.log(), - samplerConfig.concurrency(), - samplerConfig.jobId(), - executionContext.taskRegistryFactory(), - EmptyUserLogRegistryFactory.INSTANCE - ); - var graphSampleConstructor = new GraphSampleConstructor( - samplerConfig, - fromGraphStore.graphStore(), - samplerAlgorithm, - progressTracker - ); - var sampledGraphStore = graphSampleConstructor.compute(); - - var rwrProcConfig = RandomWalkWithRestartsProcConfig.of( - executionContext.username(), - graphName, - fromGraphName, - fromGraphStore.config(), - cypherMap - ); - GraphStoreCatalog.set(rwrProcConfig, sampledGraphStore); - - var projectMillis = progressTimer.stop().getDuration(); - return Stream.of(new RandomWalkSamplingResult( - graphName, - fromGraphName, - sampledGraphStore.nodeCount(), - sampledGraphStore.relationshipCount(), - samplerAlgorithm.startNodesCount(), - projectMillis - )); - } - } - - static GraphStoreWithConfig graphStoreFromCatalog(String graphName, ExecutionContext executionContext) { - var catalogRequest = ImmutableCatalogRequest.of( - executionContext.databaseId().databaseName(), - executionContext.username(), - Optional.empty(), - executionContext.isGdsAdmin() - ); - return GraphStoreCatalog.get(catalogRequest, graphName); - } - -} diff --git a/procedures/facade/src/main/java/org/neo4j/gds/procedures/catalog/CatalogFacade.java b/procedures/facade/src/main/java/org/neo4j/gds/procedures/catalog/CatalogFacade.java index 6b4419d1f13..46bc6a4f1d8 100644 --- a/procedures/facade/src/main/java/org/neo4j/gds/procedures/catalog/CatalogFacade.java +++ b/procedures/facade/src/main/java/org/neo4j/gds/procedures/catalog/CatalogFacade.java @@ -32,6 +32,7 @@ import org.neo4j.gds.applications.graphstorecatalog.GraphStreamRelationshipPropertyResult; import org.neo4j.gds.applications.graphstorecatalog.MutateLabelResult; import org.neo4j.gds.applications.graphstorecatalog.NodePropertiesWriteResult; +import org.neo4j.gds.applications.graphstorecatalog.RandomWalkSamplingResult; import org.neo4j.gds.applications.graphstorecatalog.TopologyResult; import org.neo4j.gds.applications.graphstorecatalog.WriteLabelResult; import org.neo4j.gds.applications.graphstorecatalog.WriteRelationshipPropertiesResult; @@ -692,6 +693,69 @@ public Stream writeRelationships( return Stream.of(result); } + public Stream sampleRandomWalkWithRestarts( + String graphName, + String originGraphName, + Map configuration + ) { + var user = user(); + var databaseId = databaseId(); + var taskRegistryFactory = taskRegistryFactoryService.getTaskRegistryFactory(databaseId, user); + var userLogRegistryFactory = userLogServices.getUserLogRegistryFactory(databaseId, user); + + var result = businessFacade.sampleRandomWalkWithRestarts( + user, + databaseId, + taskRegistryFactory, + userLogRegistryFactory, + graphName, + originGraphName, + configuration + ); + + return Stream.of(result); + } + + public Stream sampleCommonNeighbourAwareRandomWalk( + String graphName, + String originGraphName, + Map configuration + ) { + var user = user(); + var databaseId = databaseId(); + var taskRegistryFactory = taskRegistryFactoryService.getTaskRegistryFactory(databaseId, user); + var userLogRegistryFactory = userLogServices.getUserLogRegistryFactory(databaseId, user); + + var result = businessFacade.sampleCommonNeighbourAwareRandomWalk( + user, + databaseId, + taskRegistryFactory, + userLogRegistryFactory, + graphName, + originGraphName, + configuration + ); + + return Stream.of(result); + } + + public Stream estimateCommonNeighbourAwareRandomWalk( + String graphName, + Map configuration + ) { + var user = user(); + var databaseId = databaseId(); + + var result = businessFacade.estimateCommonNeighbourAwareRandomWalk( + user, + databaseId, + graphName, + configuration + ); + + return Stream.of(result); + } + private Stream streamRelationshipPropertyOrProperties( String graphName, List relationshipProperties, diff --git a/procedures/integration/src/main/java/org/neo4j/gds/procedures/integration/CatalogFacadeFactory.java b/procedures/integration/src/main/java/org/neo4j/gds/procedures/integration/CatalogFacadeFactory.java index d6e2b27f6c8..779b73ec5ac 100644 --- a/procedures/integration/src/main/java/org/neo4j/gds/procedures/integration/CatalogFacadeFactory.java +++ b/procedures/integration/src/main/java/org/neo4j/gds/procedures/integration/CatalogFacadeFactory.java @@ -26,10 +26,12 @@ import org.neo4j.gds.applications.graphstorecatalog.DropGraphApplication; import org.neo4j.gds.applications.graphstorecatalog.DropNodePropertiesApplication; import org.neo4j.gds.applications.graphstorecatalog.DropRelationshipsApplication; +import org.neo4j.gds.applications.graphstorecatalog.EstimateCommonNeighbourAwareRandomWalkApplication; import org.neo4j.gds.applications.graphstorecatalog.GenericProjectApplication; import org.neo4j.gds.applications.graphstorecatalog.GraphMemoryUsageApplication; import org.neo4j.gds.applications.graphstorecatalog.GraphNameValidationService; import org.neo4j.gds.applications.graphstorecatalog.GraphProjectMemoryUsageService; +import org.neo4j.gds.applications.graphstorecatalog.GraphSamplingApplication; import org.neo4j.gds.applications.graphstorecatalog.GraphStoreCatalogBusinessFacade; import org.neo4j.gds.applications.graphstorecatalog.GraphStoreCatalogBusinessFacadePreConditionsDecorator; import org.neo4j.gds.applications.graphstorecatalog.GraphStoreValidationService; @@ -159,6 +161,8 @@ public CatalogFacade createCatalogFacade(Context context) { ); var writeNodeLabelApplication = new WriteNodeLabelApplication(log, nodeLabelExporterBuilder); var writeRelationshipsApplication = new WriteRelationshipsApplication(log, relationshipExporterBuilder); + var graphSamplingApplication = new GraphSamplingApplication(log, graphStoreCatalogService); + var estimateCommonNeighbourAwareRandomWalkApplication = new EstimateCommonNeighbourAwareRandomWalkApplication(); // GDS business facade GraphStoreCatalogBusinessFacade businessFacade = new DefaultGraphStoreCatalogBusinessFacade( @@ -182,7 +186,9 @@ public CatalogFacade createCatalogFacade(Context context) { writeNodePropertiesApplication, writeRelationshipPropertiesApplication, writeNodeLabelApplication, - writeRelationshipsApplication + writeRelationshipsApplication, + graphSamplingApplication, + estimateCommonNeighbourAwareRandomWalkApplication ); // wrap in decorator to enable preconditions checks