diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultNeo4JDatabaseManager.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultNeo4JDatabaseManager.java index c1b773cf9..e7950a4e4 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultNeo4JDatabaseManager.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultNeo4JDatabaseManager.java @@ -25,6 +25,7 @@ import org.neo4j.driver.Session; import org.neo4j.driver.Transaction; import org.neo4j.driver.Values; +import org.neo4j.driver.types.TypeSystem; import java.time.Duration; import java.util.ArrayList; @@ -164,22 +165,50 @@ public long count(String entity) { } @Override - public Stream executeQuery(String cypher, Map parameters) { + public Stream cypher(String cypher, Map parameters) { Objects.requireNonNull(cypher, "Cypher query is required"); Objects.requireNonNull(parameters, "Parameters map is required"); try (Transaction tx = session.beginTransaction()) { - Stream result = tx.run(cypher, Values.parameters(flattenMap(parameters))) - .list(record -> extractEntity("QueryResult", record, false)) - .stream(); + var result = tx.run(cypher, Values.parameters(flattenMap(parameters))); + + List entities = result + .stream() + .map(record -> record.keys().stream() + .map(key -> { + var value = record.get(key); + if (value.hasType(TypeSystem.getDefault().NODE())) { + return extractEntity(key, record, false); + } else if (value.hasType(TypeSystem.getDefault().RELATIONSHIP())) { + var rel = value.asRelationship(); + List elements = new ArrayList<>(); + rel.asMap().forEach((k, v) -> elements.add(Element.of(k, v))); + elements.add(Element.of(ID, rel.elementId())); + elements.add(Element.of("start", rel.startNodeElementId())); + elements.add(Element.of("end", rel.endNodeElementId())); + return CommunicationEntity.of(key, elements); + } + return null; + }) + .filter(Objects::nonNull) + .findFirst() + .orElse(null) + ) + .filter(Objects::nonNull) + .toList(); LOGGER.fine("Executed Cypher query: " + cypher); tx.commit(); - return result; + return entities.stream(); } catch (Exception e) { throw new CommunicationException("Error executing Cypher query", e); } } + @Override + public Stream cypher(String cypher) { + return cypher(cypher, Collections.emptyMap()); + } + @Override public Stream traverse(String startNodeId, String label, int depth) { Objects.requireNonNull(startNodeId, "Start node ID is required"); @@ -376,22 +405,45 @@ var record = result.hasNext() ? result.next() : null; return entitiesResult; } - private CommunicationEntity extractEntity(String entityName, org.neo4j.driver.Record record, boolean isFullNode) { + private CommunicationEntity extractEntity(String alias, org.neo4j.driver.Record record, boolean isFullNode) { List elements = new ArrayList<>(); for (String key : record.keys()) { var value = record.get(key); - if (value.hasType(org.neo4j.driver.types.TypeSystem.getDefault().NODE())) { + if (value.hasType(TypeSystem.getDefault().NODE())) { var node = value.asNode(); - node.asMap().forEach((k, v) -> elements.add(Element.of(k, v))); // Extract properties + + node.asMap().forEach((k, v) -> elements.add(Element.of(k, v))); + elements.add(Element.of(ID, node.elementId())); - } else { - String fieldName = key.contains(".") ? key.substring(key.indexOf('.') + 1) : key; - elements.add(Element.of(fieldName, value.asObject())); + elements.add(Element.of("_alias", key)); + + var label = node.labels().iterator().hasNext() + ? node.labels().iterator().next() + : key; + + return CommunicationEntity.of(label, elements); } + + if (value.hasType(TypeSystem.getDefault().RELATIONSHIP())) { + var rel = value.asRelationship(); + + rel.asMap().forEach((k, v) -> elements.add(Element.of(k, v))); + + elements.add(Element.of(ID, rel.elementId())); + elements.add(Element.of("start", rel.startNodeElementId())); + elements.add(Element.of("end", rel.endNodeElementId())); + elements.add(Element.of("_alias", key)); + + return CommunicationEntity.of(rel.type(), elements); + } + + String fieldName = key.contains(".") ? key.substring(key.indexOf('.') + 1) : key; + elements.add(Element.of(fieldName, value.asObject())); } - return CommunicationEntity.of(entityName, elements); + // No node or relationship found: use alias as fallback + return CommunicationEntity.of(alias, elements); } } diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManager.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManager.java index a5446be16..57799c427 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManager.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManager.java @@ -59,7 +59,16 @@ public interface Neo4JDatabaseManager extends GraphDatabaseManager { * @return a stream of {@link CommunicationEntity} representing the query result. * @throws NullPointerException if {@code cypher} or {@code parameters} is null. */ - Stream executeQuery(String cypher, Map parameters); + Stream cypher(String cypher, Map parameters); + + /** + * Executes a custom Cypher query without parameters and returns a stream of {@link CommunicationEntity}. + * + * @param cypher the Cypher query to execute. + * @return a stream of {@link CommunicationEntity} representing the query result. + * @throws NullPointerException if {@code cypher} is null. + */ + Stream cypher(String cypher); /** * Traverses the graph starting from a node and follows the specified label type up to a given depth. diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/DefaultNeo4JTemplate.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/DefaultNeo4JTemplate.java index 9046753f5..2da01e6fa 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/DefaultNeo4JTemplate.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/DefaultNeo4JTemplate.java @@ -66,7 +66,15 @@ class DefaultNeo4JTemplate extends AbstractGraphTemplate implements Neo4JTemplat public Stream cypher(String cypher, Map parameters) { Objects.requireNonNull(cypher, "cypher is required"); Objects.requireNonNull(parameters, "parameters is required"); - return manager.get().executeQuery(cypher, parameters) + return manager.get().cypher(cypher, parameters) + .map(e -> (T) converter.toEntity(e)); + } + + @SuppressWarnings("unchecked") + @Override + public Stream cypher(String cypher) { + Objects.requireNonNull(cypher, "cypher is required"); + return manager.get().cypher(cypher) .map(e -> (T) converter.toEntity(e)); } diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepositoryProxy.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepositoryProxy.java index c6dc39218..97df827c5 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepositoryProxy.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepositoryProxy.java @@ -83,6 +83,7 @@ protected Neo4JTemplate template() { return template; } + @SuppressWarnings("unchecked") @Override public Object invoke(Object instance, Method method, Object[] args) throws Throwable { diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplate.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplate.java index 1ca2177d4..91c0aacc2 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplate.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplate.java @@ -30,6 +30,7 @@ * */ public interface Neo4JTemplate extends GraphTemplate { + /** * Executes a Cypher query and returns a stream of results mapped to the given entity type. * @@ -41,6 +42,16 @@ public interface Neo4JTemplate extends GraphTemplate { */ Stream cypher(String cypher, Map parameters); + /** + * Executes a Cypher query and returns a stream of results mapped to the given entity type. + * + * @param cypher The Cypher query string. + * @param The entity type representing nodes or relationships within the graph database. + * @return A stream of entities representing the query result. + * @throws NullPointerException if {@code cypher} is null. + */ + Stream cypher(String cypher); + /** * Traverses relationships from a given start node up to a specified depth. * diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerTest.java index cae60c295..d549779d9 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerTest.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerTest.java @@ -463,10 +463,11 @@ void shouldExecuteCustomQuery() { entityManager.insert(entity); String cypher = "MATCH (e:person) RETURN e"; - var result = entityManager.executeQuery(cypher, new HashMap<>()).toList(); + var result = entityManager.cypher(cypher, new HashMap<>()).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(result).isNotEmpty(); + softly.assertThat(result.get(0).name()).isEqualTo(COLLECTION_NAME); softly.assertThat(result.get(0).find("name")).isPresent(); softly.assertThat(result.get(0).find("city")).isPresent(); softly.assertThat(result.get(0).find("_id")).isPresent(); // Ensuring _id exists @@ -524,7 +525,7 @@ void shouldCreateEdge() { "id2", person2Id ); - var result = entityManager.executeQuery(cypher, parameters).toList(); + var result = entityManager.cypher(cypher, parameters).toList(); SoftAssertions.assertSoftly(softly -> softly.assertThat(result).isNotEmpty()); entityManager.remove(person1, "FRIEND", person2); @@ -546,7 +547,7 @@ void shouldRemoveEdge() { String cypher = "MATCH (p1:person { _id: $_id1 })-[r:FRIEND]-(p2:person { _id: $_id2 }) RETURN r"; Map parameters = Map.of("_id1", startNodeId, "_id2", targetNodeId); - var result = entityManager.executeQuery(cypher, parameters).toList(); + var result = entityManager.cypher(cypher, parameters).toList(); SoftAssertions.assertSoftly(softly -> softly.assertThat(result).isEmpty()); } @@ -564,7 +565,7 @@ void shouldDeleteEdgeById() { String cypher = "MATCH ()-[r]-() WHERE elementId(r) = $id RETURN r"; Map parameters = Map.of("id", edgeId); - var result = entityManager.executeQuery(cypher, parameters).toList(); + var result = entityManager.cypher(cypher, parameters).toList(); SoftAssertions.assertSoftly(softly -> softly.assertThat(result).isEmpty()); } @@ -598,7 +599,7 @@ void shouldCreateEdgeWithProperties() { "WHERE elementId(r) = $edgeId RETURN r"; Map parameters = Map.of("edgeId", edge.id()); - var result = entityManager.executeQuery(cypher, parameters).toList(); + var result = entityManager.cypher(cypher, parameters).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(result).isNotEmpty(); softly.assertThat(edge.properties()).containsEntry("since", 2019); @@ -623,7 +624,7 @@ private void removeAllEdges() { String cypher = "MATCH ()-[r]-() DELETE r"; try { - entityManager.executeQuery(cypher, new HashMap<>()).toList(); + entityManager.cypher(cypher, new HashMap<>()).toList(); } catch (Exception e) { throw new RuntimeException("Failed to remove edges before node deletion", e); } diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/GraphTemplateIntegrationTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/GraphTemplateIntegrationTest.java index 1a13c9637..692fbfdab 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/GraphTemplateIntegrationTest.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/GraphTemplateIntegrationTest.java @@ -160,7 +160,7 @@ private void removeAllEdges() { try { var entityManager = DatabaseContainer.INSTANCE.get("neo4j"); - entityManager.executeQuery(cypher, new HashMap<>()).toList(); + entityManager.cypher(cypher, new HashMap<>()).toList(); } catch (Exception e) { throw new RuntimeException("Failed to remove edges before node deletion", e); } diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/MagazineRepository.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/MagazineRepository.java index 535a34015..2c5702f6b 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/MagazineRepository.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/MagazineRepository.java @@ -15,9 +15,19 @@ package org.eclipse.jnosql.databases.neo4j.integration; +import jakarta.data.repository.Param; import jakarta.data.repository.Repository; +import org.eclipse.jnosql.databases.neo4j.mapping.Cypher; import org.eclipse.jnosql.databases.neo4j.mapping.Neo4JRepository; +import java.util.List; + @Repository public interface MagazineRepository extends Neo4JRepository { + + @Cypher("MATCH (m:Magazine) RETURN m") + List findAllByCypher(); + + @Cypher("MATCH (m:Magazine{title: $title}) RETURN m") + List findByTitle(@Param("title") String title); } diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/RepositoryIntegrationTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/RepositoryIntegrationTest.java index 023e78fed..a44eac3fe 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/RepositoryIntegrationTest.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/RepositoryIntegrationTest.java @@ -16,6 +16,7 @@ import jakarta.inject.Inject; +import org.assertj.core.api.SoftAssertions; import org.eclipse.jnosql.databases.neo4j.communication.DatabaseContainer; import org.eclipse.jnosql.databases.neo4j.communication.Neo4JConfigurations; import org.eclipse.jnosql.databases.neo4j.mapping.Neo4JExtension; @@ -28,6 +29,7 @@ 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.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; @@ -53,6 +55,11 @@ public class RepositoryIntegrationTest { @Inject private MagazineRepository repository; + @BeforeEach + void beforeEach() { + repository.deleteAll(); + } + @Test void shouldSave() { Magazine magazine = new Magazine(null, "Effective Java", 1); @@ -61,4 +68,30 @@ void shouldSave() { } + @Test + void shouldFindAll() { + for (int index = 0; index < 5; index++) { + Magazine magazine = repository.save(new Magazine(null, "Effective Java", index)); + assertThat(magazine).isNotNull(); + } + var result = repository.findAllByCypher(); + SoftAssertions.assertSoftly(soft -> { + assertThat(result).isNotNull(); + assertThat(result).hasSize(5); + }); + } + + @Test + void shouldFindByName() { + for (int index = 0; index < 5; index++) { + Magazine magazine = repository.save(new Magazine(null, "Effective Java", index)); + assertThat(magazine).isNotNull(); + } + var result = repository.findByTitle("Effective Java"); + SoftAssertions.assertSoftly(soft -> { + assertThat(result).isNotNull(); + assertThat(result).hasSize(5); + }); + } + } diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/TemplateIntegrationTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/TemplateIntegrationTest.java index 206fc93ad..4c809a57c 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/TemplateIntegrationTest.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/TemplateIntegrationTest.java @@ -15,6 +15,7 @@ package org.eclipse.jnosql.databases.neo4j.integration; import jakarta.inject.Inject; +import org.assertj.core.api.SoftAssertions; import org.eclipse.jnosql.databases.neo4j.communication.DatabaseContainer; import org.eclipse.jnosql.databases.neo4j.communication.Neo4JConfigurations; import org.eclipse.jnosql.databases.neo4j.mapping.Neo4JTemplate; @@ -30,6 +31,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; +import java.util.Map; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @@ -110,4 +112,32 @@ void shouldDeleteAll(){ template.delete(Magazine.class).execute(); assertThat(template.select(Magazine.class).result()).isEmpty(); } + + @Test + void shouldFindUsingCypher() { + for (int index = 0; index < 5; index++) { + Magazine magazine = template.insert(new Magazine(null, "Effective Java", index)); + assertThat(magazine).isNotNull(); + } + var result = template.cypher("MATCH (m:Magazine) RETURN m").toList(); + SoftAssertions.assertSoftly(soft -> { + assertThat(result).isNotNull(); + assertThat(result).hasSize(5); + }); + } + + @Test + void shouldFindUsingCypherParameter() { + for (int index = 0; index < 5; index++) { + Magazine magazine = template.insert(new Magazine(null, "Effective Java", index)); + assertThat(magazine).isNotNull(); + } + + Map parameters = Map.of("title", "Effective Java"); + var result = template.cypher("MATCH (m:Magazine{title: $title}) RETURN m", parameters).toList(); + SoftAssertions.assertSoftly(soft -> { + assertThat(result).isNotNull(); + assertThat(result).hasSize(5); + }); + } } diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/DefaultNeo4JTemplateTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/DefaultNeo4JTemplateTest.java index 066680cca..0ed9611d0 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/DefaultNeo4JTemplateTest.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/DefaultNeo4JTemplateTest.java @@ -81,7 +81,7 @@ void shouldCypher() { Map parameters = Collections.emptyMap(); CommunicationEntity entity = CommunicationEntity.of("Music"); entity.add(Element.of("name", "Ada")); - when(manager.executeQuery(cypher, parameters)).thenReturn(Stream.of(entity)); + when(manager.cypher(cypher, parameters)).thenReturn(Stream.of(entity)); Stream result = template.cypher(cypher, parameters); assertNotNull(result); diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/MusicStoreRepository.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/MusicStoreRepository.java new file mode 100644 index 000000000..57b1e8803 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/MusicStoreRepository.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 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.databases.neo4j.mapping; + + +import jakarta.data.repository.BasicRepository; +import jakarta.data.repository.Repository; + +@Repository +public interface MusicStoreRepository extends BasicRepository { +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4jExtensionTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4jExtensionTest.java index 59c78f026..f1292fc2b 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4jExtensionTest.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4jExtensionTest.java @@ -15,7 +15,11 @@ package org.eclipse.jnosql.databases.neo4j.mapping; import jakarta.inject.Inject; +import org.eclipse.jnosql.mapping.Database; +import org.eclipse.jnosql.mapping.DatabaseType; import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.graph.GraphTemplate; +import org.eclipse.jnosql.mapping.graph.spi.GraphExtension; import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; import org.eclipse.jnosql.mapping.semistructured.EntityConverter; @@ -26,17 +30,30 @@ import org.junit.jupiter.api.Test; @EnableAutoWeld -@AddPackages(value = {Converters.class, Neo4JRepository.class, EntityConverter.class}) -@AddExtensions({ReflectionEntityMetadataExtension.class, Neo4JExtension.class}) +@AddPackages(value = {Converters.class, Neo4JRepository.class, EntityConverter.class, GraphTemplate.class}) +@AddExtensions({ReflectionEntityMetadataExtension.class, Neo4JExtension.class, GraphExtension.class}) @AddPackages(Reflections.class) public class Neo4jExtensionTest { - @Inject private MusicRepository repository; + @Inject + @Database(value = DatabaseType.GRAPH) + private MusicStoreRepository repository2; + + @Inject + @Database(value = DatabaseType.GRAPH) + private MusicStoreRepository repository3; + @Test public void shouldCreteNeo4j() { Assertions.assertNotNull(repository); } + + @Test + public void shouldCreteGraph() { + Assertions.assertNotNull(repository2); + Assertions.assertNotNull(repository3); + } } \ No newline at end of file diff --git a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTinkerpopTemplate.java b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTinkerpopTemplate.java index 6621ea5a2..438304e0b 100644 --- a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTinkerpopTemplate.java +++ b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTinkerpopTemplate.java @@ -38,6 +38,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.function.Function; @@ -228,6 +229,13 @@ public Stream gremlin(String gremlin) { return executor().executeGremlin(traversal(), gremlin); } + @Override + public Stream gremlin(String gremlin, Map parameters) { + requireNonNull(gremlin, "gremlin is required"); + requireNonNull(parameters, "parameters is required"); + return executor().executeGremlin(traversal(), gremlin, parameters); + } + @Override public Optional gremlinSingleResult(String gremlin) { Stream entities = gremlin(gremlin); diff --git a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultTinkerpopTemplate.java b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultTinkerpopTemplate.java index 189db480b..d02f093ef 100644 --- a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultTinkerpopTemplate.java +++ b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultTinkerpopTemplate.java @@ -16,6 +16,7 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Default; +import jakarta.enterprise.inject.Typed; import jakarta.inject.Inject; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.structure.Graph; @@ -31,6 +32,7 @@ @Default @ApplicationScoped @Database(GRAPH) +@Typed(TinkerpopTemplate.class) class DefaultTinkerpopTemplate extends AbstractTinkerpopTemplate { private EntityConverter converter; diff --git a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/Gremlin.java b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/Gremlin.java new file mode 100644 index 000000000..f3c84b1ab --- /dev/null +++ b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/Gremlin.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025 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.databases.tinkerpop.mapping; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that the annotated method executes a Gremlin query using TinkerPop. + * The query will be executed against a graph database supporting the TinkerPop API. + * + * Example usage: + *
+ * {@code
+ * @Gremlin("g.V().hasLabel('Book').has('title', title)")
+ * List findByTitle(@Param("title") String title);
+ * }
+ * 
+ */ +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Gremlin { + + /** + * The Gremlin query to execute. + * Supports parameter substitution using method parameters annotated with @Param. + */ + String value(); +} diff --git a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerPopRepository.java b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerPopRepository.java new file mode 100644 index 000000000..41220e325 --- /dev/null +++ b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerPopRepository.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 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.databases.tinkerpop.mapping; + +import org.eclipse.jnosql.mapping.NoSQLRepository; + +/** + * A repository interface for executing {@link Gremlin} queries + * using Apache TinkerPop (Gremlin). + *

+ * This interface is meant to be extended by user-defined repositories that want to execute + * Gremlin traversals against a graph database such as JanusGraph, Neptune, or TinkerGraph. + * + *

+ * Example usage: + *

{@code
+ * public interface BookRepository extends TinkerPopRepository {
+ *
+ *     @Gremlin("g.V().hasLabel('Book').has('title', @title)")
+ *     List findByTitle(@Param("title") String title);
+ * }
+ * }
+ * + * @param the entity type + * @param the ID type + */ +public interface TinkerPopRepository extends NoSQLRepository { +} diff --git a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerpopTemplate.java b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerpopTemplate.java index 4356f2f6d..b33b12c88 100644 --- a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerpopTemplate.java +++ b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerpopTemplate.java @@ -21,6 +21,7 @@ import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; @@ -228,6 +229,15 @@ default EdgeEntity edge(O outgoing, Supplier label, I incoming) { */ Stream gremlin(String gremlin); + /** + * Executes a Gremlin then bring the result as a {@link Stream} with parameter + * @param gremlin the query gremlin + * @param parameters the parameters to be used in the query + * @return the result as {@link Stream} + * @param the entity type + */ + Stream gremlin(String gremlin, Map parameters); + /** * Executes a Gremlin query then bring the result as a unique result * diff --git a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/ParamConverterUtils.java b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/ParamConverterUtils.java new file mode 100644 index 000000000..0af25de2a --- /dev/null +++ b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/ParamConverterUtils.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 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.databases.tinkerpop.mapping.query; + +import jakarta.data.repository.Param; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +final class ParamConverterUtils { + + private ParamConverterUtils() { + } + + static Map getValues(Object[] args, Method method) { + + Map map = new HashMap<>(); + Annotation[][] annotations = method.getParameterAnnotations(); + + for (int index = 0; index < annotations.length; index++) { + + final Object arg = args[index]; + + Optional param = Stream.of(annotations[index]) + .filter(Param.class::isInstance) + .map(Param.class::cast) + .findFirst(); + param.ifPresent(p -> map.put(p.value(), arg)); + + } + + return map; + } +} diff --git a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/RepositoryGraphBean.java b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/TinkerpopRepositoryBean.java similarity index 55% rename from jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/RepositoryGraphBean.java rename to jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/TinkerpopRepositoryBean.java index 470c88b14..c2e1940ad 100644 --- a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/RepositoryGraphBean.java +++ b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/TinkerpopRepositoryBean.java @@ -14,22 +14,19 @@ */ package org.eclipse.jnosql.databases.tinkerpop.mapping.query; -import jakarta.data.repository.DataRepository; import jakarta.enterprise.context.spi.CreationalContext; +import jakarta.enterprise.inject.Default; +import jakarta.enterprise.util.AnnotationLiteral; +import org.eclipse.jnosql.databases.tinkerpop.mapping.TinkerPopRepository; import org.eclipse.jnosql.databases.tinkerpop.mapping.TinkerpopTemplate; -import org.eclipse.jnosql.mapping.DatabaseQualifier; -import org.eclipse.jnosql.mapping.DatabaseType; import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.core.spi.AbstractBean; -import org.eclipse.jnosql.mapping.core.util.AnnotationLiteralUtil; import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; -import org.eclipse.jnosql.mapping.semistructured.query.SemiStructuredRepositoryProxy; import java.lang.annotation.Annotation; import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.util.Collections; -import java.util.HashSet; import java.util.Set; @@ -44,35 +41,18 @@ * @param the type of the repository * @see AbstractBean */ -public class RepositoryGraphBean> extends AbstractBean { +public class TinkerpopRepositoryBean extends AbstractBean> { private final Class type; private final Set types; - private final String provider; + private final Set qualifiers = Collections.singleton(new AnnotationLiteral() { + }); - private final Set qualifiers; - - /** - * Constructor - * - * @param type the tye - * @param provider the provider name, that must be a - */ - @SuppressWarnings("unchecked") - public RepositoryGraphBean(Class type, String provider) { - this.type = (Class) type; + public TinkerpopRepositoryBean(Class type) { + this.type = type; this.types = Collections.singleton(type); - this.provider = provider; - if (provider.isEmpty()) { - this.qualifiers = new HashSet<>(); - qualifiers.add(DatabaseQualifier.ofGraph()); - qualifiers.add(AnnotationLiteralUtil.DEFAULT_ANNOTATION); - qualifiers.add(AnnotationLiteralUtil.ANY_ANNOTATION); - } else { - this.qualifiers = Collections.singleton(DatabaseQualifier.ofGraph(provider)); - } } @Override @@ -80,18 +60,15 @@ public Class getBeanClass() { return type; } - @Override @SuppressWarnings("unchecked") - public T create(CreationalContext context) { - EntitiesMetadata entities = getInstance(EntitiesMetadata.class); - var template = provider.isEmpty() ? getInstance(TinkerpopTemplate.class) : - getInstance(TinkerpopTemplate.class, DatabaseQualifier.ofGraph(provider)); - + @Override + public TinkerPopRepository create(CreationalContext> creationalContext) { + TinkerpopTemplate template = getInstance(TinkerpopTemplate.class); Converters converters = getInstance(Converters.class); - - var handler = new SemiStructuredRepositoryProxy<>(template, - entities, type, converters); - return (T) Proxy.newProxyInstance(type.getClassLoader(), + EntitiesMetadata entitiesMetadata = getInstance(EntitiesMetadata.class); + TinkerpopRepositoryProxy handler = new TinkerpopRepositoryProxy<>(template, type, + converters, entitiesMetadata); + return (TinkerPopRepository) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, handler); } @@ -109,7 +86,6 @@ public Set getQualifiers() { @Override public String getId() { - return type.getName() + '@' + DatabaseType.GRAPH + "-" + provider; + return type.getName() + "@tinkerpop"; } - } diff --git a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/TinkerpopRepositoryProxy.java b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/TinkerpopRepositoryProxy.java new file mode 100644 index 000000000..5c5bb60e2 --- /dev/null +++ b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/TinkerpopRepositoryProxy.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2025 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.databases.tinkerpop.mapping.query; + +import org.eclipse.jnosql.databases.tinkerpop.mapping.Gremlin; +import org.eclipse.jnosql.databases.tinkerpop.mapping.TinkerpopTemplate; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.query.AbstractRepository; +import org.eclipse.jnosql.mapping.core.repository.DynamicReturn; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.semistructured.query.AbstractSemiStructuredRepositoryProxy; +import org.eclipse.jnosql.mapping.semistructured.query.SemiStructuredRepositoryProxy; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +import static org.eclipse.jnosql.mapping.core.repository.DynamicReturn.toSingleResult; + +class TinkerpopRepositoryProxy extends AbstractSemiStructuredRepositoryProxy { + + private final Class typeClass; + + private final TinkerpopTemplate template; + + private final AbstractRepository repository; + + private final Converters converters; + + private final EntityMetadata entityMetadata; + + private final Class repositoryType; + + TinkerpopRepositoryProxy(TinkerpopTemplate template, Class repositoryType, + Converters converters, EntitiesMetadata entitiesMetadata) { + + this.template = template; + this.typeClass = Class.class.cast(ParameterizedType.class.cast(repositoryType.getGenericInterfaces()[0]) + .getActualTypeArguments()[0]); + + this.converters = converters; + this.entityMetadata = entitiesMetadata.get(typeClass); + this.repositoryType = repositoryType; + this.repository = SemiStructuredRepositoryProxy.SemiStructuredRepository.of(template, entityMetadata); + } + + @Override + protected AbstractRepository repository() { + return repository; + } + + @Override + protected Converters converters() { + return converters; + } + + @Override + protected Class repositoryType() { + return repositoryType; + } + + @Override + protected EntityMetadata entityMetadata() { + return entityMetadata; + } + + @Override + protected TinkerpopTemplate template() { + return template; + } + + @SuppressWarnings("unchecked") + @Override + public Object invoke(Object instance, Method method, Object[] args) throws Throwable { + + Gremlin cql = method.getAnnotation(Gremlin.class); + if (Objects.nonNull(cql)) { + + Stream result; + Map values = ParamConverterUtils.getValues(args, method); + if (!values.isEmpty()) { + result = template.gremlin(cql.value(), values); + } else { + result = template.gremlin(cql.value(), Collections.emptyMap()); + } + + return DynamicReturn.builder() + .classSource(typeClass) + .methodSource(method) + .result(() -> result) + .singleResult(toSingleResult(method).apply(() -> result)) + .build().execute(); + } + + return super.invoke(instance, method, args); + } +} \ No newline at end of file diff --git a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/CustomRepositoryGraphBean.java b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/CustomRepositoryGraphBean.java deleted file mode 100644 index 98b9c562c..000000000 --- a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/CustomRepositoryGraphBean.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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.databases.tinkerpop.mapping.spi; - -import jakarta.enterprise.context.spi.CreationalContext; -import org.eclipse.jnosql.databases.tinkerpop.mapping.TinkerpopTemplate; -import org.eclipse.jnosql.mapping.DatabaseQualifier; -import org.eclipse.jnosql.mapping.DatabaseType; -import org.eclipse.jnosql.mapping.core.Converters; -import org.eclipse.jnosql.mapping.core.spi.AbstractBean; -import org.eclipse.jnosql.mapping.core.util.AnnotationLiteralUtil; -import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; -import org.eclipse.jnosql.mapping.semistructured.query.CustomRepositoryHandler; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Proxy; -import java.lang.reflect.Type; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - - -/** - * This class serves as a JNoSQL discovery bean for CDI extension, responsible for registering Custom Repository instances. - * It extends {@link AbstractBean} and is parameterized with type {@code T} representing the repository type. - * - * Upon instantiation, it initializes with the provided repository type, provider name, and qualifiers. - * The provider name specifies the database provider for the repository. - * - * - * @param the type of the repository - * @see AbstractBean - */ -public class CustomRepositoryGraphBean extends AbstractBean { - - private final Class type; - - private final Set types; - - private final String provider; - - private final Set qualifiers; - - /** - * Constructor - * - * @param type the tye - * @param provider the provider name, that must be a - */ - @SuppressWarnings("unchecked") - public CustomRepositoryGraphBean(Class type, String provider) { - this.type = (Class) type; - this.types = Collections.singleton(type); - this.provider = provider; - if (provider.isEmpty()) { - this.qualifiers = new HashSet<>(); - qualifiers.add(DatabaseQualifier.ofGraph()); - qualifiers.add(AnnotationLiteralUtil.DEFAULT_ANNOTATION); - qualifiers.add(AnnotationLiteralUtil.ANY_ANNOTATION); - } else { - this.qualifiers = Collections.singleton(DatabaseQualifier.ofGraph(provider)); - } - } - - @Override - public Class getBeanClass() { - return type; - } - - @SuppressWarnings("unchecked") - @Override - public T create(CreationalContext context) { - var entities = getInstance(EntitiesMetadata.class); - var template = provider.isEmpty() ? getInstance(TinkerpopTemplate.class) : - getInstance(TinkerpopTemplate.class, DatabaseQualifier.ofGraph(provider)); - - var converters = getInstance(Converters.class); - - var handler = CustomRepositoryHandler.builder() - .entitiesMetadata(entities) - .template(template) - .customRepositoryType(type) - .converters(converters) - .build(); - - return (T) Proxy.newProxyInstance(type.getClassLoader(), - new Class[]{type}, - handler); - } - - - @Override - public Set getTypes() { - return types; - } - - @Override - public Set getQualifiers() { - return qualifiers; - } - - @Override - public String getId() { - return type.getName() + '@' + DatabaseType.GRAPH + "-" + provider; - } - -} diff --git a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtension.java b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/TinkerpopExtension.java similarity index 60% rename from jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtension.java rename to jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/TinkerpopExtension.java index bd54b6267..7715f0d6c 100644 --- a/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtension.java +++ b/jnosql-tinkerpop/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/TinkerpopExtension.java @@ -19,7 +19,8 @@ import jakarta.enterprise.inject.spi.Extension; import jakarta.enterprise.inject.spi.ProcessProducer; import org.apache.tinkerpop.gremlin.structure.Graph; -import org.eclipse.jnosql.databases.tinkerpop.mapping.query.RepositoryGraphBean; +import org.eclipse.jnosql.databases.tinkerpop.mapping.TinkerPopRepository; +import org.eclipse.jnosql.databases.tinkerpop.mapping.query.TinkerpopRepositoryBean; import org.eclipse.jnosql.mapping.DatabaseMetadata; import org.eclipse.jnosql.mapping.Databases; import org.eclipse.jnosql.mapping.metadata.ClassScanner; @@ -34,9 +35,9 @@ * Extension to start up the GraphTemplate, Repository * from the {@link org.eclipse.jnosql.mapping.Database} qualifier */ -public class GraphExtension implements Extension { +public class TinkerpopExtension implements Extension { - private static final Logger LOGGER = Logger.getLogger(GraphExtension.class.getName()); + private static final Logger LOGGER = Logger.getLogger(TinkerpopExtension.class.getName()); private final Set databases = new HashSet<>(); @@ -47,13 +48,9 @@ void observes(@Observes final ProcessProducer pp) { void onAfterBeanDiscovery(@Observes final AfterBeanDiscovery afterBeanDiscovery) { ClassScanner scanner = ClassScanner.load(); - Set> crudTypes = scanner.repositoriesStandard(); + Set> crudTypes = scanner.repositories(TinkerPopRepository.class); - Set> customRepositories = scanner.customRepositories(); - - LOGGER.info(String.format("Processing graph extension: %d databases crud %d found, custom repositories: %d", - databases.size(), crudTypes.size(), customRepositories.size())); LOGGER.info("Processing repositories as a Graph implementation: " + crudTypes); databases.forEach(type -> { @@ -63,21 +60,7 @@ void onAfterBeanDiscovery(@Observes final AfterBeanDiscovery afterBeanDiscovery) } }); + crudTypes.forEach(type -> afterBeanDiscovery.addBean(new TinkerpopRepositoryBean<>(type))); - crudTypes.forEach(type -> { - if (!databases.contains(DatabaseMetadata.DEFAULT_GRAPH)) { - afterBeanDiscovery.addBean(new RepositoryGraphBean<>(type, "")); - } - databases.forEach(database -> afterBeanDiscovery - .addBean(new RepositoryGraphBean<>(type, database.getProvider()))); - }); - - customRepositories.forEach(type -> { - if (!databases.contains(DatabaseMetadata.DEFAULT_DOCUMENT)) { - afterBeanDiscovery.addBean(new CustomRepositoryGraphBean<>(type, "")); - } - databases.forEach(database -> - afterBeanDiscovery.addBean(new CustomRepositoryGraphBean<>(type, database.getProvider()))); - }); } } diff --git a/jnosql-tinkerpop/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension b/jnosql-tinkerpop/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension index 7464d6835..30ed80e63 100644 --- a/jnosql-tinkerpop/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension +++ b/jnosql-tinkerpop/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension @@ -12,4 +12,4 @@ # # Otavio Santana # -org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension +org.eclipse.jnosql.databases.tinkerpop.mapping.spi.TinkerpopExtension diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTinkerpopTemplateTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTinkerpopTemplateTest.java index ddd63f902..295956616 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTinkerpopTemplateTest.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTinkerpopTemplateTest.java @@ -35,6 +35,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.StreamSupport; @@ -348,7 +349,7 @@ void shouldReturnErrorWhenGetEdgesHasNullId() { @Test void shouldReturnErrorWhenGetEdgesHasNullId2() { - Human otavio = Human.builder().withAge().withName("Otavio").build(); + Human otavio = Human.builder().withId(0L).withAge().withName("Otavio").build(); Collection edges = getGraphTemplate().edges(otavio, Direction.BOTH); assertThat(edges).isEmpty(); } @@ -413,6 +414,20 @@ void shouldExecuteQuery() { assertThat(people.stream().map(Human::getName).collect(toList())).contains("Otavio"); } + + @Test + void shouldExecuteQueryWithParameter() { + Human human = Human.builder().withAge() + .withName("Otavio").build(); + Map parameters = Collections.singletonMap("name", "Otavio"); + getGraphTemplate().insert(human); + List people = getGraphTemplate() + .gremlin("g.V().hasLabel('Human').has('name', @name)", parameters) + .toList(); + + assertThat(people.stream().map(Human::getName).collect(toList())).contains("Otavio"); + } + @Test void shouldReturnEmpty() { Optional person = getGraphTemplate().gremlinSingleResult("g.V().hasLabel('Person')"); diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalTest.java index 4bc2d29b7..296829c1f 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalTest.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalTest.java @@ -21,7 +21,7 @@ import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Creature; import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Human; import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Magazine; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.TinkerpopExtension; import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; @@ -53,7 +53,7 @@ @AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class}) @AddPackages(GraphProducer.class) @AddPackages(Reflections.class) -@AddExtensions({ReflectionEntityMetadataExtension.class, GraphExtension.class}) +@AddExtensions({ReflectionEntityMetadataExtension.class, TinkerpopExtension.class}) class DefaultEdgeTraversalTest extends AbstractTraversalTest { @Test diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTraversalSourceTemplateTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTraversalSourceTemplateTest.java index 86a2d8a68..eb815435e 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTraversalSourceTemplateTest.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTraversalSourceTemplateTest.java @@ -16,7 +16,7 @@ import jakarta.inject.Inject; import org.apache.tinkerpop.gremlin.structure.Graph; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.TinkerpopExtension; import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; @@ -28,7 +28,7 @@ @EnableAutoWeld @AddPackages(value = {Converters.class, EntityConverter.class, Transactional.class}) @AddPackages({MagazineRepository.class, Reflections.class, GraphProducer.class}) -@AddExtensions({ReflectionEntityMetadataExtension.class, GraphExtension.class}) +@AddExtensions({ReflectionEntityMetadataExtension.class, TinkerpopExtension.class}) class DefaultGraphTraversalSourceTemplateTest extends AbstractTinkerpopTemplateTest { @Inject diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultTinkerpopTemplateProducerTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultTinkerpopTemplateProducerTest.java index 39fb30eef..57f2cde58 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultTinkerpopTemplateProducerTest.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultTinkerpopTemplateProducerTest.java @@ -16,7 +16,7 @@ import jakarta.inject.Inject; import org.apache.tinkerpop.gremlin.structure.Graph; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.TinkerpopExtension; import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; @@ -35,7 +35,7 @@ @AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class}) @AddPackages(GraphProducer.class) @AddPackages(Reflections.class) -@AddExtensions({ReflectionEntityMetadataExtension.class, GraphExtension.class}) +@AddExtensions({ReflectionEntityMetadataExtension.class, TinkerpopExtension.class}) class DefaultGraphTemplateProducerTest { @Inject diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversalTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversalTest.java index b7b0f0970..cf24e051b 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversalTest.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversalTest.java @@ -16,7 +16,7 @@ import jakarta.data.exceptions.NonUniqueResultException; import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Human; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.TinkerpopExtension; import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; @@ -42,7 +42,7 @@ @AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class}) @AddPackages(GraphProducer.class) @AddPackages(Reflections.class) -@AddExtensions({ReflectionEntityMetadataExtension.class, GraphExtension.class}) +@AddExtensions({ReflectionEntityMetadataExtension.class, TinkerpopExtension.class}) class DefaultValueMapTraversalTest extends AbstractTraversalTest { diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalTest.java index b2725f13c..11dfeea01 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalTest.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalTest.java @@ -20,7 +20,7 @@ import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Creature; import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Human; import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Magazine; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.TinkerpopExtension; import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; @@ -54,7 +54,7 @@ @AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class}) @AddPackages(GraphProducer.class) @AddPackages(Reflections.class) -@AddExtensions({ReflectionEntityMetadataExtension.class, GraphExtension.class}) +@AddExtensions({ReflectionEntityMetadataExtension.class, TinkerpopExtension.class}) class DefaultVertexTraversalTest extends AbstractTraversalTest { diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntityTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntityTest.java index a6d9ae0a1..67fcbb453 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntityTest.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntityTest.java @@ -20,7 +20,7 @@ import org.eclipse.jnosql.communication.semistructured.Element; import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Human; import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Magazine; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.TinkerpopExtension; import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; @@ -45,7 +45,7 @@ @AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class}) @AddPackages(GraphProducer.class) @AddPackages(Reflections.class) -@AddExtensions({ReflectionEntityMetadataExtension.class, GraphExtension.class}) +@AddExtensions({ReflectionEntityMetadataExtension.class, TinkerpopExtension.class}) class EdgeEntityTest { diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateTest.java index 96c67909b..6d71a979c 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateTest.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateTest.java @@ -16,9 +16,11 @@ import jakarta.inject.Inject; import jakarta.nosql.Template; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.TinkerpopExtension; import org.eclipse.jnosql.mapping.Database; import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.graph.GraphTemplate; +import org.eclipse.jnosql.mapping.graph.spi.GraphExtension; import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; import org.eclipse.jnosql.mapping.semistructured.EntityConverter; @@ -31,10 +33,10 @@ import static org.eclipse.jnosql.mapping.DatabaseType.GRAPH; @EnableAutoWeld -@AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class}) +@AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class, GraphTemplate.class}) @AddPackages(GraphProducer.class) @AddPackages(Reflections.class) -@AddExtensions({ReflectionEntityMetadataExtension.class, GraphExtension.class}) +@AddExtensions({ReflectionEntityMetadataExtension.class, TinkerpopExtension.class, GraphExtension.class}) class GraphTemplateTest { @Inject diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineTemplateTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineTemplateTest.java index 5d7142eed..bc5bbcf22 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineTemplateTest.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineTemplateTest.java @@ -20,7 +20,7 @@ import org.apache.tinkerpop.gremlin.structure.Transaction.Status; import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Magazine; import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.MagazineTemplate; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.TinkerpopExtension; import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; @@ -42,7 +42,7 @@ @AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class}) @AddPackages(GraphProducer.class) @AddPackages(Reflections.class) -@AddExtensions({ReflectionEntityMetadataExtension.class, GraphExtension.class}) +@AddExtensions({ReflectionEntityMetadataExtension.class, TinkerpopExtension.class}) class MagazineTemplateTest { @Inject diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/Population.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/Population.java new file mode 100644 index 000000000..1a946f88d --- /dev/null +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/Population.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 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.databases.tinkerpop.mapping; + +import jakarta.data.repository.Param; +import jakarta.data.repository.Repository; +import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Human; + +import java.util.List; + +@Repository +public interface Population extends TinkerPopRepository { + + @Gremlin("g.V().hasLabel('Human').order().by('name', Order.asc)") + List allHumans(); + + @Gremlin("g.V().hasLabel('Human').has('name', @name)") + List findByName (@Param("name") String name); + +} diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/PopulationTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/PopulationTest.java new file mode 100644 index 000000000..c2d51d81b --- /dev/null +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/PopulationTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2025 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.databases.tinkerpop.mapping; + + +import jakarta.inject.Inject; +import org.apache.tinkerpop.gremlin.process.traversal.Order; +import org.assertj.core.api.SoftAssertions; +import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Human; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.TinkerpopExtension; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.graph.GraphTemplate; +import org.eclipse.jnosql.mapping.graph.spi.GraphExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; +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.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class, GraphTemplate.class}) +@AddPackages(GraphProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({ReflectionEntityMetadataExtension.class, TinkerpopExtension.class, GraphExtension.class}) +public class PopulationTest { + + @Inject + private Population population; + + @BeforeEach + void setUp() { + this.population.deleteAll(); + } + + @Test + void shouldSave() { + var human = Human.builder().withAge().withName("Otavio").build(); + Human saved = population.save(human); + + SoftAssertions.assertSoftly(soft ->{ + soft.assertThat(saved).isNotNull(); + soft.assertThat(saved.getId()).isNotNull(); + soft.assertThat(saved.getName()).isEqualTo(human.getName()); + soft.assertThat(saved.getAge()).isEqualTo(human.getAge()); + }); + + } + + @Test + void shouldFindByQuery() { + var otavio = population.save(Human.builder().withAge().withName("Otavio").build()); + var poliana = population.save(Human.builder().withAge().withName("Poliana").build()); + var ada = population.save(Human.builder().withAge().withName("Ada").build()); + + List people = population.allHumans(); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(people).isNotNull(); + soft.assertThat(people).hasSize(3); + soft.assertThat(people).containsExactly(ada, otavio, poliana); + }); + } + + @Test + void shouldFindByNameQuery() { + var otavio = population.save(Human.builder().withAge().withName("Otavio").build()); + var poliana = population.save(Human.builder().withAge().withName("Poliana").build()); + var ada = population.save(Human.builder().withAge().withName("Ada").build()); + + List people = population.findByName("Otavio"); + + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(people).isNotNull(); + soft.assertThat(people).hasSize(1); + soft.assertThat(people).containsExactly(otavio); + }); + } +} diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerpopTemplateProducerTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerpopTemplateProducerTest.java index 37c17c2e7..d14ad4c3b 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerpopTemplateProducerTest.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerpopTemplateProducerTest.java @@ -16,7 +16,7 @@ import jakarta.inject.Inject; import org.apache.tinkerpop.gremlin.structure.Graph; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.TinkerpopExtension; import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; @@ -34,7 +34,7 @@ @AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class}) @AddPackages(GraphProducer.class) @AddPackages(Reflections.class) -@AddExtensions({ReflectionEntityMetadataExtension.class, GraphExtension.class}) +@AddExtensions({ReflectionEntityMetadataExtension.class, TinkerpopExtension.class}) class GraphTemplateProducerTest { @Inject diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerpopTemplateTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerpopTemplateTest.java index 5f25d7a25..2f6885b10 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerpopTemplateTest.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TinkerpopTemplateTest.java @@ -16,9 +16,11 @@ import jakarta.inject.Inject; import jakarta.nosql.Template; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.TinkerpopExtension; import org.eclipse.jnosql.mapping.Database; import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.graph.GraphTemplate; +import org.eclipse.jnosql.mapping.graph.spi.GraphExtension; import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; import org.eclipse.jnosql.mapping.semistructured.EntityConverter; @@ -31,10 +33,10 @@ import static org.eclipse.jnosql.mapping.DatabaseType.GRAPH; @EnableAutoWeld -@AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class}) +@AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class, GraphTemplate.class}) @AddPackages(GraphProducer.class) @AddPackages(Reflections.class) -@AddExtensions({ReflectionEntityMetadataExtension.class, GraphExtension.class}) +@AddExtensions({ReflectionEntityMetadataExtension.class, TinkerpopExtension.class, GraphExtension.class}) class TinkerpopTemplateTest { @Inject diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphDatabaseConfiguration.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphDatabaseConfiguration.java new file mode 100644 index 000000000..40ca74043 --- /dev/null +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphDatabaseConfiguration.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025 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.databases.tinkerpop.mapping.configuration; + +import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.communication.graph.GraphDatabaseManager; +import org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration; +import org.eclipse.jnosql.communication.semistructured.DatabaseManager; +import org.eclipse.jnosql.communication.semistructured.DatabaseManagerFactory; +import org.mockito.Mockito; + +public class GraphDatabaseConfiguration implements DatabaseConfiguration { + @Override + public DatabaseManagerFactory apply(Settings settings) { + return new DatabaseManagerFactoryGraph(); + } + + static class DatabaseManagerFactoryGraph implements DatabaseManagerFactory{ + + @Override + public void close() { + + } + + @Override + public DatabaseManager apply(String s) { + return Mockito.mock(GraphDatabaseManager.class); + } + } +} diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplierTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplierTest.java index 2b4cbac84..deebe7a92 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplierTest.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplierTest.java @@ -18,7 +18,7 @@ import org.apache.tinkerpop.gremlin.structure.Graph; import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphProducer; import org.eclipse.jnosql.databases.tinkerpop.mapping.TinkerpopTemplate; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.TinkerpopExtension; import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; @@ -38,7 +38,7 @@ @AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class}) @AddPackages(GraphProducer.class) @AddPackages(Reflections.class) -@AddExtensions({ReflectionEntityMetadataExtension.class, GraphExtension.class}) +@AddExtensions({ReflectionEntityMetadataExtension.class, TinkerpopExtension.class}) class GraphSupplierTest { @Inject diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Human.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Human.java index dabc87030..0667fcfce 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Human.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Human.java @@ -28,7 +28,7 @@ public class Human { @Id - private long id; + private Long id; @Column private String name; @@ -42,7 +42,7 @@ public class Human { private String ignore; - public long getId() { + public Long getId() { return id; } @@ -69,7 +69,7 @@ public boolean isAdult() { Human() { } - Human(long id, String name, int age, List phones, String ignore) { + Human(Long id, String name, int age, List phones, String ignore) { this.id = id; this.name = name; this.age = age; diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanBuilder.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanBuilder.java index 4d7ee50e8..7caaf886d 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanBuilder.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanBuilder.java @@ -17,7 +17,7 @@ import java.util.List; public class HumanBuilder { - private long id; + private Long id; private String name; private int age; private List phones; diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphCustomExtensionTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphCustomExtensionTest.java deleted file mode 100644 index 88bd3642f..000000000 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphCustomExtensionTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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.databases.tinkerpop.mapping.spi; - -import jakarta.inject.Inject; -import org.assertj.core.api.SoftAssertions; -import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphProducer; -import org.eclipse.jnosql.databases.tinkerpop.mapping.TinkerpopTemplate; -import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Human; -import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.People; -import org.eclipse.jnosql.mapping.Database; -import org.eclipse.jnosql.mapping.DatabaseType; -import org.eclipse.jnosql.mapping.core.Converters; -import org.eclipse.jnosql.mapping.reflection.Reflections; -import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; -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 static org.junit.jupiter.api.Assertions.assertNotNull; - - -@EnableAutoWeld -@AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class}) -@AddPackages(GraphProducer.class) -@AddPackages(Reflections.class) -@AddExtensions({ReflectionEntityMetadataExtension.class, GraphExtension.class}) -class GraphCustomExtensionTest { - - @Inject - @Database(value = DatabaseType.GRAPH) - private People people; - - @Inject - @Database(value = DatabaseType.GRAPH, provider = "graphRepositoryMock") - private People pepoleMock; - - @Inject - private People repository; - - @Test - void shouldInitiate() { - assertNotNull(people); - Human human = people.insert(Human.builder().build()); - SoftAssertions.assertSoftly(soft -> soft.assertThat(human).isNotNull()); - } - - @Test - void shouldUseMock(){ - assertNotNull(pepoleMock); - - Human human = pepoleMock.insert(Human.builder().build()); - SoftAssertions.assertSoftly(soft -> soft.assertThat(human).isNotNull()); - } - - @Test - void shouldUseDefault(){ - assertNotNull(repository); - - Human human = repository.insert(Human.builder().build()); - SoftAssertions.assertSoftly(soft -> soft.assertThat(human).isNotNull()); - } -} diff --git a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtensionTest.java b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/TinkerpopExtensionTest.java similarity index 85% rename from jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtensionTest.java rename to jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/TinkerpopExtensionTest.java index e646840b2..9683d70cd 100644 --- a/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtensionTest.java +++ b/jnosql-tinkerpop/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/TinkerpopExtensionTest.java @@ -22,6 +22,8 @@ import org.eclipse.jnosql.mapping.Database; import org.eclipse.jnosql.mapping.DatabaseType; import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.graph.GraphTemplate; +import org.eclipse.jnosql.mapping.graph.spi.GraphExtension; import org.eclipse.jnosql.mapping.reflection.Reflections; import org.eclipse.jnosql.mapping.reflection.spi.ReflectionEntityMetadataExtension; import org.eclipse.jnosql.mapping.semistructured.EntityConverter; @@ -35,11 +37,11 @@ @EnableAutoWeld -@AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class}) +@AddPackages(value = {Converters.class, EntityConverter.class, TinkerpopTemplate.class, GraphTemplate.class}) @AddPackages(GraphProducer.class) @AddPackages(Reflections.class) -@AddExtensions({ReflectionEntityMetadataExtension.class, GraphExtension.class}) -class GraphExtensionTest { +@AddExtensions({ReflectionEntityMetadataExtension.class, TinkerpopExtension.class, GraphExtension.class}) +class TinkerpopExtensionTest { @Inject @@ -47,7 +49,10 @@ class GraphExtensionTest { private HumanRepository repository; @Inject - @Database(value = DatabaseType.GRAPH, provider = "graphRepositoryMock") + private HumanRepository repository2; + + @Inject + @Database(value = DatabaseType.GRAPH) private HumanRepository repositoryMock; @Inject @@ -61,8 +66,6 @@ class GraphExtensionTest { @Test void shouldInitiate() { assertNotNull(repository); - Human human = repository.save(Human.builder().build()); - assertNull(human.getName()); } @Test @@ -74,6 +77,7 @@ void shouldUseMock(){ void shouldInjectTemplate() { assertNotNull(templateMock); assertNotNull(template); + assertNotNull(repository2); } @Test diff --git a/jnosql-tinkerpop/src/main/resources/META-INF/services/org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration b/jnosql-tinkerpop/src/test/resources/META-INF/services/org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration similarity index 100% rename from jnosql-tinkerpop/src/main/resources/META-INF/services/org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration rename to jnosql-tinkerpop/src/test/resources/META-INF/services/org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration