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

apoc.bolt.BoltConnection Maven / Gradle / Ivy

package apoc.bolt;

import apoc.result.RowResult;
import apoc.result.VirtualNode;
import apoc.result.VirtualRelationship;
import apoc.util.MissingDependencyException;
import apoc.util.UriResolver;
import apoc.util.Util;
import apoc.util.collection.Iterators;
import org.apache.commons.lang3.StringUtils;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.Record;
import org.neo4j.driver.Result;
import org.neo4j.driver.Session;
import org.neo4j.driver.Transaction;
import org.neo4j.driver.Value;
import org.neo4j.driver.internal.InternalEntity;
import org.neo4j.driver.internal.InternalPath;
import org.neo4j.driver.summary.SummaryCounters;
import org.neo4j.driver.types.Node;
import org.neo4j.driver.types.Path;
import org.neo4j.driver.types.Relationship;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.RelationshipType;

import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static apoc.util.MapUtil.map;

public class BoltConnection {
    private final BoltConfig config;
    private final UriResolver resolver;

    public BoltConnection(BoltConfig config, UriResolver resolver) {
        this.config = config;
        this.resolver = resolver;
    }
    
    public static BoltConnection from(Map config, String url) throws URISyntaxException {
        try {
            final UriResolver resolver = new UriResolver(url, "bolt");
            resolver.initialize();
            return new BoltConnection(new BoltConfig(config), resolver);
        } catch (NoClassDefFoundError e) {
            throw new MissingDependencyException("Cannot find the needed jar into the plugins folder in order to use . \n" +
                    "Please see the documentation: https://neo4j.com/labs/apoc/4.4/database-integration/bolt-neo4j/#_install_dependencies");
        }
    }

    // methods from Bolt.java
    public Stream loadFromSession(String statement, Map params) {
        return withDriverAndSession(session -> {
            if (config.isAddStatistics()) {
                Result statementResult = session.run(statement, params);
                SummaryCounters counters = statementResult.consume().counters();
                return Stream.of(new RowResult(toMap(counters)));
            } else
                return getRowResultStream(config.isVirtual(), session, params, statement);
        });
    }

    public Stream loadFromLocal(String localStatement, String remoteStatement, GraphDatabaseService db) {
        return withDriverAndSession(session -> {
            try (org.neo4j.graphdb.Transaction tx = db.beginTx()) {
                final org.neo4j.graphdb.Result localResult = tx.execute(localStatement, config.getLocalParams());
                String withColumns = "WITH " + localResult.columns().stream()
                        .map(c -> "$" + c + " AS " + c)
                        .collect(Collectors.joining(", ")) + "\n";
                Map nodesCache = new HashMap<>();
                List response = new ArrayList<>();
                while (localResult.hasNext()) {
                    final Result statementResult;
                    Map row = localResult.next();
                    if (config.isStreamStatements()) {
                        final String statement = (String) row.get("statement");
                        if (StringUtils.isBlank(statement)) continue;
                        final Map params = Collections.singletonMap("params", row.getOrDefault("params", Collections.emptyMap()));
                        statementResult = session.run(statement, params);
                    } else {
                        statementResult = session.run(withColumns + remoteStatement, row);
                    }
                    if (config.isStreamStatements()) {
                        response.add(new RowResult(toMap(statementResult.consume().counters())));
                    } else {
                        response.addAll(
                                statementResult.stream()
                                        .map(record -> buildRowResult(record, nodesCache, config.isVirtual()))
                                        .collect(Collectors.toList())
                        );
                    }
                }
                return response.stream();
            }
        });
    }
    
    private  Stream withDriverAndSession(Function> funSession) {
        return withDriver(driver -> withSession(driver,funSession));
    }
    
    private  Stream withDriver(Function> function) {
        Driver driver = GraphDatabase.driver(resolver.getConfiguredUri(), resolver.getToken(), config.getDriverConfig());
        return function.apply(driver).onClose(driver::close);
    }

    private  Stream withSession(Driver driver, Function> function) {
        Session session = driver.session(config.getSessionConfig());
        return function.apply(session).onClose(session::close);
    }

    private  Stream withTransaction(Session session, Function> function) {
        Transaction transaction = session.beginTransaction();
        return function.apply(transaction).onClose(transaction::commit).onClose(transaction::close);
    }

    private RowResult buildRowResult(Record record, Map nodesCache, boolean virtual) {
        return 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 Stream getRowResultStream(boolean virtual, Session session, Map params, String statement) {
        Map nodesCache = new HashMap<>();

        return withTransaction(session, tx -> {
            ClosedAwareDelegatingIterator iterator = new ClosedAwareDelegatingIterator(tx.run(statement, params));
            return Iterators.stream(iterator).map(record -> buildRowResult(record, nodesCache, virtual));
        });
    }

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