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

apoc.merge.Merge Maven / Gradle / Ivy

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

import apoc.cypher.Cypher;
import apoc.result.NodeResult;
import apoc.result.NodeResultWithStats;
import apoc.result.RelationshipResultWithStats;
import apoc.result.RelationshipResult;
import apoc.util.Util;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Result;
import org.neo4j.graphdb.Transaction;
import org.neo4j.procedure.*;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static apoc.util.Util.labelString;
import static java.util.Collections.emptyMap;

public class Merge {

    @Context
    public Transaction tx;

    @Procedure(value="apoc.merge.node.eager", mode = Mode.WRITE, eager = true)
    @Description("Merges the given node(s) with the given dynamic labels eagerly.")
    public Stream nodesEager(@Name("labels") List labelNames,
                                        @Name("identProps") Map identProps,
                                        @Name(value = "props",defaultValue = "{}") Map props,
                                        @Name(value = "onMatchProps",defaultValue = "{}") Map onMatchProps) {
        return nodes(labelNames, identProps,props,onMatchProps);
    }

    @Procedure(value="apoc.merge.node", mode = Mode.WRITE)
    @Description("Merges the given node(s) with the given dynamic labels.")
    public Stream nodes(@Name("labels") List labelNames,
                                          @Name("identProps") Map identProps,
                                          @Name(value = "props",defaultValue = "{}") Map props,
                                          @Name(value = "onMatchProps",defaultValue = "{}") Map onMatchProps) {
        final Result nodeResult = getNodeResult(labelNames, identProps, props, onMatchProps);
        return nodeResult.columnAs("n").stream().map(node -> new NodeResult((Node) node));
    }

    @Procedure(value="apoc.merge.nodeWithStats.eager", mode = Mode.WRITE, eager = true)
    @Description("Merges the given node(s) with the given dynamic labels eagerly. Provides queryStatistics in the result.")
    public Stream nodeWithStatsEager(@Name("labels") List labelNames,
                                                          @Name("identProps") Map identProps,
                                                          @Name(value = "props",defaultValue = "{}") Map props,
                                                          @Name(value = "onMatchProps",defaultValue = "{}") Map onMatchProps) {
        return nodeWithStats(labelNames, identProps,props,onMatchProps);
    }

    @Procedure(value="apoc.merge.nodeWithStats", mode = Mode.WRITE)
    @Description("Merges the given node(s) with the given dynamic labels. Provides queryStatistics in the result.")
    public Stream nodeWithStats(@Name("labels") List labelNames,
                                                     @Name("identProps") Map identProps,
                                                     @Name(value = "props",defaultValue = "{}") Map props,
                                                     @Name(value = "onMatchProps",defaultValue = "{}") Map onMatchProps) {
        final Result nodeResult = getNodeResult(labelNames, identProps, props, onMatchProps);
        return nodeResult.columnAs("n").stream()
                .map(node -> new NodeResultWithStats((Node) node, Cypher.toMap(nodeResult.getQueryStatistics())));
    }

    private Result getNodeResult(List labelNames, Map identProps, Map props, Map onMatchProps) {
        if (identProps ==null || identProps.isEmpty()) {
            throw new IllegalArgumentException("you need to supply at least one identifying property for a merge");
        }

        String labels = labelString(labelNames);

        Map params = Util.map("identProps", identProps, "onCreateProps", props, "onMatchProps", onMatchProps);
        String identPropsString = buildIdentPropsString(identProps);

        final String cypher = "MERGE (n:" + labels + "{" + identPropsString + "}) ON CREATE SET n += $onCreateProps ON MATCH SET n += $onMatchProps RETURN n";
        return tx.execute(cypher, params);
    }

    @Procedure(value = "apoc.merge.relationship", mode = Mode.WRITE)
    @Description("Merges the given relationship(s) with the given dynamic types/properties.")
    public Stream relationship(@Name("startNode") Node startNode, @Name("relType") String relType,
                                                        @Name("identProps") Map identProps,
                                                        @Name("props") Map onCreateProps,
                                                        @Name("endNode") Node endNode,
                                                        @Name(value = "onMatchProps",defaultValue = "{}") Map onMatchProps) {
        final Result execute = getRelResult(startNode, relType, identProps, onCreateProps, endNode, onMatchProps);
        return execute.columnAs("r").stream().map(rel -> new RelationshipResult((Relationship) rel));
    }

    @Procedure(value = "apoc.merge.relationshipWithStats", mode = Mode.WRITE)
    @Description("Merges the given relationship(s) with the given dynamic types/properties. Provides queryStatistics in the result.")
    public Stream relationshipWithStats(@Name("startNode") Node startNode, @Name("relType") String relType,
                                                                     @Name("identProps") Map identProps,
                                                                     @Name("props") Map onCreateProps,
                                                                     @Name("endNode") Node endNode,
                                                                     @Name(value = "onMatchProps",defaultValue = "{}") Map onMatchProps) {
        final Result relResult = getRelResult(startNode, relType, identProps, onCreateProps, endNode, onMatchProps);
        return relResult.columnAs("r").stream()
                .map(rel -> new RelationshipResultWithStats((Relationship) rel, Cypher.toMap(relResult.getQueryStatistics())));
    }

    private Result getRelResult(Node startNode, String relType, Map identProps, Map onCreateProps, Node endNode, Map onMatchProps) {
        String identPropsString = buildIdentPropsString(identProps);

        Map params = Util.map("identProps", identProps, "onCreateProps", onCreateProps ==null ? emptyMap() : onCreateProps,
                "onMatchProps", onMatchProps == null ? emptyMap() : onMatchProps, "startNode", startNode, "endNode", endNode);

        final String cypher =
                "WITH $startNode as startNode, $endNode as endNode " +
                "MERGE (startNode)-[r:"+ Util.quote(relType) +"{"+identPropsString+"}]->(endNode) " +
                "ON CREATE SET r+= $onCreateProps " +
                "ON MATCH SET r+= $onMatchProps " +
                "RETURN r";
        return tx.execute(cypher, params);
    }

    @Procedure(value = "apoc.merge.relationship.eager", mode = Mode.WRITE, eager = true)
    @Description("Merges the given relationship(s) with the given dynamic types/properties eagerly.")
    public Stream relationshipEager(@Name("startNode") Node startNode, @Name("relType") String relType,
                                                        @Name("identProps") Map identProps,
                                                        @Name("props") Map onCreateProps,
                                                        @Name("endNode") Node endNode,
                                                        @Name(value = "onMatchProps",defaultValue = "{}") Map onMatchProps) {
        return relationship(startNode, relType, identProps, onCreateProps, endNode, onMatchProps );
    }

    @Procedure(value = "apoc.merge.relationshipWithStats.eager", mode = Mode.WRITE, eager = true)
    @Description("Merges the given relationship(s) with the given dynamic types/properties eagerly. Provides queryStatistics in the result.")
    public Stream relationshipWithStatsEager(@Name("startNode") Node startNode, @Name("relType") String relType,
                                                                          @Name("identProps") Map identProps,
                                                                          @Name("props") Map onCreateProps,
                                                                          @Name("endNode") Node endNode,
                                                                          @Name(value = "onMatchProps",defaultValue = "{}") Map onMatchProps) {
        return relationshipWithStats(startNode, relType, identProps, onCreateProps, endNode, onMatchProps );
    }


    private String buildIdentPropsString(Map identProps) {
        if (identProps == null) return "";
        return identProps.keySet().stream().map(Util::quote)
                .map(s -> s + ":$identProps." + s)
                .collect(Collectors.joining(","));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy