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

org.liquigraph.trinity.neo4jv3.EmbeddedClient Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017 the original author or authors.
 *
 * 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 org.liquigraph.trinity.neo4jv3;

import org.liquigraph.trinity.ClosedTransaction;
import org.liquigraph.trinity.CypherClient;
import org.liquigraph.trinity.Data;
import org.liquigraph.trinity.Fault;
import org.liquigraph.trinity.Row;
import org.liquigraph.trinity.internal.FunctionalEither;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.QueryExecutionException;
import org.neo4j.graphdb.Result;
import org.neo4j.graphdb.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.liquigraph.trinity.internal.Streams.createStream;
import static java.util.Arrays.stream;

public final class EmbeddedClient implements CypherClient {
    private static final Logger LOGGER = LoggerFactory.getLogger(EmbeddedClient.class);

    private GraphDatabaseService graphDatabase;

    public EmbeddedClient(GraphDatabaseService graphDatabase) {
        this.graphDatabase = graphDatabase;
    }

    @Override
    public FunctionalEither, List> runSingleTransaction(String query, String... queries) {
        LOGGER.debug("About to run {} queries in a single transaction", 1 + queries.length);
        try (Transaction transaction = graphDatabase.beginTx()) {
            return map(
                executeAll(query, queries),
                transaction,
                Transaction::failure,
                Transaction::success,
                Function.identity()
            );
        }
    }

    @Override
    public FunctionalEither, OngoingLocalTransaction> openTransaction(String... queries) {
        LOGGER.debug("About to open a transaction and run {} queries", queries.length);
        return executeInTransaction(graphDatabase.beginTx(), queries);
    }

    @Override
    public FunctionalEither, OngoingLocalTransaction> execute(OngoingLocalTransaction ongoingTransaction, String... queries) {
        LOGGER.debug("About to run {} queries in currently open transaction", queries.length);
        return executeInTransaction(ongoingTransaction.getTransaction(), queries);
    }

    @Override
    public FunctionalEither, ClosedTransaction> commit(OngoingLocalTransaction transaction, String... queries) {
        LOGGER.debug("About to run {} queries and commit open transaction", queries.length);
        return map(
            executeAll(stream(queries)),
            transaction.getTransaction(),
            this::rollbackAndClose,
            (tx) -> {},
            (data) -> new ClosedTransaction(data, false)
        );
    }

    @Override
    public FunctionalEither, ClosedTransaction> rollback(OngoingLocalTransaction transaction) {
        LOGGER.debug("About to roll back open transaction");
        return map(
            Collections.emptyList(),
            transaction.getTransaction(),
            this::rollbackAndClose,
            this::rollbackAndClose,
            (data) -> new ClosedTransaction(data, true)
        );
    }

    private FunctionalEither, OngoingLocalTransaction> executeInTransaction(Transaction transaction, String[] queries) {
        return map(
            executeAll(stream(queries)),
            transaction,
            this::rollbackAndClose,
            (tx) -> {},
            (data) -> new OngoingLocalTransaction(transaction, data)
        );
    }

    private  FunctionalEither, T> map(List> results,
                                                     Transaction transaction,
                                                     Consumer onError,
                                                     Consumer onSuccess,
                                                     Function, T> resultMapper) {

        List errors = collectErrors(results);
        if (!errors.isEmpty()) {
            LOGGER.warn("Encountered {} errors on {} queries", errors.size(), results.size());
            onError.accept(transaction);
            return FunctionalEither.left(errors);
        }

        onSuccess.accept(transaction);
        return FunctionalEither.right(this.collectResults(results, resultMapper));
    }

    private List collectErrors(List> rawResults) {
        return rawResults.stream()
            .filter(FunctionalEither::isLeft)
            .map(FunctionalEither::getLeft)
            .collect(Collectors.toList());
    }

    private  T collectResults(List> rawResults, Function, T> resultExtractor) {
        return resultExtractor.apply(
            rawResults.stream()
                .map(FunctionalEither::getRight)
                .map(this::asResultData)
                .collect(Collectors.toList()));
    }

    private List> executeAll(String query, String[] queries) {
        Stream allQueries = Stream.concat(Stream.of(query), Arrays.stream(queries));
        return executeAll(allQueries);
    }

    private List> executeAll(Stream stream) {
        return stream.map(this::execute).collect(Collectors.toList());
    }

    private FunctionalEither execute(String query) {
        try {
            return FunctionalEither.right(graphDatabase.execute(query));
        }
        catch (QueryExecutionException exception) {
            LOGGER.error("An unexpected error happened while executing the Cypher query", exception);
            return FunctionalEither.left(new Fault(exception.getStatusCode(), exception.getMessage()));
        }
    }

    private Data asResultData(Result result) {
        return new Data(result.columns(), rowsOf(result));
    }

    private List rowsOf(Result result) {
        return createStream(result, Spliterator.ORDERED)
            .map(Row::new)
            .collect(Collectors.toList());
    }

    private void rollbackAndClose(Transaction tx) {
        LOGGER.warn("Rolling back and closing the transaction");
        tx.failure();
        tx.close();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy