Skip to content

Commit

Permalink
added vNodes - vRelationships
Browse files Browse the repository at this point in the history
  • Loading branch information
vga91 committed Sep 28, 2021
1 parent 68069a7 commit cd3e05f
Show file tree
Hide file tree
Showing 10 changed files with 524 additions and 162 deletions.
100 changes: 21 additions & 79 deletions core/src/main/java/apoc/create/Create.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.LongStream;
import java.util.stream.Stream;

Expand All @@ -23,8 +19,6 @@
public class Create {

public static final String[] EMPTY_ARRAY = new String[0];

public static final VirtualEntitiesHandler handler = new VirtualEntitiesHandler();

@Context
public Transaction tx;
Expand Down Expand Up @@ -151,91 +145,39 @@ public Stream<RelationshipResult> relationship(@Name("from") Node from,

@Procedure
@Description("apoc.create.vNode(['Label'], {key:value,...}) returns a virtual node")
public Stream<NodeResult> vNode(@Name("label") List<String> labelNames, @Name("props") Map<String, Object> props, @Name(value = "config",defaultValue = "{}") Map<String, Object> config) {
return Stream.of(new NodeResult(vNodeFunction(labelNames, props, config)));
public Stream<NodeResult> vNode(@Name("label") List<String> labelNames, @Name("props") Map<String, Object> props) {
return Stream.of(new NodeResult(vNodeFunction(labelNames, props)));
}

@UserFunction("apoc.create.vNode")
@Description("apoc.create.vNode(['Label'], {key:value,...}) returns a virtual node")
public Node vNodeFunction(@Name("label") List<String> labelNames, @Name(value = "props",defaultValue = "{}") Map<String, Object> props, @Name(value = "config",defaultValue = "{}") Map<String, Object> config) {
VirtualConfig conf = new VirtualConfig(config);
if (!conf.isMerge()) {
return createVirtualNode(labelNames, props);
} else {
return handler.getNodes().stream().filter(node ->
Iterables.asSet(Iterables.map(Label::name, node.getLabels())).equals(Set.copyOf(labelNames))
&& isaBoolean(props, node))
.findAny()
.map(getVirtualNodeVirtualNodeFunction(conf))
.orElseGet(getVirtualNodeSupplier(()-> createVirtualNode(labelNames, props), conf));
}
}

private <T extends Entity> Supplier<T> getVirtualNodeSupplier(Supplier<T> supplier, VirtualConfig conf) {
return () -> {
final T node = supplier.get();
conf.getOnCreate().forEach(node::setProperty);
return node;
};
}

private <T extends Entity> Function<T, T> getVirtualNodeVirtualNodeFunction(VirtualConfig conf) {
return node -> {
conf.getOnMatch().forEach(node::setProperty);
return node;
};
}

private VirtualNode createVirtualNode(List<String> labelNames, Map<String, Object> props) {
return new VirtualNode(Util.labels(labelNames), props, handler);
public Node vNodeFunction(@Name("label") List<String> labelNames, @Name(value = "props",defaultValue = "{}") Map<String, Object> props) {
return new VirtualNode(Util.labels(labelNames), props);
}

@UserFunction("apoc.create.virtual.fromNode")
@Description("apoc.create.virtual.fromNode(node, [propertyNames]) returns a virtual node built from an existing node with only the requested properties")
public Node virtualFromNodeFunction(@Name("node") Node node, @Name("propertyNames") List<String> propertyNames) {
return new VirtualNode(node, propertyNames, handler);
return new VirtualNode(node, propertyNames);
}

@Procedure
@Description("apoc.create.vNodes(['Label'], [{key:value,...}]) returns virtual nodes")
public Stream<NodeResult> vNodes(@Name("label") List<String> labelNames, @Name("props") List<Map<String, Object>> props) {
Label[] labels = Util.labels(labelNames);
return props.stream().map(p -> new NodeResult(new VirtualNode(labels, p, handler)));
return props.stream().map(p -> new NodeResult(new VirtualNode(labels, p)));
}

@Procedure
@Description("apoc.create.vRelationship(nodeFrom,'KNOWS',{key:value,...}, nodeTo) returns a virtual relationship")
public Stream<RelationshipResult> vRelationship(@Name("from") Node from, @Name("relType") String relType, @Name("props") Map<String, Object> props, @Name("to") Node to,
@Name(value = "config",defaultValue = "{}") Map<String, Object> config) {
return Stream.of(new RelationshipResult(vRelationshipFunction(from, relType, props, to, config)));
public Stream<RelationshipResult> vRelationship(@Name("from") Node from, @Name("relType") String relType, @Name("props") Map<String, Object> props, @Name("to") Node to) {
return Stream.of(new RelationshipResult(vRelationshipFunction(from, relType, props, to)));
}

@UserFunction("apoc.create.vRelationship")
@Description("apoc.create.vRelationship(nodeFrom,'KNOWS',{key:value,...}, nodeTo) returns a virtual relationship")
public Relationship vRelationshipFunction(@Name("from") Node from, @Name("relType") String relType, @Name("props") Map<String, Object> props, @Name("to") Node to,
@Name(value = "config",defaultValue = "{}") Map<String, Object> config) {
VirtualConfig conf = new VirtualConfig(config);
final RelationshipType type = withName(relType);
if (!conf.isMerge()) {
return createVirtualRelationship(from, props, to, type);
} else {
return handler.getRels().stream().filter(rel -> rel.getType().equals(type)
&& rel.getStartNode().equals(from)
&& rel.getEndNode().equals(to)
&& isaBoolean(props, rel))
.findAny()
.map(getVirtualNodeVirtualNodeFunction(conf))
.orElseGet(getVirtualNodeSupplier(() -> createVirtualRelationship(from, props, to, type), conf));
}

}

private <T extends Entity> boolean isaBoolean(Map<String, Object> props, T entity) {
return props.entrySet().stream().allMatch(e -> Objects.deepEquals(e.getValue(), entity.getProperty(e.getKey(), null)));
}

private VirtualRelationship createVirtualRelationship(Node from, Map<String, Object> props, Node to, RelationshipType type) {
return new VirtualRelationship(from, to, type, handler).withProperties(props);
public Relationship vRelationshipFunction(@Name("from") Node from, @Name("relType") String relType, @Name("props") Map<String, Object> props, @Name("to") Node to) {
return new VirtualRelationship(from, to, withName(relType)).withProperties(props);
}

@Procedure(deprecatedBy = "apoc.create.virtualPath")
Expand All @@ -247,9 +189,9 @@ public Stream<VirtualPathResult> vPattern(@Name("from") Map<String, Object> n,
n = new LinkedHashMap<>(n);
m = new LinkedHashMap<>(m);
RelationshipType type = withName(relType);
VirtualNode from = new VirtualNode(Util.labels(n.remove("_labels")), n, handler);
VirtualNode to = new VirtualNode(Util.labels(m.remove("_labels")), m, handler);
Relationship rel = createVirtualRelationship(from, props, to, withName(relType));
VirtualNode from = new VirtualNode(Util.labels(n.remove("_labels")), n);
VirtualNode to = new VirtualNode(Util.labels(m.remove("_labels")), m);
Relationship rel = new VirtualRelationship(from, to, withName(relType)).withProperties(props);
return Stream.of(new VirtualPathResult(from, rel, to));
}

Expand All @@ -262,7 +204,7 @@ public Stream<VirtualPathResult> vPatternFull(@Name("labelsN") List<String> labe
RelationshipType type = withName(relType);
VirtualNode from = new VirtualNode(Util.labels(labelsN), n);
VirtualNode to = new VirtualNode(Util.labels(labelsM), m);
Relationship rel = createVirtualRelationship(from, props, to, type);
Relationship rel = new VirtualRelationship(from, to, type).withProperties(props);
return Stream.of(new VirtualPathResult(from, rel, to));
}

Expand All @@ -272,9 +214,9 @@ public Stream<VirtualPathResult> virtualPath(@Name("labelsN") List<String> label
@Name("relType") String relType, @Name("props") Map<String, Object> props,
@Name("labelsM") List<String> labelsM, @Name("m") Map<String, Object> m) {
RelationshipType type = withName(relType);
VirtualNode from = new VirtualNode(Util.labels(labelsN), n, handler);
VirtualNode to = new VirtualNode(Util.labels(labelsM), m, handler);
Relationship rel = createVirtualRelationship(from, props, to, type);
VirtualNode from = new VirtualNode(Util.labels(labelsN), n);
VirtualNode to = new VirtualNode(Util.labels(labelsM), m);
Relationship rel = new VirtualRelationship(from, to, type).withProperties(props);
return Stream.of(new VirtualPathResult(from, rel, to));
}

Expand All @@ -293,11 +235,11 @@ public Stream<PathResult> clonePathsToVirtual(@Name("paths") List<Path> paths) {
private PathResult createVirtualPath(Path path) {
final Iterable<Relationship> relationships = path.relationships();
final Node first = path.startNode();
VirtualPath virtualPath = new VirtualPath(new VirtualNode(first, Iterables.asList(first.getPropertyKeys()), handler));
VirtualPath virtualPath = new VirtualPath(new VirtualNode(first, Iterables.asList(first.getPropertyKeys())));
for (Relationship rel : relationships) {
VirtualNode start = VirtualNode.from(rel.getStartNode(), handler);
VirtualNode end = VirtualNode.from(rel.getEndNode(), handler);
virtualPath.addRel(VirtualRelationship.from(start, end, rel, handler));
VirtualNode start = VirtualNode.from(rel.getStartNode());
VirtualNode end = VirtualNode.from(rel.getEndNode());
virtualPath.addRel(VirtualRelationship.from(start, end, rel));
}
return new PathResult(virtualPath);
}
Expand Down
13 changes: 12 additions & 1 deletion core/src/main/java/apoc/create/VirtualEntitiesHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

import apoc.result.VirtualNode;
import apoc.result.VirtualRelationship;
import org.neo4j.kernel.lifecycle.LifecycleAdapter;

import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class VirtualEntitiesHandler {
public class VirtualEntitiesHandler extends LifecycleAdapter {
private final Set<VirtualNode> nodes = ConcurrentHashMap.newKeySet();
private final Set<VirtualRelationship> rels = ConcurrentHashMap.newKeySet();

Expand All @@ -25,4 +26,14 @@ public void addNode(VirtualNode node) {
public void addRel(VirtualRelationship rel) {
this.rels.add(rel);
}

@Override
public void start() {}

@Override
public void stop() {
nodes.clear();
rels.clear();
}

}
Loading

0 comments on commit cd3e05f

Please sign in to comment.