From 901f25e8a2148b9e9dc0aaf3e04e237a39630394 Mon Sep 17 00:00:00 2001 From: vga91 Date: Tue, 2 Apr 2024 14:53:08 +0200 Subject: [PATCH] changed procedure signature --- docs/asciidoc/modules/ROOT/nav.adoc | 2 + .../apoc.graph.filterProperties.adoc | 32 ++++++++----- ...apoc.graph.filterPropertiesProcedure.adoc} | 29 ++++++++---- .../ROOT/pages/overview/apoc.graph/index.adoc | 8 ++-- .../index.adoc | 8 ++++ .../partials/generated-documentation/nav.adoc | 4 +- .../main/java/apoc/graph/GraphsExtended.java | 46 ++++++++++++++----- .../java/apoc/graph/GraphsExtendedTest.java | 24 ++++++---- 8 files changed, 107 insertions(+), 46 deletions(-) rename docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/{apoc.virtual.graph.adoc => apoc.graph.filterPropertiesProcedure.adoc} (52%) create mode 100644 docs/asciidoc/modules/ROOT/pages/virtual-nodes-and-relationships/index.adoc diff --git a/docs/asciidoc/modules/ROOT/nav.adoc b/docs/asciidoc/modules/ROOT/nav.adoc index 99df307811..ae2f30addc 100644 --- a/docs/asciidoc/modules/ROOT/nav.adoc +++ b/docs/asciidoc/modules/ROOT/nav.adoc @@ -52,6 +52,8 @@ include::partial$generated-documentation/nav.adoc[] ** xref::cypher-execution/cypher-based-procedures-functions.adoc[] ** xref::cypher-execution/parallel.adoc[] +* xref:virtual-nodes-and-relationships/index.adoc[] + * xref:virtual-resource/index.adoc[] * xref:nlp/index.adoc[] diff --git a/docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/apoc.graph.filterProperties.adoc b/docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/apoc.graph.filterProperties.adoc index abf5b619c2..b237a5c16f 100644 --- a/docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/apoc.graph.filterProperties.adoc +++ b/docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/apoc.graph.filterProperties.adoc @@ -4,25 +4,23 @@ label:function[] label:apoc-extended[] [.emphasis] -apoc.graph.filterProperties(, [propertiesToRemove]) - aggregation function which returns an object {node: [virtual nodes], relationships: [virtual relationships]} without the properties defined in propertiesToRemove +apoc.graph.filterProperties(anyEntityObject, nodePropertiesToRemove, relPropertiesToRemove) - aggregation function which returns an object {node: [virtual nodes], relationships: [virtual relationships]} without the properties defined in nodePropertiesToRemove and relPropertiesToRemove == Signature [source] ---- -apoc.graph.filterProperties(value :: ANY?, type :: LIST OF STRING?) :: ANY +apoc.graph.filterProperties(value :: ANY?, nodePropertiesToRemove :: MAP?, relPropertiesToRemove :: MAP?) :: ANY? ---- -== Input parameters -[.procedures, opts=header] -|=== -| Name | Type | Default -|value|ANY|null -|predicate|STRING|null -|=== +The `nodePropertiesToRemove` and `relPropertiesToRemove` parameter are maps +with key the label/relationship type and value the list of properties to remove from the virtual entities. +The key can also be `_all`, for both of them, which means that the properties of each label/rel-type are filtered. + == Usage Examples + Given the following dataset: [source,cypher] ---- @@ -30,12 +28,12 @@ CREATE (:Person {name: "foo", plotEmbedding: "11"})-[:REL {idRel: 1, posterEmbed (:Person {name: "baz", plotEmbedding: "33"})-[:REL {idRel: 1, posterEmbedding: "66"}]->(:Movie {name: "ajeje", plotEmbedding: "44"}) ---- -we can execute: +we can execute: [source,cypher] ---- MATCH path=(:Person)-[:REL]->(:Movie) -WITH apoc.graph.filterProperties(path, ['plotEmbedding', 'posterEmbedding', 'plot', 'bio']) as graph +WITH apoc.graph.filterProperties(path, {Movie: ['posterEmbedding'], Person: ['posterEmbedding', 'plotEmbedding', 'plot', 'bio']}) as graph RETURN graph.nodes AS nodes, graph.relationships AS relationships ---- @@ -46,4 +44,16 @@ RETURN graph.nodes AS nodes, graph.relationships AS relationships | [(:Person {name: "1"}), (:Movie {name: "bar"}), (:Movie {title: "1",tmdbId: "ajeje"}), (:Person {name: "baz"}), (:Person {name: "uno"}), (:Movie {name: "ajeje"}), (:Movie {title: "1",tmdbId: "due"}), (:Movie {title: "1",tmdbId: "ajeje"}), (:Person {name: "1"}), (:Movie {title: "1",tmdbId: "ajeje"}), (:Person {name: "foo"}), (:Person {name: "1"})] | [[:REL], [:REL {idRel: 1}], [:REL {idRel: 1}], [:REL], [:REL], [:REL]]│ |=== +or: + +[source,cypher] +---- +MATCH path=(:Person)-[:REL]->(:Movie) +WITH apoc.graph.filterProperties(path, {_all: ['plotEmbedding', 'posterEmbedding', 'plot', 'bio']}) as graph +RETURN graph.nodes AS nodes, graph.relationships AS relationships +---- + +with the same result as above. + + diff --git a/docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/apoc.virtual.graph.adoc b/docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/apoc.graph.filterPropertiesProcedure.adoc similarity index 52% rename from docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/apoc.virtual.graph.adoc rename to docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/apoc.graph.filterPropertiesProcedure.adoc index 68df3b5755..b39502b8d6 100644 --- a/docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/apoc.virtual.graph.adoc +++ b/docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/apoc.graph.filterPropertiesProcedure.adoc @@ -1,20 +1,16 @@ -//// -This file is generated by DocsTest, so don't change it! -//// - -= apoc.virtual.graph -:description: This section contains reference documentation for the apoc.virtual.graph procedure. += apoc.graph.filterProperties +:description: This section contains reference documentation for the apoc.graph.filterProperties procedure. label:procedure[] label:apoc-extended[] [.emphasis] -CALL apoc.virtual.graph(, [propertiesToRemove]) YIELD nodes, relationships - returns a set of virtual nodes and relationships without the properties defined in propertiesToRemove +CALL apoc.graph.filterProperties(anyEntityObject, nodePropertiesToRemove, relPropertiesToRemove) YIELD nodes, relationships - returns a set of virtual nodes and relationships without the properties defined in nodePropertiesToRemove and relPropertiesToRemove == Signature [source] ---- -apoc.dv.catalog.list(value :: ANY?, type :: LIST OF STRING?) :: (nodes :: LIST OF NODE?, relationships :: LIST OF RELATIONSHIP?) +apoc.graph.filterProperties(value :: ANY?, nodePropertiesToRemove = {} :: MAP? , relPropertiesToRemove = {} :: MAP?) :: ANY? ---- == Output parameters @@ -25,6 +21,10 @@ apoc.dv.catalog.list(value :: ANY?, type :: LIST OF STRING?) :: (nodes :: LIST O |relationships|LIST OF RELATIONSHIP? |=== +The `nodePropertiesToRemove` and `relPropertiesToRemove` parameter are maps +with key the label/relationship type and value the list of properties to remove from the virtual entities. +The key can also be `_all`, for both of them, which means that the properties of each label/rel-type are filtered. + == Usage examples Given the following dataset: @@ -40,7 +40,7 @@ we can execute: ---- MATCH path=(:Person)-[:REL]->(:Movie) WITH collect(path) AS paths -CALL apoc.virtual.graph(paths, ['plotEmbedding', 'posterEmbedding', 'plot', 'bio']) +CALL apoc.graph.filterProperties(paths, {Movie: ['posterEmbedding'], Person: ['posterEmbedding', 'plotEmbedding', 'plot', 'bio']}) YIELD nodes, relationships RETURN nodes, relationships ---- @@ -52,3 +52,14 @@ RETURN nodes, relationships | [(:Person {name: "1"}), (:Movie {name: "bar"}), (:Movie {title: "1",tmdbId: "ajeje"}), (:Person {name: "baz"}), (:Person {name: "uno"}), (:Movie {name: "ajeje"}), (:Movie {title: "1",tmdbId: "due"}), (:Movie {title: "1",tmdbId: "ajeje"}), (:Person {name: "1"}), (:Movie {title: "1",tmdbId: "ajeje"}), (:Person {name: "foo"}), (:Person {name: "1"})] | [[:REL], [:REL {idRel: 1}], [:REL {idRel: 1}], [:REL], [:REL], [:REL]]│ |=== +or: +[source,cypher] +---- +MATCH path=(:Person)-[:REL]->(:Movie) +WITH collect(path) AS paths +CALL apoc.graph.filterProperties(paths, {_all: ['plotEmbedding', 'posterEmbedding', 'plot', 'bio']}) +YIELD nodes, relationships +RETURN nodes, relationships +---- + +with the same result as above. diff --git a/docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/index.adoc b/docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/index.adoc index 45093b89dc..d34aa4a243 100644 --- a/docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/index.adoc +++ b/docs/asciidoc/modules/ROOT/pages/overview/apoc.graph/index.adoc @@ -4,12 +4,12 @@ [.procedures, opts=header, cols='5a,1a'] |=== | Qualified Name | Type -|xref::overview/apoc.graph/apoc.virtual.graph.adoc[apoc.virtual.graph icon:book[]] +|xref::overview/apoc.graph/apoc.graph.filterProperties.adoc[apoc.graph.filterProperties icon:book[]] -CALL apoc.virtual.graph(, [propertiesToRemove]) YIELD nodes, relationships - returns a set of virtual nodes and relationships without the properties defined in propertiesToRemove +CALL apoc.graph.filterProperties(anyEntityObject, nodePropertiesToRemove, relPropertiesToRemove) YIELD nodes, relationships - returns a set of virtual nodes and relationships without the properties defined in nodePropertiesToRemove and relPropertiesToRemove |label:procedure[] -|xref::overview/apoc.graph/apoc.graph.filterProperties.adoc[apoc.graph.filterProperties icon:book[]] +|xref::overview/apoc.graph/apoc.graph.filterPropertiesProcedure.adoc[apoc.graph.filterProperties icon:book[]] -apoc.graph.filterProperties(, [propertiesToRemove]) - aggregation function which returns an object {node: [virtual nodes], relationships: [virtual relationships]} without the properties defined in propertiesToRemove +apoc.graph.filterProperties(anyEntityObject, nodePropertiesToRemove, relPropertiesToRemove) - aggregation function which returns an object {node: [virtual nodes], relationships: [virtual relationships]} without the properties defined in nodePropertiesToRemove and relPropertiesToRemove |label:function[] |=== diff --git a/docs/asciidoc/modules/ROOT/pages/virtual-nodes-and-relationships/index.adoc b/docs/asciidoc/modules/ROOT/pages/virtual-nodes-and-relationships/index.adoc new file mode 100644 index 0000000000..67f177511f --- /dev/null +++ b/docs/asciidoc/modules/ROOT/pages/virtual-nodes-and-relationships/index.adoc @@ -0,0 +1,8 @@ +[[virtual-nodes-and-relationships]] += Virtual Nodes and Relationships + + +This section includes: + +* xref::overview/apoc.graph/apoc.graph.filterPropertiesProcedure.adoc[apoc.graph.filterProperties (procedure)] +* xref::overview/apoc.graph/apoc.graph.filterProperties.adoc[apoc.graph.filterProperties (aggregation function)] diff --git a/docs/asciidoc/modules/ROOT/partials/generated-documentation/nav.adoc b/docs/asciidoc/modules/ROOT/partials/generated-documentation/nav.adoc index fb6b411772..26ff1b38bc 100644 --- a/docs/asciidoc/modules/ROOT/partials/generated-documentation/nav.adoc +++ b/docs/asciidoc/modules/ROOT/partials/generated-documentation/nav.adoc @@ -65,8 +65,8 @@ This file is generated by DocsTest, so don't change it! *** xref::overview/apoc.get/apoc.get.nodes.adoc[] *** xref::overview/apoc.get/apoc.get.rels.adoc[] ** xref::overview/apoc.graph/index.adoc[] -*** xref::overview/apoc.get/apoc.virtual.graph.adoc[] -*** xref::overview/apoc.get/apoc.graph.filterProperties.adoc[] +*** xref::overview/apoc.graph/apoc.graph.filterProperties.adoc[] +*** xref::overview/apoc.graph/apoc.graph.filterPropertiesProcedure.adoc[] ** xref::overview/apoc.import/index.adoc[] *** xref::overview/apoc.import/apoc.import.arrow.adoc[] ** xref::overview/apoc.load/index.adoc[] diff --git a/extended/src/main/java/apoc/graph/GraphsExtended.java b/extended/src/main/java/apoc/graph/GraphsExtended.java index abf35a2237..09bf5e86f8 100644 --- a/extended/src/main/java/apoc/graph/GraphsExtended.java +++ b/extended/src/main/java/apoc/graph/GraphsExtended.java @@ -5,9 +5,11 @@ import apoc.result.VirtualNode; import apoc.result.VirtualRelationship; import apoc.util.collection.Iterables; +import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Path; import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; import org.neo4j.procedure.Description; import org.neo4j.procedure.Name; import org.neo4j.procedure.Procedure; @@ -25,12 +27,15 @@ @Extended public class GraphsExtended { - @Procedure("apoc.virtual.graph") + @Procedure("apoc.graph.filterProperties") @Description( - "CALL apoc.virtual.graph(, [propertiesToRemove]) YIELD nodes, relationships - returns a set of virtual nodes and relationships without the properties defined in propertiesToRemove") + "CALL apoc.graph.filterProperties(anyEntityObject, nodePropertiesToRemove, relPropertiesToRemove) YIELD nodes, relationships - returns a set of virtual nodes and relationships without the properties defined in nodePropertiesToRemove and relPropertiesToRemove") public Stream fromData( - @Name("value") Object value, @Name("propertiesToRemove") List propertiesToRemove) { - VirtualGraphExtractor extractor = new VirtualGraphExtractor(propertiesToRemove); + @Name("value") Object value, + @Name(value = "nodePropertiesToRemove", defaultValue = "{}") Map> nodePropertiesToRemove, + @Name(value = "relPropertiesToRemove", defaultValue = "{}") Map> relPropertiesToRemove) { + + VirtualGraphExtractor extractor = new VirtualGraphExtractor(nodePropertiesToRemove, relPropertiesToRemove); extractor.extract(value); GraphResult result = new GraphResult( extractor.nodes(), extractor.rels() ); return Stream.of(result); @@ -38,7 +43,7 @@ public Stream fromData( @UserAggregationFunction("apoc.graph.filterProperties") @Description( - "apoc.graph.filterProperties(, [propertiesToRemove]) - aggregation function which returns an object {node: [virtual nodes], relationships: [virtual relationships]} without the properties defined in propertiesToRemove") + "apoc.graph.filterProperties(anyEntityObject, nodePropertiesToRemove, relPropertiesToRemove) - aggregation function which returns an object {node: [virtual nodes], relationships: [virtual relationships]} without the properties defined in nodePropertiesToRemove and relPropertiesToRemove") public GraphFunction filterProperties() { return new GraphFunction(); } @@ -50,9 +55,13 @@ public static class GraphFunction { private VirtualGraphExtractor virtualGraphExtractor; @UserAggregationUpdate - public void filterProperties(@Name("value") Object value, @Name("propertiesToRemove") List propertiesToRemove) { + public void filterProperties( + @Name("value") Object value, + @Name(value = "nodePropertiesToRemove", defaultValue = "{}") Map> nodePropertiesToRemove, + @Name(value = "relPropertiesToRemove", defaultValue = "{}") Map> relPropertiesToRemove) { + if (virtualGraphExtractor == null) { - virtualGraphExtractor = new VirtualGraphExtractor(propertiesToRemove); + virtualGraphExtractor = new VirtualGraphExtractor(nodePropertiesToRemove, relPropertiesToRemove); } virtualGraphExtractor.extract(value); } @@ -69,14 +78,18 @@ public Object result() { } public static class VirtualGraphExtractor { + private static final String ALL_FILTER = "_all"; + private final Map nodes; private final Map rels; - private final List propertiesToRemove; + private final Map> nodePropertiesToRemove; + private final Map> relPropertiesToRemove; - public VirtualGraphExtractor(List propertiesToRemove) { + public VirtualGraphExtractor(Map> nodePropertiesToRemove, Map> relPropertiesToRemove) { this.nodes = new HashMap<>(); this.rels = new HashMap<>(); - this.propertiesToRemove = propertiesToRemove; + this.nodePropertiesToRemove = nodePropertiesToRemove; + this.relPropertiesToRemove = relPropertiesToRemove; } public void extract(Object value) { @@ -123,7 +136,11 @@ private void addVirtualNode(Node node) { private Node createVirtualNode(Node startNode) { List props = Iterables.asList(startNode.getPropertyKeys()); - props.removeAll(propertiesToRemove); + nodePropertiesToRemove.forEach((k,v) -> { + if (k.equals(ALL_FILTER) || startNode.hasLabel(Label.label(k))) { + props.removeAll(v); + } + }); return new VirtualNode(startNode, props); } @@ -136,7 +153,12 @@ private Relationship createVirtualRel(Relationship rel) { endNode = nodes.putIfAbsent(endNode.getElementId(), createVirtualNode(endNode)); Map props = rel.getAllProperties(); - propertiesToRemove.forEach(props.keySet()::remove); + + relPropertiesToRemove.forEach((k,v) -> { + if (k.equals(ALL_FILTER) || rel.isType(RelationshipType.withName(k))) { + v.forEach(props.keySet()::remove); + } + }); return new VirtualRelationship(startNode, endNode, rel.getType(), props); } diff --git a/extended/src/test/java/apoc/graph/GraphsExtendedTest.java b/extended/src/test/java/apoc/graph/GraphsExtendedTest.java index f0794c956c..c925725b9c 100644 --- a/extended/src/test/java/apoc/graph/GraphsExtendedTest.java +++ b/extended/src/test/java/apoc/graph/GraphsExtendedTest.java @@ -84,7 +84,7 @@ with collect(path) as paths testCall(db, """ MATCH path=(:Person)-[:REL]->(:Movie) - WITH apoc.graph.filterProperties(path, ['plotEmbedding', 'posterEmbedding', 'plot', 'bio']) as graph + WITH apoc.graph.filterProperties(path, {_all: ['plotEmbedding', 'posterEmbedding', 'plot', 'bio']}) as graph RETURN graph.nodes AS nodes, graph.relationships AS relationships""", this::commonFilterPropertiesAssertions); @@ -113,11 +113,19 @@ with collect(path) as paths } @Test - public void testVirtualGraph() { + public void testFilterPropertiesProcedure() { testCall(db, """ MATCH path=(:Person)-[:REL]->(:Movie) WITH collect(path) AS paths - CALL apoc.virtual.graph(paths, ['plotEmbedding', 'posterEmbedding', 'plot', 'bio']) + CALL apoc.graph.filterProperties(paths, {_all: ['plotEmbedding', 'posterEmbedding', 'plot', 'bio']}) + YIELD nodes, relationships + RETURN nodes, relationships""", + this::commonFilterPropertiesAssertions); + + testCall(db, """ + MATCH path=(:Person)-[:REL]->(:Movie) + WITH collect(path) AS paths + CALL apoc.graph.filterProperties(paths, {Movie: ['posterEmbedding'], Person: ['posterEmbedding', 'plotEmbedding', 'plot', 'bio']}) YIELD nodes, relationships RETURN nodes, relationships""", this::commonFilterPropertiesAssertions); @@ -161,14 +169,14 @@ public void filterPropertiesWithPathsWithMultipleRels() { testCall(db, """ MATCH path=(:Foo)--(:Bar)--(:Baz) WITH collect(path) AS paths - CALL apoc.virtual.graph(paths, ['remove']) + CALL apoc.graph.filterProperties(paths, {_all: ['remove']}, {_all: ['remove']}) YIELD nodes, relationships RETURN nodes, relationships""", r -> assertNodeAndRelIdProps(r, expectedIdNodes, expectedIdRels)); testCall(db, """ MATCH path=(:Foo)--(:Bar)--(:Baz) - WITH apoc.graph.filterProperties(path, ['remove']) as graph + WITH apoc.graph.filterProperties(path, {_all: ['remove']}, {_all: ['remove']}) as graph RETURN graph.nodes AS nodes, graph.relationships AS relationships""", r -> assertNodeAndRelIdProps(r, expectedIdNodes, expectedIdRels)); } @@ -180,21 +188,21 @@ public void testWithCompositeDataTypes() { testCall(db, """ MATCH p1=(:One)--(:Two), p2=(:Two)--(:Three) - CALL apoc.virtual.graph([p1, p2], ['remove']) + CALL apoc.graph.filterProperties([p1, p2], {_all: ['remove']}, {_all: ['remove']}) YIELD nodes, relationships RETURN nodes, relationships""", r -> assertNodeAndRelIdProps(r, expectedIdNodes, expectedIdRels)); testCall(db, """ MATCH p1=(:One)--(:Two), p2=(:Two)--(:Three) - CALL apoc.virtual.graph([{key1: p1, key2: [p1, p2]}], ['remove']) + CALL apoc.graph.filterProperties([{key1: p1, key2: [p1, p2]}], {_all: ['remove']}, {_all: ['remove']}) YIELD nodes, relationships RETURN nodes, relationships""", r -> assertNodeAndRelIdProps(r, expectedIdNodes, expectedIdRels)); testCall(db, """ MATCH p1=(:One)--(:Two), p2=(:Two)--(:Three) - CALL apoc.virtual.graph([{key2: {subKey: [p1, p2]}}], ['remove']) + CALL apoc.graph.filterProperties([{key2: {subKey: [p1, p2]}}], {_all: ['remove']}, {_all: ['remove']}) YIELD nodes, relationships RETURN nodes, relationships""", r -> assertNodeAndRelIdProps(r, expectedIdNodes, expectedIdRels));