diff --git a/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/communication/graph/DefaultGraphDatabaseManager.java b/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/communication/graph/DefaultGraphDatabaseManager.java index e52a9f93f..62e99d3fb 100644 --- a/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/communication/graph/DefaultGraphDatabaseManager.java +++ b/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/communication/graph/DefaultGraphDatabaseManager.java @@ -71,6 +71,7 @@ public CommunicationEntity insert(CommunicationEntity entity) { entity.elements().forEach(e -> vertex.property(e.name(), ValueUtil.convert(e.value()))); entity.add(ID_PROPERTY, vertex.id()); vertex.property(ID_PROPERTY, vertex.id()); + GraphTransactionUtil.transaction(graph); return entity; } @@ -102,6 +103,7 @@ public CommunicationEntity update(CommunicationEntity entity) { Vertex vertex = vertices.next(); entity.elements().forEach(e -> vertex.property(e.name(), ValueUtil.convert(e.value()))); }); + GraphTransactionUtil.transaction(graph); return entity; } @@ -122,6 +124,7 @@ public void delete(DeleteQuery query) { }); traversal.drop().iterate(); + GraphTransactionUtil.transaction(graph); } @Override diff --git a/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/GraphTransactionUtil.java b/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/communication/graph/GraphTransactionUtil.java similarity index 51% rename from jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/GraphTransactionUtil.java rename to jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/communication/graph/GraphTransactionUtil.java index 7a84acbe9..32dc15866 100644 --- a/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/GraphTransactionUtil.java +++ b/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/communication/graph/GraphTransactionUtil.java @@ -12,7 +12,7 @@ * * Otavio Santana */ -package org.eclipse.jnosql.mapping.graph; +package org.eclipse.jnosql.communication.graph; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.Transaction; @@ -24,9 +24,10 @@ import static org.eclipse.jnosql.mapping.core.config.MappingConfigurations.GRAPH_TRANSACTION_AUTOMATIC; /** - * Utilitarian to {@link Transaction} + * Utility class providing methods to manage transactions in a graph database. + * This class offers functionality to lock and unlock transactions, as well as automatic transaction management. */ -final class GraphTransactionUtil { +public final class GraphTransactionUtil { private static final Logger LOGGER = Logger.getLogger(GraphTransactionUtil.class.getName()); private static final ThreadLocal THREAD_LOCAL = new ThreadLocal<>(); @@ -35,53 +36,60 @@ private GraphTransactionUtil() { } /** - * Holds the current transaction to don't allow a {@link Transaction#commit()} + * Locks the current transaction, preventing it from being committed. * - * @param transaction the {@link Transaction} + * @param transaction the transaction to lock */ - static void lock(Transaction transaction) { + public static void lock(Transaction transaction) { THREAD_LOCAL.set(transaction); } /** - * Unlocks the {@link Transaction} of the current thread + * Unlocks the current transaction. + * Allows the transaction to be committed. */ - static void unlock() { + public static void unlock() { THREAD_LOCAL.remove(); } /** - * Checks if possible to {@link Transaction#commit()}, - * if checks it the {@link Transaction} holds and if it is defined as an automatic transaction. + * Automatically commits a transaction if enabled and not locked. * - * @param graph the graph + * @param graph the graph instance */ - static void transaction(Graph graph) { - if (isAutomatic() && isNotLock() && Objects.nonNull(graph)) { - try { - Transaction transaction = graph.tx(); - if (transaction != null) { - transaction.commit(); + public static void transaction(Graph graph) { + synchronized (graph) { + if (isAutomatic() && isNotLock() && Objects.nonNull(graph)) { + try { + Transaction transaction = graph.tx(); + if (transaction != null) { + transaction.commit(); + } + } catch (Exception exception) { + LOGGER.info("Unable to do transaction automatically in the graph, reason: " + + exception.getMessage()); } - } catch (Exception exception) { - LOGGER.info("Unable to do transaction automatically in the graph, reason: " + - exception.getMessage()); - } + } } } /** - * Check if the transaction is enabled + * Checks if automatic transaction management is enabled. * - * @return Check if the transaction is enabled + * @return true if automatic transaction management is enabled, false otherwise */ - static boolean isAutomatic() { + public static boolean isAutomatic() { return MicroProfileSettings.INSTANCE.get(GRAPH_TRANSACTION_AUTOMATIC, String.class) .map(Boolean::valueOf) .orElse(true); } + /** + * Checks if the current transaction is not locked. + * + * @return true if the current transaction is not locked, false otherwise + */ private static boolean isNotLock() { return THREAD_LOCAL.get() == null; } diff --git a/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/AbstractGraphTemplate.java b/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/AbstractGraphTemplate.java index 36730746d..59f1f7afc 100644 --- a/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/AbstractGraphTemplate.java +++ b/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/AbstractGraphTemplate.java @@ -11,9 +11,9 @@ 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.CommunicationException; 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; @@ -198,7 +198,7 @@ public EdgeTraversal traversalEdge(Object... edgeIds) { if (Stream.of(edgeIds).anyMatch(Objects::isNull)) { throw new IllegalStateException("No one edgeId element cannot be null"); } - return null; + return new DefaultEdgeTraversal(() -> traversal().E(edgeIds), INITIAL_EDGE, converter()); } @Override diff --git a/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/DefaultEdgeTraversal.java b/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/DefaultEdgeTraversal.java index 71f373ed5..4397c8020 100644 --- a/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/DefaultEdgeTraversal.java +++ b/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/DefaultEdgeTraversal.java @@ -131,7 +131,7 @@ public EdgeTraversal dedup(String... labels) { @Override public Optional next() { Optional edgeOptional = flow.apply(supplier.get()).tryNext(); - return edgeOptional.map(edge ->EdgeEntity.of(converter, edge)); + return edgeOptional.map(edge -> EdgeEntity.of(converter, edge)); } @Override diff --git a/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/EdgeEntity.java b/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/EdgeEntity.java index 6fa97a85a..1395ae87c 100644 --- a/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/EdgeEntity.java +++ b/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/EdgeEntity.java @@ -145,8 +145,8 @@ static EdgeEntity of(EntityConverter converter, Edge edge) { Objects.requireNonNull(converter, "converter is required"); Objects.requireNonNull(edge, "edge is required"); var entityConverter = CommunicationEntityConverter.INSTANCE; - return new DefaultEdgeEntity<>(edge, converter.toEntity(entityConverter.apply(edge.outVertex())), - converter.toEntity(entityConverter.apply(edge.inVertex()))); + return new DefaultEdgeEntity<>(edge, converter.toEntity(entityConverter.apply(edge.inVertex())), + converter.toEntity(entityConverter.apply(edge.outVertex()))); } } diff --git a/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/TransactionalInterceptor.java b/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/TransactionalInterceptor.java index 87c0cb8e9..5eece4be2 100644 --- a/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/TransactionalInterceptor.java +++ b/jnosql-mapping/jnosql-mapping-graph/src/main/java/org/eclipse/jnosql/mapping/graph/TransactionalInterceptor.java @@ -24,6 +24,7 @@ import jakarta.interceptor.AroundInvoke; import jakarta.interceptor.Interceptor; import jakarta.interceptor.InvocationContext; +import org.eclipse.jnosql.communication.graph.GraphTransactionUtil; @Transactional diff --git a/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/AbstractTraversalTest.java b/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/AbstractTraversalTest.java new file mode 100644 index 000000000..6b379ce51 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/AbstractTraversalTest.java @@ -0,0 +1,96 @@ +/* + * + * 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.inject.Inject; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.mapping.graph.entities.Book; +import org.eclipse.jnosql.mapping.graph.entities.Person; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +public abstract class AbstractTraversalTest { + + static final String READS = "reads"; + + @Inject + protected GraphTemplate graphTemplate; + + @Inject + protected Graph graph; + + + protected Person otavio; + protected Person poliana; + protected Person paulo; + + protected Book shack; + protected Book license; + protected Book effectiveJava; + + protected EdgeEntity reads; + protected EdgeEntity reads2; + protected EdgeEntity reads3; + + @BeforeEach + public void setUp() { + + graph.traversal().V().toList().forEach(Vertex::remove); + graph.traversal().E().toList().forEach(Edge::remove); + + otavio = graphTemplate.insert(Person.builder().withAge(27) + .withName("Otavio").build()); + poliana = graphTemplate.insert(Person.builder().withAge(26) + .withName("Poliana").build()); + paulo = graphTemplate.insert(Person.builder().withAge(50) + .withName("Paulo").build()); + + shack = graphTemplate.insert(Book.builder().withAge(2007).withName("The Shack").build()); + license = graphTemplate.insert(Book.builder().withAge(2013).withName("Software License").build()); + effectiveJava = graphTemplate.insert(Book.builder().withAge(2001).withName("Effective Java").build()); + + + reads = graphTemplate.edge(otavio, READS, effectiveJava); + reads2 = graphTemplate.edge(poliana, READS, shack); + reads3 = graphTemplate.edge(paulo, READS, license); + + reads.add("motivation", "hobby"); + reads.add("language", "Java"); + reads2.add("motivation", "love"); + reads3.add("motivation", "job"); + } + + @AfterEach + public void after() { + graphTemplate.delete(otavio.getId()); + graphTemplate.delete(poliana.getId()); + graphTemplate.delete(paulo.getId()); + + graphTemplate.deleteEdge(shack.getId()); + graphTemplate.deleteEdge(license.getId()); + graphTemplate.deleteEdge(effectiveJava.getId()); + + reads.delete(); + reads2.delete(); + reads3.delete(); + + graph.traversal().V().toList().forEach(Vertex::remove); + graph.traversal().E().toList().forEach(Edge::remove); + } +} diff --git a/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/BookTemplateTest.java b/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/BookTemplateTest.java new file mode 100644 index 000000000..f44e44e89 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/BookTemplateTest.java @@ -0,0 +1,98 @@ +/* + * 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.inject.Inject; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Transaction; +import org.apache.tinkerpop.gremlin.structure.Transaction.Status; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.graph.entities.Book; +import org.eclipse.jnosql.mapping.graph.entities.BookTemplate; +import org.eclipse.jnosql.mapping.graph.spi.GraphExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.atomic.AtomicReference; + +import static org.apache.tinkerpop.gremlin.structure.Transaction.Status.COMMIT; +import static org.apache.tinkerpop.gremlin.structure.Transaction.Status.ROLLBACK; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) +@AddPackages(GraphProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) +class BookTemplateTest { + + @Inject + private BookTemplate template; + + @Inject + private Graph graph; + + @Test + void shouldSaveWithTransaction() { + AtomicReference status = new AtomicReference<>(); + + Book book = Book.builder().withName("The Book").build(); + Transaction transaction = graph.tx(); + transaction.addTransactionListener(status::set); + template.insert(book); + assertFalse(transaction.isOpen()); + assertEquals(COMMIT, status.get()); + } + + @Test + void shouldSaveWithRollback() { + AtomicReference status = new AtomicReference<>(); + + Book book = Book.builder().withName("The Book").build(); + Transaction transaction = graph.tx(); + transaction.addTransactionListener(status::set); + try { + template.insertException(book); + assert false; + }catch (Exception ignored){ + + } + + assertFalse(transaction.isOpen()); + assertEquals(ROLLBACK, status.get()); + } + + @Test + void shouldUseAutomaticNormalTransaction() { + AtomicReference status = new AtomicReference<>(); + + Book book = Book.builder().withName("The Book").build(); + Transaction transaction = graph.tx(); + transaction.addTransactionListener(status::set); + assertNull(status.get()); + template.normalInsertion(book); + assertEquals(COMMIT, status.get()); + assertFalse(transaction.isOpen()); + } +} + diff --git a/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/DefaultEdgeTraversalTest.java b/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/DefaultEdgeTraversalTest.java new file mode 100644 index 000000000..44acf592d --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/DefaultEdgeTraversalTest.java @@ -0,0 +1,540 @@ +/* + * 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.NonUniqueResultException; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.structure.T; +import org.assertj.core.api.SoftAssertions; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.graph.entities.Animal; +import org.eclipse.jnosql.mapping.graph.entities.Book; +import org.eclipse.jnosql.mapping.graph.entities.Person; +import org.eclipse.jnosql.mapping.graph.spi.GraphExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.toList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) +@AddPackages(GraphProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) +class DefaultEdgeTraversalTest extends AbstractTraversalTest { + + @Test + void shouldReturnErrorWhenEdgeIdIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalEdge(null)); + } + + @Test + void shouldReturnEdgeId() { + Optional edgeEntity = graphTemplate.traversalEdge(reads.id()) + .next(); + + assertTrue(edgeEntity.isPresent()); + assertEquals(reads.id(), edgeEntity.get().id()); + } + + @Test + void shouldReturnOutE() { + List edges = graphTemplate.traversalVertex().outE(READS) + .stream() + .collect(toList()); + + assertEquals(3, edges.size()); + assertThat(edges).contains(reads, reads2, reads3); + } + + @Test + void shouldReturnOutEWithSupplier() { + List edges = graphTemplate.traversalVertex().outE(() -> READS) + .stream() + .collect(toList()); + + assertEquals(3, edges.size()); + assertThat(edges).contains(reads, reads2, reads3); + } + + @Test + void shouldReturnErrorOutEWhenIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().outE((String) null) + .stream() + .collect(toList())); + } + + @Test + void shouldReturnInE() { + List edges = graphTemplate.traversalVertex().inE(READS) + .stream() + .collect(toList()); + + assertEquals(3, edges.size()); + assertThat(edges).contains(reads, reads2, reads3); + } + + @Test + void shouldReturnInEWitSupplier() { + List edges = graphTemplate.traversalVertex().inE(() -> READS) + .stream() + .collect(toList()); + + assertEquals(3, edges.size()); + assertThat(edges).contains(reads, reads2, reads3); + } + + + @Test + void shouldReturnErrorWhenInEIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().inE((String) null) + .stream() + .collect(toList())); + + } + + @Test + void shouldReturnBothE() { + List edges = graphTemplate.traversalVertex().bothE(READS) + .stream() + .toList(); + + assertEquals(6, edges.size()); + } + + @Test + void shouldReturnBothEWithSupplier() { + List edges = graphTemplate.traversalVertex().bothE(() -> READS) + .stream() + .toList(); + + assertEquals(6, edges.size()); + } + + @Test + void shouldReturnErrorWhenBothEIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().bothE((String) null) + .stream() + .collect(toList())); + } + + + @Test + void shouldReturnOut() { + List people = graphTemplate.traversalVertex().outE(READS).outV().result().collect(toList()); + assertEquals(3, people.size()); + assertThat(people).contains(poliana, otavio, paulo); + } + + @Test + void shouldReturnIn() { + List books = graphTemplate.traversalVertex().outE(READS).inV().result().collect(toList()); + assertEquals(3, books.size()); + assertThat(books).contains(shack, effectiveJava, license); + } + + + @Test + void shouldReturnBoth() { + List entities = graphTemplate.traversalVertex().outE(READS).bothV().result().collect(toList()); + assertEquals(6, entities.size()); + assertThat(entities).contains(shack, effectiveJava, license, paulo, otavio, poliana); + } + + + @Test + void shouldHasPropertyFromAccessor() { + + Optional edgeEntity = graphTemplate.traversalVertex() + .outE(READS) + .has(T.id, "notFound").next(); + + assertFalse(edgeEntity.isPresent()); + } + + + @Test + void shouldHasProperty() { + Optional edgeEntity = graphTemplate.traversalVertex() + .outE(READS) + .has("motivation", "hobby").next(); + + assertTrue(edgeEntity.isPresent()); + assertEquals(reads.id(), edgeEntity.get().id()); + } + + @Test + void shouldHasSupplierProperty() { + Optional edgeEntity = graphTemplate.traversalVertex() + .outE(READS) + .has(() -> "motivation", "hobby").next(); + + assertTrue(edgeEntity.isPresent()); + assertEquals(reads.id(), edgeEntity.get().id()); + } + + @Test + void shouldHasPropertyPredicate() { + + Optional edgeEntity = graphTemplate.traversalVertex() + .outE(READS) + .has("motivation", P.eq("hobby")).next(); + + assertTrue(edgeEntity.isPresent()); + assertEquals(reads.id(), edgeEntity.get().id()); + } + + + @Test + void shouldHasPropertyKeySupplierPredicate() { + + Optional edgeEntity = graphTemplate.traversalVertex() + .outE(READS) + .has(() -> "motivation", P.eq("hobby")).next(); + + assertTrue(edgeEntity.isPresent()); + assertEquals(reads.id(), edgeEntity.get().id()); + } + + + @Test + void shouldReturnErrorWhenHasPropertyWhenKeyIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex() + .outE(READS) + .has((String) null, "hobby").next()); + } + + @Test + void shouldReturnErrorWhenHasPropertyWhenValueIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex() + .outE(READS) + .has("motivation", null).next()); + } + + @Test + void shouldHasNot() { + List edgeEntities = graphTemplate.traversalVertex() + .outE(READS).hasNot("language") + .stream() + .toList(); + + assertEquals(2, edgeEntities.size()); + } + + @Test + void shouldCount() { + long count = graphTemplate.traversalVertex().outE(READS).count(); + assertEquals(3L, count); + } + + @Test + void shouldReturnZeroWhenCountIsEmpty() { + long count = graphTemplate.traversalVertex().outE("WRITES").count(); + assertEquals(0L, count); + } + + @Test + void shouldReturnErrorWhenHasNotIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().outE(READS).hasNot((String) null)); + } + + + @Test + void shouldDefinesLimit() { + long count = graphTemplate.traversalEdge().limit(1L).count(); + assertEquals(1L, count); + assertNotEquals(graphTemplate.traversalEdge().count(), count); + } + + @Test + void shouldDefinesRange() { + long count = graphTemplate.traversalEdge().range(1, 3).count(); + assertEquals(2L, count); + assertNotEquals(graphTemplate.traversalEdge().count(), count); + } + + @Test + void shouldMapValuesAsStream() { + List> maps = graphTemplate.traversalVertex().inE("reads") + .valueMap("motivation").stream().toList(); + + assertFalse(maps.isEmpty()); + assertEquals(3, maps.size()); + + List names = new ArrayList<>(); + + maps.forEach(m -> names.add(m.get("motivation").toString())); + + assertThat(names).contains("hobby", "love", "job"); + } + + @Test + void shouldMapValuesAsStreamLimit() { + List> maps = graphTemplate.traversalVertex().inE("reads") + .valueMap("motivation").next(2).toList(); + + assertFalse(maps.isEmpty()); + assertEquals(2, maps.size()); + } + + + @Test + void shouldReturnMapValueAsEmptyStream() { + Stream> stream = graphTemplate.traversalVertex().inE("reads") + .valueMap("noFoundProperty").stream(); + assertTrue(stream.allMatch(m -> Objects.isNull(m.get("noFoundProperty")))); + } + + @Test + void shouldReturnNext() { + Map map = graphTemplate.traversalVertex().inE("reads") + .valueMap("motivation").next(); + + assertNotNull(map); + assertFalse(map.isEmpty()); + } + + + @Test + void shouldReturnHas() { + Animal lion = graphTemplate.insert(new Animal("lion")); + Animal snake = graphTemplate.insert(new Animal("snake")); + Animal mouse = graphTemplate.insert(new Animal("mouse")); + Animal plant = graphTemplate.insert(new Animal("plant")); + + graphTemplate.edge(lion, "eats", snake).add("when", "night"); + graphTemplate.edge(snake, "eats", mouse); + graphTemplate.edge(mouse, "eats", plant); + + + Optional result = graphTemplate.traversalEdge().has("when").next(); + assertNotNull(result); + + graphTemplate.deleteEdge(lion.getId()); + } + + @Test + void shouldRepeatTimesTraversal() { + Animal lion = graphTemplate.insert(new Animal("lion")); + Animal snake = graphTemplate.insert(new Animal("snake")); + Animal mouse = graphTemplate.insert(new Animal("mouse")); + Animal plant = graphTemplate.insert(new Animal("plant")); + + graphTemplate.edge(lion, "eats", snake).add("when", "night"); + graphTemplate.edge(snake, "eats", mouse); + graphTemplate.edge(mouse, "eats", plant); + Optional result = graphTemplate.traversalEdge().repeat().has("when").times(2).next(); + assertNotNull(result); + assertEquals(snake, result.get().incoming()); + assertEquals(lion, result.get().outgoing()); + } + + @Test + void shouldRepeatUntilTraversal() { + Animal lion = graphTemplate.insert(new Animal("lion")); + Animal snake = graphTemplate.insert(new Animal("snake")); + Animal mouse = graphTemplate.insert(new Animal("mouse")); + Animal plant = graphTemplate.insert(new Animal("plant")); + + graphTemplate.edge(lion, "eats", snake).add("when", "night"); + graphTemplate.edge(snake, "eats", mouse); + graphTemplate.edge(mouse, "eats", plant); + + Optional result = graphTemplate.traversalEdge().repeat().has("when") + .until().has("when").next(); + + assertTrue(result.isPresent()); + + assertEquals(snake, result.get().incoming()); + assertEquals(lion, result.get().outgoing()); + + } + + @Test + void shouldRepeatUntilHasValueTraversal() { + Animal lion = graphTemplate.insert(new Animal("lion")); + Animal snake = graphTemplate.insert(new Animal("snake")); + Animal mouse = graphTemplate.insert(new Animal("mouse")); + Animal plant = graphTemplate.insert(new Animal("plant")); + + graphTemplate.edge(lion, "eats", snake).add("when", "night"); + graphTemplate.edge(snake, "eats", mouse); + graphTemplate.edge(mouse, "eats", plant); + + Optional result = graphTemplate.traversalEdge().repeat().has("when") + .until().has("when", "night").next(); + + assertTrue(result.isPresent()); + + assertEquals(snake, result.get().incoming()); + assertEquals(lion, result.get().outgoing()); + + } + + @Test + void shouldRepeatUntilHasPredicateTraversal() { + Animal lion = graphTemplate.insert(new Animal("lion")); + Animal snake = graphTemplate.insert(new Animal("snake")); + Animal mouse = graphTemplate.insert(new Animal("mouse")); + Animal plant = graphTemplate.insert(new Animal("plant")); + + graphTemplate.edge(lion, "eats", snake).add("when", "night"); + graphTemplate.edge(snake, "eats", mouse); + graphTemplate.edge(mouse, "eats", plant); + + EdgeEntity result = graphTemplate.traversalEdge().repeat().has("when") + .until().has("when", new P((a, b) -> true, "night")).next().orElseThrow(); + + + SoftAssertions.assertSoftly(softly -> { + Animal incoming = result.incoming(); + Animal outgoing = result.outgoing(); + softly.assertThat(incoming).isEqualTo(snake); + softly.assertThat(outgoing).isEqualTo(lion); + }); + + } + + + @Test + void shouldReturnErrorWhenTheOrderIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalEdge().orderBy(null)); + } + + @Test + void shouldReturnErrorWhenThePropertyDoesNotExist() { + assertThrows(NoSuchElementException.class, () -> + graphTemplate.traversalEdge().orderBy("wrong property").asc().next().get()); + } + + @Test + void shouldOrderAsc() { + String property = "motivation"; + + List properties = graphTemplate.traversalEdge() + .has(property) + .orderBy(property) + .asc().stream() + .map(e -> e.get(property)) + .filter(Optional::isPresent) + .map(Optional::get) + .map(v -> v.get(String.class)) + .collect(toList()); + + assertThat(properties).contains("hobby", "job", "love"); + } + + @Test + void shouldOrderDesc() { + String property = "motivation"; + + List properties = graphTemplate.traversalEdge() + .has(property) + .orderBy(property) + .desc().stream() + .map(e -> e.get(property)) + .filter(Optional::isPresent) + .map(Optional::get) + .map(v -> v.get(String.class)) + .collect(toList()); + + assertThat(properties).contains("love", "job", "hobby"); + } + + + @Test + void shouldReturnResultAsList() { + List entities = graphTemplate.traversalEdge().result() + .toList(); + assertEquals(3, entities.size()); + } + + @Test + void shouldReturnErrorWhenThereAreMoreThanOneInGetSingleResult() { + assertThrows(NonUniqueResultException.class, () -> graphTemplate.traversalEdge().singleResult()); + } + + @Test + void shouldReturnOptionalEmptyWhenThereIsNotResultInSingleResult() { + Optional entity = graphTemplate.traversalEdge(-1L).singleResult(); + assertFalse(entity.isPresent()); + } + + @Test + void shouldReturnSingleResult() { + String name = "Poliana"; + Optional entity = graphTemplate.traversalEdge(reads.id()).singleResult(); + assertEquals(reads, entity.get()); + } + + @Test + void shouldReturnErrorWhenPredicateIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalEdge().filter(null)); + } + + @Test + void shouldReturnFromPredicate() { + long count = graphTemplate.traversalEdge().filter(reads::equals).count(); + assertEquals(1L, count); + } + + @Test + void shouldDedup() { + + graphTemplate.edge(otavio, "knows", paulo); + graphTemplate.edge(paulo, "knows", otavio); + graphTemplate.edge(otavio, "knows", poliana); + graphTemplate.edge(poliana, "knows", otavio); + graphTemplate.edge(poliana, "knows", paulo); + graphTemplate.edge(paulo, "knows", poliana); + + List edges = graphTemplate.traversalVertex() + .hasLabel(Person.class) + .inE("knows").result() + .collect(Collectors.toList()); + + assertEquals(6, edges.size()); + + edges = graphTemplate.traversalVertex() + .hasLabel(Person.class) + .inE("knows") + .dedup() + .result() + .collect(Collectors.toList()); + + assertEquals(6, edges.size()); + } +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/DefaultValueMapTraversalTest.java b/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/DefaultValueMapTraversalTest.java new file mode 100644 index 000000000..b6eaa0d14 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/DefaultValueMapTraversalTest.java @@ -0,0 +1,107 @@ +/* + * 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.NonUniqueResultException; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.graph.entities.Person; +import org.eclipse.jnosql.mapping.graph.spi.GraphExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) +@AddPackages(GraphProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) +class DefaultValueMapTraversalTest extends AbstractTraversalTest { + + + @Test + void shouldCount() { + long count = graphTemplate.traversalVertex() + .hasLabel(Person.class).valueMap("name").count(); + assertEquals(3L, count); + } + + + @Test + void shouldReturnMapValues() { + List names = graphTemplate.traversalVertex() + .hasLabel(Person.class).valueMap("name") + .stream() + .map(m -> m.getOrDefault("name", "").toString()).collect(Collectors.toList()); + + + assertThat(names).contains("[Poliana]", "[Otavio]", "[Paulo]"); + } + + @Test + void shouldReturnStream() { + Stream> stream = graphTemplate.traversalVertex() + .hasLabel(Person.class).valueMap("name") + .stream(); + assertNotNull(stream); + assertEquals(3L, stream.count()); + } + + + @Test + void shouldReturnResultAsList() { + List> maps = graphTemplate.traversalVertex() + .hasLabel(Person.class).valueMap("name") + .resultList(); + assertEquals(3, maps.size()); + } + + @Test + void shouldReturnErrorWhenThereAreMoreThanOneInGetSingleResult() { + assertThrows(NonUniqueResultException.class, () -> graphTemplate.traversalVertex() + .hasLabel(Person.class).valueMap("name") + .singleResult()); + } + + @Test + void shouldReturnOptionalEmptyWhenThereIsNotResultInSingleResult() { + Optional> entity = graphTemplate.traversalVertex() + .hasLabel("not_found").valueMap("name").singleResult(); + assertFalse(entity.isPresent()); + } + + @Test + void shouldReturnSingleResult() { + String name = "Poliana"; + Optional> poliana = graphTemplate.traversalVertex().hasLabel("Person"). + has("name", name).valueMap("name").singleResult(); + assertEquals(name, poliana.map(m -> ((List) m.get("name")).get(0)).orElse("")); + } +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/DefaultVertexTraversalTest.java b/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/DefaultVertexTraversalTest.java new file mode 100644 index 000000000..efceae962 --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/DefaultVertexTraversalTest.java @@ -0,0 +1,519 @@ +/* + * 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.NonUniqueResultException; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.structure.T; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.graph.entities.Animal; +import org.eclipse.jnosql.mapping.graph.entities.Book; +import org.eclipse.jnosql.mapping.graph.entities.Person; +import org.eclipse.jnosql.mapping.graph.spi.GraphExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.toList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) +@AddPackages(GraphProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) +class DefaultVertexTraversalTest extends AbstractTraversalTest { + + + @Test + void shouldReturnErrorWhenVertexIdIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex(null)); + } + + @Test + void shouldGetVertexFromId() { + List people = graphTemplate.traversalVertex(otavio.getId(), poliana.getId()).result() + .collect(toList()); + + assertThat(people).contains(otavio, poliana); + } + + @Test + void shouldDefineLimit() { + List people = graphTemplate.traversalVertex(otavio.getId(), poliana.getId(), + paulo.getId()).limit(1) + .result() + .collect(toList()); + + assertEquals(1, people.size()); + assertThat(people).contains(otavio); + } + + @Test + void shouldDefineLimit2() { + List people = graphTemplate.traversalVertex(otavio.getId(), poliana.getId(), paulo.getId()). + next(2) + .collect(toList()); + + assertEquals(2, people.size()); + assertThat(people).contains(otavio, poliana); + } + + @Test + void shouldNext() { + Optional next = graphTemplate.traversalVertex().next(); + assertTrue(next.isPresent()); + } + + @Test + void shouldEmptyNext() { + Optional next = graphTemplate.traversalVertex(-12).next(); + assertFalse(next.isPresent()); + } + + + @Test + void shouldHave() { + Optional person = graphTemplate.traversalVertex().has("name", "Poliana").next(); + assertTrue(person.isPresent()); + assertEquals(person.get(), poliana); + } + + @Test + void shouldReturnErrorWhenHasNullKey() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex() + .has((String) null, "Poliana") + .next()); + } + + + @Test + void shouldReturnErrorWhenHasNullValue() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().has("name", null) + .next()); + } + + @Test + void shouldHaveId() { + Optional person = graphTemplate.traversalVertex().has(T.id, poliana.getId()).next(); + assertTrue(person.isPresent()); + assertEquals(person.get(), poliana); + } + + @Test + void shouldReturnErrorWhenHasIdHasNullValue() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().has(T.id, null).next()); + } + + @Test + void shouldReturnErrorWhenHasIdHasNullAccessor() { + assertThrows(NullPointerException.class, () -> { + T id = null; + graphTemplate.traversalVertex().has(id, poliana.getId()).next(); + }); + } + + + @Test + void shouldHavePredicate() { + List result = graphTemplate.traversalVertex().has("age", P.gt(26)) + .result() + .toList(); + assertEquals(5, result.size()); + } + + @Test + void shouldReturnErrorWhenHasPredicateIsNull() { + assertThrows(NullPointerException.class, () -> { + P gt = null; + graphTemplate.traversalVertex().has("age", gt) + .result() + .collect(toList()); + }); + } + + @Test + void shouldReturnErrorWhenHasKeyIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().has((String) null, + P.gt(26)) + .result() + .collect(toList())); + } + + @Test + void shouldHaveLabel() { + List books = graphTemplate.traversalVertex().hasLabel("Book").result().collect(toList()); + assertEquals(3, books.size()); + assertThat(books).contains(shack, license, effectiveJava); + } + + @Test + void shouldHaveLabel2() { + + List entities = graphTemplate.traversalVertex() + .hasLabel(P.eq("Book").or(P.eq("Person"))) + .result().collect(toList()); + assertThat(entities).hasSize(6).contains(shack, license, effectiveJava, otavio, poliana, paulo); + } + + @Test + void shouldReturnErrorWhenHasLabelHasNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().hasLabel((String) null) + .result().collect(toList())); + } + + @Test + void shouldIn() { + List books = graphTemplate.traversalVertex().out(READS).result().collect(toList()); + assertEquals(3, books.size()); + assertThat(books).contains(shack, license, effectiveJava); + } + + @Test + void shouldReturnErrorWhenInIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().out((String) null).result().collect(toList())); + } + + @Test + void shouldOut() { + List people = graphTemplate.traversalVertex().in(READS).result().collect(toList()); + assertEquals(3, people.size()); + assertThat(people).contains(otavio, poliana, paulo); + } + + @Test + void shouldReturnErrorWhenOutIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().in((String) null).result().collect(toList())); + } + + @Test + void shouldBoth() { + List entities = graphTemplate.traversalVertex().both(READS) + .result().toList(); + assertEquals(6, entities.size()); + } + + @Test + void shouldReturnErrorWhenBothIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().both((String) null) + .result().collect(toList())); + } + + @Test + void shouldNot() { + List result = graphTemplate.traversalVertex().hasNot("year").result().toList(); + assertEquals(6, result.size()); + } + + @Test + void shouldReturnErrorWhenHasNotIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().hasNot((String) null) + .result().collect(toList())); + } + + @Test + void shouldCount() { + long count = graphTemplate.traversalVertex().both(READS).count(); + assertEquals(6L, count); + } + + @Test + void shouldReturnZeroWhenCountIsEmpty() { + long count = graphTemplate.traversalVertex().both("WRITES").count(); + assertEquals(0L, count); + } + + @Test + void shouldDefinesLimit() { + long count = graphTemplate.traversalVertex().limit(1L).count(); + assertEquals(1L, count); + assertNotEquals(graphTemplate.traversalVertex().count(), count); + } + + @Test + void shouldDefinesRange() { + long count = graphTemplate.traversalVertex().range(1, 3).count(); + assertEquals(2L, count); + assertNotEquals(graphTemplate.traversalVertex().count(), count); + } + + @Test + void shouldMapValuesAsStream() { + List> maps = graphTemplate.traversalVertex().hasLabel("Person") + .valueMap("name").stream().toList(); + + assertFalse(maps.isEmpty()); + assertEquals(3, maps.size()); + + List names = new ArrayList<>(); + + maps.forEach(m -> names.add(((List) m.get("name")).get(0).toString())); + + assertThat(names).contains("Otavio", "Poliana", "Paulo"); + } + + @Test + void shouldMapValuesAsStreamLimit() { + List> maps = graphTemplate.traversalVertex().hasLabel("Person") + .valueMap("name").next(2).toList(); + + assertFalse(maps.isEmpty()); + assertEquals(2, maps.size()); + } + + + @Test + void shouldReturnMapValueAsEmptyStream() { + Stream> stream = graphTemplate.traversalVertex().hasLabel("Person") + .valueMap("noField").stream(); + assertTrue(stream.allMatch(m -> Objects.isNull(m.get("noFoundProperty")))); + } + + @Test + void shouldReturnNext() { + Map map = graphTemplate.traversalVertex().hasLabel("Person") + .valueMap("name").next(); + + assertNotNull(map); + assertFalse(map.isEmpty()); + } + + + @Test + void shouldRepeatTimesTraversal() { + Animal lion = graphTemplate.insert(new Animal("lion")); + Animal snake = graphTemplate.insert(new Animal("snake")); + Animal mouse = graphTemplate.insert(new Animal("mouse")); + Animal plant = graphTemplate.insert(new Animal("plant")); + + graphTemplate.edge(lion, "eats", snake).add("when", "night"); + graphTemplate.edge(snake, "eats", mouse); + graphTemplate.edge(mouse, "eats", plant); + Optional animal = graphTemplate.traversalVertex().repeat().out("eats").times(3).next(); + assertTrue(animal.isPresent()); + assertEquals(plant, animal.get()); + + } + + @Test + void shouldRepeatTimesTraversal2() { + Animal lion = graphTemplate.insert(new Animal("lion")); + Animal snake = graphTemplate.insert(new Animal("snake")); + Animal mouse = graphTemplate.insert(new Animal("mouse")); + Animal plant = graphTemplate.insert(new Animal("plant")); + + graphTemplate.edge(lion, "eats", snake).add("when", "night"); + graphTemplate.edge(snake, "eats", mouse); + graphTemplate.edge(mouse, "eats", plant); + Optional animal = graphTemplate.traversalVertex().repeat().in("eats").times(3).next(); + assertTrue(animal.isPresent()); + assertEquals(lion, animal.get()); + + } + + @Test + void shouldRepeatUntilTraversal() { + Animal lion = graphTemplate.insert(new Animal("lion")); + Animal snake = graphTemplate.insert(new Animal("snake")); + Animal mouse = graphTemplate.insert(new Animal("mouse")); + Animal plant = graphTemplate.insert(new Animal("plant")); + + graphTemplate.edge(lion, "eats", snake); + graphTemplate.edge(snake, "eats", mouse); + graphTemplate.edge(mouse, "eats", plant); + + Optional animal = graphTemplate.traversalVertex() + .repeat().out("eats") + .until().has("name", "plant").next(); + + assertTrue(animal.isPresent()); + + + assertEquals(plant, animal.get()); + } + + @Test + void shouldRepeatUntilTraversal2() { + Animal lion = graphTemplate.insert(new Animal("lion")); + Animal snake = graphTemplate.insert(new Animal("snake")); + Animal mouse = graphTemplate.insert(new Animal("mouse")); + Animal plant = graphTemplate.insert(new Animal("plant")); + + graphTemplate.edge(lion, "eats", snake); + graphTemplate.edge(snake, "eats", mouse); + graphTemplate.edge(mouse, "eats", plant); + + Optional animal = graphTemplate.traversalVertex() + .repeat().in("eats") + .until().has("name", "lion").next(); + + assertTrue(animal.isPresent()); + + + assertEquals(lion, animal.get()); + } + + + @Test + void shouldReturnErrorWhenTheOrderIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().orderBy(null)); + } + + @Test + void shouldReturnErrorWhenThePropertyDoesNotExist() { + assertThrows(NoSuchElementException.class, () -> + graphTemplate.traversalVertex().orderBy("wrong property").asc().next().get()); + } + + @Test + void shouldOrderAsc() { + String property = "name"; + + List properties = graphTemplate.traversalVertex() + .hasLabel("Book") + .has(property) + .orderBy(property) + .asc().result() + .map(Book::getName) + .collect(toList()); + + assertThat(properties).contains("Effective Java", "Software License", "The Shack"); + } + + @Test + void shouldOrderDesc() { + String property = "name"; + + List properties = graphTemplate.traversalVertex() + .hasLabel("Book") + .has(property) + .orderBy(property) + .desc().result() + .map(Book::getName) + .collect(toList()); + + assertThat(properties).contains("The Shack", "Software License", "Effective Java"); + } + + @Test + void shouldReturnErrorWhenHasLabelStringNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().hasLabel((String) null)); + } + + @Test + void shouldReturnErrorWhenHasLabelSupplierNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().hasLabel((Supplier) null)); + } + + @Test + void shouldReturnErrorWhenHasLabelEntityClassNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().hasLabel((Class) null)); + } + + @Test + void shouldReturnHasLabel() { + assertTrue(graphTemplate.traversalVertex().hasLabel("Person").result().allMatch(Person.class::isInstance)); + assertTrue(graphTemplate.traversalVertex().hasLabel(() -> "Book").result().allMatch(Book.class::isInstance)); + assertTrue(graphTemplate.traversalVertex().hasLabel(Animal.class).result().allMatch(Animal.class::isInstance)); + } + + @Test + void shouldReturnResultAsList() { + List people = graphTemplate.traversalVertex().hasLabel("Person") + .result() + .toList(); + assertEquals(3, people.size()); + } + + @Test + void shouldReturnErrorWhenThereAreMoreThanOneInGetSingleResult() { + assertThrows(NonUniqueResultException.class, () -> graphTemplate.traversalVertex().hasLabel("Person").singleResult()); + } + + @Test + void shouldReturnOptionalEmptyWhenThereIsNotResultInSingleResult() { + Optional entity = graphTemplate.traversalVertex().hasLabel("NoEntity").singleResult(); + assertFalse(entity.isPresent()); + } + + @Test + void shouldReturnSingleResult() { + String name = "Poliana"; + Optional poliana = graphTemplate.traversalVertex().hasLabel("Person"). + has("name", name).singleResult(); + assertEquals(name, poliana.map(Person::getName).orElse("")); + } + + @Test + void shouldReturnErrorWhenPredicateIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().filter(null)); + } + + @Test + void shouldPredicate() { + long count = graphTemplate.traversalVertex() + .hasLabel(Person.class) + .filter(Person::isAdult).count(); + assertEquals(3L, count); + } + + @Test + void shouldDedup() { + + graphTemplate.edge(otavio, "knows", paulo); + graphTemplate.edge(paulo, "knows", otavio); + graphTemplate.edge(otavio, "knows", poliana); + graphTemplate.edge(poliana, "knows", otavio); + graphTemplate.edge(poliana, "knows", paulo); + graphTemplate.edge(paulo, "knows", poliana); + + List people = graphTemplate.traversalVertex() + .hasLabel(Person.class) + .in("knows").result() + .collect(Collectors.toList()); + + assertEquals(6, people.size()); + + people = graphTemplate.traversalVertex() + .hasLabel(Person.class) + .in("knows").dedup().result() + .collect(Collectors.toList()); + + assertEquals(3, people.size()); + } + + +} \ No newline at end of file diff --git a/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/entities/BookTemplate.java b/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/entities/BookTemplate.java new file mode 100644 index 000000000..45198effe --- /dev/null +++ b/jnosql-mapping/jnosql-mapping-graph/src/test/java/org/eclipse/jnosql/mapping/graph/entities/BookTemplate.java @@ -0,0 +1,44 @@ +/* + * 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.entities; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.eclipse.jnosql.mapping.graph.GraphTemplate; +import org.eclipse.jnosql.mapping.graph.Transactional; +import org.eclipse.jnosql.mapping.graph.entities.Book; + +@ApplicationScoped +public class BookTemplate { + + @Inject + private GraphTemplate graphTemplate; + + @Transactional + public void insert(Book actor) { + graphTemplate.insert(actor); + } + + @Transactional + public void insertException(Book actor) { + graphTemplate.insert(actor); + throw new NullPointerException("should get a rollback"); + } + + public void normalInsertion(Book actor) { + graphTemplate.insert(actor); + } + +}