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

apoc.systemdb.SystemDb Maven / Gradle / Ivy

package apoc.systemdb;

import apoc.ApocConfig;
import apoc.Description;
import apoc.Extended;
import apoc.export.cypher.ExportFileManager;
import apoc.export.cypher.FileManagerFactory;
import apoc.export.util.ProgressReporter;
import apoc.result.ProgressInfo;
import apoc.result.RowResult;
import apoc.result.VirtualNode;
import apoc.result.VirtualRelationship;
import apoc.systemdb.metadata.ExportMetadata;
import apoc.util.Util;
import apoc.util.collection.Iterables;
import org.apache.commons.lang3.tuple.Pair;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;
import org.neo4j.internal.kernel.api.procs.ProcedureCallContext;
import org.neo4j.internal.kernel.api.security.SecurityContext;
import org.neo4j.kernel.impl.coreapi.TransactionImpl;
import org.neo4j.procedure.Admin;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.Procedure;

import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;


@Extended
public class SystemDb {

    @Context
    public ApocConfig apocConfig;

    @Context
    public SecurityContext securityContext;

    @Context
    public ProcedureCallContext callContext;

    @Context
    public GraphDatabaseService db;

    public static class NodesAndRelationshipsResult {
        public List nodes;
        public List relationships;

        public NodesAndRelationshipsResult(List nodes, List relationships) {
            this.nodes = nodes;
            this.relationships = relationships;
        }
    }

    @Admin
    @Procedure(name = "apoc.systemdb.export.metadata")
    @Description("apoc.systemdb.export.metadata($conf) - export the apoc feature saved in system db (that is: customProcedures, triggers, uuids, and dvCatalogs) in multiple files called ...cypher")
    public Stream metadata(@Name(value = "config",defaultValue = "{}") Map config) {
        final SystemDbConfig conf = new SystemDbConfig(config);
        final String fileName = conf.getFileName();
        apocConfig.checkWriteAllowed(null, fileName);

        ProgressInfo progressInfo = new ProgressInfo(fileName, null, "cypher");
        ProgressReporter progressReporter = new ProgressReporter(null, null, progressInfo);
        ExportFileManager cypherFileManager = FileManagerFactory.createFileManager(fileName + ".cypher", true);
        withSystemDbTransaction(tx -> {
            tx.getAllNodes()
                    .stream()
                    .flatMap(node -> StreamSupport.stream(node.getLabels().spliterator(), false)
                            .map(label -> ExportMetadata.Type.from(label, conf))
                            .filter(Optional::isPresent)
                            .map(Optional::get)
                            .flatMap(type -> type.export(node, progressReporter).stream())
                    )
                    .collect(Collectors.groupingBy(Pair::getLeft, Collectors.toList()))
                    .forEach((fileNameSuffix, fileContent) -> {
                        try(PrintWriter writer = cypherFileManager.getPrintWriter(fileNameSuffix)) {
                            final String stringStatement = fileContent.stream()
                                    .map(Pair::getRight)
                                    .collect(Collectors.joining("\n"));
                            writer.write(stringStatement);
                        }
                    });
            return null;
        });

        progressReporter.done();
        return progressReporter.stream();
    }

    @Procedure
    public Stream graph() {
        Util.checkAdmin(securityContext, callContext,"apoc.systemdb.graph");
        return withSystemDbTransaction(tx -> {
            Map virtualNodes = new HashMap<>();
            for (Node node: tx.getAllNodes())  {
                virtualNodes.put(-node.getId(), new VirtualNode(-node.getId(), Iterables.asArray(Label.class, node.getLabels()), node.getAllProperties()));
            }

            List relationships = tx.getAllRelationships().stream().map(rel -> new VirtualRelationship(
                    -rel.getId(),
                    virtualNodes.get(-rel.getStartNodeId()),
                    virtualNodes.get(-rel.getEndNodeId()),
                    rel.getType(),
                    rel.getAllProperties())).collect(Collectors.toList()
            );
            return Stream.of(new NodesAndRelationshipsResult(Iterables.asList(virtualNodes.values()), relationships) );
        });
    }

    @Procedure
    public Stream execute(@Name("DDL commands, either a string or a list of strings") Object ddlStringOrList, @Name(value="params", defaultValue = "{}") Map params) {
        Util.checkAdmin(securityContext, callContext, "apoc.systemdb.execute");

        List commands;
        if (ddlStringOrList instanceof String) {
            commands = Collections.singletonList((String)ddlStringOrList);
        } else if (ddlStringOrList instanceof List) {
            commands = (List) ddlStringOrList;
        } else {
            throw new IllegalArgumentException("don't know how to handle " + ddlStringOrList + ". Supply either a string or a list of strings");
        }

        Transaction tx = apocConfig.getSystemDb().beginTx();  // we can't use try-with-resources otherwise tx gets closed too early
        return commands.stream().flatMap(command -> tx.execute(command, params).stream().map(RowResult::new)).onClose(() -> {
            boolean isOpen = ((TransactionImpl) tx).isOpen(); // no other way to check if a tx is still open
            if (isOpen) {
                tx.commit();
            }
            tx.close();
        });
    }

    private  T withSystemDbTransaction(Function function) {
        try (Transaction tx = apocConfig.getSystemDb().beginTx()) {
            T result = function.apply(tx);
            tx.commit();
            return result;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy