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

apoc.bolt.Bolt Maven / Gradle / Ivy

package apoc.bolt;

import apoc.Description;
import apoc.result.RowResult;
import apoc.result.VirtualNode;
import apoc.result.VirtualRelationship;
import apoc.util.UriResolver;
import apoc.util.Util;
import org.neo4j.driver.internal.InternalEntity;
import org.neo4j.driver.internal.InternalPath;
import org.neo4j.driver.internal.logging.JULogging;
import org.neo4j.driver.v1.*;
import org.neo4j.driver.v1.summary.SummaryCounters;
import org.neo4j.driver.v1.types.Node;
import org.neo4j.driver.v1.types.Path;
import org.neo4j.driver.v1.types.Relationship;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.Procedure;

import java.io.File;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import static apoc.util.MapUtil.map;

/**
 * @author AgileLARUS
 * @since 29.08.17
 */
public class Bolt {

    @Context
    public GraphDatabaseService db;

    @Procedure()
    @Description("apoc.bolt.load(url-or-key, kernelTransaction, params, config) - access to other databases via bolt for read")
    public Stream load(@Name("url") String url, @Name("kernelTransaction") String statement, @Name(value = "params", defaultValue = "{}") Map params, @Name(value = "config", defaultValue = "{}") Map config) throws URISyntaxException {
        if (params == null) params = Collections.emptyMap();
        if (config == null) config = Collections.emptyMap();
        boolean virtual = (boolean) config.getOrDefault("virtual", false);
        boolean addStatistics = (boolean) config.getOrDefault("statistics", false);
        boolean readOnly = (boolean) config.getOrDefault("readOnly", true);

        Config driverConfig = toDriverConfig(config.getOrDefault("driverConfig", map()));
        UriResolver uri = new UriResolver(url, "bolt");
        uri.initialize();

        try (Driver driver = GraphDatabase.driver(uri.getConfiguredUri(), uri.getToken(), driverConfig);
             Session session = driver.session()) {
            if (addStatistics)
                return Stream.of(new RowResult(toMap(runStatement(statement, session, params, readOnly).summary().counters())));
            else
                return getRowResultStream(virtual, session, params, statement, readOnly);
        } catch (Exception e) {
            throw new RuntimeException("It's not possible to create a connection due to: " + e.getMessage());
        }
    }

    @Procedure()
    @Description("apoc.bolt.execute(url-or-key, kernelTransaction, params, config) - access to other databases via bolt for read")
    public Stream execute(@Name("url") String url, @Name("kernelTransaction") String statement, @Name(value = "params", defaultValue = "{}") Map params, @Name(value = "config", defaultValue = "{}") Map config) throws URISyntaxException {
        Map configuration = new HashMap<>(config);
        configuration.put("readOnly", false);
        return load(url, statement, params, configuration);
    }

    private StatementResult runStatement(@Name("kernelTransaction") String statement, Session session, Map finalParams, boolean read) {
        if (read) return session.readTransaction((Transaction tx) -> tx.run(statement, finalParams));
        else return session.writeTransaction((Transaction tx) -> tx.run(statement, finalParams));
    }

    private Stream getRowResultStream(boolean virtual, Session session, Map params, String statement, boolean read) {
        Map nodesCache = new HashMap<>();
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(runStatement(statement, session, params, read), 0), true)
                .map(record -> new RowResult(record.asMap(value -> {
                    Object entity = value.asObject();
                    if (entity instanceof Node) return toNode(entity, virtual, nodesCache);
                    if (entity instanceof Relationship) return toRelationship(entity, virtual, nodesCache);
                    if (entity instanceof Path) return toPath(entity, virtual, nodesCache);
                    return entity;
                })));
    }

    private Object toNode(Object value, boolean virtual, Map nodesCache) {
        Value internalValue = ((InternalEntity) value).asValue();
        Node node = internalValue.asNode();
        if (virtual) {
            List