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

apoc.trigger.TriggerMetadata Maven / Gradle / Ivy

There is a newer version: 5.24.0
Show 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 static apoc.util.Util.map;

import apoc.convert.ConvertUtils;
import apoc.result.VirtualNode;
import apoc.result.VirtualRelationship;
import apoc.util.Util;
import apoc.util.collection.Iterables;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.neo4j.graphdb.Entity;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.event.LabelEntry;
import org.neo4j.graphdb.event.PropertyEntry;
import org.neo4j.graphdb.event.TransactionData;

public class TriggerMetadata {
    private final long transactionId;
    private final long commitTime;
    private final List createdNodes;
    private final List createdRelationships;
    private final List deletedNodes;
    private final List deletedRelationships;
    private final Map> removedLabels;
    private final Map>> removedNodeProperties;
    private final Map>> removedRelationshipProperties;
    private final Map> assignedLabels;
    private final Map>> assignedNodeProperties;
    private final Map>> assignedRelationshipProperties;
    private final Map metaData;

    private TriggerMetadata(
            long transactionId,
            long commitTime,
            List createdNodes,
            List createdRelationships,
            List deletedNodes,
            List deletedRelationships,
            Map> removedLabels,
            Map>> removedNodeProperties,
            Map>> removedRelationshipProperties,
            Map> assignedLabels,
            Map>> assignedNodeProperties,
            Map>> assignedRelationshipProperties,
            Map metaData) {
        this.transactionId = transactionId;
        this.commitTime = commitTime;
        this.createdNodes = createdNodes;
        this.createdRelationships = createdRelationships;
        this.deletedNodes = deletedNodes;
        this.deletedRelationships = deletedRelationships;
        this.removedLabels = removedLabels;
        this.removedNodeProperties = removedNodeProperties;
        this.removedRelationshipProperties = removedRelationshipProperties;
        this.assignedLabels = assignedLabels;
        this.assignedNodeProperties = assignedNodeProperties;
        this.assignedRelationshipProperties = assignedRelationshipProperties;
        this.metaData = metaData;
    }

    public static TriggerMetadata from(TransactionData txData, boolean rebindDeleted) {
        long txId, commitTime;
        try {
            txId = txData.getTransactionId();
        } catch (Exception ignored) {
            txId = -1L;
        }
        try {
            commitTime = txData.getCommitTime();
        } catch (Exception ignored) {
            commitTime = -1L;
        }
        List createdNodes = ConvertUtils.convertToList(txData.createdNodes());
        List createdRelationships = ConvertUtils.convertToList(txData.createdRelationships());
        List deletedNodes = rebindDeleted
                ? rebindDeleted(ConvertUtils.convertToList(txData.deletedNodes()), txData)
                : ConvertUtils.convertToList(txData.deletedNodes());
        List deletedRelationships = rebindDeleted
                ? rebindDeleted(ConvertUtils.convertToList(txData.deletedRelationships()), txData)
                : ConvertUtils.convertToList(txData.deletedRelationships());
        Map> removedLabels = aggregateLabels(txData.removedLabels());
        Map> assignedLabels = aggregateLabels(txData.assignedLabels());
        Map>> removedNodeProperties =
                aggregatePropertyKeys(txData.removedNodeProperties(), true);
        Map>> removedRelationshipProperties =
                aggregatePropertyKeys(txData.removedRelationshipProperties(), true);
        final Map>> assignedNodeProperties =
                aggregatePropertyKeys(txData.assignedNodeProperties(), false);
        final Map>> assignedRelationshipProperties =
                aggregatePropertyKeys(txData.assignedRelationshipProperties(), false);
        if (rebindDeleted) {
            removedLabels = removedLabels.entrySet().stream()
                    .collect(Collectors.toMap(Map.Entry::getKey, e -> rebindDeleted(e.getValue(), txData)));
            removedNodeProperties = rebindPropsEntries(txData, removedNodeProperties);
            removedRelationshipProperties = rebindPropsEntries(txData, removedRelationshipProperties);
        }
        return new TriggerMetadata(
                txId,
                commitTime,
                createdNodes,
                createdRelationships,
                deletedNodes,
                deletedRelationships,
                removedLabels,
                removedNodeProperties,
                removedRelationshipProperties,
                assignedLabels,
                assignedNodeProperties,
                assignedRelationshipProperties,
                txData.metaData());
    }

    private static  Map>> rebindPropsEntries(
            TransactionData txData, Map>> removedNodeProperties) {
        return removedNodeProperties.entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream()
                        .map(entry -> entry.copy((T) toVirtualEntity(txData, entry.entity)))
                        .collect(Collectors.toList())));
    }

    private static  List rebindDeleted(List entities, TransactionData txData) {
        return entities.stream().map(e -> (T) toVirtualEntity(txData, e)).collect(Collectors.toList());
    }

    private static  Entity toVirtualEntity(TransactionData txData, T e) {
        if (e instanceof Node) {
            Node node = (Node) e;
            final Label[] labels = Iterables.stream(txData.removedLabels())
                    .filter(label -> label.node().equals(node))
                    .map(LabelEntry::label)
                    .toArray(Label[]::new);
            final Map props = getProps(txData.removedNodeProperties(), node);
            return new VirtualNode(labels, props);
        } else {
            Relationship rel = (Relationship) e;
            final Map props = getProps(txData.removedRelationshipProperties(), rel);
            return new VirtualRelationship(rel.getStartNode(), rel.getEndNode(), rel.getType(), props);
        }
    }

    private static  Map getProps(
            Iterable> propertyEntries, T entity) {
        return Iterables.stream(propertyEntries)
                .filter(label -> label.entity().equals(entity))
                .collect(Collectors.toMap(PropertyEntry::key, PropertyEntry::previouslyCommittedValue));
    }

    public TriggerMetadata rebind(Transaction tx) {
        final List createdNodes = Util.rebind(this.createdNodes, tx);
        final List createdRelationships = Util.rebind(this.createdRelationships, tx);
        //        final List deletedNodes = Util.rebind(this.deletedNodes, tx);
        //        final List deletedRelationships = Util.rebind(this.deletedRelationships, tx);
        final Map> removedLabels = rebindMap(this.removedLabels, tx);
        final Map> assignedLabels = rebindMap(this.assignedLabels, tx);
        final Map>> removedNodeProperties =
                rebindPropertyEntryContainer(this.removedNodeProperties, tx);
        final Map>> removedRelationshipProperties =
                rebindPropertyEntryContainer(this.removedRelationshipProperties, tx);
        final Map>> assignedNodeProperties =
                rebindPropertyEntryContainer(this.assignedNodeProperties, tx);
        final Map>> assignedRelationshipProperties =
                rebindPropertyEntryContainer(this.assignedRelationshipProperties, tx);
        return new TriggerMetadata(
                transactionId,
                commitTime,
                createdNodes,
                createdRelationships,
                deletedNodes,
                deletedRelationships,
                removedLabels,
                removedNodeProperties,
                removedRelationshipProperties,
                assignedLabels,
                assignedNodeProperties,
                assignedRelationshipProperties,
                metaData);
    }

    private  Map>> rebindPropertyEntryContainer(
            Map>> map, Transaction tx) {
        return map.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().stream()
                .map(p -> p.rebind(tx))
                .collect(Collectors.toList())));
    }

    private  Map> rebindMap(Map> map, Transaction tx) {
        return map.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> Util.rebind(e.getValue(), tx)));
    }

    private  Map>> convertMapOfPropertyEntryContainers(
            Map>> map) {
        return map.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().stream()
                .map(PropertyEntryContainer::toMap)
                .collect(Collectors.toList())));
    }

    public Map toMap() {
        return map(
                "transactionId",
                transactionId,
                "commitTime",
                commitTime,
                "createdNodes",
                createdNodes,
                "createdRelationships",
                createdRelationships,
                "deletedNodes",
                deletedNodes,
                "deletedRelationships",
                deletedRelationships,
                "removedLabels",
                removedLabels,
                "removedNodeProperties",
                convertMapOfPropertyEntryContainers(removedNodeProperties),
                "removedRelationshipProperties",
                convertMapOfPropertyEntryContainers(removedRelationshipProperties),
                "assignedLabels",
                assignedLabels,
                "assignedNodeProperties",
                convertMapOfPropertyEntryContainers(assignedNodeProperties),
                "assignedRelationshipProperties",
                convertMapOfPropertyEntryContainers(assignedRelationshipProperties),
                "metaData",
                metaData);
    }

    private static Map> aggregateLabels(Iterable labelEntries) {
        if (!labelEntries.iterator().hasNext()) return Collections.emptyMap();
        Map> result = new HashMap<>();
        for (LabelEntry entry : labelEntries) {
            result.compute(entry.label().name(), (k, v) -> {
                if (v == null) v = new ArrayList<>(100);
                v.add(entry.node());
                return v;
            });
        }
        return result;
    }

    private static class PropertyEntryContainer {
        private final String key;
        private final T entity;
        private final Object oldVal;
        private final Object newVal;

        PropertyEntryContainer(String key, T entity, Object oldVal, Object newVal) {
            this.key = key;
            this.entity = entity;
            this.oldVal = oldVal;
            this.newVal = newVal;
        }

        PropertyEntryContainer rebind(Transaction tx) {
            return new PropertyEntryContainer(key, Util.rebind(tx, entity), oldVal, newVal);
        }

        PropertyEntryContainer copy(T entity) {
            return new PropertyEntryContainer(key, entity, oldVal, newVal);
        }

        Map toMap() {
            final Map map =
                    map("key", key, entity instanceof Node ? "node" : "relationship", entity, "old", oldVal);
            if (newVal != null) {
                map.put("new", newVal);
            }
            return map;
        }
    }

    private static  Map>> aggregatePropertyKeys(
            Iterable> entries, boolean removed) {
        if (!entries.iterator().hasNext()) return Collections.emptyMap();
        Map>> result = new HashMap<>();
        for (PropertyEntry entry : entries) {
            result.compute(entry.key(), (k, v) -> {
                if (v == null) v = new ArrayList<>(100);
                v.add(new PropertyEntryContainer<>(
                        k, entry.entity(), entry.previouslyCommittedValue(), removed ? null : entry.value()));
                return v;
            });
        }
        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy