Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.eclipse.jnosql.mapping.graph.AbstractGraphTemplate Maven / Gradle / Ivy
/*
* Copyright (c) 2022 Contributors to the Eclipse Foundation
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php.
*
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
*
* Otavio Santana
*/
package org.eclipse.jnosql.mapping.graph;
import jakarta.data.exceptions.EmptyResultException;
import jakarta.data.exceptions.NonUniqueResultException;
import org.eclipse.jnosql.mapping.PreparedStatement;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.Transaction;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.eclipse.jnosql.communication.graph.CommunicationEntityConverter;
import org.eclipse.jnosql.communication.graph.GraphDatabaseManager;
import org.eclipse.jnosql.communication.graph.GraphTransactionUtil;
import org.eclipse.jnosql.mapping.IdNotFoundException;
import org.eclipse.jnosql.mapping.metadata.EntityMetadata;
import org.eclipse.jnosql.mapping.metadata.FieldMetadata;
import org.eclipse.jnosql.mapping.semistructured.AbstractSemiStructuredTemplate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static java.util.Objects.isNull;
import static java.util.Objects.requireNonNull;
import static org.apache.tinkerpop.gremlin.structure.T.id;
abstract class AbstractGraphTemplate extends AbstractSemiStructuredTemplate implements GraphTemplate {
private static final Function, GraphTraversal> INITIAL_VERTEX =
g -> (GraphTraversal) g;
private static final Function, GraphTraversal> INITIAL_EDGE =
g -> (GraphTraversal) g;
/**
* Retrieves the {@link GraphDatabaseManager} associated with this graph template.
*
* @return the {@link GraphDatabaseManager} associated with this graph template
*/
protected abstract GraphDatabaseManager manager();
/**
* Retrieves the {@link GraphTraversalSource} associated with this graph template.
*
* @return the {@link GraphTraversalSource} associated with this graph template
*/
protected abstract GraphTraversalSource traversal();
/**
* Retrieves the {@link Graph} associated with this graph template.
*
* @return the {@link Graph} associated with this graph template
*/
protected abstract Graph graph();
private GremlinExecutor gremlinExecutor;
@Override
public Optional find(K idValue) {
requireNonNull(idValue, "id is required");
Optional vertex = traversal().V(idValue).tryNext();
return vertex.map(v -> converter().toEntity(CommunicationEntityConverter.INSTANCE.apply(v)));
}
@Override
public void delete(T id) {
requireNonNull(id, "id is required");
traversal().V(id).toStream().forEach(Vertex::remove);
}
@Override
public void deleteEdge(T id) {
requireNonNull(id, "id is required");
traversal().E(id).toStream().forEach(Edge::remove);
}
@Override
public void deleteEdge(Iterable ids) {
requireNonNull(ids, "ids is required");
final Object[] edgeIds = StreamSupport.stream(ids.spliterator(), false).toArray(Object[]::new);
traversal().E(edgeIds).toStream().forEach(Edge::remove);
}
@Override
public EdgeEntity edge(O outgoing, String label, I incoming) {
requireNonNull(incoming, "incoming is required");
requireNonNull(label, "label is required");
requireNonNull(outgoing, "outgoing is required");
checkId(outgoing);
checkId(incoming);
if (isIdNull(outgoing)) {
throw new IllegalStateException("outgoing Id field is required");
}
if (isIdNull(incoming)) {
throw new IllegalStateException("incoming Id field is required");
}
Vertex outVertex = vertex(outgoing).orElseThrow(() -> new EmptyResultException("Outgoing entity does not found"));
Vertex inVertex = vertex(incoming).orElseThrow(() -> new EmptyResultException("Incoming entity does not found"));
final Predicate> predicate = t -> {
Edge e = t.get();
return e.inVertex().id().equals(inVertex.id())
&& e.outVertex().id().equals(outVertex.id());
};
Optional edge = traversal().V(outVertex.id())
.out(label).has(id, inVertex.id()).inE(label).filter(predicate).tryNext();
return edge.map(edge1 -> new DefaultEdgeEntity<>(edge1, incoming, outgoing))
.orElseGet(() -> new DefaultEdgeEntity<>(getEdge(label, outVertex, inVertex), incoming, outgoing));
}
@Override
public Collection edgesById(K id, Direction direction, String... labels) {
requireNonNull(id, "id is required");
requireNonNull(direction, "direction is required");
Iterator vertices = vertices(id);
if (vertices.hasNext()) {
List edges = new ArrayList<>();
vertices.next().edges(direction, labels).forEachRemaining(edges::add);
return edges.stream().map(e ->EdgeEntity.of(converter(), e)).toList();
}
return Collections.emptyList();
}
@SafeVarargs
@Override
public final Collection edgesById(K id, Direction direction, Supplier... labels) {
checkLabelsSupplier(labels);
return edgesByIdImpl(id, direction, Stream.of(labels).map(Supplier::get).toArray(String[]::new));
}
@Override
public Collection edgesById(K id, Direction direction) {
return edgesByIdImpl(id, direction);
}
@Override
public Collection edges(T entity, Direction direction, String... labels) {
return edgesImpl(entity, direction, labels);
}
@SafeVarargs
@Override
public final Collection edges(T entity, Direction direction, Supplier... labels) {
checkLabelsSupplier(labels);
return edgesImpl(entity, direction, Stream.of(labels).map(Supplier::get).toArray(String[]::new));
}
@Override
public Collection edges(T entity, Direction direction) {
return edgesImpl(entity, direction);
}
@Override
public Optional edge(E edgeId) {
requireNonNull(edgeId, "edgeId is required");
Optional edgeOptional = traversal().E(edgeId).tryNext();
if (edgeOptional.isPresent()) {
Edge edge = edgeOptional.get();
return Optional.of(EdgeEntity.of(converter(), edge));
}
return Optional.empty();
}
@Override
public VertexTraversal traversalVertex(Object... vertexIds) {
if (Stream.of(vertexIds).anyMatch(Objects::isNull)) {
throw new IllegalStateException("No one vertexId element cannot be null");
}
return new DefaultVertexTraversal(() -> traversal().V(vertexIds), INITIAL_VERTEX, converter());
}
@Override
public EdgeTraversal traversalEdge(Object... edgeIds) {
if (Stream.of(edgeIds).anyMatch(Objects::isNull)) {
throw new IllegalStateException("No one edgeId element cannot be null");
}
return new DefaultEdgeTraversal(() -> traversal().E(edgeIds), INITIAL_EDGE, converter());
}
@Override
public Transaction transaction() {
return graph().tx();
}
@Override
public Stream gremlin(String gremlin) {
requireNonNull(gremlin, "gremlin is required");
return executor().executeGremlin(traversal(), gremlin);
}
@Override
public Optional gremlinSingleResult(String gremlin) {
Stream entities = gremlin(gremlin);
final Iterator iterator = entities.iterator();
if (!iterator.hasNext()) {
return Optional.empty();
}
final T entity = iterator.next();
if (!iterator.hasNext()) {
return Optional.ofNullable(entity);
}
throw new NonUniqueResultException("The gremlin query returns more than one result: " + gremlin);
}
@Override
public PreparedStatement gremlinPrepare(String gremlin){
requireNonNull(gremlin, "query is required");
return new DefaultPreparedStatement(executor(), gremlin, traversal());
}
private void checkId(T entity) {
EntityMetadata entityMetadata = entities().get(entity.getClass());
entityMetadata.id().orElseThrow(() -> IdNotFoundException.newInstance(entity.getClass()));
}
private boolean isIdNull(T entity) {
EntityMetadata entityMetadata = entities().get(entity.getClass());
FieldMetadata field = entityMetadata.id().orElseThrow(() -> IdNotFoundException.newInstance(entity.getClass()));
return isNull(field.read(entity));
}
private Optional vertex(T entity) {
EntityMetadata entityMetadata = entities().get(entity.getClass());
FieldMetadata field = entityMetadata.id().orElseThrow(() -> IdNotFoundException.newInstance(entity.getClass()));
Object id = field.read(entity);
Iterator vertices = vertices(id);
if (vertices.hasNext()) {
return Optional.of(vertices.next());
}
return Optional.empty();
}
protected Iterator vertices(Object id) {
return graph().vertices(id);
}
private Edge getEdge(String label, Vertex outVertex, Vertex inVertex) {
final Edge edge = outVertex.addEdge(label, inVertex);
GraphTransactionUtil.transaction(graph());
return edge;
}
private void checkLabelsSupplier(Supplier[] labels) {
if (Stream.of(labels).anyMatch(Objects::isNull)) {
throw new IllegalStateException("Item cannot be null");
}
}
private Collection edgesByIdImpl(K id, Direction direction, String... labels) {
requireNonNull(id, "id is required");
requireNonNull(direction, "direction is required");
Iterator vertices = vertices(id);
if (vertices.hasNext()) {
List edges = new ArrayList<>();
vertices.next().edges(direction, labels).forEachRemaining(edges::add);
return edges.stream().map(e -> EdgeEntity.of(converter(), e)).toList();
}
return Collections.emptyList();
}
private Collection edgesImpl(T entity, Direction direction, String... labels) {
requireNonNull(entity, "entity is required");
if (isIdNull(entity)) {
throw new IllegalStateException("Entity id is required");
}
Optional vertex = vertex(entity);
if (vertex.isEmpty()) {
return Collections.emptyList();
}
Object id = vertex.orElseThrow().id();
return edgesByIdImpl(id, direction, labels);
}
private GremlinExecutor executor() {
if (Objects.isNull(gremlinExecutor)) {
this.gremlinExecutor = new GremlinExecutor(converter());
}
return gremlinExecutor;
}
}