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

apoc.trigger.TriggerExtended Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [http://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package apoc.trigger;

import apoc.Description;
import apoc.Extended;
import apoc.coll.SetBackedList;
import apoc.result.VirtualNode;
import apoc.result.VirtualRelationship;
import java.util.*;
import java.util.stream.Collectors;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.procedure.*;

/**
 * @author mh
 * @since 20.09.16
 */
@Extended
public class TriggerExtended {
    public static class TriggerInfo {
        public String name;
        public String query;
        public Map selector;
        public Map params;
        public boolean installed;
        public boolean paused;

        public TriggerInfo(String name, String query, Map selector, boolean installed, boolean paused) {
            this.name = name;
            this.query = query;
            this.selector = selector;
            this.installed = installed;
            this.paused = paused;
        }

        public TriggerInfo(
                String name,
                String query,
                Map selector,
                Map params,
                boolean installed,
                boolean paused) {
            this.name = name;
            this.query = query;
            this.selector = selector;
            this.params = params;
            this.installed = installed;
            this.paused = paused;
        }
    }

    @Context
    public GraphDatabaseService db;

    @UserFunction
    @Description(
            "function to filter labelEntries by label, to be used within a trigger kernelTransaction with {assignedLabels}, {removedLabels}, {assigned/removedNodeProperties}")
    public List nodesByLabel(@Name("labelEntries") Object entries, @Name("label") String labelString) {
        if (!(entries instanceof Map)) return Collections.emptyList();
        Map map = (Map) entries;
        if (map.isEmpty()) return Collections.emptyList();
        Object result = ((Map) entries).get(labelString);
        if (result instanceof List) return (List) result;
        Object anEntry = map.values().iterator().next();

        if (anEntry instanceof List) {
            List list = (List) anEntry;
            if (!list.isEmpty()) {
                if (list.get(0) instanceof Map) {
                    Set nodeSet = new HashSet<>(100);
                    Label label = labelString == null ? null : Label.label(labelString);
                    for (List> entry : (Collection>>) map.values()) {
                        for (Map propertyEntry : entry) {
                            Object node = propertyEntry.get("node");
                            if (node instanceof Node && (label == null || ((Node) node).hasLabel(label))) {
                                nodeSet.add((Node) node);
                            }
                        }
                    }
                    if (!nodeSet.isEmpty()) return new SetBackedList<>(nodeSet);
                } else if (list.get(0) instanceof Node) {
                    if (labelString == null) {
                        Set nodeSet = new HashSet<>(map.size() * list.size());
                        map.values().forEach((l) -> nodeSet.addAll((Collection) l));
                        return new SetBackedList<>(nodeSet);
                    }
                }
            }
        }
        return Collections.emptyList();
    }

    @UserFunction
    @Description(
            "function to filter propertyEntries by property-key, to be used within a trigger kernelTransaction with {assignedNode/RelationshipProperties} and {removedNode/RelationshipProperties}. Returns [{old,new,key,node,relationship}]")
    public List> propertiesByKey(
            @Name("propertyEntries") Map>> propertyEntries, @Name("key") String key) {
        return propertyEntries.getOrDefault(key, Collections.emptyList());
    }

    public TriggerInfo toTriggerInfo(Map.Entry e) {
        String name = e.getKey();
        if (e.getValue() instanceof Map) {
            try {
                Map value = (Map) e.getValue();
                return new TriggerInfo(
                        name,
                        (String) value.get("statement"),
                        (Map) value.get("selector"),
                        (Map) value.get("params"),
                        false,
                        false);
            } catch (Exception ex) {
                return new TriggerInfo(name, ex.getMessage(), null, false, false);
            }
        }
        return new TriggerInfo(name, null, null, false, false);
    }

    @UserFunction
    @Description(
            "apoc.trigger.toNode(node, $removedLabels, $removedNodeProperties) | function to rebuild a node as a virtual, to be used in triggers with a not 'afterAsync' phase")
    public Node toNode(
            @Name("id") Node node,
            @Name("removedLabels") Map> removedLabels,
            @Name("removedNodeProperties") Map> removedNodeProperties) {

        final long id = node.getId();
        final Label[] labels = removedLabels.entrySet().stream()
                .filter(i -> i.getValue().stream().anyMatch(l -> l.getId() == id))
                .map(e -> Label.label(e.getKey()))
                .toArray(Label[]::new);

        final Map props = removedNodeProperties.entrySet().stream()
                .map(i -> i.getValue().stream()
                        .filter(l -> ((Node) l.get("node")).getId() == id)
                        .findAny()
                        .map(v -> new AbstractMap.SimpleEntry<>(i.getKey(), v.get("old"))))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));

        return new VirtualNode(labels, props);
    }

    @UserFunction
    @Description(
            "apoc.trigger.toRelationship(rel, $removedRelationshipProperties) | function to rebuild a relationship as a virtual, to be used in triggers with a not 'afterAsync' phase")
    public Relationship toRelationship(
            @Name("id") Relationship rel,
            @Name("removedRelationshipProperties") Map> removedRelationshipProperties) {
        final Map props = removedRelationshipProperties.entrySet().stream()
                .map(i -> i.getValue().stream()
                        .filter(l -> ((Relationship) l.get("relationship")).getId() == rel.getId())
                        .findAny()
                        .map(v -> new AbstractMap.SimpleEntry<>(i.getKey(), v.get("old"))))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));

        return new VirtualRelationship(rel.getStartNode(), rel.getEndNode(), rel.getType(), props);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy