From d36ccfa9ffe9782af72b4370507e39f8a4b3c5f0 Mon Sep 17 00:00:00 2001 From: Lasse Westh-Nielsen Date: Tue, 9 Jul 2024 17:51:56 +0200 Subject: [PATCH] migrate scaleproperties mutate --- .../MiscAlgorithmMutateBusinessFacade.java | 7 -- .../algorithms/metadata/Algorithm.java | 1 + .../metadata/LabelForProgressTracking.java | 2 + .../miscellaneous-algorithms/build.gradle | 15 +++ .../MiscellaneousAlgorithms.java | 64 ++++++++++++ .../MiscellaneousApplications.java | 66 +++++++++++++ ...ellaneousEstimationModeBusinessFacade.java | 30 ++++++ ...MiscellaneousMutateModeBusinessFacade.java | 67 +++++++++++++ .../ScalePropertiesMutateStep.java | 61 ++++++++++++ applications/facade/build.gradle | 1 + .../gds/applications/ApplicationsFacade.java | 15 +++ .../ApplicationsFacadeBuilder.java | 8 ++ .../ConfigurationParsersForMutateMode.java | 2 + .../pipeline/MutateModeAlgorithmLibrary.java | 1 + .../neo4j/gds/ml/pipeline/StubbyHolder.java | 2 + .../gds/ml/pipeline/ValidationService.java | 2 +- .../pipeline/stubs/ScalePropertiesStub.java | 31 ++++++ ...sificationPredictPipelineExecutorTest.java | 2 +- .../scaling/ScalePropertiesMutateProc.java | 8 +- .../scaling/ScalePropertiesMutateSpec.java | 2 +- procedures/algorithms-facade/build.gradle | 1 + .../algorithms/AlgorithmsProcedureFacade.java | 8 ++ .../configuration/ConfigurationCreator.java | 37 +++---- .../configuration/ConfigurationParser.java | 61 ++++-------- .../ConfigurationValidationHook.java | 26 +++++ .../MiscellaneousProcedureFacade.java | 51 ++++++++++ .../ScalePropertiesMutateResult.java | 20 +++- ...PropertiesConfigurationValidationHook.java | 44 +++++++++ .../stubs/ScalePropertiesMutateStub.java | 98 +++++++++++++++++++ ...ePropertiesResultBuilderForMutateMode.java | 62 ++++++++++++ .../DefaultAlgorithmExecutionScaffolding.java | 7 +- .../runners/EstimationModeRunner.java | 4 +- .../algorithms/stubs/GenericStub.java | 31 +++++- .../ConfigurationParserTest.java | 24 ++--- procedures/facade/build.gradle | 1 + .../AlgorithmProcedureFacadeBuilder.java | 7 +- .../GraphDataScienceProcedures.java | 4 +- .../GraphDataScienceProceduresBuilder.java | 8 ++ .../misc/MiscAlgorithmsProcedureFacade.java | 42 ++------ ...ropertiesComputationResultTransformer.java | 2 +- settings.gradle | 3 + 41 files changed, 796 insertions(+), 132 deletions(-) create mode 100644 applications/algorithms/miscellaneous-algorithms/build.gradle create mode 100644 applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousAlgorithms.java create mode 100644 applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousApplications.java create mode 100644 applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousEstimationModeBusinessFacade.java create mode 100644 applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousMutateModeBusinessFacade.java create mode 100644 applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/ScalePropertiesMutateStep.java create mode 100644 pipeline/src/main/java/org/neo4j/gds/ml/pipeline/stubs/ScalePropertiesStub.java create mode 100644 procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationValidationHook.java create mode 100644 procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/MiscellaneousProcedureFacade.java rename procedures/{facade/src/main/java/org/neo4j/gds/procedures/misc/scaleproperties => algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous}/ScalePropertiesMutateResult.java (80%) create mode 100644 procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/stubs/ScalePropertiesConfigurationValidationHook.java create mode 100644 procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/stubs/ScalePropertiesMutateStub.java create mode 100644 procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/stubs/ScalePropertiesResultBuilderForMutateMode.java diff --git a/algo/src/main/java/org/neo4j/gds/algorithms/misc/MiscAlgorithmMutateBusinessFacade.java b/algo/src/main/java/org/neo4j/gds/algorithms/misc/MiscAlgorithmMutateBusinessFacade.java index 33b5e5054e..c8f2c540ce 100644 --- a/algo/src/main/java/org/neo4j/gds/algorithms/misc/MiscAlgorithmMutateBusinessFacade.java +++ b/algo/src/main/java/org/neo4j/gds/algorithms/misc/MiscAlgorithmMutateBusinessFacade.java @@ -33,13 +33,6 @@ public MiscAlgorithmMutateBusinessFacade(MiscAlgorithmsFacade miscAlgorithmsFaca this.mutateNodePropertyService = mutateNodePropertyService; } - public NodePropertyMutateResult scaleProperties( - String graphName, - ScalePropertiesMutateConfig configuration - ) { - return scaleProperties(graphName,configuration,false); - } - public NodePropertyMutateResult alphaScaleProperties( String graphName, ScalePropertiesMutateConfig configuration diff --git a/applications/algorithms/machinery/src/main/java/org/neo4j/gds/applications/algorithms/metadata/Algorithm.java b/applications/algorithms/machinery/src/main/java/org/neo4j/gds/applications/algorithms/metadata/Algorithm.java index 2b42c1fd61..80c147b206 100644 --- a/applications/algorithms/machinery/src/main/java/org/neo4j/gds/applications/algorithms/metadata/Algorithm.java +++ b/applications/algorithms/machinery/src/main/java/org/neo4j/gds/applications/algorithms/metadata/Algorithm.java @@ -66,6 +66,7 @@ public enum Algorithm { Node2Vec, PageRank, RandomWalk, + ScaleProperties, SCC, SingleSourceDijkstra, SpanningTree, diff --git a/applications/algorithms/machinery/src/main/java/org/neo4j/gds/applications/algorithms/metadata/LabelForProgressTracking.java b/applications/algorithms/machinery/src/main/java/org/neo4j/gds/applications/algorithms/metadata/LabelForProgressTracking.java index 8a4b67a27e..0d4817c69d 100644 --- a/applications/algorithms/machinery/src/main/java/org/neo4j/gds/applications/algorithms/metadata/LabelForProgressTracking.java +++ b/applications/algorithms/machinery/src/main/java/org/neo4j/gds/applications/algorithms/metadata/LabelForProgressTracking.java @@ -59,6 +59,7 @@ public enum LabelForProgressTracking { Node2Vec("Node2Vec"), PageRank("PageRank"), RandomWalk("RandomWalk"), + ScaleProperties("ScaleProperties"), SCC("SCC"), SingleSourceDijkstra("All Shortest Paths"), SpanningTree("SpanningTree"), @@ -114,6 +115,7 @@ public static LabelForProgressTracking from(Algorithm algorithm) { case Node2Vec -> Node2Vec; case PageRank -> PageRank; case RandomWalk -> RandomWalk; + case ScaleProperties -> ScaleProperties; case SCC -> SCC; case SingleSourceDijkstra -> SingleSourceDijkstra; case SpanningTree -> SpanningTree; diff --git a/applications/algorithms/miscellaneous-algorithms/build.gradle b/applications/algorithms/miscellaneous-algorithms/build.gradle new file mode 100644 index 0000000000..d7785beeef --- /dev/null +++ b/applications/algorithms/miscellaneous-algorithms/build.gradle @@ -0,0 +1,15 @@ +apply plugin: 'java-library' + +description = 'Neo4j Graph Data Science :: Miscellaneous Applications' + +group = 'org.neo4j.gds' + +dependencies { + implementation project(":algo") + implementation project(":algo-common") + implementation project(":algorithms-machinery") + implementation project(":config-api") + implementation project(":core") + implementation project(":memory-usage") + implementation project(":progress-tracking") +} diff --git a/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousAlgorithms.java b/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousAlgorithms.java new file mode 100644 index 0000000000..f25e7f43a0 --- /dev/null +++ b/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousAlgorithms.java @@ -0,0 +1,64 @@ +/* + * 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.algorithms.miscellaneous; + +import org.neo4j.gds.api.Graph; +import org.neo4j.gds.applications.algorithms.machinery.AlgorithmMachinery; +import org.neo4j.gds.applications.algorithms.machinery.ProgressTrackerCreator; +import org.neo4j.gds.applications.algorithms.metadata.LabelForProgressTracking; +import org.neo4j.gds.core.concurrency.DefaultPool; +import org.neo4j.gds.core.utils.progress.tasks.Tasks; +import org.neo4j.gds.scaleproperties.ScaleProperties; +import org.neo4j.gds.scaleproperties.ScalePropertiesMutateConfig; +import org.neo4j.gds.scaleproperties.ScalePropertiesResult; + +public class MiscellaneousAlgorithms { + private final AlgorithmMachinery algorithmMachinery = new AlgorithmMachinery(); + + private final ProgressTrackerCreator progressTrackerCreator; + + MiscellaneousAlgorithms(ProgressTrackerCreator progressTrackerCreator) { + this.progressTrackerCreator = progressTrackerCreator; + } + + ScalePropertiesResult scaleProperties(Graph graph, ScalePropertiesMutateConfig configuration) { + int totalPropertyDimension = configuration + .nodeProperties() + .stream() + .map(graph::nodeProperties) + .mapToInt(p -> p.dimension().orElseThrow(/* already validated in config */)) + .sum(); + var task = Tasks.task( + LabelForProgressTracking.ScaleProperties.value, + Tasks.leaf("Prepare scalers", graph.nodeCount() * totalPropertyDimension), + Tasks.leaf("Scale properties", graph.nodeCount() * totalPropertyDimension) + ); + var progressTracker = progressTrackerCreator.createProgressTracker(configuration, task); + + var algorithm = new ScaleProperties( + graph, + configuration, + progressTracker, + DefaultPool.INSTANCE + ); + + return algorithmMachinery.runAlgorithmsAndManageProgressTracker(algorithm, progressTracker, true); + } +} diff --git a/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousApplications.java b/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousApplications.java new file mode 100644 index 0000000000..3a810885f2 --- /dev/null +++ b/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousApplications.java @@ -0,0 +1,66 @@ +/* + * 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.algorithms.miscellaneous; + +import org.neo4j.gds.applications.algorithms.machinery.AlgorithmProcessingTemplateConvenience; +import org.neo4j.gds.applications.algorithms.machinery.MutateNodeProperty; +import org.neo4j.gds.applications.algorithms.machinery.ProgressTrackerCreator; + +public final class MiscellaneousApplications { + private final MiscellaneousEstimationModeBusinessFacade estimation; + private final MiscellaneousMutateModeBusinessFacade mutation; + + private MiscellaneousApplications( + MiscellaneousEstimationModeBusinessFacade estimation, + MiscellaneousMutateModeBusinessFacade mutation + ) { + this.estimation = estimation; + this.mutation = mutation; + } + + public static MiscellaneousApplications create( + AlgorithmProcessingTemplateConvenience algorithmProcessingTemplateConvenience, + ProgressTrackerCreator progressTrackerCreator, + MutateNodeProperty mutateNodeProperty + ) { + var algorithms = new MiscellaneousAlgorithms(progressTrackerCreator); + + var estimation = new MiscellaneousEstimationModeBusinessFacade(); + var mutation = new MiscellaneousMutateModeBusinessFacade( + estimation, + algorithms, + algorithmProcessingTemplateConvenience, + mutateNodeProperty + ); + + return new MiscellaneousApplications( + estimation, + mutation + ); + } + + public MiscellaneousEstimationModeBusinessFacade estimate() { + return estimation; + } + + public MiscellaneousMutateModeBusinessFacade mutate() { + return mutation; + } +} diff --git a/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousEstimationModeBusinessFacade.java b/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousEstimationModeBusinessFacade.java new file mode 100644 index 0000000000..9ab77e3e17 --- /dev/null +++ b/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousEstimationModeBusinessFacade.java @@ -0,0 +1,30 @@ +/* + * 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.algorithms.miscellaneous; + +import org.neo4j.gds.mem.MemoryEstimation; +import org.neo4j.gds.scaleproperties.ScalePropertiesBaseConfig; +import org.neo4j.gds.scaleproperties.ScalePropertiesMemoryEstimateDefinition; + +public class MiscellaneousEstimationModeBusinessFacade { + public MemoryEstimation scaleProperties(ScalePropertiesBaseConfig configuration) { + return new ScalePropertiesMemoryEstimateDefinition(configuration.nodeProperties()).memoryEstimation(); + } +} diff --git a/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousMutateModeBusinessFacade.java b/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousMutateModeBusinessFacade.java new file mode 100644 index 0000000000..ff311606e1 --- /dev/null +++ b/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/MiscellaneousMutateModeBusinessFacade.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.applications.algorithms.miscellaneous; + +import org.neo4j.gds.api.GraphName; +import org.neo4j.gds.applications.algorithms.machinery.AlgorithmProcessingTemplateConvenience; +import org.neo4j.gds.applications.algorithms.machinery.MutateNodeProperty; +import org.neo4j.gds.applications.algorithms.machinery.ResultBuilder; +import org.neo4j.gds.applications.algorithms.metadata.NodePropertiesWritten; +import org.neo4j.gds.scaleproperties.ScalePropertiesMutateConfig; +import org.neo4j.gds.scaleproperties.ScalePropertiesResult; + +import static org.neo4j.gds.applications.algorithms.metadata.LabelForProgressTracking.ScaleProperties; + +public class MiscellaneousMutateModeBusinessFacade { + private final MiscellaneousEstimationModeBusinessFacade estimation; + private final MiscellaneousAlgorithms algorithms; + private final AlgorithmProcessingTemplateConvenience algorithmProcessingTemplateConvenience; + private final MutateNodeProperty mutateNodeProperty; + + MiscellaneousMutateModeBusinessFacade( + MiscellaneousEstimationModeBusinessFacade estimation, + MiscellaneousAlgorithms algorithms, + AlgorithmProcessingTemplateConvenience algorithmProcessingTemplateConvenience, + MutateNodeProperty mutateNodeProperty + ) { + this.estimation = estimation; + this.algorithms = algorithms; + this.algorithmProcessingTemplateConvenience = algorithmProcessingTemplateConvenience; + this.mutateNodeProperty = mutateNodeProperty; + } + + public RESULT scaleProperties( + GraphName graphName, + ScalePropertiesMutateConfig configuration, + ResultBuilder resultBuilder + ) { + var mutateStep = new ScalePropertiesMutateStep(mutateNodeProperty, configuration); + + return algorithmProcessingTemplateConvenience.processRegularAlgorithmInMutateOrWriteMode( + graphName, + configuration, + ScaleProperties, + () -> estimation.scaleProperties(configuration), + graph -> algorithms.scaleProperties(graph, configuration), + mutateStep, + resultBuilder + ); + } +} diff --git a/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/ScalePropertiesMutateStep.java b/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/ScalePropertiesMutateStep.java new file mode 100644 index 0000000000..db1d29f2af --- /dev/null +++ b/applications/algorithms/miscellaneous-algorithms/src/main/java/org/neo4j/gds/applications/algorithms/miscellaneous/ScalePropertiesMutateStep.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.applications.algorithms.miscellaneous; + +import org.neo4j.gds.algorithms.misc.ScaledPropertiesNodePropertyValues; +import org.neo4j.gds.api.Graph; +import org.neo4j.gds.api.GraphStore; +import org.neo4j.gds.api.ResultStore; +import org.neo4j.gds.applications.algorithms.machinery.MutateNodeProperty; +import org.neo4j.gds.applications.algorithms.machinery.MutateOrWriteStep; +import org.neo4j.gds.applications.algorithms.metadata.NodePropertiesWritten; +import org.neo4j.gds.core.utils.progress.JobId; +import org.neo4j.gds.scaleproperties.ScalePropertiesMutateConfig; +import org.neo4j.gds.scaleproperties.ScalePropertiesResult; + +class ScalePropertiesMutateStep implements MutateOrWriteStep { + private final MutateNodeProperty mutateNodeProperty; + private final ScalePropertiesMutateConfig configuration; + + ScalePropertiesMutateStep(MutateNodeProperty mutateNodeProperty, ScalePropertiesMutateConfig configuration) { + this.mutateNodeProperty = mutateNodeProperty; + this.configuration = configuration; + } + + @Override + public NodePropertiesWritten execute( + Graph graph, + GraphStore graphStore, + ResultStore resultStore, + ScalePropertiesResult result, + JobId jobId + ) { + var nodePropertyValues = new ScaledPropertiesNodePropertyValues( + graph.nodeCount(), + result.scaledProperties() + ); + return mutateNodeProperty.mutateNodeProperties( + graph, + graphStore, + configuration, + nodePropertyValues + ); + } +} diff --git a/applications/facade/build.gradle b/applications/facade/build.gradle index 3dac570eb8..21600105b7 100644 --- a/applications/facade/build.gradle +++ b/applications/facade/build.gradle @@ -16,6 +16,7 @@ dependencies { implementation project(":memory-estimation") implementation project(":metrics-api") implementation project(":model-catalog-api") + implementation project(":miscellaneous-algorithms") implementation project(":node-embedding-algorithms") implementation project(":path-finding-algorithms") implementation project(":progress-tracking") diff --git a/applications/facade/src/main/java/org/neo4j/gds/applications/ApplicationsFacade.java b/applications/facade/src/main/java/org/neo4j/gds/applications/ApplicationsFacade.java index 6a472aa6c6..d3dff91b3b 100644 --- a/applications/facade/src/main/java/org/neo4j/gds/applications/ApplicationsFacade.java +++ b/applications/facade/src/main/java/org/neo4j/gds/applications/ApplicationsFacade.java @@ -31,6 +31,7 @@ import org.neo4j.gds.applications.algorithms.machinery.ProgressTrackerCreator; import org.neo4j.gds.applications.algorithms.machinery.RequestScopedDependencies; import org.neo4j.gds.applications.algorithms.machinery.WriteContext; +import org.neo4j.gds.applications.algorithms.miscellaneous.MiscellaneousApplications; import org.neo4j.gds.applications.graphstorecatalog.CatalogBusinessFacade; import org.neo4j.gds.applications.graphstorecatalog.DefaultCatalogBusinessFacade; import org.neo4j.gds.core.loading.GraphStoreCatalogService; @@ -54,6 +55,7 @@ public final class ApplicationsFacade { private final CatalogBusinessFacade catalogBusinessFacade; private final CentralityApplications centralityApplications; private final CommunityApplications communityApplications; + private final MiscellaneousApplications miscellaneousApplications; private final NodeEmbeddingApplications nodeEmbeddingApplications; private final PathFindingApplications pathFindingApplications; private final SimilarityApplications similarityApplications; @@ -62,6 +64,7 @@ public final class ApplicationsFacade { CatalogBusinessFacade catalogBusinessFacade, CentralityApplications centralityApplications, CommunityApplications communityApplications, + MiscellaneousApplications miscellaneousApplications, NodeEmbeddingApplications nodeEmbeddingApplications, PathFindingApplications pathFindingApplications, SimilarityApplications similarityApplications @@ -69,6 +72,7 @@ public final class ApplicationsFacade { this.catalogBusinessFacade = catalogBusinessFacade; this.centralityApplications = centralityApplications; this.communityApplications = communityApplications; + this.miscellaneousApplications = miscellaneousApplications; this.nodeEmbeddingApplications = nodeEmbeddingApplications; this.pathFindingApplications = pathFindingApplications; this.similarityApplications = similarityApplications; @@ -142,6 +146,12 @@ public static ApplicationsFacade create( mutateNodeProperty ); + var miscellaneousApplications = MiscellaneousApplications.create( + algorithmProcessingTemplateConvenience, + progressTrackerCreator, + mutateNodeProperty + ); + var nodeEmbeddingApplications = NodeEmbeddingApplications.create( log, requestScopedDependencies, @@ -178,6 +188,7 @@ public static ApplicationsFacade create( .with(catalogBusinessFacade) .with(centralityApplications) .with(communityApplications) + .with(miscellaneousApplications) .with(nodeEmbeddingApplications) .with(pathFindingApplications) .with(similarityApplications) @@ -234,6 +245,10 @@ public CommunityApplications community() { return communityApplications; } + public MiscellaneousApplications miscellaneous() { + return miscellaneousApplications; + } + public NodeEmbeddingApplications nodeEmbeddings() { return nodeEmbeddingApplications; } diff --git a/applications/facade/src/main/java/org/neo4j/gds/applications/ApplicationsFacadeBuilder.java b/applications/facade/src/main/java/org/neo4j/gds/applications/ApplicationsFacadeBuilder.java index 701a466da2..558aec54dd 100644 --- a/applications/facade/src/main/java/org/neo4j/gds/applications/ApplicationsFacadeBuilder.java +++ b/applications/facade/src/main/java/org/neo4j/gds/applications/ApplicationsFacadeBuilder.java @@ -19,6 +19,7 @@ */ package org.neo4j.gds.applications; +import org.neo4j.gds.applications.algorithms.miscellaneous.MiscellaneousApplications; import org.neo4j.gds.applications.graphstorecatalog.CatalogBusinessFacade; /** @@ -28,6 +29,7 @@ public class ApplicationsFacadeBuilder { private CatalogBusinessFacade catalogBusinessFacade; private CentralityApplications centralityApplications; private CommunityApplications communityApplications; + private MiscellaneousApplications miscellaneousApplications; private NodeEmbeddingApplications nodeEmbeddingApplications; private PathFindingApplications pathFindingApplications; private SimilarityApplications similarityApplications; @@ -47,6 +49,11 @@ public ApplicationsFacadeBuilder with(CommunityApplications communityApplication return this; } + public ApplicationsFacadeBuilder with(MiscellaneousApplications miscellaneousApplications) { + this.miscellaneousApplications = miscellaneousApplications; + return this; + } + public ApplicationsFacadeBuilder with(NodeEmbeddingApplications nodeEmbeddingApplications) { this.nodeEmbeddingApplications = nodeEmbeddingApplications; return this; @@ -67,6 +74,7 @@ public ApplicationsFacade build() { catalogBusinessFacade, centralityApplications, communityApplications, + miscellaneousApplications, nodeEmbeddingApplications, pathFindingApplications, similarityApplications diff --git a/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/ConfigurationParsersForMutateMode.java b/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/ConfigurationParsersForMutateMode.java index ba0ae6e485..e5915f8414 100644 --- a/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/ConfigurationParsersForMutateMode.java +++ b/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/ConfigurationParsersForMutateMode.java @@ -49,6 +49,7 @@ import org.neo4j.gds.paths.traverse.BfsMutateConfig; import org.neo4j.gds.paths.traverse.DfsMutateConfig; import org.neo4j.gds.paths.yens.config.ShortestPathYensMutateConfig; +import org.neo4j.gds.scaleproperties.ScalePropertiesMutateConfig; import org.neo4j.gds.scc.SccMutateConfig; import org.neo4j.gds.similarity.filteredknn.FilteredKnnMutateConfig; import org.neo4j.gds.similarity.filterednodesim.FilteredNodeSimilarityMutateConfig; @@ -110,6 +111,7 @@ public Function lookup(Algorithm algorithm) { case Node2Vec -> Node2VecMutateConfig::of; case PageRank -> PageRankMutateConfig::of; case RandomWalk -> null; + case ScaleProperties -> ScalePropertiesMutateConfig::of; case SCC -> SccMutateConfig::of; case SingleSourceDijkstra -> AllShortestPathsDijkstraMutateConfig::of; case SpanningTree -> SpanningTreeMutateConfig::of; diff --git a/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/MutateModeAlgorithmLibrary.java b/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/MutateModeAlgorithmLibrary.java index a0fc454aaf..7b272a8f81 100644 --- a/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/MutateModeAlgorithmLibrary.java +++ b/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/MutateModeAlgorithmLibrary.java @@ -97,6 +97,7 @@ static CanonicalProcedureName algorithmToName(Algorithm algorithm) { case Node2Vec -> CanonicalProcedureName.parse("gds.node2vec"); case PageRank -> CanonicalProcedureName.parse("gds.pageRank"); case RandomWalk -> null; + case ScaleProperties -> CanonicalProcedureName.parse("gds.scaleProperties"); case SCC -> CanonicalProcedureName.parse("gds.scc"); case SingleSourceDijkstra -> CanonicalProcedureName.parse("gds.allShortestPaths.dijkstra"); case SpanningTree -> CanonicalProcedureName.parse("gds.spanningTree"); diff --git a/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/StubbyHolder.java b/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/StubbyHolder.java index 9f5e64c2f0..a7fb7b6d0a 100644 --- a/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/StubbyHolder.java +++ b/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/StubbyHolder.java @@ -49,6 +49,7 @@ import org.neo4j.gds.ml.pipeline.stubs.Node2VecStub; import org.neo4j.gds.ml.pipeline.stubs.NodeSimilarityStub; import org.neo4j.gds.ml.pipeline.stubs.PageRankStub; +import org.neo4j.gds.ml.pipeline.stubs.ScalePropertiesStub; import org.neo4j.gds.ml.pipeline.stubs.SccStub; import org.neo4j.gds.ml.pipeline.stubs.SinglePairShortestPathAStarStub; import org.neo4j.gds.ml.pipeline.stubs.SinglePairShortestPathDijkstraStub; @@ -110,6 +111,7 @@ Stub get(Algorithm algorithm) { case Node2Vec -> new Node2VecStub(); case PageRank -> new PageRankStub(); case RandomWalk -> null; + case ScaleProperties -> new ScalePropertiesStub(); case SCC -> new SccStub(); case SingleSourceDijkstra -> new SingleSourceShortestPathDijkstraStub(); case SpanningTree -> new SpanningTreeStub(); diff --git a/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/ValidationService.java b/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/ValidationService.java index cc4ad4b7e4..bbb23d4374 100644 --- a/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/ValidationService.java +++ b/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/ValidationService.java @@ -66,7 +66,7 @@ private void validateAnonymously( Function parser, Map configuration ) { - configurationParser.parse( + configurationParser.parseConfiguration( defaultsConfiguration, limitsConfiguration, Username.EMPTY_USERNAME.username(), diff --git a/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/stubs/ScalePropertiesStub.java b/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/stubs/ScalePropertiesStub.java new file mode 100644 index 0000000000..64ed90c3db --- /dev/null +++ b/pipeline/src/main/java/org/neo4j/gds/ml/pipeline/stubs/ScalePropertiesStub.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.ml.pipeline.stubs; + +import org.neo4j.gds.procedures.algorithms.AlgorithmsProcedureFacade; +import org.neo4j.gds.procedures.algorithms.miscellaneous.ScalePropertiesMutateResult; +import org.neo4j.gds.procedures.algorithms.stubs.MutateStub; +import org.neo4j.gds.scaleproperties.ScalePropertiesMutateConfig; + +public class ScalePropertiesStub extends AbstractStub { + protected MutateStub stub(AlgorithmsProcedureFacade facade) { + return facade.miscellaneous().scalePropertiesMutateStub(); + } +} diff --git a/proc/machine-learning/src/test/java/org/neo4j/gds/ml/pipeline/node/classification/predict/NodeClassificationPredictPipelineExecutorTest.java b/proc/machine-learning/src/test/java/org/neo4j/gds/ml/pipeline/node/classification/predict/NodeClassificationPredictPipelineExecutorTest.java index 03f31f9e12..d26b993c27 100644 --- a/proc/machine-learning/src/test/java/org/neo4j/gds/ml/pipeline/node/classification/predict/NodeClassificationPredictPipelineExecutorTest.java +++ b/proc/machine-learning/src/test/java/org/neo4j/gds/ml/pipeline/node/classification/predict/NodeClassificationPredictPipelineExecutorTest.java @@ -452,7 +452,7 @@ private static AlgorithmsProcedureFacade createAlgorithmsProcedureFacade() { null, null ); - return new AlgorithmsProcedureFacade(centralityProcedureFacade, null, null, null, null); + return new AlgorithmsProcedureFacade(centralityProcedureFacade, null, null, null, null, null); } diff --git a/proc/misc/src/main/java/org/neo4j/gds/scaling/ScalePropertiesMutateProc.java b/proc/misc/src/main/java/org/neo4j/gds/scaling/ScalePropertiesMutateProc.java index 5be34a1bdb..b95a3261f7 100644 --- a/proc/misc/src/main/java/org/neo4j/gds/scaling/ScalePropertiesMutateProc.java +++ b/proc/misc/src/main/java/org/neo4j/gds/scaling/ScalePropertiesMutateProc.java @@ -19,9 +19,9 @@ */ package org.neo4j.gds.scaling; -import org.neo4j.gds.procedures.GraphDataScienceProcedures; -import org.neo4j.gds.procedures.misc.scaleproperties.ScalePropertiesMutateResult; import org.neo4j.gds.applications.algorithms.machinery.MemoryEstimateResult; +import org.neo4j.gds.procedures.GraphDataScienceProcedures; +import org.neo4j.gds.procedures.algorithms.miscellaneous.ScalePropertiesMutateResult; import org.neo4j.procedure.Context; import org.neo4j.procedure.Description; import org.neo4j.procedure.Internal; @@ -45,7 +45,7 @@ public Stream mutate( @Name(value = "graphName") String graphName, @Name(value = "configuration", defaultValue = "{}") Map configuration ) { - return facade.miscellaneousAlgorithms().scalePropertiesMutate(graphName, configuration); + return facade.algorithms().miscellaneous().scalePropertiesMutateStub().execute(graphName, configuration); } @Procedure(value = "gds.scaleProperties.mutate.estimate", mode = READ) @@ -54,7 +54,7 @@ public Stream estimate( @Name(value = "graphNameOrConfiguration") Object graphName, @Name(value = "algoConfiguration") Map configuration ) { - return facade.miscellaneousAlgorithms().scalePropertiesMutateEstimate(graphName, configuration); + return facade.algorithms().miscellaneous().scalePropertiesMutateStub().estimate(graphName, configuration); } @Internal diff --git a/proc/misc/src/main/java/org/neo4j/gds/scaling/ScalePropertiesMutateSpec.java b/proc/misc/src/main/java/org/neo4j/gds/scaling/ScalePropertiesMutateSpec.java index 2613af6e2e..0b10301437 100644 --- a/proc/misc/src/main/java/org/neo4j/gds/scaling/ScalePropertiesMutateSpec.java +++ b/proc/misc/src/main/java/org/neo4j/gds/scaling/ScalePropertiesMutateSpec.java @@ -27,7 +27,7 @@ import org.neo4j.gds.executor.ExecutionContext; import org.neo4j.gds.executor.GdsCallable; import org.neo4j.gds.procedures.algorithms.configuration.NewConfigFunction; -import org.neo4j.gds.procedures.misc.scaleproperties.ScalePropertiesMutateResult; +import org.neo4j.gds.procedures.algorithms.miscellaneous.ScalePropertiesMutateResult; import org.neo4j.gds.result.AbstractResultBuilder; import org.neo4j.gds.scaleproperties.ScaleProperties; import org.neo4j.gds.scaleproperties.ScalePropertiesFactory; diff --git a/procedures/algorithms-facade/build.gradle b/procedures/algorithms-facade/build.gradle index 1395049f6d..0693e7a218 100644 --- a/procedures/algorithms-facade/build.gradle +++ b/procedures/algorithms-facade/build.gradle @@ -23,6 +23,7 @@ dependencies { implementation project(':defaults-and-limits-configuration') implementation project(':memory-estimation') implementation project(':memory-usage') + implementation project(':miscellaneous-algorithms') implementation project(':ml-algo') implementation project(':ml-core') implementation project(':model-catalog-api') diff --git a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/AlgorithmsProcedureFacade.java b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/AlgorithmsProcedureFacade.java index 26e8bfdad7..27c15a4e54 100644 --- a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/AlgorithmsProcedureFacade.java +++ b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/AlgorithmsProcedureFacade.java @@ -22,6 +22,7 @@ import org.neo4j.gds.procedures.algorithms.centrality.CentralityProcedureFacade; import org.neo4j.gds.procedures.algorithms.community.CommunityProcedureFacade; import org.neo4j.gds.procedures.algorithms.embeddings.NodeEmbeddingsProcedureFacade; +import org.neo4j.gds.procedures.algorithms.miscellaneous.MiscellaneousProcedureFacade; import org.neo4j.gds.procedures.algorithms.pathfinding.PathFindingProcedureFacade; import org.neo4j.gds.procedures.algorithms.similarity.SimilarityProcedureFacade; @@ -32,6 +33,7 @@ public class AlgorithmsProcedureFacade { private final CentralityProcedureFacade centralityProcedureFacade; private final CommunityProcedureFacade communityProcedureFacade; + private final MiscellaneousProcedureFacade miscellaneousProcedureFacade; private final NodeEmbeddingsProcedureFacade nodeEmbeddingsProcedureFacade; private final PathFindingProcedureFacade pathFindingProcedureFacade; private final SimilarityProcedureFacade similarityProcedureFacade; @@ -39,12 +41,14 @@ public class AlgorithmsProcedureFacade { public AlgorithmsProcedureFacade( CentralityProcedureFacade centralityProcedureFacade, CommunityProcedureFacade communityProcedureFacade, + MiscellaneousProcedureFacade miscellaneousProcedureFacade, NodeEmbeddingsProcedureFacade nodeEmbeddingsProcedureFacade, PathFindingProcedureFacade pathFindingProcedureFacade, SimilarityProcedureFacade similarityProcedureFacade ) { this.centralityProcedureFacade = centralityProcedureFacade; this.communityProcedureFacade = communityProcedureFacade; + this.miscellaneousProcedureFacade = miscellaneousProcedureFacade; this.nodeEmbeddingsProcedureFacade = nodeEmbeddingsProcedureFacade; this.pathFindingProcedureFacade = pathFindingProcedureFacade; this.similarityProcedureFacade = similarityProcedureFacade; @@ -58,6 +62,10 @@ public CommunityProcedureFacade community() { return communityProcedureFacade; } + public MiscellaneousProcedureFacade miscellaneous() { + return miscellaneousProcedureFacade; + } + public NodeEmbeddingsProcedureFacade nodeEmbeddings() { return nodeEmbeddingsProcedureFacade; } diff --git a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationCreator.java b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationCreator.java index 1ec9d4fa31..7a06a0bd79 100644 --- a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationCreator.java +++ b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationCreator.java @@ -25,7 +25,7 @@ import org.neo4j.gds.core.CypherMapWrapper; import java.util.Map; -import java.util.function.BiFunction; +import java.util.Optional; import java.util.function.Function; public class ConfigurationCreator { @@ -43,19 +43,20 @@ public ConfigurationCreator( this.user = user; } - - public C createConfiguration( + public CONFIGURATION createConfiguration( Map rawConfiguration, - BiFunction parser + Function parser, + Optional> configurationValidation ) { - return configurationParser.produceConfig(rawConfiguration, parser, user); + return parseAndValidate(rawConfiguration, parser, configurationValidation); } - public C createConfigurationForStream( + public CONFIGURATION createConfigurationForStream( Map rawConfiguration, - BiFunction parser + Function parser, + Optional> configurationValidation ) { - C configuration = createConfiguration(rawConfiguration, parser); + CONFIGURATION configuration = parseAndValidate(rawConfiguration, parser, configurationValidation); // yay, side effects algorithmMetaDataSetter.set(configuration); @@ -63,19 +64,19 @@ public C createConfigurationForStream( return configuration; } - - public C createConfiguration( + private CONFIGURATION parseAndValidate( Map rawConfiguration, - Function parser + Function parser, + Optional> configurationValidation ) { - return createConfiguration(rawConfiguration, (__, cypherMapWrapper) -> parser.apply(cypherMapWrapper)); - } + CONFIGURATION configuration = configurationParser.parseConfiguration( + rawConfiguration, + (__, cypherMapWrapper) -> parser.apply(cypherMapWrapper), + user + ); - public C createConfigurationForStream( - Map rawConfiguration, - Function parser - ) { - return createConfigurationForStream(rawConfiguration, (__, cypherMapWrapper) -> parser.apply(cypherMapWrapper)); + configurationValidation.ifPresent(hook -> hook.onConfigurationParsed(configuration)); + return configuration; } } diff --git a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationParser.java b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationParser.java index a27484711c..7848e4e558 100644 --- a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationParser.java +++ b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationParser.java @@ -31,14 +31,10 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.Map; +import java.util.Set; import java.util.function.BiFunction; public class ConfigurationParser { - public static final ConfigurationParser EMPTY = new ConfigurationParser( - DefaultsConfiguration.Empty, - LimitsConfiguration.Empty - ); - private final DefaultsConfiguration defaults; private final LimitsConfiguration limits; @@ -50,19 +46,18 @@ public ConfigurationParser(DefaultsConfiguration defaults, LimitsConfiguration l /** * Convenient configuration parsing using globally configured defaults and limits */ - public CONFIG produceConfig( + CONFIG parseConfiguration( Map configuration, - BiFunction configCreator, - String username + BiFunction parser, + User user ) { - return parse(defaults, limits, username, configuration, configCreator); + return parseConfiguration(defaults, limits, user.getUsername(), configuration, parser); } /** - * This is effectively static, not using any state. - * But we do not do statics, because that can make testing difficult and gives no real advantage here. + * Configuration parsing using directly configured defaults and limits */ - public CONFIG parse( + public CONFIG parseConfiguration( DefaultsConfiguration defaultsConfiguration, LimitsConfiguration limitsConfiguration, String username, @@ -75,28 +70,13 @@ public CONFIG parse( var configuration = parser.apply(username, cypherMapWrapper); - validateOriginalConfig(rawConfiguration, configuration.configKeys()); + validateOriginalConfiguration(rawConfiguration, configuration.configKeys()); validateLimits(configuration, username, configurationWithDefaultsAdded, limitsConfiguration); return configuration; } - /* - * Non-public things below - */ - - /** - * Overload handling User - */ - CONFIG produceConfig( - Map configuration, - BiFunction configCreator, - User user - ) { - return produceConfig(configuration, configCreator, user.getUsername()); - } - Map applyDefaults( Map configuration, String username, DefaultsConfiguration defaultsConfiguration @@ -104,25 +84,19 @@ Map applyDefaults( return defaultsConfiguration.apply(configuration, username); } - void validateOriginalConfig( - Map configuration, - Collection allowedConfigKeys - ) throws IllegalArgumentException { - Map newConfiguration = new HashMap<>(configuration); - - CypherMapWrapper.create(newConfiguration) - .requireOnlyKeysFrom(allowedConfigKeys); //ensure user has not included any incorrect params - //TODO: no reason creating CyperMapWrapper object for this, we should pull the logic here in a function here. + /** + * @throws java.lang.IllegalArgumentException if user has included any incorrect parameters + */ + void validateOriginalConfiguration(Map configuration, Collection allowedConfigKeys) { + CypherMapWrapper.create(configuration).requireOnlyKeysFrom(allowedConfigKeys); } - void validateLimits( - CONFIG algorithmConfiguration, + void validateLimits( + CONFIGURATION algorithmConfiguration, String username, Map userInputWithDefaults, LimitsConfiguration limitsConfiguration ) throws IllegalArgumentException { - - // handle limits var allowedKeys = new HashSet<>(algorithmConfiguration.configKeys()); var irrelevantInputtedKeys = getIrrelevantInputtedKeys(userInputWithDefaults, allowedKeys); var configurationButWithIrrelevantInputtedKeysRemoved = getConfigurationForLimitValidation( @@ -131,10 +105,9 @@ void validateLimits( ); //remove any useless configuration parameters e.g., sourceNode for Wcc validateLimits(configurationButWithIrrelevantInputtedKeysRemoved, username, limitsConfiguration); - } - private HashSet getIrrelevantInputtedKeys( + private Set getIrrelevantInputtedKeys( Map configuration, Collection allowedKeys ) { @@ -143,7 +116,7 @@ private HashSet getIrrelevantInputtedKeys( return irrelevantInputtedKeys; } - private HashMap getConfigurationForLimitValidation( + private Map getConfigurationForLimitValidation( Map configuration, Collection irrelevantInputtedKeys ) { diff --git a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationValidationHook.java b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationValidationHook.java new file mode 100644 index 0000000000..84e0a4a76d --- /dev/null +++ b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationValidationHook.java @@ -0,0 +1,26 @@ +/* + * 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.procedures.algorithms.configuration; + +import org.neo4j.gds.config.AlgoBaseConfig; + +public interface ConfigurationValidationHook { + void onConfigurationParsed(CONFIGURATION configuration); +} diff --git a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/MiscellaneousProcedureFacade.java b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/MiscellaneousProcedureFacade.java new file mode 100644 index 0000000000..7769dcb256 --- /dev/null +++ b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/MiscellaneousProcedureFacade.java @@ -0,0 +1,51 @@ +/* + * 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.procedures.algorithms.miscellaneous; + +import org.neo4j.gds.api.ProcedureReturnColumns; +import org.neo4j.gds.applications.ApplicationsFacade; +import org.neo4j.gds.procedures.algorithms.miscellaneous.stubs.ScalePropertiesMutateStub; +import org.neo4j.gds.procedures.algorithms.stubs.GenericStub; + +public final class MiscellaneousProcedureFacade { + private final ScalePropertiesMutateStub scalePropertiesMutateStub; + + private MiscellaneousProcedureFacade(ScalePropertiesMutateStub scalePropertiesMutateStub) { + this.scalePropertiesMutateStub = scalePropertiesMutateStub; + } + + public static MiscellaneousProcedureFacade create( + GenericStub genericStub, + ApplicationsFacade applicationsFacade, + ProcedureReturnColumns procedureReturnColumns + ) { + var scalePropertiesMutateStub = new ScalePropertiesMutateStub( + genericStub, + applicationsFacade, + procedureReturnColumns + ); + + return new MiscellaneousProcedureFacade(scalePropertiesMutateStub); + } + + public ScalePropertiesMutateStub scalePropertiesMutateStub() { + return scalePropertiesMutateStub; + } +} diff --git a/procedures/facade/src/main/java/org/neo4j/gds/procedures/misc/scaleproperties/ScalePropertiesMutateResult.java b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/ScalePropertiesMutateResult.java similarity index 80% rename from procedures/facade/src/main/java/org/neo4j/gds/procedures/misc/scaleproperties/ScalePropertiesMutateResult.java rename to procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/ScalePropertiesMutateResult.java index a631f8bb3f..5673cb3be0 100644 --- a/procedures/facade/src/main/java/org/neo4j/gds/procedures/misc/scaleproperties/ScalePropertiesMutateResult.java +++ b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/ScalePropertiesMutateResult.java @@ -17,16 +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.procedures.misc.scaleproperties; +package org.neo4j.gds.procedures.algorithms.miscellaneous; +import org.neo4j.gds.applications.algorithms.machinery.AlgorithmProcessingTimings; import org.neo4j.gds.result.AbstractResultBuilder; import org.neo4j.gds.procedures.algorithms.results.StandardMutateResult; +import java.util.Collections; import java.util.List; import java.util.Map; public final class ScalePropertiesMutateResult extends StandardMutateResult { - public final Map>> scalerStatistics; public final long nodePropertiesWritten; @@ -49,8 +50,21 @@ public ScalePropertiesMutateResult( this.nodePropertiesWritten = nodePropertiesWritten; } - public static class Builder extends AbstractResultBuilder { + public static ScalePropertiesMutateResult emptyFrom( + AlgorithmProcessingTimings timings, + Map configurationMap + ) { + return new ScalePropertiesMutateResult( + Collections.emptyMap(), + timings.preProcessingMillis, + timings.computeMillis, + timings.mutateOrWriteMillis, + 0, + configurationMap + ); + } + public static class Builder extends AbstractResultBuilder { private Map>> scalerStatistics; public Builder withScalerStatistics(Map>> stats) { diff --git a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/stubs/ScalePropertiesConfigurationValidationHook.java b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/stubs/ScalePropertiesConfigurationValidationHook.java new file mode 100644 index 0000000000..4727c33029 --- /dev/null +++ b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/stubs/ScalePropertiesConfigurationValidationHook.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.procedures.algorithms.miscellaneous.stubs; + +import org.neo4j.gds.procedures.algorithms.configuration.ConfigurationValidationHook; +import org.neo4j.gds.scaleproperties.ScalePropertiesBaseConfig; +import org.neo4j.gds.scaling.L1Norm; +import org.neo4j.gds.scaling.L2Norm; +import org.neo4j.gds.scaling.ScalerFactory; + +class ScalePropertiesConfigurationValidationHook implements ConfigurationValidationHook { + private final boolean allowL1L2; + + ScalePropertiesConfigurationValidationHook(boolean allowL1L2) { + this.allowL1L2 = allowL1L2; + } + + @Override + public void onConfigurationParsed(CONFIGURATION configuration) { + if (!allowL1L2) { + var specifiedScaler = configuration.scaler().type(); + if ((specifiedScaler.equals(L1Norm.TYPE) || specifiedScaler.equals(L2Norm.TYPE))) { + ScalerFactory.throwForInvalidScaler(specifiedScaler); + } + } + } +} diff --git a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/stubs/ScalePropertiesMutateStub.java b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/stubs/ScalePropertiesMutateStub.java new file mode 100644 index 0000000000..c3454466a2 --- /dev/null +++ b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/stubs/ScalePropertiesMutateStub.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.procedures.algorithms.miscellaneous.stubs; + +import org.neo4j.gds.api.ProcedureReturnColumns; +import org.neo4j.gds.applications.ApplicationsFacade; +import org.neo4j.gds.applications.algorithms.machinery.MemoryEstimateResult; +import org.neo4j.gds.applications.algorithms.miscellaneous.MiscellaneousEstimationModeBusinessFacade; +import org.neo4j.gds.mem.MemoryEstimation; +import org.neo4j.gds.procedures.algorithms.miscellaneous.ScalePropertiesMutateResult; +import org.neo4j.gds.procedures.algorithms.stubs.GenericStub; +import org.neo4j.gds.procedures.algorithms.stubs.MutateStub; +import org.neo4j.gds.scaleproperties.ScalePropertiesMutateConfig; + +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +public class ScalePropertiesMutateStub implements MutateStub { + private final GenericStub genericStub; + private final ApplicationsFacade applicationsFacade; + private final ProcedureReturnColumns procedureReturnColumns; + + public ScalePropertiesMutateStub( + GenericStub genericStub, + ApplicationsFacade applicationsFacade, ProcedureReturnColumns procedureReturnColumns + ) { + this.genericStub = genericStub; + this.applicationsFacade = applicationsFacade; + this.procedureReturnColumns = procedureReturnColumns; + } + + @Override + public ScalePropertiesMutateConfig parseConfiguration(Map configuration) { + return genericStub.parseConfiguration(ScalePropertiesMutateConfig::of, configuration); + } + + @Override + public MemoryEstimation getMemoryEstimation(String username, Map rawConfiguration) { + return genericStub.getMemoryEstimation( + username, + rawConfiguration, + ScalePropertiesMutateConfig::of, + configuration -> estimationMode().scaleProperties(configuration) + ); + } + + @Override + public Stream estimate(Object graphNameAsString, Map rawConfiguration) { + return genericStub.estimate( + graphNameAsString, + rawConfiguration, + ScalePropertiesMutateConfig::of, + configuration -> estimationMode().scaleProperties(configuration) + ); + } + + @Override + public Stream execute( + String graphNameAsString, + Map rawConfiguration + ) { + var validationHook = new ScalePropertiesConfigurationValidationHook(false); + + var shouldDisplayScalerStatistics = procedureReturnColumns.contains("scalerStatistics"); + var resultBuilder = new ScalePropertiesResultBuilderForMutateMode(shouldDisplayScalerStatistics); + + return genericStub.executeWithValidation( + graphNameAsString, + rawConfiguration, + ScalePropertiesMutateConfig::of, + Optional.of(validationHook), + applicationsFacade.miscellaneous().mutate()::scaleProperties, + resultBuilder + ); + } + + private MiscellaneousEstimationModeBusinessFacade estimationMode() { + return applicationsFacade.miscellaneous().estimate(); + } +} diff --git a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/stubs/ScalePropertiesResultBuilderForMutateMode.java b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/stubs/ScalePropertiesResultBuilderForMutateMode.java new file mode 100644 index 0000000000..c6cb605ba7 --- /dev/null +++ b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/miscellaneous/stubs/ScalePropertiesResultBuilderForMutateMode.java @@ -0,0 +1,62 @@ +/* + * 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.procedures.algorithms.miscellaneous.stubs; + +import org.neo4j.gds.api.Graph; +import org.neo4j.gds.api.GraphStore; +import org.neo4j.gds.applications.algorithms.machinery.AlgorithmProcessingTimings; +import org.neo4j.gds.applications.algorithms.machinery.ResultBuilder; +import org.neo4j.gds.applications.algorithms.metadata.NodePropertiesWritten; +import org.neo4j.gds.procedures.algorithms.miscellaneous.ScalePropertiesMutateResult; +import org.neo4j.gds.scaleproperties.ScalePropertiesMutateConfig; +import org.neo4j.gds.scaleproperties.ScalePropertiesResult; + +import java.util.Optional; + +class ScalePropertiesResultBuilderForMutateMode implements ResultBuilder { + private final boolean shouldDisplayScalerStatistics; + + ScalePropertiesResultBuilderForMutateMode(boolean shouldDisplayScalerStatistics) { + this.shouldDisplayScalerStatistics = shouldDisplayScalerStatistics; + } + + @Override + public ScalePropertiesMutateResult build( + Graph graph, + GraphStore graphStore, + ScalePropertiesMutateConfig configuration, + Optional result, + AlgorithmProcessingTimings timings, + Optional metadata + ) { + if (result.isEmpty()) return ScalePropertiesMutateResult.emptyFrom(timings, configuration.toMap()); + + var scalePropertiesResult = result.get(); + + return new ScalePropertiesMutateResult( + shouldDisplayScalerStatistics ? scalePropertiesResult.scalerStatistics() : null, + timings.preProcessingMillis, + timings.computeMillis, + timings.mutateOrWriteMillis, + metadata.orElseThrow().value, + configuration.toMap() + ); + } +} diff --git a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/runners/DefaultAlgorithmExecutionScaffolding.java b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/runners/DefaultAlgorithmExecutionScaffolding.java index 1e8b832799..8521f407a5 100644 --- a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/runners/DefaultAlgorithmExecutionScaffolding.java +++ b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/runners/DefaultAlgorithmExecutionScaffolding.java @@ -27,6 +27,7 @@ import org.neo4j.gds.procedures.algorithms.configuration.ConfigurationCreator; import java.util.Map; +import java.util.Optional; import java.util.function.Function; public class DefaultAlgorithmExecutionScaffolding implements AlgorithmExecutionScaffolding { @@ -44,7 +45,11 @@ public resultBuilder ) { var graphName = GraphName.parse(graphNameAsString); - var configuration = configurationCreator.createConfiguration(rawConfiguration, configurationSupplier); + var configuration = configurationCreator.createConfiguration( + rawConfiguration, + configurationSupplier, + Optional.empty() + ); return algorithm.compute(graphName, configuration, resultBuilder); } diff --git a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/runners/EstimationModeRunner.java b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/runners/EstimationModeRunner.java index f2d51bb9ef..089c0fb4f4 100644 --- a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/runners/EstimationModeRunner.java +++ b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/runners/EstimationModeRunner.java @@ -25,6 +25,7 @@ import org.neo4j.gds.procedures.algorithms.configuration.ConfigurationCreator; import java.util.Map; +import java.util.Optional; import java.util.function.Function; public class EstimationModeRunner { @@ -41,7 +42,8 @@ public MemoryEstimateResult runEstimation ) { var configuration = configurationCreator.createConfiguration( algorithmConfiguration, - configurationParser + configurationParser, + Optional.empty() ); return supplier.apply(configuration); diff --git a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/stubs/GenericStub.java b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/stubs/GenericStub.java index 3b2f4bb1be..2a6f3d145b 100644 --- a/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/stubs/GenericStub.java +++ b/procedures/algorithms-facade/src/main/java/org/neo4j/gds/procedures/algorithms/stubs/GenericStub.java @@ -35,8 +35,10 @@ import org.neo4j.gds.procedures.algorithms.AlgorithmHandle; import org.neo4j.gds.procedures.algorithms.configuration.ConfigurationCreator; import org.neo4j.gds.procedures.algorithms.configuration.ConfigurationParser; +import org.neo4j.gds.procedures.algorithms.configuration.ConfigurationValidationHook; import java.util.Map; +import java.util.Optional; import java.util.function.Function; import java.util.stream.Stream; @@ -99,7 +101,7 @@ public CONFIGURATION parseConfiguration( Function parser, Map configuration ) { - return configurationParser.parse( + return configurationParser.parseConfiguration( defaultsConfiguration, limitsConfiguration, user.getUsername(), @@ -117,7 +119,7 @@ public MemoryEstimation getMemoryEstimati Function parser, Function estimator ) { - var configuration = configurationParser.parse( + var configuration = configurationParser.parseConfiguration( DefaultsConfiguration.Empty, LimitsConfiguration.Empty, username, @@ -151,7 +153,7 @@ public Stream estim } /** - * @see org.neo4j.gds.procedures.algorithms.stubs.MutateStub#execute(String, java.util.Map) + * NB: no configuration validation hook */ public Stream execute( String graphNameAsString, @@ -159,9 +161,30 @@ public parser, AlgorithmHandle executor, ResultBuilder resultBuilder + ) { + return executeWithValidation( + graphNameAsString, + rawConfiguration, + parser, + Optional.empty(), // no extra configuration validation + executor, + resultBuilder + ); + } + + /** + * @see org.neo4j.gds.procedures.algorithms.stubs.MutateStub#execute(String, java.util.Map) + */ + public Stream executeWithValidation( + String graphNameAsString, + Map rawConfiguration, + Function parser, + Optional> configurationValidation, + AlgorithmHandle executor, + ResultBuilder resultBuilder ) { var graphName = GraphName.parse(graphNameAsString); - var configuration = configurationCreator.createConfiguration(rawConfiguration, parser); + var configuration = configurationCreator.createConfiguration(rawConfiguration, parser, configurationValidation); var result = executor.compute(graphName, configuration, resultBuilder); diff --git a/procedures/algorithms-facade/src/test/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationParserTest.java b/procedures/algorithms-facade/src/test/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationParserTest.java index 15348c280d..3ea3263373 100644 --- a/procedures/algorithms-facade/src/test/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationParserTest.java +++ b/procedures/algorithms-facade/src/test/java/org/neo4j/gds/procedures/algorithms/configuration/ConfigurationParserTest.java @@ -20,6 +20,7 @@ package org.neo4j.gds.procedures.algorithms.configuration; import org.junit.jupiter.api.Test; +import org.neo4j.gds.api.User; import org.neo4j.gds.config.AlgoBaseConfig; import org.neo4j.gds.configuration.Default; import org.neo4j.gds.configuration.DefaultsConfiguration; @@ -114,7 +115,7 @@ void shouldComplainAboutArbitraryFields() { Map userInput = Map.of("concurrency", 8L, "sudo", false); - assertThatException().isThrownBy(() -> configurationParser.validateOriginalConfig( + assertThatException().isThrownBy(() -> configurationParser.validateOriginalConfiguration( userInput, algorithmConfigurationMock.configKeys() )).withMessageContaining("Unexpected configuration key: sudo"); @@ -123,7 +124,6 @@ void shouldComplainAboutArbitraryFields() { @Test void shouldParseConfigSuccessfully() { - var configurationParser = new ConfigurationParser( new DefaultsConfiguration( Map.of("concurrency", new Default(3L), "bar", new Default(false)), @@ -131,9 +131,9 @@ void shouldParseConfigSuccessfully() { ), new LimitsConfiguration(Map.of("concurrency", LimitFactory.create(4L)), Collections.emptyMap()) ); - BiFunction configCreator = (__, cypherMapWrapper) -> WccStreamConfig.of( + BiFunction parser = (__, cypherMapWrapper) -> WccStreamConfig.of( cypherMapWrapper); - assertThat(configurationParser.produceConfig(Map.of(), configCreator, "foo").concurrency()).isEqualTo(new Concurrency(3)); + assertThat(configurationParser.parseConfiguration(Map.of(), parser, new User("foo", false)).concurrency()).isEqualTo(new Concurrency(3)); } @@ -147,13 +147,13 @@ void shouldComplainOfIrrelevantFields() { ), new LimitsConfiguration(Map.of("concurrency", LimitFactory.create(4L)), Collections.emptyMap()) ); - BiFunction configCreator = (__, cypherMapWrapper) -> WccStreamConfig.of( + BiFunction parser = (__, cypherMapWrapper) -> WccStreamConfig.of( cypherMapWrapper); - assertThatException().isThrownBy(() -> configurationParser.produceConfig( + assertThatException().isThrownBy(() -> configurationParser.parseConfiguration( Map.of("conurrency", 20), - configCreator, - "bogus" + parser, + new User("bogus", false) )).withMessageContaining("Unexpected configuration key: conurrency (Did you mean [concurrency]?)"); } @@ -171,9 +171,9 @@ void shouldTakeIntoConsiderationPersonalLimitsAndPersonalDefaults() { Map.of("bogus", Map.of("concurrency", LimitFactory.create(1L))) ) ); - BiFunction configCreator = (__, cypherMapWrapper) -> WccStreamConfig.of( + BiFunction parser = (__, cypherMapWrapper) -> WccStreamConfig.of( cypherMapWrapper); - assertThat(configurationParser.produceConfig(Map.of(), configCreator, "bogus").concurrency()).isEqualTo(new Concurrency(1)); + assertThat(configurationParser.parseConfiguration(Map.of(), parser, new User("bogus", false)).concurrency()).isEqualTo(new Concurrency(1)); } @@ -190,9 +190,9 @@ void shouldTakeIntoConsiderationPersonalLimitsAndPersonalDefaultsAndThrow() { Map.of("bogus", Map.of("concurrency", LimitFactory.create(1L))) ) ); - BiFunction configCreator = (__, cypherMapWrapper) -> WccStreamConfig.of( + BiFunction parser = (__, cypherMapWrapper) -> WccStreamConfig.of( cypherMapWrapper); - assertThatException().isThrownBy(() -> configurationParser.produceConfig(Map.of(), configCreator, "bogus")) + assertThatException().isThrownBy(() -> configurationParser.parseConfiguration(Map.of(), parser, new User("bogus", false))) .withMessage("Configuration parameter 'concurrency' with value '3' exceeds it's limit of '1'"); } diff --git a/procedures/facade/build.gradle b/procedures/facade/build.gradle index b3a6c22f65..0cd2273b95 100644 --- a/procedures/facade/build.gradle +++ b/procedures/facade/build.gradle @@ -35,6 +35,7 @@ dependencies { implementation project(':memory-estimation') implementation project(':memory-usage') implementation project(':metrics-api') + implementation project(':miscellaneous-algorithms') implementation project(':ml-algo') implementation project(':ml-api') implementation project(':ml-core') diff --git a/procedures/facade/src/main/java/org/neo4j/gds/procedures/AlgorithmProcedureFacadeBuilder.java b/procedures/facade/src/main/java/org/neo4j/gds/procedures/AlgorithmProcedureFacadeBuilder.java index 69208c432b..75e8d1abc3 100644 --- a/procedures/facade/src/main/java/org/neo4j/gds/procedures/AlgorithmProcedureFacadeBuilder.java +++ b/procedures/facade/src/main/java/org/neo4j/gds/procedures/AlgorithmProcedureFacadeBuilder.java @@ -38,6 +38,7 @@ import org.neo4j.gds.procedures.algorithms.community.CommunityProcedureFacade; import org.neo4j.gds.procedures.algorithms.configuration.ConfigurationCreator; import org.neo4j.gds.procedures.algorithms.embeddings.NodeEmbeddingsProcedureFacade; +import org.neo4j.gds.procedures.algorithms.miscellaneous.MiscellaneousProcedureFacade; import org.neo4j.gds.procedures.algorithms.pathfinding.PathFindingProcedureFacade; import org.neo4j.gds.procedures.algorithms.runners.AlgorithmExecutionScaffolding; import org.neo4j.gds.procedures.algorithms.runners.EstimationModeRunner; @@ -116,7 +117,11 @@ CommunityProcedureFacade createCommunityProcedureFacade() { ); } - MiscAlgorithmsProcedureFacade createMiscellaneousProcedureFacade() { + MiscellaneousProcedureFacade createMiscellaneousProcedureFacade() { + return MiscellaneousProcedureFacade.create(genericStub, applicationsFacade, procedureReturnColumns); + } + + MiscAlgorithmsProcedureFacade createMiscellaneousAlgorithmsProcedureFacade() { // algorithm facade var miscAlgorithmsFacade = new MiscAlgorithmsFacade(algorithmRunner); diff --git a/procedures/facade/src/main/java/org/neo4j/gds/procedures/GraphDataScienceProcedures.java b/procedures/facade/src/main/java/org/neo4j/gds/procedures/GraphDataScienceProcedures.java index 5652101c3f..5111a6002a 100644 --- a/procedures/facade/src/main/java/org/neo4j/gds/procedures/GraphDataScienceProcedures.java +++ b/procedures/facade/src/main/java/org/neo4j/gds/procedures/GraphDataScienceProcedures.java @@ -145,7 +145,8 @@ public static GraphDataScienceProcedures create( var centralityProcedureFacade = algorithmProcedureFacadeBuilder.createCentralityProcedureFacade(); var communityProcedureFacade = algorithmProcedureFacadeBuilder.createCommunityProcedureFacade(); - var miscAlgorithmsProcedureFacade = algorithmProcedureFacadeBuilder.createMiscellaneousProcedureFacade(); + var miscellaneousProcedureFacade = algorithmProcedureFacadeBuilder.createMiscellaneousProcedureFacade(); + var miscAlgorithmsProcedureFacade = algorithmProcedureFacadeBuilder.createMiscellaneousAlgorithmsProcedureFacade(); var nodeEmbeddingsProcedureFacade = algorithmProcedureFacadeBuilder.createNodeEmbeddingsProcedureFacade(); var pathFindingProcedureFacade = algorithmProcedureFacadeBuilder.createPathFindingProcedureFacade(); var similarityProcedureFacade = algorithmProcedureFacadeBuilder.createSimilarityProcedureFacade(); @@ -156,6 +157,7 @@ public static GraphDataScienceProcedures create( .with(catalogProcedureFacade) .with(centralityProcedureFacade) .with(communityProcedureFacade) + .with(miscellaneousProcedureFacade) .with(miscAlgorithmsProcedureFacade) .with(nodeEmbeddingsProcedureFacade) .with(pathFindingProcedureFacade) diff --git a/procedures/facade/src/main/java/org/neo4j/gds/procedures/GraphDataScienceProceduresBuilder.java b/procedures/facade/src/main/java/org/neo4j/gds/procedures/GraphDataScienceProceduresBuilder.java index 185e488c25..d95b764c32 100644 --- a/procedures/facade/src/main/java/org/neo4j/gds/procedures/GraphDataScienceProceduresBuilder.java +++ b/procedures/facade/src/main/java/org/neo4j/gds/procedures/GraphDataScienceProceduresBuilder.java @@ -25,6 +25,7 @@ import org.neo4j.gds.procedures.algorithms.centrality.CentralityProcedureFacade; import org.neo4j.gds.procedures.algorithms.community.CommunityProcedureFacade; import org.neo4j.gds.procedures.algorithms.embeddings.NodeEmbeddingsProcedureFacade; +import org.neo4j.gds.procedures.algorithms.miscellaneous.MiscellaneousProcedureFacade; import org.neo4j.gds.procedures.algorithms.pathfinding.PathFindingProcedureFacade; import org.neo4j.gds.procedures.algorithms.similarity.SimilarityProcedureFacade; import org.neo4j.gds.procedures.catalog.CatalogProcedureFacade; @@ -42,6 +43,7 @@ public class GraphDataScienceProceduresBuilder { private CentralityProcedureFacade centralityProcedureFacade; private CatalogProcedureFacade catalogProcedureFacade; private CommunityProcedureFacade communityProcedureFacade; + private MiscellaneousProcedureFacade miscellaneousProcedureFacade; private MiscAlgorithmsProcedureFacade miscAlgorithmsProcedureFacade; private NodeEmbeddingsProcedureFacade nodeEmbeddingsProcedureFacade; private PathFindingProcedureFacade pathFindingProcedureFacade; @@ -68,6 +70,11 @@ public GraphDataScienceProceduresBuilder with(CommunityProcedureFacade community return this; } + public GraphDataScienceProceduresBuilder with(MiscellaneousProcedureFacade miscellaneousProcedureFacade) { + this.miscellaneousProcedureFacade = miscellaneousProcedureFacade; + return this; + } + public GraphDataScienceProceduresBuilder with(MiscAlgorithmsProcedureFacade miscAlgorithmsProcedureFacade) { this.miscAlgorithmsProcedureFacade = miscAlgorithmsProcedureFacade; return this; @@ -102,6 +109,7 @@ public GraphDataScienceProcedures build() { var algorithmsProcedureFacade = new AlgorithmsProcedureFacade( centralityProcedureFacade, communityProcedureFacade, + miscellaneousProcedureFacade, nodeEmbeddingsProcedureFacade, pathFindingProcedureFacade, similarityProcedureFacade diff --git a/procedures/facade/src/main/java/org/neo4j/gds/procedures/misc/MiscAlgorithmsProcedureFacade.java b/procedures/facade/src/main/java/org/neo4j/gds/procedures/misc/MiscAlgorithmsProcedureFacade.java index c6f2332271..c21e804d0c 100644 --- a/procedures/facade/src/main/java/org/neo4j/gds/procedures/misc/MiscAlgorithmsProcedureFacade.java +++ b/procedures/facade/src/main/java/org/neo4j/gds/procedures/misc/MiscAlgorithmsProcedureFacade.java @@ -26,7 +26,7 @@ import org.neo4j.gds.algorithms.misc.MiscAlgorithmsEstimateBusinessFacade; import org.neo4j.gds.api.ProcedureReturnColumns; import org.neo4j.gds.procedures.algorithms.configuration.ConfigurationCreator; -import org.neo4j.gds.procedures.misc.scaleproperties.ScalePropertiesMutateResult; +import org.neo4j.gds.procedures.algorithms.miscellaneous.ScalePropertiesMutateResult; import org.neo4j.gds.procedures.misc.scaleproperties.ScalePropertiesStatsResult; import org.neo4j.gds.procedures.misc.scaleproperties.ScalePropertiesStreamResult; import org.neo4j.gds.procedures.misc.scaleproperties.ScalePropertiesWriteResult; @@ -37,6 +37,7 @@ import org.neo4j.gds.scaleproperties.ScalePropertiesWriteConfig; import java.util.Map; +import java.util.Optional; import java.util.stream.Stream; public class MiscAlgorithmsProcedureFacade { @@ -74,7 +75,7 @@ public Stream scalePropertiesStream( Map configuration ){ - var config = configurationCreator.createConfigurationForStream(configuration, ScalePropertiesStreamConfig::of); + var config = configurationCreator.createConfigurationForStream(configuration, ScalePropertiesStreamConfig::of, Optional.empty()); var streamResult = streamBusinessFacade.scaleProperties(graphName, config); return ScalePropertiesComputationResultTransformer.toStreamResult(streamResult); @@ -85,7 +86,7 @@ public Stream alphaScalePropertiesStream( Map configuration ){ - var config = configurationCreator.createConfigurationForStream(configuration, ScalePropertiesStreamConfig::of); + var config = configurationCreator.createConfigurationForStream(configuration, ScalePropertiesStreamConfig::of, Optional.empty()); var streamResult = streamBusinessFacade.alphaScaleProperties(graphName, config); return ScalePropertiesComputationResultTransformer.toStreamResult(streamResult); @@ -95,7 +96,7 @@ public Stream scalePropertiesStreamEstimate( Object graphNameOrConfiguration, Map algoConfiguration ) { - var config = configurationCreator.createConfiguration(algoConfiguration, ScalePropertiesStreamConfig::of); + var config = configurationCreator.createConfiguration(algoConfiguration, ScalePropertiesStreamConfig::of, Optional.empty()); return Stream.of(estimateBusinessFacade.scaleProperties(graphNameOrConfiguration, config)); } @@ -104,7 +105,7 @@ public Stream scalePropertiesStats( Map configuration ) { - var config = configurationCreator.createConfiguration(configuration, ScalePropertiesStatsConfig::of); + var config = configurationCreator.createConfiguration(configuration, ScalePropertiesStatsConfig::of, Optional.empty()); var returnStatistics = procedureReturnColumns.contains("scalerStatistics"); var statsResult = statsBusinessFacade.scaleProperties(graphName, config); return Stream.of(ScalePropertiesComputationResultTransformer.toStatsResult( @@ -118,30 +119,16 @@ public Stream scalePropertiesStatsEstimate( Object graphNameOrConfiguration, Map algoConfiguration ) { - var config = configurationCreator.createConfiguration(algoConfiguration, ScalePropertiesStatsConfig::of); + var config = configurationCreator.createConfiguration(algoConfiguration, ScalePropertiesStatsConfig::of, Optional.empty()); return Stream.of(estimateBusinessFacade.scaleProperties(graphNameOrConfiguration, config)); } - public Stream scalePropertiesMutate( - String graphName, - Map configuration - ) { - - var config = configurationCreator.createConfiguration(configuration, ScalePropertiesMutateConfig::of); - var returnStatistics = procedureReturnColumns.contains("scalerStatistics"); - var mutateResult = mutateBusinessFacade.scaleProperties(graphName, config); - return Stream.of(ScalePropertiesComputationResultTransformer.toMutateResult( - mutateResult, - returnStatistics - )); - } - public Stream alphaScalePropertiesMutate( String graphName, Map configuration ) { - var config = configurationCreator.createConfiguration(configuration, ScalePropertiesMutateConfig::of); + var config = configurationCreator.createConfiguration(configuration, ScalePropertiesMutateConfig::of, Optional.empty()); var returnStatistics = procedureReturnColumns.contains("scalerStatistics"); var mutateResult = mutateBusinessFacade.alphaScaleProperties(graphName, config); return Stream.of(ScalePropertiesComputationResultTransformer.toMutateResult( @@ -150,21 +137,12 @@ public Stream alphaScalePropertiesMutate( )); } - public Stream scalePropertiesMutateEstimate( - Object graphNameOrConfiguration, - Map algoConfiguration - ) { - var config = configurationCreator.createConfiguration(algoConfiguration, ScalePropertiesMutateConfig::of); - return Stream.of(estimateBusinessFacade.scaleProperties(graphNameOrConfiguration, config)); - } - - public Stream scalePropertiesWrite( String graphName, Map configuration ) { - var config = configurationCreator.createConfiguration(configuration, ScalePropertiesWriteConfig::of); + var config = configurationCreator.createConfiguration(configuration, ScalePropertiesWriteConfig::of, Optional.empty()); var returnStatistics = procedureReturnColumns.contains("scalerStatistics"); var writeResult = writeBusinessFacade.scaleProperties(graphName, config); return Stream.of(ScalePropertiesComputationResultTransformer.toWriteResult( @@ -177,7 +155,7 @@ public Stream scalePropertiesWriteEstimate( Object graphNameOrConfiguration, Map algoConfiguration ) { - var config = configurationCreator.createConfiguration(algoConfiguration, ScalePropertiesWriteConfig::of); + var config = configurationCreator.createConfiguration(algoConfiguration, ScalePropertiesWriteConfig::of, Optional.empty()); return Stream.of(estimateBusinessFacade.scaleProperties(graphNameOrConfiguration, config)); } diff --git a/procedures/facade/src/main/java/org/neo4j/gds/procedures/misc/ScalePropertiesComputationResultTransformer.java b/procedures/facade/src/main/java/org/neo4j/gds/procedures/misc/ScalePropertiesComputationResultTransformer.java index 7076669282..8c32a92ed5 100644 --- a/procedures/facade/src/main/java/org/neo4j/gds/procedures/misc/ScalePropertiesComputationResultTransformer.java +++ b/procedures/facade/src/main/java/org/neo4j/gds/procedures/misc/ScalePropertiesComputationResultTransformer.java @@ -25,7 +25,7 @@ import org.neo4j.gds.algorithms.StreamComputationResult; import org.neo4j.gds.algorithms.misc.ScalePropertiesSpecificFields; import org.neo4j.gds.algorithms.misc.ScaledPropertiesNodePropertyValues; -import org.neo4j.gds.procedures.misc.scaleproperties.ScalePropertiesMutateResult; +import org.neo4j.gds.procedures.algorithms.miscellaneous.ScalePropertiesMutateResult; import org.neo4j.gds.procedures.misc.scaleproperties.ScalePropertiesStatsResult; import org.neo4j.gds.procedures.misc.scaleproperties.ScalePropertiesStreamResult; import org.neo4j.gds.procedures.misc.scaleproperties.ScalePropertiesWriteResult; diff --git a/settings.gradle b/settings.gradle index 9171e69c6b..3e2169e356 100644 --- a/settings.gradle +++ b/settings.gradle @@ -107,6 +107,9 @@ project(':centrality-algorithms').projectDir = file('applications/algorithms/cen include('community-algorithms') project(':community-algorithms').projectDir = file('applications/algorithms/community') +include('miscellaneous-algorithms') +project(':miscellaneous-algorithms').projectDir = file('applications/algorithms/miscellaneous-algorithms') + include('node-embedding-algorithms') project(':node-embedding-algorithms').projectDir = file('applications/algorithms/node-embeddings')