All Downloads are FREE. Search and download functionalities are using the official Maven repository.

apoc.create.Create Maven / Gradle / Ivy

There is a newer version: 5.24.0
Show newest version
package apoc.create;

import apoc.get.Get;
import apoc.result.*;
import apoc.util.Util;
import apoc.util.collection.Iterables;
import apoc.uuid.UuidUtil;
import org.neo4j.graphdb.*;
import org.neo4j.procedure.*;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.LongStream;
import java.util.stream.Stream;

import static org.neo4j.graphdb.RelationshipType.withName;

public class Create {

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

    @Context
    public Transaction tx;

    @Procedure(name = "apoc.create.node", mode = Mode.WRITE)
    @Description("Creates a node with the given dynamic labels.")
    public Stream node(@Name("labels") List labelNames, @Name("props") Map props) {
        return Stream.of(new NodeResult(setProperties(tx.createNode(Util.labels(labelNames)),props)));
    }


    @Procedure(name = "apoc.create.addLabels", mode = Mode.WRITE)
    @Description("Adds the given labels to the given nodes.")
    public Stream addLabels(@Name("nodes") Object nodes, @Name("labels") List labelNames) {
        Label[] labels = Util.labels(labelNames);
        return new Get(tx).nodes(nodes).map((r) -> {
            Node node = r.node;
            for (Label label : labels) {
                node.addLabel(label);
            }
            return r;
        });
    }

    @Procedure(name = "apoc.create.setProperty", mode = Mode.WRITE)
    @Description("Sets the given property to the given node(s).")
    public Stream setProperty(@Name("nodes") Object nodes, @Name("key") String key, @Name("value") Object value) {
        return new Get(tx).nodes(nodes).map((r) -> {
            setProperty(r.node, key,toPropertyValue(value));
            return r;
        });
    }

    @Procedure(name = "apoc.create.setRelProperty", mode = Mode.WRITE)
    @Description("Sets the given property on the relationship(s).")
    public Stream setRelProperty(@Name("rels") Object rels, @Name("key") String key, @Name("value") Object value) {
        return new Get(tx).rels(rels).map((r) -> {
            setProperty(r.rel,key,toPropertyValue(value));
            return r;
        });
    }

    @Procedure(name = "apoc.create.setProperties", mode = Mode.WRITE)
    @Description("Sets the given properties to the given node(s).")
    public Stream setProperties(@Name("nodes") Object nodes, @Name("keys") List keys, @Name("values") List values) {
        return new Get(tx).nodes(nodes).map((r) -> {
            setProperties(r.node, Util.mapFromLists(keys,values));
            return r;
        });
    }

    @Procedure(name = "apoc.create.removeProperties", mode = Mode.WRITE)
    @Description("Removes the given properties from the given node(s).")
    public Stream removeProperties(@Name("nodes") Object nodes, @Name("keys") List keys) {
        return new Get(tx).nodes(nodes).map((r) -> {
            keys.forEach( r.node::removeProperty );
            return r;
        });
    }

    @Procedure(name = "apoc.create.setRelProperties", mode = Mode.WRITE)
    @Description("Sets the given properties on the relationship(s).")
    public Stream setRelProperties(@Name("rels") Object rels, @Name("keys") List keys, @Name("values") List values) {
        return new Get(tx).rels(rels).map((r) -> {
            setProperties(r.rel, Util.mapFromLists(keys,values));
            return r;
        });
    }

    @Procedure(name = "apoc.create.removeRelProperties", mode = Mode.WRITE)
    @Description("Removes the given properties from the given relationship(s).")
    public Stream removeRelProperties(@Name("rels") Object rels, @Name("keys") List keys) {
        return new Get(tx).rels(rels).map((r) -> {
            keys.forEach( r.rel::removeProperty);
            return r;
        });
    }

    @Procedure(name = "apoc.create.setLabels", mode = Mode.WRITE)
    @Description("Sets the given labels to the given node(s). Non-matching labels are removed from the nodes.")
    public Stream setLabels(@Name("nodes") Object nodes, @Name("labels") List labelNames) {
        Label[] labels = Util.labels(labelNames);
        return new Get(tx).nodes(nodes).map((r) -> {
            Node node = r.node;
            for (Label label : node.getLabels()) {
                if (labelNames.contains(label.name())) continue;
                node.removeLabel(label);
            }
            for (Label label : labels) {
                if (node.hasLabel(label)) continue;
                node.addLabel(label);
            }
            return r;
        });
    }

    @Procedure(name = "apoc.create.removeLabels", mode = Mode.WRITE)
    @Description("Removes the given labels from the given node(s).")
    public Stream removeLabels(@Name("nodes") Object nodes, @Name("labels") List labelNames) {
        Label[] labels = Util.labels(labelNames);
        return new Get(tx).nodes(nodes).map((r) -> {
            Node node = r.node;
            for (Label label : labels) {
                node.removeLabel(label);
            }
            return r;
        });
    }

    @Procedure(name = "apoc.create.nodes", mode = Mode.WRITE)
    @Description("Creates nodes with the given dynamic labels.")
    public Stream nodes(@Name("labels") List labelNames, @Name("props") List> props) {
        Label[] labels = Util.labels(labelNames);
        return props.stream().map(p -> new NodeResult(setProperties(tx.createNode(labels), p)));
    }

    @Procedure(name = "apoc.create.relationship", mode = Mode.WRITE)
    @Description("Creates a relationship with the given dynamic relationship type.")
    public Stream relationship(@Name("from") Node from,
                                                   @Name("relType") String relType, @Name("props") Map props,
                                                   @Name("to") Node to) {
        VirtualRelationship.validateNodes(from, to);
        return Stream.of(new RelationshipResult(setProperties(from.createRelationshipTo(to, withName(relType)), props)));
    }

    @Procedure("apoc.create.vNode")
    @Description("Returns a virtual node.")
    public Stream vNode(@Name("labels") List labelNames, @Name("props") Map props) {
        return Stream.of(new NodeResult(vNodeFunction(labelNames, props)));
    }

    @UserFunction("apoc.create.vNode")
    @Description("Returns a virtual node.")
    public Node vNodeFunction(@Name("labels") List labelNames, @Name(value = "props",defaultValue = "{}") Map props) {
        return new VirtualNode(Util.labels(labelNames), props);
    }

    @UserFunction("apoc.create.virtual.fromNode")
    @Description("Returns a virtual node from the given existing node.")
    public Node virtualFromNodeFunction(@Name("node") Node node, @Name("propertyNames") List propertyNames) {
        return new VirtualNode(node, propertyNames);
    }

    @Procedure("apoc.create.vNodes")
    @Description("Returns virtual nodes.")
    public Stream vNodes(@Name("labels") List labelNames, @Name("props") List> props) {
        Label[] labels = Util.labels(labelNames);
        return props.stream().map(p -> new NodeResult(new VirtualNode(labels, p)));
    }

    @Procedure("apoc.create.vRelationship")
    @Description("Returns a virtual relationship.")
    public Stream vRelationship(@Name("from") Node from, @Name("relType") String relType, @Name("props") Map props, @Name("to") Node to) {
        return Stream.of(new RelationshipResult(vRelationshipFunction(from, relType, props, to)));
    }

    @UserFunction("apoc.create.vRelationship")
    @Description("Returns a virtual relationship.")
    public Relationship vRelationshipFunction(@Name("from") Node from, @Name("relType") String relType, @Name("props") Map props, @Name("to") Node to) {
        return new VirtualRelationship(from, to, withName(relType)).withProperties(props);
    }

    @Procedure("apoc.create.virtualPath")
    @Description("Returns a virtual path.")
    public Stream virtualPath(@Name("labelsN") List labelsN, @Name("n") Map n,
                                                  @Name("relType") String relType, @Name("props") Map props,
                                                  @Name("labelsM") List labelsM, @Name("m") Map m) {
        RelationshipType type = withName(relType);
        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));
    }

    @Procedure("apoc.create.clonePathToVirtual")
    @Description("Takes the given path and returns a virtual representation of it.")
    public Stream clonePathToVirtual(@Name("path") Path path) {
        return Stream.of(createVirtualPath(path));
    }

    @Procedure("apoc.create.clonePathsToVirtual")
    @Description("Takes the given paths and returns a virtual representation of them.")
    public Stream clonePathsToVirtual(@Name("paths") List paths) {
        return paths.stream().map(this::createVirtualPath);
    }

    private PathResult createVirtualPath(Path path) {
        final Iterable relationships = path.relationships();
        final Node first = path.startNode();
        VirtualPath virtualPath = new VirtualPath(new VirtualNode(first, Iterables.asList(first.getPropertyKeys())));
        for (Relationship rel : relationships) {
            VirtualNode start = VirtualNode.from(rel.getStartNode());
            VirtualNode end = VirtualNode.from(rel.getEndNode());
            virtualPath.addRel(VirtualRelationship.from(start, end, rel));
        }
        return new PathResult(virtualPath);
    }

    private  T setProperties(T pc, Map p) {
        if (p == null) return pc;
        for (Map.Entry entry : p.entrySet()) {
            setProperty(pc, entry.getKey(), entry.getValue());
        }
        return pc;
    }

    private  void setProperty(T pc, String key, Object value) {
        if (value == null) pc.removeProperty(key);
        else pc.setProperty(key, toPropertyValue(value));
    }

    @UserFunction(name = "apoc.create.uuid", deprecatedBy = "Neo4j randomUUID() function")
    @Deprecated
    @Description("Returns a UUID.")
    public String uuid() {
        return UUID.randomUUID().toString();
    }

    @UserFunction("apoc.create.uuidBase64")
    @Description("Returns a UUID encoded with base64.")
    public String uuidBase64() {
        return UuidUtil.generateBase64Uuid(UUID.randomUUID());
    }

    @UserFunction("apoc.create.uuidBase64ToHex")
    @Description("Takes the given base64 encoded UUID and returns it as a hexadecimal string.")
    public String uuidBase64ToHex(@Name("base64Uuid") String base64Uuid) {
        return UuidUtil.fromBase64ToHex(base64Uuid);
    }

    @UserFunction("apoc.create.uuidHexToBase64")
    @Description("Takes the given UUID represented as a hexadecimal string and returns it encoded with base64.")
    public String uuidHexToBase64(@Name("uuid") String uuidHex) {
        return UuidUtil.fromHexToBase64(uuidHex);
    }

    private Object toPropertyValue(Object value) {
        if (value instanceof Iterable) {
            Iterable it = (Iterable) value;
            Object first = Iterables.firstOrNull(it);
            if (first == null) return EMPTY_ARRAY;
            return Iterables.asArray(first.getClass(), it);
        }
        return value;
    }

    @Procedure(name = "apoc.create.uuids", deprecatedBy = "Neo4j's randomUUID() function can be used as a replacement, for example: `UNWIND range(0,$count) AS row RETURN row, randomUUID() AS uuid`")
    @Deprecated
    @Description("Returns a stream of UUIDs.")
    public Stream uuids(@Name("count") long count) {
        return LongStream.range(0, count).mapToObj(UUIDResult::new);
    }

    public static class UUIDResult {
        public final long row;
        public final String uuid;

        public UUIDResult(long row) {
            this.row = row;
            this.uuid = UUID.randomUUID().toString();
            // TODO Long.toHexString(uuid.getMostSignificantBits())+Long.toHexString(uuid.getLeastSignificantBits());
        }
    }

}