From 43ac5e59c38e425d805f973c3dc797c5f714c56e Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 18:19:21 +0000 Subject: [PATCH 001/148] chore: include neo4j module Signed-off-by: Otavio Santana --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 45a61bb51..9f8b8743e 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,7 @@ jnosql-riak jnosql-solr jnosql-oracle-nosql + jnosql-neo4j jnosql-tinkerpop From 7ed102477495972cce9e636aa93455e0f6396788 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 18:20:06 +0000 Subject: [PATCH 002/148] chore: create initial structure for neo4j Signed-off-by: Otavio Santana --- .../eclipse/jnosql/query/grammar/data/JDQL.g4 | 160 +++++ .../jnosql/query/grammar/method/Method.g4 | 40 ++ jnosql-neo4j/pom.xml | 88 +++ .../CommunicationEntityConverter.java | 33 ++ .../DefaultGraphDatabaseManager.java | 170 ++++++ .../communication/GraphConfiguration.java | 60 ++ .../communication/GraphDatabaseManager.java | 48 ++ .../communication/GraphTransactionUtil.java | 94 +++ .../communication/TraversalExecutor.java | 86 +++ .../tinkerpop/communication/package-info.java | 31 + .../mapping/AbstractEdgeTraversal.java | 41 ++ .../mapping/AbstractGraphTemplate.java | 327 +++++++++++ .../mapping/AbstractVertexTraversal.java | 40 ++ .../tinkerpop/mapping/DefaultEdgeEntity.java | 163 ++++++ .../DefaultEdgeRepeatStepTraversal.java | 42 ++ .../mapping/DefaultEdgeRepeatTraversal.java | 88 +++ .../mapping/DefaultEdgeTraversal.java | 186 ++++++ .../mapping/DefaultEdgeTraversalOrder.java | 48 ++ .../mapping/DefaultEdgeUntilTraversal.java | 84 +++ .../mapping/DefaultGraphTemplate.java | 98 ++++ .../mapping/DefaultPreparedStatement.java | 82 +++ .../mapping/DefaultValueMapTraversal.java | 91 +++ .../DefaultVertexRepeatStepTraversal.java | 41 ++ .../mapping/DefaultVertexRepeatTraversal.java | 99 ++++ .../mapping/DefaultVertexTraversal.java | 237 ++++++++ .../mapping/DefaultVertexTraversalOrder.java | 46 ++ .../mapping/DefaultVertexUntilTraversal.java | 130 +++++ .../mapping/EdgeConditionTraversal.java | 127 ++++ .../tinkerpop/mapping/EdgeEntity.java | 152 +++++ .../mapping/EdgeRepeatStepTraversal.java | 36 ++ .../mapping/EdgeRepeatTraversal.java | 129 +++++ .../tinkerpop/mapping/EdgeTraversal.java | 157 +++++ .../tinkerpop/mapping/EdgeTraversalOrder.java | 33 ++ .../tinkerpop/mapping/EdgeUntilTraversal.java | 23 + .../mapping/GraphEntityConverter.java | 30 + .../tinkerpop/mapping/GraphTemplate.java | 250 ++++++++ .../mapping/GraphTemplateProducer.java | 125 ++++ .../tinkerpop/mapping/GremlinExecutor.java | 85 +++ .../tinkerpop/mapping/GremlinParamParser.java | 70 +++ .../mapping/GremlinQueryException.java | 43 ++ .../tinkerpop/mapping/Transactional.java | 35 ++ .../mapping/TransactionalInterceptor.java | 59 ++ .../tinkerpop/mapping/ValueMapTraversal.java | 71 +++ .../mapping/VertexConditionTraversal.java | 236 ++++++++ .../mapping/VertexRepeatStepTraversal.java | 35 ++ .../mapping/VertexRepeatTraversal.java | 148 +++++ .../tinkerpop/mapping/VertexTraversal.java | 217 +++++++ .../mapping/VertexTraversalOrder.java | 33 ++ .../mapping/VertexUntilTraversal.java | 22 + .../mapping/configuration/GraphSupplier.java | 54 ++ .../mapping/query/RepositoryGraphBean.java | 115 ++++ .../spi/CustomRepositoryGraphBean.java | 119 ++++ .../tinkerpop/mapping/spi/GraphExtension.java | 83 +++ .../tinkerpop/mapping/spi/TemplateBean.java | 83 +++ .../src/main/resources/META-INF/beans.xml | 21 + .../jakarta.enterprise.inject.spi.Extension | 15 + .../DefaultGraphDatabaseManagerTest.java | 465 +++++++++++++++ .../communication/GraphSupplier.java | 45 ++ .../mapping/AbstractGraphTemplateTest.java | 546 ++++++++++++++++++ .../mapping/AbstractTraversalTest.java | 96 +++ .../mapping/DefaultEdgeTraversalTest.java | 540 +++++++++++++++++ .../DefaultGraphTemplateProducerTest.java | 56 ++ ...faultGraphTraversalSourceTemplateTest.java | 49 ++ .../mapping/DefaultValueMapTraversalTest.java | 107 ++++ .../mapping/DefaultVertexTraversalTest.java | 519 +++++++++++++++++ .../tinkerpop/mapping/EdgeEntityTest.java | 360 ++++++++++++ .../tinkerpop/mapping/GraphProducer.java | 103 ++++ .../mapping/GraphTemplateProducerTest.java | 56 ++ .../tinkerpop/mapping/GraphTemplateTest.java | 57 ++ .../tinkerpop/mapping/MagazineRepository.java | 24 + .../mapping/MagazineTemplateTest.java | 97 ++++ .../configuration/GraphConfigurationMock.java | 83 +++ .../GraphConfigurationMock2.java | 83 +++ .../configuration/GraphSupplierTest.java | 82 +++ .../tinkerpop/mapping/entities/Creature.java | 70 +++ .../tinkerpop/mapping/entities/Human.java | 117 ++++ .../mapping/entities/HumanBuilder.java | 60 ++ .../mapping/entities/HumanRepository.java | 23 + .../tinkerpop/mapping/entities/Magazine.java | 113 ++++ .../mapping/entities/MagazineTemplate.java | 43 ++ .../tinkerpop/mapping/entities/People.java | 26 + .../mapping/entities/WrongEntity.java | 56 ++ .../mapping/spi/GraphCustomExtensionTest.java | 77 +++ .../mapping/spi/GraphExtensionTest.java | 84 +++ .../src/test/resources/META-INF/beans.xml | 21 + .../META-INF/microprofile-config.properties | 4 + ...tinkerpop.communication.GraphConfiguration | 1 + 87 files changed, 9322 insertions(+) create mode 100644 jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 create mode 100644 jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 create mode 100644 jnosql-neo4j/pom.xml create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/CommunicationEntityConverter.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/DefaultGraphDatabaseManager.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphConfiguration.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphDatabaseManager.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphTransactionUtil.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/TraversalExecutor.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/package-info.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractEdgeTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplate.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractVertexTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeEntity.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatStepTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalOrder.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeUntilTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplate.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultPreparedStatement.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatStepTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalOrder.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexUntilTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeConditionTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntity.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatStepTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversalOrder.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeUntilTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphEntityConverter.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplate.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducer.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinExecutor.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinParamParser.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinQueryException.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/Transactional.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TransactionalInterceptor.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/ValueMapTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexConditionTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatStepTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversalOrder.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexUntilTraversal.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplier.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/RepositoryGraphBean.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/CustomRepositoryGraphBean.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtension.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/TemplateBean.java create mode 100644 jnosql-neo4j/src/main/resources/META-INF/beans.xml create mode 100644 jnosql-neo4j/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/communication/DefaultGraphDatabaseManagerTest.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphSupplier.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplateTest.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTraversalTest.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalTest.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplateProducerTest.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTraversalSourceTemplateTest.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversalTest.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalTest.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntityTest.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphProducer.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducerTest.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateTest.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineRepository.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineTemplateTest.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock2.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplierTest.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Creature.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Human.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanBuilder.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanRepository.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Magazine.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/MagazineTemplate.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/People.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/WrongEntity.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphCustomExtensionTest.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtensionTest.java create mode 100644 jnosql-neo4j/src/test/resources/META-INF/beans.xml create mode 100644 jnosql-neo4j/src/test/resources/META-INF/microprofile-config.properties create mode 100644 jnosql-neo4j/src/test/resources/META-INF/services/org.eclipse.jnosql.databases.tinkerpop.communication.GraphConfiguration diff --git a/jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 b/jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 new file mode 100644 index 000000000..2cdb92cb8 --- /dev/null +++ b/jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 @@ -0,0 +1,160 @@ +grammar JDQL; + +statement : select_statement | update_statement | delete_statement; + +select_statement : select_clause? from_clause? where_clause? orderby_clause?; +update_statement : UPDATE entity_name set_clause where_clause?; +delete_statement : DELETE from_clause where_clause?; + +from_clause : FROM entity_name; + +where_clause : WHERE conditional_expression; + +set_clause : SET update_item (COMMA update_item)*; +update_item : state_field_path_expression EQ (scalar_expression | NULL); + +select_clause : SELECT select_list; +select_list + : state_field_path_expression (COMMA state_field_path_expression)* + | aggregate_expression + ; +aggregate_expression : COUNT '(' THIS ')'; + +orderby_clause : ORDER BY orderby_item (COMMA orderby_item)*; +orderby_item : state_field_path_expression (ASC | DESC)?; + +conditional_expression + // highest to lowest precedence + : LPAREN conditional_expression RPAREN + | null_comparison_expression + | in_expression + | between_expression + | like_expression + | comparison_expression + | NOT conditional_expression + | conditional_expression AND conditional_expression + | conditional_expression OR conditional_expression + ; + +comparison_expression : scalar_expression comparison_operator scalar_expression; +comparison_operator : EQ | GT | GTEQ | LT | LTEQ | NEQ; + +between_expression : scalar_expression NOT? BETWEEN scalar_expression AND scalar_expression; +like_expression : scalar_expression NOT? LIKE (STRING | input_parameter); + +in_expression : state_field_path_expression NOT? IN '(' in_item (',' in_item)* ')'; +in_item : literal | enum_literal | input_parameter; // could simplify to just literal + +null_comparison_expression : state_field_path_expression IS NOT? NULL; + +scalar_expression + // highest to lowest precedence + : LPAREN scalar_expression RPAREN + | primary_expression + | scalar_expression MUL scalar_expression + | scalar_expression DIV scalar_expression + | scalar_expression PLUS scalar_expression + | scalar_expression MINUS scalar_expression + | scalar_expression CONCAT scalar_expression + ; + +primary_expression + : function_expression + | special_expression + | state_field_path_expression + | enum_literal + | input_parameter + | literal + ; + +function_expression + : ('abs(' | 'ABS(') scalar_expression ')' + | ('length(' | 'LENGTH(') scalar_expression ')' + | ('lower(' | 'LOWER(') scalar_expression ')' + | ('upper(' | 'UPPER(') scalar_expression ')' + | ('left(' | 'LEFT(') scalar_expression ',' scalar_expression ')' + | ('right(' | 'RIGHT(') scalar_expression ',' scalar_expression ')' + ; + +special_expression + : LOCAL_DATE + | LOCAL_DATETIME + | LOCAL_TIME + | TRUE + | FALSE + ; + +state_field_path_expression : IDENTIFIER (DOT IDENTIFIER)* | FULLY_QUALIFIED_IDENTIFIER; + +entity_name : IDENTIFIER; // no ambiguity + +enum_literal : IDENTIFIER (DOT IDENTIFIER)* | FULLY_QUALIFIED_IDENTIFIER; // ambiguity with state_field_path_expression resolvable semantically + +input_parameter : COLON IDENTIFIER | QUESTION INTEGER; + +literal : STRING | INTEGER | DOUBLE; + +// Tokens defined to be case-insensitive using character classes +SELECT : [sS][eE][lL][eE][cC][tT]; +UPDATE : [uU][pP][dD][aA][tT][eE]; +DELETE : [dD][eE][lL][eE][tT][eE]; +FROM : [fF][rR][oO][mM]; +WHERE : [wW][hH][eE][rR][eE]; +SET : [sS][eE][tT]; +ORDER : [oO][rR][dD][eE][rR]; +BY : [bB][yY]; +NOT : [nN][oO][tT]; +IN : [iI][nN]; +IS : [iI][sS]; +NULL : [nN][uU][lL][lL]; +COUNT : [cC][oO][uU][nN][tT]; +TRUE : [tT][rR][uU][eE]; +FALSE : [fF][aA][lL][sS][eE]; +ASC : [aA][sS][cC]; +DESC : [dD][eE][sS][cC]; +AND : [aA][nN][dD]; +OR : [oO][rR]; +LOCAL_DATE : [lL][oO][cC][aA][lL] [dD][aA][tT][eE]; +LOCAL_DATETIME : [lL][oO][cC][aA][lL] [dD][aA][tT][eE][tT][iI][mM][eE]; +LOCAL_TIME : [lL][oO][cC][aA][lL] [tT][iI][mM][eE]; +BETWEEN : [bB][eE][tT][wW][eE][eE][nN]; +LIKE : [lL][iI][kK][eE]; +THIS : [tT][hH][iI][sS]; +LOCAL : [lL][oO][cC][aA][lL]; +DATE : [dD][aA][tT][eE]; +DATETIME : [dD][aA][tT][eE][tT][iI][mM][eE]; +TIME : [tT][iI][mM][eE]; + +// Operators +EQ : '='; +GT : '>'; +LT : '<'; +NEQ : '<>'; +GTEQ : '>='; +LTEQ : '<='; +PLUS : '+'; +MINUS : '-'; +MUL : '*'; +DIV : '/'; +CONCAT : '||'; + +// Special Characters +COMMA : ','; +DOT : '.'; +LPAREN : '('; +RPAREN : ')'; +COLON : ':'; +QUESTION : '?'; + +// Identifier and literals +FULLY_QUALIFIED_IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_]* (DOT [a-zA-Z_][a-zA-Z0-9_]*)+; +IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_]*; +STRING : '\'' ( ~('\'' | '\\') | '\\' . | '\'\'' )* '\'' // single quoted strings with embedded single quotes handled + | '"' ( ~["\\] | '\\' . )* '"' ; // double quoted strings +INTEGER : '-'?[0-9]+; +DOUBLE : '-'?[0-9]+'.'[0-9]* | '-'?'.'[0-9]+; + +// Whitespace and Comments +WS : [ \t\r\n]+ -> skip ; +LINE_COMMENT : '//' ~[\r\n]* -> skip; +BLOCK_COMMENT : '/*' .*? '*/' -> skip; diff --git a/jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 b/jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 new file mode 100644 index 000000000..21f5c25a7 --- /dev/null +++ b/jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 @@ -0,0 +1,40 @@ +grammar Method; +select: selectStart where? order? EOF; +deleteBy: 'deleteBy' where? EOF; + +selectStart: 'find' limit 'By' | 'findBy' | 'countAll' | 'countBy' | 'existsBy'; +where: condition (and condition| or condition)* ; +condition: eq | gt | gte | lt | lte | between | in | like | truth | untruth | nullable | contains | endsWith | startsWith; +order: 'OrderBy' orderName (orderName)*; +orderName: variable | variable asc | variable desc; +limit: firstLimit | firstOne; +firstLimit : 'First' limitNumber; +firstOne: 'First'; +and: 'And'; +or: 'Or'; +asc: 'Asc'; +desc: 'Desc'; +truth: variable 'True'; +untruth: variable 'False'; +eq: variable | variable ignoreCase? not? 'Equals'?; +gt: variable ignoreCase? not? 'GreaterThan'; +gte: variable ignoreCase? not? 'GreaterThanEqual'; +lt: variable ignoreCase? not? 'LessThan'; +lte: variable ignoreCase? not? 'LessThanEqual'; +between: variable ignoreCase? not? 'Between'; +in: variable ignoreCase? not? 'In'; +like: variable ignoreCase? not? 'Like'; +contains: variable ignoreCase? not? 'Contains'; +endsWith: variable ignoreCase? not? 'EndsWith'; +startsWith: variable ignoreCase? not? 'StartsWith'; +nullable: variable ignoreCase? not? 'Null'; +ignoreCase: 'IgnoreCase'; +not: 'Not'; +variable: ANY_NAME; +limitNumber: INT; +ANY_NAME: [a-zA-Z_.][a-zA-Z_.0-9-]*; +WS: [ \t\r\n]+ -> skip ; +INT: [0-9]+; +fragment ESC : '\\' (["\\/bfnrt] | UNICODE) ; +fragment UNICODE : 'u' HEX HEX HEX HEX ; +fragment HEX : [0-9a-fA-F] ; \ No newline at end of file diff --git a/jnosql-neo4j/pom.xml b/jnosql-neo4j/pom.xml new file mode 100644 index 000000000..ea2855b4f --- /dev/null +++ b/jnosql-neo4j/pom.xml @@ -0,0 +1,88 @@ + + + + + 4.0.0 + + org.eclipse.jnosql.databases + jnosql-databases-parent + 1.1.5-SNAPSHOT + + + jnosql-tinkerpop + + + 3.7.3 + 0.9-3.4.0 + 4.9.1 + + --add-opens java.base/java.lang=ALL-UNNAMED --add-exports java.base/sun.nio.ch=ALL-UNNAMED + + + + + + org.eclipse.jnosql.mapping + jnosql-mapping-semistructured + ${project.version} + + + org.antlr + antlr4-runtime + + + + + org.apache.tinkerpop + gremlin-core + ${tinkerpop.version} + provided + + + org.apache.tinkerpop + neo4j-gremlin + ${tinkerpop.version} + test + + + org.neo4j + neo4j-tinkerpop-api-impl + ${neo4j.connector.version} + test + + + + + + + org.antlr + antlr4-maven-plugin + ${antlr4.version} + + antlr4 + + + + + antlr4 + + + + + + + diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/CommunicationEntityConverter.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/CommunicationEntityConverter.java new file mode 100644 index 000000000..116c7f151 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/CommunicationEntityConverter.java @@ -0,0 +1,33 @@ +/* + * 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.communication; + +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; + +import java.util.function.Function; + +public enum CommunicationEntityConverter implements Function{ + INSTANCE; + + + @Override + public CommunicationEntity apply(Vertex vertex) { + var entity = CommunicationEntity.of(vertex.label()); + vertex.properties().forEachRemaining(p -> entity.add(p.key(), p.value())); + entity.add(DefaultGraphDatabaseManager.ID_PROPERTY, vertex.id()); + return entity; + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/DefaultGraphDatabaseManager.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/DefaultGraphDatabaseManager.java new file mode 100644 index 000000000..d1eb5ee04 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/DefaultGraphDatabaseManager.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2024 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.communication; + +import jakarta.data.exceptions.EmptyResultException; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.communication.CommunicationException; +import org.eclipse.jnosql.communication.ValueUtil; +import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; +import org.eclipse.jnosql.communication.semistructured.DeleteQuery; +import org.eclipse.jnosql.communication.semistructured.SelectQuery; + +import java.time.Duration; +import java.util.Iterator; +import java.util.Objects; +import java.util.stream.Stream; + +import static org.apache.tinkerpop.gremlin.process.traversal.Order.asc; +import static org.apache.tinkerpop.gremlin.process.traversal.Order.desc; + +/** + * Default implementation of {@link GraphDatabaseManager} that serves as an adapter to the TinkerPop + * graph database provided by the Apache TinkerPop framework. + *

+ * This implementation wraps a TinkerPop {@link Graph} instance and provides methods to interact with + * the underlying graph database, execute graph traversals, and perform other graph-related operations. + *

+ *

+ * Note that this implementation does not support certain operations such as insertions with a duration, + * as indicated by the UnsupportedOperationException thrown by those methods. + *

+ */ +public class DefaultGraphDatabaseManager implements GraphDatabaseManager { + + public static final String ID_PROPERTY = "_id"; + private final Graph graph; + + DefaultGraphDatabaseManager(Graph graph) { + this.graph = graph; + } + + @Override + public Graph get() { + return graph; + } + + @Override + public String name() { + return "The tinkerpop graph database manager"; + } + + @Override + public CommunicationEntity insert(CommunicationEntity entity) { + + Objects.requireNonNull(entity, "entity is required"); + Vertex vertex = graph.addVertex(entity.name()); + 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; + } + + @Override + public CommunicationEntity insert(CommunicationEntity entity, Duration duration) { + throw new UnsupportedOperationException("There is no support to insert with duration"); + } + + @Override + public Iterable insert(Iterable entities) { + Objects.requireNonNull(entities, "entities is required"); + entities.forEach(this::insert); + return entities; + } + + @Override + public Iterable insert(Iterable iterable, Duration duration) { + throw new UnsupportedOperationException("There is no support to insert with duration"); + } + + @Override + public CommunicationEntity update(CommunicationEntity entity) { + Objects.requireNonNull(entity, "entity is required"); + entity.find(ID_PROPERTY).ifPresent(id -> { + Iterator vertices = graph.vertices(id.get()); + if(!vertices.hasNext()) { + throw new EmptyResultException("The entity does not exist with the id: " + id); + } + Vertex vertex = vertices.next(); + entity.elements().forEach(e -> vertex.property(e.name(), ValueUtil.convert(e.value()))); + }); + GraphTransactionUtil.transaction(graph); + return entity; + } + + @Override + public Iterable update(Iterable entities) { + Objects.requireNonNull(entities, "entities is required"); + Stream.of(entities).forEach(this::update); + return entities; + } + + @Override + public void delete(DeleteQuery query) { + Objects.requireNonNull(query, "delete is required"); + GraphTraversal traversal = graph.traversal().V().hasLabel(query.name()); + query.condition().ifPresent(c ->{ + GraphTraversal predicate = TraversalExecutor.getPredicate(c); + traversal.filter(predicate); + }); + + traversal.drop().iterate(); + GraphTransactionUtil.transaction(graph); + } + + @Override + public Stream select(SelectQuery query) { + Objects.requireNonNull(query, "query is required"); + GraphTraversal traversal = graph.traversal().V().hasLabel(query.name()); + query.condition().ifPresent(c ->{ + GraphTraversal predicate = TraversalExecutor.getPredicate(c); + traversal.filter(predicate); + }); + + if(query.limit()> 0) { + traversal.limit(query.limit()); + } else if(query.skip() > 0) { + traversal.skip(query.skip()); + } + query.sorts().forEach( + s -> { + if (s.isAscending()) { + traversal.order().by(s.property(), asc); + } else { + traversal.order().by(s.property(), desc); + } + }); + return traversal.toStream().map(CommunicationEntityConverter.INSTANCE); + } + + @Override + public long count(String entity) { + Objects.requireNonNull(entity, "entity is required"); + GraphTraversal count = graph.traversal().V().hasLabel(entity).count(); + return count.next(); + } + + @Override + public void close() { + try { + graph.close(); + } catch (Exception e) { + throw new CommunicationException("There is an issue when close the Graph connection", e); + } + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphConfiguration.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphConfiguration.java new file mode 100644 index 000000000..c0fd5719c --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphConfiguration.java @@ -0,0 +1,60 @@ +/* + * 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.communication; + +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.eclipse.jnosql.communication.CommunicationException; +import org.eclipse.jnosql.communication.Settings; + +import java.util.ServiceLoader; +import java.util.function.Function; + +/** + * The Configuration that creates an instance of {@link Graph} that given a {@link Settings} make an {@link Graph} instance. + */ +public interface GraphConfiguration extends Function { + + + /** + * creates and returns a {@link GraphConfiguration} instance from {@link ServiceLoader} + * + * @param the configuration type + * @return {@link GraphConfiguration} instance + */ + @SuppressWarnings("unchecked") + static T getConfiguration() { + return (T) ServiceLoader.load(GraphConfiguration.class) + .stream() + .map(ServiceLoader.Provider::get) + .findFirst().orElseThrow(() -> new CommunicationException("It does not find GraphConfiguration")); + } + + /** + * creates and returns a {@link GraphConfiguration} instance from {@link ServiceLoader} + * for a particular provider implementation. + * + * @param the configuration type + * @param type the particular provider + * @return {@link GraphConfiguration} instance + */ + @SuppressWarnings("unchecked") + static T getConfiguration(Class type) { + return (T) ServiceLoader.load(GraphConfiguration.class) + .stream() + .map(ServiceLoader.Provider::get) + .filter(type::isInstance) + .findFirst().orElseThrow(() -> new CommunicationException("It does not find GraphConfiguration")); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphDatabaseManager.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphDatabaseManager.java new file mode 100644 index 000000000..19f9415ee --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphDatabaseManager.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 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.communication; + +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.eclipse.jnosql.communication.semistructured.DatabaseManager; + +import java.util.Objects; +import java.util.function.Supplier; + +/** + * A specialized extension of {@link DatabaseManager} that provides access to a graph database represented by the + * {@link org.apache.tinkerpop.gremlin.structure.Graph} interface from Apache TinkerPop. + *

+ * Implementations of this interface are expected to provide methods for interacting with the underlying graph + * database, such as retrieving vertices, edges, and properties, executing graph traversals, and performing other + * graph-related operations. + *

+ * In addition to the functionality inherited from {@link DatabaseManager}, implementations of this interface + * also act as suppliers of the underlying {@link org.apache.tinkerpop.gremlin.structure.Graph} instance. + *

+ */ +public interface GraphDatabaseManager extends DatabaseManager, Supplier { + + /** + * Creates a new instance of DefaultGraphDatabaseManager with the specified TinkerPop Graph. + * + * @param graph the TinkerPop Graph instance to be managed + * @return a new DefaultGraphDatabaseManager instance + * @throws NullPointerException if the graph parameter is null + */ + static GraphDatabaseManager of(Graph graph) { + Objects.requireNonNull(graph, "graph is required"); + return new DefaultGraphDatabaseManager(graph); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphTransactionUtil.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphTransactionUtil.java new file mode 100644 index 000000000..13158306f --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphTransactionUtil.java @@ -0,0 +1,94 @@ +/* + * 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.communication; + +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Transaction; +import org.eclipse.jnosql.mapping.core.config.MicroProfileSettings; + +import java.util.Objects; +import java.util.logging.Logger; + +import static org.eclipse.jnosql.mapping.core.config.MappingConfigurations.GRAPH_TRANSACTION_AUTOMATIC; + +/** + * 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. + */ +public final class GraphTransactionUtil { + + private static final Logger LOGGER = Logger.getLogger(GraphTransactionUtil.class.getName()); + private static final ThreadLocal THREAD_LOCAL = new ThreadLocal<>(); + + private GraphTransactionUtil() { + } + + /** + * Locks the current transaction, preventing it from being committed. + * + * @param transaction the transaction to lock + */ + public static void lock(Transaction transaction) { + THREAD_LOCAL.set(transaction); + } + + /** + * Unlocks the current transaction. + * Allows the transaction to be committed. + */ + public static void unlock() { + THREAD_LOCAL.remove(); + } + + /** + * Automatically commits a transaction if enabled and not locked. + * + * @param graph the graph instance + */ + public synchronized static void transaction(Graph 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()); + } + + } + } + + /** + * Checks if automatic transaction management is enabled. + * + * @return true if automatic transaction management is enabled, false otherwise + */ + 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-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/TraversalExecutor.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/TraversalExecutor.java new file mode 100644 index 000000000..cf1085151 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/TraversalExecutor.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024 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.communication; + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.communication.Condition; +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.ValueUtil; +import org.eclipse.jnosql.communication.semistructured.CriteriaCondition; +import org.eclipse.jnosql.communication.semistructured.Element; + +import java.util.List; + +final class TraversalExecutor { + + private TraversalExecutor() { + } + + static GraphTraversal getPredicate(CriteriaCondition condition) { + Condition operator = condition.condition(); + Element element = condition.element(); + String name = element.name(); + var value = ValueUtil.convert(element.value()); + + switch (operator) { + case EQUALS -> { + return __.has(name, P.eq(value)); + } + case GREATER_THAN -> { + return __.has(name, P.gt(value)); + } + case GREATER_EQUALS_THAN -> { + return __.has(name, P.gte(value)); + } + case LESSER_THAN -> { + return __.has(name, P.lt(value)); + } + case LESSER_EQUALS_THAN -> { + return __.has(name, P.lte(value)); + } + case BETWEEN -> { + List values = ValueUtil.convertToList(element.value()); + if(values.size() == 2) { + return __.has(name, P.between(values.get(0), values.get(1))); + } + throw new IllegalStateException("The between condition requires two parameters"); + } + case IN -> { + return __.has(name, P.within(ValueUtil.convertToList(element.value()))); + } + case NOT -> { + var notCondition = element.value().get(CriteriaCondition.class); + return __.not(getPredicate(notCondition)); + } + case AND -> { + return condition.element().value().get(new TypeReference>() { + }).stream().map(TraversalExecutor::getPredicate) + .reduce(GraphTraversal::and) + .orElseThrow(() -> new UnsupportedOperationException("There is an inconsistency at the AND operator")); + } + case OR -> { + return condition.element().value().get(new TypeReference>() { + }).stream().map(TraversalExecutor::getPredicate) + .reduce(GraphTraversal::or) + .orElseThrow(() -> new UnsupportedOperationException("There is an inconsistency at the OR operator")); + } + default -> + throw new UnsupportedOperationException("There is not support to the type " + operator + " in graph"); + } + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/package-info.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/package-info.java new file mode 100644 index 000000000..bcd663d8e --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/package-info.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 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 + */ + +/** + * This package provides communication facilities and abstractions for interacting with graph databases + * in the JNoSQL project. It contains interfaces, classes, and utilities that enable developers to + * communicate with and manage graph databases. + *

+ * The core interface in this package is {@link org.eclipse.jnosql.databases.tinkerpop.communication.GraphDatabaseManager}, + * which extends {@link org.eclipse.jnosql.communication.semistructured.DatabaseManager} + * and acts as a specialized extension for managing graph databases. Implementations of this interface + * provide methods for interacting with the underlying graph database, executing graph traversals, + * and performing other graph-related operations. + *

+ * Additionally, this package may contain specific implementations, adapters, or utilities tailored + * for working with different graph database technologies supported by JNoSQL. + *

+ */ +package org.eclipse.jnosql.databases.tinkerpop.communication; diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractEdgeTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractEdgeTraversal.java new file mode 100644 index 000000000..9e2a1b96e --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractEdgeTraversal.java @@ -0,0 +1,41 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * The base class to {@link EdgeTraversal} + */ +abstract class AbstractEdgeTraversal { + + protected final Supplier> supplier; + protected final Function, GraphTraversal> flow; + protected final EntityConverter converter; + + AbstractEdgeTraversal(Supplier> supplier, + Function, GraphTraversal> flow, + EntityConverter converter) { + this.supplier = supplier; + this.flow = flow; + this.converter = converter; + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplate.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplate.java new file mode 100644 index 000000000..2b49f6218 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplate.java @@ -0,0 +1,327 @@ +/* + * 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; + +import jakarta.data.exceptions.EmptyResultException; +import jakarta.data.exceptions.NonUniqueResultException; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Transaction; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.databases.tinkerpop.communication.CommunicationEntityConverter; +import org.eclipse.jnosql.databases.tinkerpop.communication.GraphDatabaseManager; +import org.eclipse.jnosql.databases.tinkerpop.communication.GraphTransactionUtil; +import org.eclipse.jnosql.mapping.IdNotFoundException; +import org.eclipse.jnosql.mapping.PreparedStatement; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.metadata.FieldMetadata; +import org.eclipse.jnosql.mapping.semistructured.AbstractSemiStructuredTemplate; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import static java.util.Objects.isNull; +import static java.util.Objects.requireNonNull; +import static org.apache.tinkerpop.gremlin.structure.T.id; + +abstract class AbstractGraphTemplate extends AbstractSemiStructuredTemplate implements GraphTemplate { + + @SuppressWarnings("unchecked") + private static final Function, GraphTraversal> INITIAL_VERTEX = + g -> (GraphTraversal) g; + + @SuppressWarnings("unchecked") + private static final Function, GraphTraversal> INITIAL_EDGE = + g -> (GraphTraversal) g; + + + /** + * Retrieves the {@link GraphDatabaseManager} associated with this graph template. + * + * @return the {@link GraphDatabaseManager} associated with this graph template + */ + protected abstract GraphDatabaseManager manager(); + + /** + * Retrieves the {@link GraphTraversalSource} associated with this graph template. + * + * @return the {@link GraphTraversalSource} associated with this graph template + */ + protected abstract GraphTraversalSource traversal(); + + /** + * Retrieves the {@link Graph} associated with this graph template. + * + * @return the {@link Graph} associated with this graph template + */ + protected abstract Graph graph(); + + private GremlinExecutor gremlinExecutor; + + + @Override + public Optional find(K idValue) { + requireNonNull(idValue, "id is required"); + Optional vertex = traversal().V(idValue).tryNext(); + return vertex.map(v -> converter().toEntity(CommunicationEntityConverter.INSTANCE.apply(v))); + } + + @Override + public void delete(T id) { + requireNonNull(id, "id is required"); + traversal().V(id).toStream().forEach(Vertex::remove); + } + + @Override + public void deleteEdge(T id) { + requireNonNull(id, "id is required"); + traversal().E(id).toStream().forEach(Edge::remove); + } + + @Override + public void deleteEdge(Iterable ids) { + requireNonNull(ids, "ids is required"); + final Object[] edgeIds = StreamSupport.stream(ids.spliterator(), false).toArray(Object[]::new); + traversal().E(edgeIds).toStream().forEach(Edge::remove); + } + + @Override + public EdgeEntity edge(O outgoing, String label, I incoming) { + requireNonNull(incoming, "incoming is required"); + requireNonNull(label, "label is required"); + requireNonNull(outgoing, "outgoing is required"); + + checkId(outgoing); + checkId(incoming); + + if (isIdNull(outgoing)) { + throw new IllegalStateException("outgoing Id field is required"); + } + + if (isIdNull(incoming)) { + throw new IllegalStateException("incoming Id field is required"); + } + + Vertex outVertex = vertex(outgoing).orElseThrow(() -> new EmptyResultException("Outgoing entity does not found")); + Vertex inVertex = vertex(incoming).orElseThrow(() -> new EmptyResultException("Incoming entity does not found")); + + final Predicate> predicate = t -> { + Edge e = t.get(); + return e.inVertex().id().equals(inVertex.id()) + && e.outVertex().id().equals(outVertex.id()); + }; + + Optional edge = traversal().V(outVertex.id()) + .out(label).has(id, inVertex.id()).inE(label).filter(predicate).tryNext(); + + return edge.map(edge1 -> new DefaultEdgeEntity<>(edge1, incoming, outgoing)) + .orElseGet(() -> new DefaultEdgeEntity<>(getEdge(label, outVertex, inVertex), incoming, outgoing)); + } + + @Override + public Collection edgesById(K id, Direction direction, String... labels) { + requireNonNull(id, "id is required"); + requireNonNull(direction, "direction is required"); + + Iterator vertices = vertices(id); + if (vertices.hasNext()) { + List edges = new ArrayList<>(); + vertices.next().edges(direction, labels).forEachRemaining(edges::add); + return edges.stream().map(e ->EdgeEntity.of(converter(), e)).toList(); + } + return Collections.emptyList(); + } + + @SafeVarargs + @Override + public final Collection edgesById(K id, Direction direction, Supplier... labels) { + checkLabelsSupplier(labels); + return edgesByIdImpl(id, direction, Stream.of(labels).map(Supplier::get).toArray(String[]::new)); + } + + @Override + public Collection edgesById(K id, Direction direction) { + return edgesByIdImpl(id, direction); + } + + @Override + public Collection edges(T entity, Direction direction, String... labels) { + return edgesImpl(entity, direction, labels); + } + + @SafeVarargs + @Override + public final Collection edges(T entity, Direction direction, Supplier... labels) { + checkLabelsSupplier(labels); + return edgesImpl(entity, direction, Stream.of(labels).map(Supplier::get).toArray(String[]::new)); + } + + @Override + public Collection edges(T entity, Direction direction) { + return edgesImpl(entity, direction); + } + + @Override + public Optional edge(E edgeId) { + requireNonNull(edgeId, "edgeId is required"); + + Optional edgeOptional = traversal().E(edgeId).tryNext(); + + if (edgeOptional.isPresent()) { + Edge edge = edgeOptional.get(); + return Optional.of(EdgeEntity.of(converter(), edge)); + } + + return Optional.empty(); + } + + @Override + public VertexTraversal traversalVertex(Object... vertexIds) { + if (Stream.of(vertexIds).anyMatch(Objects::isNull)) { + throw new IllegalStateException("No one vertexId element cannot be null"); + } + return new DefaultVertexTraversal(() -> traversal().V(vertexIds), INITIAL_VERTEX, converter()); + } + + @Override + public EdgeTraversal traversalEdge(Object... edgeIds) { + if (Stream.of(edgeIds).anyMatch(Objects::isNull)) { + throw new IllegalStateException("No one edgeId element cannot be null"); + } + return new DefaultEdgeTraversal(() -> traversal().E(edgeIds), INITIAL_EDGE, converter()); + } + + @Override + public Transaction transaction() { + return graph().tx(); + } + + @Override + public Stream gremlin(String gremlin) { + requireNonNull(gremlin, "gremlin is required"); + return executor().executeGremlin(traversal(), gremlin); + } + + @Override + public Optional gremlinSingleResult(String gremlin) { + Stream entities = gremlin(gremlin); + final Iterator iterator = entities.iterator(); + if (!iterator.hasNext()) { + return Optional.empty(); + } + final T entity = iterator.next(); + if (!iterator.hasNext()) { + return Optional.ofNullable(entity); + } + throw new NonUniqueResultException("The gremlin query returns more than one result: " + gremlin); + } + + @Override + public PreparedStatement gremlinPrepare(String gremlin){ + requireNonNull(gremlin, "query is required"); + return new DefaultPreparedStatement(executor(), gremlin, traversal()); + } + + private void checkId(T entity) { + EntityMetadata entityMetadata = entities().get(entity.getClass()); + entityMetadata.id().orElseThrow(() -> IdNotFoundException.newInstance(entity.getClass())); + } + + private boolean isIdNull(T entity) { + EntityMetadata entityMetadata = entities().get(entity.getClass()); + FieldMetadata field = entityMetadata.id().orElseThrow(() -> IdNotFoundException.newInstance(entity.getClass())); + return isNull(field.read(entity)); + + } + + private Optional vertex(T entity) { + EntityMetadata entityMetadata = entities().get(entity.getClass()); + FieldMetadata field = entityMetadata.id().orElseThrow(() -> IdNotFoundException.newInstance(entity.getClass())); + Object id = field.read(entity); + Iterator vertices = vertices(id); + if (vertices.hasNext()) { + return Optional.of(vertices.next()); + } + return Optional.empty(); + } + + protected Iterator vertices(Object id) { + return graph().vertices(id); + } + + private Edge getEdge(String label, Vertex outVertex, Vertex inVertex) { + final Edge edge = outVertex.addEdge(label, inVertex); + GraphTransactionUtil.transaction(graph()); + return edge; + } + + private void checkLabelsSupplier(Supplier[] labels) { + if (Stream.of(labels).anyMatch(Objects::isNull)) { + throw new IllegalStateException("Item cannot be null"); + } + } + + private Collection edgesByIdImpl(K id, Direction direction, String... labels) { + + requireNonNull(id, "id is required"); + requireNonNull(direction, "direction is required"); + + Iterator vertices = vertices(id); + if (vertices.hasNext()) { + List edges = new ArrayList<>(); + vertices.next().edges(direction, labels).forEachRemaining(edges::add); + return edges.stream().map(e -> EdgeEntity.of(converter(), e)).toList(); + } + return Collections.emptyList(); + } + + private Collection edgesImpl(T entity, Direction direction, String... labels) { + requireNonNull(entity, "entity is required"); + + if (isIdNull(entity)) { + throw new IllegalStateException("Entity id is required"); + } + + Optional vertex = vertex(entity); + if (vertex.isEmpty()) { + return Collections.emptyList(); + } + + Object id = vertex.orElseThrow().id(); + return edgesByIdImpl(id, direction, labels); + } + + private GremlinExecutor executor() { + if (Objects.isNull(gremlinExecutor)) { + this.gremlinExecutor = new GremlinExecutor(converter()); + } + return gremlinExecutor; + } + +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractVertexTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractVertexTraversal.java new file mode 100644 index 000000000..4b2081081 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractVertexTraversal.java @@ -0,0 +1,40 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * The base class to {@link VertexTraversal} + */ +abstract class AbstractVertexTraversal { + + protected final Supplier> supplier; + protected final Function, GraphTraversal> flow; + protected final EntityConverter converter; + + AbstractVertexTraversal(Supplier> supplier, + Function, GraphTraversal> flow, + EntityConverter converter) { + this.supplier = supplier; + this.flow = flow; + this.converter = converter; + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeEntity.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeEntity.java new file mode 100644 index 000000000..a016290bd --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeEntity.java @@ -0,0 +1,163 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.eclipse.jnosql.communication.Value; +import org.eclipse.jnosql.communication.semistructured.Element; +import org.eclipse.jnosql.databases.tinkerpop.communication.CommunicationEntityConverter; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.toList; + +class DefaultEdgeEntity implements EdgeEntity { + + private final O outgoing; + + private final Edge edge; + + private final I incoming; + + DefaultEdgeEntity(Edge edge, I incoming, O outgoing) { + this.edge = edge; + this.incoming = incoming; + this.outgoing = outgoing; + } + + @Override + public Object id() { + return edge.id(); + } + + @Override + public T id(Class type) { + Objects.requireNonNull(type, "type is required"); + return Value.of(edge.id()).get(type); + } + + @Override + public String label() { + return edge.label(); + } + + @SuppressWarnings("unchecked") + @Override + public I incoming() { + return incoming; + } + + @SuppressWarnings("unchecked") + @Override + public O outgoing() { + return outgoing; + } + + @Override + public List properties() { + return edge.keys() + .stream() + .map(k -> Element.of(k, edge.value(k))) + .collect(collectingAndThen(toList(), Collections::unmodifiableList)); + } + + @Override + public void add(String key, Object value) { + requireNonNull(key, "key is required"); + requireNonNull(value, "value is required"); + edge.property(key, value); + + } + + @Override + public void add(String key, Value value) { + requireNonNull(key, "key is required"); + requireNonNull(value, "value is required"); + edge.property(key, value.get()); + } + + @Override + public void remove(String key) { + requireNonNull(key, "key is required"); + Property property = edge.property(key); + property.ifPresent(o -> property.remove()); + } + + @Override + public Optional get(String key) { + requireNonNull(key, "key is required"); + Property property = edge.property(key); + if (property.isPresent()) { + return Optional.of(Value.of(property.value())); + } + return Optional.empty(); + } + + @Override + public boolean isEmpty() { + return edge.keys().isEmpty(); + } + + @Override + public int size() { + return edge.keys().size(); + } + + @Override + public void delete() { + edge.remove(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DefaultEdgeEntity that = (DefaultEdgeEntity) o; + return Objects.equals(edge.id(), that.edge.id()); + } + + @Override + public int hashCode() { + return Objects.hashCode(edge.id()); + } + + @Override + public String toString() { + return outgoing + + "---" + edge.label() + + " --->" + incoming; + } + + + public 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()))); + } + +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatStepTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatStepTraversal.java new file mode 100644 index 000000000..ab7a585dc --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatStepTraversal.java @@ -0,0 +1,42 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import java.util.function.Function; +import java.util.function.Supplier; + +class DefaultEdgeRepeatStepTraversal extends AbstractEdgeTraversal implements EdgeRepeatStepTraversal { + + DefaultEdgeRepeatStepTraversal(Supplier> supplier, + Function, GraphTraversal> flow, + EntityConverter converter) { + super(supplier, flow, converter); + } + + @Override + public EdgeTraversal times(int times) { + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.times(times)), converter); + } + + @Override + public EdgeUntilTraversal until() { + return new DefaultEdgeUntilTraversal(supplier, flow, converter); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatTraversal.java new file mode 100644 index 000000000..c42d26e67 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatTraversal.java @@ -0,0 +1,88 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import java.util.function.Function; +import java.util.function.Supplier; + +import static java.util.Objects.requireNonNull; + +class DefaultEdgeRepeatTraversal extends AbstractEdgeTraversal implements EdgeRepeatTraversal { + + + DefaultEdgeRepeatTraversal(Supplier> supplier, + Function, GraphTraversal> flow, + EntityConverter converter) { + super(supplier, flow, converter); + } + + @Override + public EdgeRepeatStepTraversal has(String propertyKey) { + requireNonNull(propertyKey, "propertyKey is required"); + + Traversal condition = __.has(propertyKey); + return new DefaultEdgeRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); + } + + @Override + public EdgeRepeatStepTraversal has(String propertyKey, Object value) { + requireNonNull(propertyKey, "propertyKey is required"); + requireNonNull(value, "value is required"); + Traversal condition = __.has(propertyKey, value); + return new DefaultEdgeRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); + } + + @Override + public EdgeRepeatStepTraversal has(String propertyKey, P predicate) { + requireNonNull(propertyKey, "propertyKey is required"); + requireNonNull(predicate, "predicate is required"); + Traversal condition = __.has(propertyKey, predicate); + return new DefaultEdgeRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); + } + + @Override + public EdgeRepeatStepTraversal has(T accessor, Object value) { + requireNonNull(accessor, "accessor is required"); + requireNonNull(value, "value is required"); + Traversal condition = __.has(accessor, value); + return new DefaultEdgeRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); + } + + @Override + public EdgeRepeatStepTraversal has(T accessor, P predicate) { + requireNonNull(accessor, "accessor is required"); + requireNonNull(predicate, "predicate is required"); + Traversal condition = __.has(accessor, predicate); + return new DefaultEdgeRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); + } + + @Override + public EdgeRepeatStepTraversal hasNot(String propertyKey) { + requireNonNull(propertyKey, "propertyKey is required"); + Traversal condition = __.hasNot(propertyKey); + return new DefaultEdgeRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); + } +} + + diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversal.java new file mode 100644 index 000000000..be5c8c196 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversal.java @@ -0,0 +1,186 @@ +/* + * 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; + +import jakarta.data.exceptions.NonUniqueResultException; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import java.util.Iterator; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static java.util.Objects.requireNonNull; + +class DefaultEdgeTraversal extends AbstractEdgeTraversal implements EdgeTraversal { + + + DefaultEdgeTraversal(Supplier> supplier, + Function, GraphTraversal> flow, + EntityConverter converter) { + super(supplier, flow, converter); + } + + @Override + public EdgeTraversal has(String propertyKey) { + requireNonNull(propertyKey, "propertyKey is required"); + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.has(propertyKey)), converter); + + } + + @Override + public EdgeTraversal has(String propertyKey, Object value) { + requireNonNull(propertyKey, "propertyKey is required"); + requireNonNull(value, "value is required"); + + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.has(propertyKey, value)), converter); + } + + @Override + public EdgeTraversal has(String propertyKey, P predicate) { + requireNonNull(propertyKey, "propertyKey is required"); + requireNonNull(predicate, "predicate is required"); + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.has(propertyKey, predicate)), converter); + } + + @Override + public EdgeTraversal has(T accessor, Object value) { + requireNonNull(accessor, "accessor is required"); + requireNonNull(value, "value is required"); + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.has(accessor, value)), converter); + } + + @Override + public EdgeTraversal has(T accessor, P predicate) { + requireNonNull(accessor, "accessor is required"); + requireNonNull(predicate, "predicate is required"); + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.has(accessor, predicate)), converter); + } + + @Override + public EdgeTraversal hasNot(String propertyKey) { + requireNonNull(propertyKey, "propertyKey is required"); + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.hasNot(propertyKey)), converter); + } + + @Override + public EdgeTraversal filter(Predicate predicate) { + requireNonNull(predicate, "predicate is required"); + + Predicate> p = e -> predicate.test(EdgeEntity.of(converter, e.get())); + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.filter(p)), converter); + } + + @Override + public EdgeTraversal limit(long limit) { + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.limit(limit)), converter); + } + + @Override + public EdgeTraversal range(long start, long end) { + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.range(start, end)), converter); + } + + @Override + public EdgeRepeatTraversal repeat() { + return new DefaultEdgeRepeatTraversal(supplier, flow, converter); + } + + + @Override + public VertexTraversal inV() { + return new DefaultVertexTraversal(supplier, flow.andThen(GraphTraversal::inV), converter); + } + + @Override + public VertexTraversal outV() { + return new DefaultVertexTraversal(supplier, flow.andThen(GraphTraversal::outV), converter); + } + + @Override + public VertexTraversal bothV() { + return new DefaultVertexTraversal(supplier, flow.andThen(GraphTraversal::bothV), converter); + } + + @Override + public EdgeTraversal dedup(String... labels) { + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.dedup(labels)), converter); + } + + + @Override + public Optional next() { + Optional edgeOptional = flow.apply(supplier.get()).tryNext(); + return edgeOptional.map(edge -> EdgeEntity.of(converter, edge)); + } + + @Override + public Optional singleResult() { + Stream result = result(); + final Iterator iterator = result.iterator(); + + if (!iterator.hasNext()) { + return Optional.empty(); + } + final EdgeEntity entity = iterator.next(); + if (!iterator.hasNext()) { + return Optional.of(entity); + } + throw new NonUniqueResultException("The Edge traversal query returns more than one result"); + } + + @Override + public Stream result() { + return stream(); + } + + @Override + public Stream stream() { + return flow.apply(supplier.get()).toList().stream() + .map(edge -> EdgeEntity.of(converter, edge)); + } + + @Override + public Stream next(int limit) { + return flow.apply(supplier.get()).next(limit).stream() + .map(edge -> EdgeEntity.of(converter, edge)); + } + + @Override + public ValueMapTraversal valueMap(String... propertyKeys) { + return new DefaultValueMapTraversal(supplier, flow.andThen(g -> g.elementMap(propertyKeys))); + } + + @Override + public EdgeTraversalOrder orderBy(String property) { + requireNonNull(property, "property is required"); + return new DefaultEdgeTraversalOrder(supplier, flow, converter, property); + } + + @Override + public long count() { + return flow.apply(supplier.get()).count().tryNext().orElse(0L); + } + + +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalOrder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalOrder.java new file mode 100644 index 000000000..0435df63b --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalOrder.java @@ -0,0 +1,48 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import java.util.function.Function; +import java.util.function.Supplier; + +import static org.apache.tinkerpop.gremlin.process.traversal.Order.asc; +import static org.apache.tinkerpop.gremlin.process.traversal.Order.desc; + +final class DefaultEdgeTraversalOrder extends AbstractEdgeTraversal implements EdgeTraversalOrder { + + + private final String property; + + DefaultEdgeTraversalOrder(Supplier> supplier, Function, + GraphTraversal> flow, EntityConverter converter, String property) { + super(supplier, flow, converter); + this.property = property; + } + + @Override + public EdgeTraversal asc() { + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.order().by(property, asc)), converter); + } + + @Override + public EdgeTraversal desc() { + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.order().by(property, desc)), converter); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeUntilTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeUntilTraversal.java new file mode 100644 index 000000000..00023d859 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeUntilTraversal.java @@ -0,0 +1,84 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import java.util.function.Function; +import java.util.function.Supplier; + +import static java.util.Objects.requireNonNull; + +class DefaultEdgeUntilTraversal extends AbstractEdgeTraversal implements EdgeUntilTraversal { + + DefaultEdgeUntilTraversal(Supplier> supplier, Function, + GraphTraversal> flow, EntityConverter converter) { + super(supplier, flow, converter); + } + + @Override + public EdgeTraversal has(String propertyKey) { + + requireNonNull(propertyKey, "propertyKey is required"); + Traversal condition = __.has(propertyKey); + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public EdgeTraversal has(String propertyKey, Object value) { + requireNonNull(propertyKey, "propertyKey is required"); + requireNonNull(value, "value is required"); + Traversal condition = __.has(propertyKey, value); + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public EdgeTraversal has(String propertyKey, P predicate) { + requireNonNull(propertyKey, "propertyKey is required"); + requireNonNull(predicate, "predicate is required"); + Traversal condition = __.has(propertyKey, predicate); + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public EdgeTraversal has(T accessor, Object value) { + requireNonNull(accessor, "accessor is required"); + requireNonNull(value, "value is required"); + Traversal condition = __.has(accessor, value); + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public EdgeTraversal has(T accessor, P predicate) { + requireNonNull(accessor, "accessor is required"); + requireNonNull(predicate, "predicate is required"); + Traversal condition = __.has(accessor, predicate); + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public EdgeTraversal hasNot(String propertyKey) { + requireNonNull(propertyKey, "propertyKey is required"); + Traversal condition = __.hasNot(propertyKey); + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplate.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplate.java new file mode 100644 index 000000000..8cb811ef3 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplate.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.databases.tinkerpop.mapping; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Default; +import jakarta.inject.Inject; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.eclipse.jnosql.databases.tinkerpop.communication.GraphDatabaseManager; +import org.eclipse.jnosql.mapping.Database; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; +import org.eclipse.jnosql.mapping.semistructured.EventPersistManager; + +import static org.eclipse.jnosql.mapping.DatabaseType.GRAPH; + +@Default +@ApplicationScoped +@Database(GRAPH) +class DefaultGraphTemplate extends AbstractGraphTemplate { + + private final EntityConverter converter; + + private final GraphDatabaseManager manager; + + private final EventPersistManager eventManager; + + private final EntitiesMetadata entities; + + private final Converters converters; + private final Graph graph; + + + @Inject + DefaultGraphTemplate(EntityConverter converter, Graph graph, + EventPersistManager eventManager, + EntitiesMetadata entities, Converters converters) { + this.converter = converter; + this.graph = graph; + this.eventManager = eventManager; + this.entities = entities; + this.converters = converters; + this.manager = GraphDatabaseManager.of(graph); + } + + DefaultGraphTemplate() { + this(null, null, null, null, null); + } + + @Override + protected EntityConverter converter() { + return converter; + } + + @Override + protected GraphDatabaseManager manager() { + return manager; + } + + @Override + protected GraphTraversalSource traversal() { + return graph.traversal(); + } + + @Override + protected Graph graph() { + return graph; + } + + @Override + protected EventPersistManager eventManager() { + return eventManager; + } + + @Override + protected EntitiesMetadata entities() { + return entities; + } + + @Override + protected Converters converters() { + return converters; + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultPreparedStatement.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultPreparedStatement.java new file mode 100644 index 000000000..82336750b --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultPreparedStatement.java @@ -0,0 +1,82 @@ +/* + * 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; + +import jakarta.data.exceptions.NonUniqueResultException; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.eclipse.jnosql.mapping.PreparedStatement; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import static java.util.Objects.requireNonNull; + +final class DefaultPreparedStatement implements PreparedStatement { + + private final GremlinExecutor executor; + + private final String gremlin; + + private final Map params = new HashMap<>(); + + private final GraphTraversalSource traversalSource; + + DefaultPreparedStatement(GremlinExecutor executor, String gremlin, GraphTraversalSource traversalSource) { + this.executor = executor; + this.gremlin = gremlin; + this.traversalSource = traversalSource; + } + + + @Override + public PreparedStatement bind(String name, Object value) { + requireNonNull(name, "name is required"); + requireNonNull(value, "value is required"); + params.put(name, value); + return this; + } + + @Override + public Stream result() { + return executor.executeGremlin(traversalSource, gremlin, params); + } + + @Override + public Optional singleResult() { + Stream entities = result(); + final Iterator iterator = entities.iterator(); + if (!iterator.hasNext()) { + return Optional.empty(); + } + final T entity = iterator.next(); + if (!iterator.hasNext()) { + return Optional.of(entity); + } + throw new NonUniqueResultException("There is more than one result found in the gremlin query: " + gremlin); + } + + @Override + public long count() { + throw new UnsupportedOperationException("There is no count operation in the graph database with Gremlin"); + } + + @Override + public boolean isCount() { + return false; + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversal.java new file mode 100644 index 000000000..aae94ce98 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversal.java @@ -0,0 +1,91 @@ +/* + * 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; + +import jakarta.data.exceptions.NonUniqueResultException; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.Collections.emptyMap; +import static java.util.stream.Collectors.toList; + +/** + * The default implementation of {@link ValueMapTraversal} + */ +class DefaultValueMapTraversal implements ValueMapTraversal { + + private final Supplier> supplier; + private final Function, GraphTraversal>> flow; + + DefaultValueMapTraversal(Supplier> supplier, Function, + GraphTraversal>> flow) { + this.supplier = supplier; + this.flow = flow; + } + + + @Override + public Stream> stream() { + return flow.apply(supplier.get()).toList().stream().map(toMap()); + } + + + @Override + public Stream> next(int limit) { + return flow.apply(supplier.get()).next(limit).stream().map(toMap()); + } + + @Override + public Map next() { + return flow.apply(supplier.get()).tryNext().map(toMap()).orElse(emptyMap()); + } + + @Override + public Optional> singleResult() { + List> result = resultList(); + if (result.isEmpty()) { + return Optional.empty(); + } + if (result.size() == 1) { + return Optional.of(result.get(0)); + } + throw new NonUniqueResultException("The Edge traversal query returns more than one result"); + } + + @Override + public List> resultList() { + return stream().collect(toList()); + } + + @Override + public long count() { + return flow.apply(supplier.get()).count().tryNext().orElse(0L); + } + + private Function, Map> toMap() { + return map -> map + .entrySet() + .stream() + .collect(Collectors.toMap(k -> k.getKey().toString(), Map.Entry::getValue)); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatStepTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatStepTraversal.java new file mode 100644 index 000000000..efdfd966a --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatStepTraversal.java @@ -0,0 +1,41 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import java.util.function.Function; +import java.util.function.Supplier; + +class DefaultVertexRepeatStepTraversal extends AbstractVertexTraversal implements VertexRepeatStepTraversal { + + + DefaultVertexRepeatStepTraversal(Supplier> supplier, Function, + GraphTraversal> flow, EntityConverter converter) { + super(supplier, flow, converter); + } + + @Override + public VertexTraversal times(int times) { + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.times(times)), converter); + } + + @Override + public VertexUntilTraversal until() { + return new DefaultVertexUntilTraversal(supplier, flow, converter); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatTraversal.java new file mode 100644 index 000000000..2ce6512ae --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatTraversal.java @@ -0,0 +1,99 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static java.util.Objects.requireNonNull; + +class DefaultVertexRepeatTraversal extends AbstractVertexTraversal implements VertexRepeatTraversal { + + DefaultVertexRepeatTraversal(Supplier> supplier, + Function, GraphTraversal> flow, + EntityConverter converter) { + super(supplier, flow, converter); + } + + @Override + public VertexRepeatStepTraversal has(String propertyKey, Object value) { + requireNonNull(propertyKey, "propertyKey is required"); + requireNonNull(value, "value is required"); + Traversal condition = __.has(propertyKey, value); + return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); + } + + @Override + public VertexRepeatStepTraversal has(String propertyKey, P predicate) { + requireNonNull(propertyKey, "propertyKey is required"); + requireNonNull(predicate, "predicate is required"); + Traversal condition = __.has(propertyKey, predicate); + return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); + } + + @Override + public VertexRepeatStepTraversal has(T accessor, Object value) { + requireNonNull(accessor, "accessor is required"); + requireNonNull(value, "value is required"); + Traversal condition = __.has(accessor, value); + return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); + } + + @Override + public VertexRepeatStepTraversal has(T accessor, P predicate) { + requireNonNull(accessor, "accessor is required"); + requireNonNull(predicate, "predicate is required"); + Traversal condition = __.has(accessor, predicate); + return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); + } + + @Override + public VertexRepeatStepTraversal hasNot(String propertyKey) { + requireNonNull(propertyKey, "propertyKey is required"); + Traversal condition = __.hasNot(propertyKey); + return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); + } + + @Override + public VertexRepeatStepTraversal out(String... labels) { + Stream.of(labels).forEach(l -> Objects.requireNonNull(l, "label is required")); + Traversal condition = __.out(labels); + return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); + } + + @Override + public VertexRepeatStepTraversal in(String... labels) { + Stream.of(labels).forEach(l -> Objects.requireNonNull(l, "label is required")); + Traversal condition = __.in(labels); + return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); + } + + @Override + public VertexRepeatStepTraversal both(String... labels) { + Stream.of(labels).forEach(l -> Objects.requireNonNull(l, "label is required")); + Traversal condition = __.both(labels); + return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversal.java new file mode 100644 index 000000000..36298e9fc --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversal.java @@ -0,0 +1,237 @@ +/* + * 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; + +import jakarta.data.exceptions.NonUniqueResultException; +import jakarta.nosql.Entity; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.databases.tinkerpop.communication.CommunicationEntityConverter; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import java.util.Iterator; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static java.util.Objects.requireNonNull; + +/** + * The default implementation of {@link VertexTraversal} + */ +class DefaultVertexTraversal extends AbstractVertexTraversal implements VertexTraversal { + + + private static final Predicate IS_EMPTY = String::isEmpty; + private static final Predicate NOT_EMPTY = IS_EMPTY.negate(); + + DefaultVertexTraversal(Supplier> supplier, + Function, GraphTraversal> flow, + EntityConverter converter) { + super(supplier, flow, converter); + } + + @Override + public VertexTraversal has(String propertyKey, Object value) { + requireNonNull(propertyKey, "propertyKey is required"); + requireNonNull(value, "value is required"); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.has(propertyKey, value)), converter); + } + + @Override + public VertexTraversal has(String propertyKey) { + requireNonNull(propertyKey, "propertyKey is required"); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.has(propertyKey)), converter); + } + + @Override + public VertexTraversal has(String propertyKey, P predicate) { + requireNonNull(propertyKey, "propertyKey is required"); + requireNonNull(predicate, "predicate is required"); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.has(propertyKey, predicate)), converter); + } + + @Override + public VertexTraversal has(T accessor, Object value) { + requireNonNull(accessor, "accessor is required"); + requireNonNull(value, "value is required"); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.has(accessor, value)), converter); + } + + @Override + public VertexTraversal has(T accessor, P predicate) { + requireNonNull(accessor, "accessor is required"); + requireNonNull(predicate, "predicate is required"); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.has(accessor, predicate)), converter); + } + + @Override + public VertexTraversal out(String... labels) { + Stream.of(labels).forEach(l -> requireNonNull(l, "label is required")); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.out(labels)), converter); + } + + @Override + public VertexTraversal filter(Predicate predicate) { + requireNonNull(predicate, "predicate is required"); + + Predicate> p = v -> predicate.test(GraphEntityConverter.INSTANCE.toEntity(converter, v.get())); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.filter(p)), converter); + } + + @Override + public EdgeTraversal outE(String... edgeLabels) { + Stream.of(edgeLabels).forEach(l -> requireNonNull(l, "label is required")); + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.outE(edgeLabels)), converter); + } + + @Override + public VertexTraversal in(String... labels) { + Stream.of(labels).forEach(l -> requireNonNull(l, "label is required")); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.in(labels)), converter); + } + + @Override + public EdgeTraversal inE(String... edgeLabels) { + Stream.of(edgeLabels).forEach(l -> requireNonNull(l, "edgeLabel is required")); + + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.inE(edgeLabels)), converter); + } + + @Override + public VertexTraversal both(String... labels) { + Stream.of(labels).forEach(l -> requireNonNull(l, "labels is required")); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.both(labels)), converter); + } + + @Override + public EdgeTraversal bothE(String... edgeLabels) { + Stream.of(edgeLabels).forEach(l -> requireNonNull(l, "edgeLabel is required")); + return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.bothE(edgeLabels)), converter); + } + + @Override + public VertexTraversal dedup(String... labels) { + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.dedup(labels)), converter); + } + + @Override + public VertexRepeatTraversal repeat() { + return new DefaultVertexRepeatTraversal(supplier, flow, converter); + } + + @Override + public VertexTraversal limit(long limit) { + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.limit(limit)), converter); + } + + @Override + public VertexTraversal skip(long skip) { + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.skip(skip)), converter); + } + + @Override + public VertexTraversal range(long start, long end) { + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.range(start, end)), converter); + } + + + @Override + public VertexTraversal hasLabel(String label) { + requireNonNull(label, "label is required"); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.hasLabel(label)), converter); + } + + @Override + public VertexTraversal hasLabel(Class type) { + requireNonNull(type, "type is required"); + Entity entity = type.getAnnotation(Entity.class); + String label = Optional.ofNullable(entity).map(Entity::value) + .filter(NOT_EMPTY) + .orElse(type.getSimpleName()); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.hasLabel(label)), converter); + } + + @Override + public VertexTraversal hasLabel(P predicate) { + requireNonNull(predicate, "predicate is required"); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.hasLabel(predicate)), converter); + } + + @Override + public VertexTraversal hasNot(String propertyKey) { + requireNonNull(propertyKey, "propertyKey is required"); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.hasNot(propertyKey)), converter); + } + + @Override + public Optional next() { + Optional vertex = flow.apply(supplier.get()).tryNext(); + return vertex.map(CommunicationEntityConverter.INSTANCE).map(converter::toEntity); + } + + @Override + public Stream result() { + return flow.apply(supplier.get()) + .toStream() + .map(CommunicationEntityConverter.INSTANCE) + .map(converter::toEntity); + } + + @Override + public Optional singleResult() { + final Stream stream = result(); + final Iterator iterator = stream.iterator(); + + if (!iterator.hasNext()) { + return Optional.empty(); + } + final T entity = iterator.next(); + if (!iterator.hasNext()) { + return Optional.of(entity); + } + throw new NonUniqueResultException("The Vertex traversal query returns more than one result"); + } + + @Override + public Stream next(int limit) { + return flow.apply(supplier.get()) + .next(limit) + .stream() + .map(CommunicationEntityConverter.INSTANCE) + .map(converter::toEntity); + } + + @Override + public ValueMapTraversal valueMap(String... propertyKeys) { + return new DefaultValueMapTraversal(supplier, flow.andThen(g -> g.elementMap(propertyKeys))); + } + + @Override + public long count() { + return flow.apply(supplier.get()).count().tryNext().orElse(0L); + } + + @Override + public VertexTraversalOrder orderBy(String property) { + requireNonNull(property, "property is required"); + return new DefaultVertexTraversalOrder(supplier, flow, converter, property); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalOrder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalOrder.java new file mode 100644 index 000000000..9c1d6fa99 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalOrder.java @@ -0,0 +1,46 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import java.util.function.Function; +import java.util.function.Supplier; + +import static org.apache.tinkerpop.gremlin.process.traversal.Order.asc; +import static org.apache.tinkerpop.gremlin.process.traversal.Order.desc; + +final class DefaultVertexTraversalOrder extends AbstractVertexTraversal implements VertexTraversalOrder { + + private final String property; + + DefaultVertexTraversalOrder(Supplier> supplier, Function, + GraphTraversal> flow, EntityConverter converter, String property) { + super(supplier, flow, converter); + this.property = property; + } + + @Override + public VertexTraversal asc() { + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.order().by(property, asc)), converter); + } + + @Override + public VertexTraversal desc() { + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.order().by(property, desc)), converter); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexUntilTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexUntilTraversal.java new file mode 100644 index 000000000..bd151a1fd --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexUntilTraversal.java @@ -0,0 +1,130 @@ +/* + * 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; + +import jakarta.nosql.Entity; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static java.util.Objects.requireNonNull; + +class DefaultVertexUntilTraversal extends AbstractVertexTraversal implements VertexUntilTraversal { + + + DefaultVertexUntilTraversal(Supplier> supplier, Function, + GraphTraversal> flow, EntityConverter converter) { + super(supplier, flow, converter); + } + + @Override + public VertexTraversal has(String propertyKey, Object value) { + + requireNonNull(propertyKey, "propertyKey is required"); + requireNonNull(value, "value is required"); + Traversal condition = __.has(propertyKey, value); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public VertexTraversal has(String propertyKey) { + requireNonNull(propertyKey, "propertyKey is required"); + Traversal condition = __.has(propertyKey); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public VertexTraversal has(String propertyKey, P predicate) { + requireNonNull(propertyKey, "propertyKey is required"); + requireNonNull(predicate, "predicate is required"); + Traversal condition = __.has(propertyKey, predicate); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public VertexTraversal has(T accessor, Object value) { + requireNonNull(accessor, "accessor is required"); + requireNonNull(value, "value is required"); + Traversal condition = __.has(accessor, value); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public VertexTraversal has(T accessor, P predicate) { + requireNonNull(accessor, "accessor is required"); + requireNonNull(predicate, "predicate is required"); + Traversal condition = __.has(accessor, predicate); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public VertexTraversal hasNot(String propertyKey) { + requireNonNull(propertyKey, "propertyKey is required"); + Traversal condition = __.hasNot(propertyKey); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public VertexTraversal out(String... labels) { + Stream.of(labels).forEach(l -> Objects.requireNonNull(l, "label is required")); + Traversal condition = __.out(labels); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public VertexTraversal in(String... labels) { + Stream.of(labels).forEach(l -> Objects.requireNonNull(l, "label is required")); + Traversal condition = __.in(labels); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public VertexTraversal both(String... labels) { + Stream.of(labels).forEach(l -> Objects.requireNonNull(l, "label is required")); + Traversal condition = __.both(labels); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public VertexTraversal hasLabel(String label) { + requireNonNull(label, "label is required"); + Traversal condition = __.hasLabel(label); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); + } + + @Override + public VertexTraversal hasLabel(Class type) { + requireNonNull(type, "type is required"); + Entity entity = type.getAnnotation(Entity.class); + String label = Optional.ofNullable(entity).map(Entity::value).orElse(type.getName()); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.hasLabel(label)), converter); + } + + @Override + public VertexTraversal hasLabel(P predicate) { + requireNonNull(predicate, "predicate is required"); + return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.hasLabel(predicate)), converter); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeConditionTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeConditionTraversal.java new file mode 100644 index 000000000..4cb8fb26b --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeConditionTraversal.java @@ -0,0 +1,127 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.structure.T; + +import java.util.function.Supplier; + +import static java.util.Objects.requireNonNull; + +/** + * The base conditions to {@link EdgeTraversal} and {@link EdgeUntilTraversal} + */ +public interface EdgeConditionTraversal { + + /** + * Map the {@link org.apache.tinkerpop.gremlin.structure.Element} to the values of the associated properties given the provide property keys. + * If no property keys are provided, then all property values are emitted. + * + * @param propertyKey the key + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when propertyKey is null + */ + EdgeTraversal has(String propertyKey); + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param value the value to the condition + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + EdgeTraversal has(String propertyKey, Object value); + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param predicate the predicate condition + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when either key or predicate condition are null + */ + EdgeTraversal has(String propertyKey, P predicate); + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param value the value to the condition + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + default EdgeTraversal has(Supplier propertyKey, Object value) { + requireNonNull(propertyKey, "the supplier is required"); + return has(propertyKey.get(), value); + } + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param predicate the predicate condition + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when either key or predicate condition are null + */ + default EdgeTraversal has(Supplier propertyKey, P predicate) { + requireNonNull(propertyKey, "the supplier is required"); + return has(propertyKey.get(), predicate); + } + + /** + * Adds a equals condition to a query + * + * @param accessor the key + * @param value the value to the condition + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + EdgeTraversal has(T accessor, Object value); + + /** + * Adds a equals condition to a query + * + * @param accessor the key + * @param predicate the predicate condition + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + EdgeTraversal has(T accessor, P predicate); + + + /** + * Defines Vertex has not a property + * + * @param propertyKey the property key + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when propertyKey is null + */ + EdgeTraversal hasNot(String propertyKey); + + + /** + * Defines Vertex has not a property + * + * @param propertyKey the property key + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when propertyKey is null + */ + default EdgeTraversal hasNot(Supplier propertyKey) { + requireNonNull(propertyKey, "the supplier is required"); + return hasNot(propertyKey.get()); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntity.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntity.java new file mode 100644 index 000000000..222cd643e --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntity.java @@ -0,0 +1,152 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.eclipse.jnosql.communication.Value; +import org.eclipse.jnosql.communication.semistructured.Element; +import org.eclipse.jnosql.databases.tinkerpop.communication.CommunicationEntityConverter; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * It is a wrapper of {@link org.apache.tinkerpop.gremlin.structure.Edge} that links two Entity. + * Along with its Property objects, an Edge has both a Direction and a label. + * Any Change at the Edge is automatically continued in the database. However, any, change in the Entity will be ignored. + * {@link GraphTemplate#update(Object)} + * + *
outVertex ---label---> inVertex.
+ */ +public interface EdgeEntity { + /** + * Returns the identifier of this edge. + * + * @return the identifier of this edge + */ + Object id(); + + /** + * Returns the identifier of this edge converted to the specified type. + * + * @param type the class type to convert the identifier to + * @param the type to convert the identifier to + * @return the identifier of this edge converted to the specified type + */ + T id(Class type); + + /** + * Returns the label of this edge. + * + * @return the label of this edge + */ + String label(); + + /** + * Retrieves the incoming entity connected to this edge. + * + * @param the type of the incoming entity + * @return the incoming entity connected to this edge + */ + T incoming(); + + /** + * Retrieves the outgoing entity connected to this edge. + * + * @param the type of the outgoing entity + * @return the outgoing entity connected to this edge + */ + T outgoing(); + + /** + * Returns the properties of this edge. + * + * @return the properties of this edge + */ + List properties(); + + /** + * Adds a new property to this edge with the specified key and value. + * + * @param key the key of the property + * @param value the value of the property + * @throws NullPointerException if either key or value is null + */ + void add(String key, Object value); + + /** + * Adds a new property to this edge with the specified key and value. + * + * @param key the key of the property + * @param value the value of the property + * @throws NullPointerException if either key or value is null + */ + void add(String key, Value value); + + /** + * Removes the property with the specified key from this edge. + * + * @param key the key of the property to be removed + * @throws NullPointerException if the key is null + */ + void remove(String key); + + /** + * Returns the property value associated with the specified key, if present. + * + * @param key the key of the property to retrieve + * @return the value associated with the specified key, or {@link Optional#empty()} if the key is not present + * @throws NullPointerException if the key is null + */ + Optional get(String key); + + /** + * Returns true if this edge contains no properties. + * + * @return true if this edge contains no properties + */ + boolean isEmpty(); + + /** + * Returns the number of properties in this edge. + * + * @return the number of properties in this edge + */ + int size(); + + /** + * Deletes this edge from the database. After this operation, any modification attempts such as adding or removing properties will result in an illegal state. + */ + void delete(); + + /** + * Creates an {@link EdgeEntity} instance from the provided {@link EntityConverter} and {@link Edge}. + * + * @param converter the entity converter to use + * @param edge the edge to create the entity from + * @return the created {@link EdgeEntity} instance + * @throws NullPointerException if either converter or edge is null + */ + 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.inVertex())), + converter.toEntity(entityConverter.apply(edge.outVertex()))); + } + +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatStepTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatStepTraversal.java new file mode 100644 index 000000000..087d74609 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatStepTraversal.java @@ -0,0 +1,36 @@ +/* + * 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; + +/** + * After the {@link EdgeRepeatTraversal} condition the next step is + * {@link EdgeRepeatStepTraversal#times(int)} and {@link EdgeRepeatStepTraversal#until()} + */ +public interface EdgeRepeatStepTraversal { + + /** + * Starts a loop traversal n times + * @param times the repeat times that is required + * @return a {@link EdgeTraversal} instance + */ + EdgeTraversal times(int times); + + /** + * Define the loop traversal until a defined condition + * @return {@link EdgeUntilTraversal} + */ + EdgeUntilTraversal until(); + +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatTraversal.java new file mode 100644 index 000000000..38ba9338d --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatTraversal.java @@ -0,0 +1,129 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.structure.T; + +import java.util.function.Supplier; + +import static java.util.Objects.requireNonNull; + +/** + * The wrapper step to + * {@link org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#repeat(org.apache.tinkerpop.gremlin.process.traversal.Traversal)} + * in the Edge type. + */ +public interface EdgeRepeatTraversal { + + + /** + * Map the {@link org.apache.tinkerpop.gremlin.structure.Element} to the values of the associated properties given the provide property keys. + * If no property keys are provided, then all property values are emitted. + * + * @param propertyKey the key + * @return a {@link EdgeRepeatStepTraversal} with the new condition + * @throws NullPointerException when propertyKey is null + */ + EdgeRepeatStepTraversal has(String propertyKey); + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param value the value to the condition + * @return a {@link EdgeRepeatStepTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + EdgeRepeatStepTraversal has(String propertyKey, Object value); + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param predicate the predicate condition + * @return a {@link EdgeRepeatStepTraversal} with the new condition + * @throws NullPointerException when either key or predicate condition are null + */ + EdgeRepeatStepTraversal has(String propertyKey, P predicate); + // + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param value the value to the condition + * @return a {@link EdgeRepeatStepTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + default EdgeRepeatStepTraversal has(Supplier propertyKey, Object value) { + requireNonNull(propertyKey, "the supplier is required"); + return has(propertyKey.get(), value); + } + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param predicate the predicate condition + * @return a {@link EdgeRepeatStepTraversal} with the new condition + * @throws NullPointerException when either key or predicate condition are null + */ + default EdgeRepeatStepTraversal has(Supplier propertyKey, P predicate) { + requireNonNull(propertyKey, "the supplier is required"); + return has(propertyKey.get(), predicate); + } + + /** + * Adds a equals condition to a query + * + * @param accessor the key + * @param value the value to the condition + * @return a {@link EdgeRepeatStepTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + EdgeRepeatStepTraversal has(T accessor, Object value); + + /** + * Adds a equals condition to a query + * + * @param accessor the key + * @param predicate the predicate condition + * @return a {@link EdgeRepeatStepTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + EdgeRepeatStepTraversal has(T accessor, P predicate); + + + /** + * Defines Vertex has not a property + * + * @param propertyKey the property key + * @return a {@link EdgeRepeatStepTraversal} with the new condition + * @throws NullPointerException when propertyKey is null + */ + EdgeRepeatStepTraversal hasNot(String propertyKey); + + /** + * Defines Vertex has not a property + * + * @param propertyKey the property key + * @return a {@link EdgeRepeatStepTraversal} with the new condition + * @throws NullPointerException when propertyKey is null + */ + default EdgeRepeatStepTraversal hasNot(Supplier propertyKey) { + requireNonNull(propertyKey, "the supplier is required"); + return hasNot(propertyKey.get()); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversal.java new file mode 100644 index 000000000..45bc12f64 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversal.java @@ -0,0 +1,157 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DedupGlobalStep; + +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Stream; + +/** + * The Graph Traversal that maps {@link org.apache.tinkerpop.gremlin.structure.Edge}. + * This Traversal is lazy, in other words, that just run after any finalizing method. + */ +public interface EdgeTraversal extends EdgeConditionTraversal { + + /** + * Does a filter predicate based + * @param predicate a predicate to apply to each element to determine if it should be included + * @return a with the Vertex predicate + * @throws NullPointerException when predicate is null + */ + EdgeTraversal filter(Predicate predicate); + + /** + * Filter the objects in the traversal by the number of them to pass through the next, where only the first + * {@code n} objects are allowed as defined by the {@code limit} argument. + * + * @param limit the number at which to end the next + * @return a with the limit + */ + EdgeTraversal limit(long limit); + + /** + * Returns an EdgeTraversal with range defined + * + * @param start the start inclusive + * @param end the end exclusive + * @return a with the range set + */ + EdgeTraversal range(long start, long end); + + + /** + * Starts the loop traversal graph + * + * @return a {@link EdgeRepeatTraversal} + */ + EdgeRepeatTraversal repeat(); + + + /** + * Returns the next elements in the traversal. + * If the traversal is empty, then an {@link Optional#empty()} is returned. + * + * @return the EdgeEntity result otherwise {@link Optional#empty()} + */ + Optional next(); + + /** + * Concludes the traversal that returns a single {@link EdgeEntity} result + * @return the EdgeEntity result otherwise {@link Optional#empty()} + */ + Optional singleResult(); + + /** + * Concludes the traversal then returns the result as list. + * @return the entities result + */ + Stream result(); + + + /** + * Converts to vertex traversal taking the incoming Vertex + * + * @return {@link VertexTraversal} + */ + VertexTraversal inV(); + + /** + * Converts to vertex traversal taking the outgoing Vertex + * + * @return {@link VertexTraversal} + */ + VertexTraversal outV(); + + /** + * Converts to vertex traversal taking both incoming and outgoing Vertex + * + * @return {@link VertexTraversal} + */ + VertexTraversal bothV(); + + /** + * Remove all duplicates in the traversal stream up to this point. + * + * @param labels if labels are provided, then the scoped object's labels determine de-duplication. No labels implies current object. + * @return the traversal with an appended {@link DedupGlobalStep}. + */ + EdgeTraversal dedup(final String... labels); + + /** + * Get all the result in the traversal as Stream + * + * @return the entity result as {@link Stream} + */ + Stream stream(); + + /** + * Get the next n elements result as next, the number of elements is limit based + * + * @param limit the limit to result + * @return the entity result as {@link Stream} + */ + Stream next(int limit); + + /** + * Map the {@link org.apache.tinkerpop.gremlin.structure.Element} to a {@link java.util.Map} of the properties key'd according + * to their {@link org.apache.tinkerpop.gremlin.structure.Property#key}. + * If no property keys are provided, then all properties are retrieved. + * + * @param propertyKeys the properties to retrieve + * @return a {@link ValueMapTraversal} instance + */ + ValueMapTraversal valueMap(final String... propertyKeys); + + /** + * Defines the order of the Edge, the property must have in all elements. + * Otherwise, it'll return an exception. As recommendation use + * {@link EdgeTraversal#has(String)} before this method + * + * @param property the property to be order + * @return the {@link EdgeTraversalOrder} to define the order way + * @throws NullPointerException when the property is null + * @throws IllegalStateException when there any Edge that does not have the property + */ + EdgeTraversalOrder orderBy(String property); + + /** + * Map the traversal next to its reduction as a sum of the elements + * + * @return the sum + */ + long count(); +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversalOrder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversalOrder.java new file mode 100644 index 000000000..e2ff71627 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversalOrder.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * The traversal order to Edge + */ +public interface EdgeTraversalOrder { + + /** + * Defines the ascending order + * @return the {@link EdgeTraversal} ordered ascending + */ + EdgeTraversal asc(); + + /** + * Defines the descending order + * @return the {@link EdgeTraversal} ordered descending + */ + EdgeTraversal desc(); +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeUntilTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeUntilTraversal.java new file mode 100644 index 000000000..4cdb90005 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeUntilTraversal.java @@ -0,0 +1,23 @@ +/* + * 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; + + +/** + * The Edge until wrapper of + * {@link org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#until(java.util.function.Predicate)} + */ +public interface EdgeUntilTraversal extends EdgeConditionTraversal { +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphEntityConverter.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphEntityConverter.java new file mode 100644 index 000000000..64870f375 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphEntityConverter.java @@ -0,0 +1,30 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.databases.tinkerpop.communication.CommunicationEntityConverter; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +enum GraphEntityConverter { + + INSTANCE; + + public T toEntity(EntityConverter converter, Vertex vertex) { + var entity = CommunicationEntityConverter.INSTANCE.apply(vertex); + return converter.toEntity(entity); + } + +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplate.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplate.java new file mode 100644 index 000000000..01880d341 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplate.java @@ -0,0 +1,250 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Transaction; +import org.eclipse.jnosql.mapping.PreparedStatement; +import org.eclipse.jnosql.mapping.semistructured.SemiStructuredTemplate; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.Stream; + + +/** + * GraphTemplate is a helper class that increases productivity when performing common Graph operations. + * Includes integrated object mapping between documents and POJOs {@link org.apache.tinkerpop.gremlin.structure.Vertex} + * and {@link org.apache.tinkerpop.gremlin.structure.Edge}. + * It represents the common operation between an entity and {@link org.apache.tinkerpop.gremlin.structure.Graph} + * + * @see org.apache.tinkerpop.gremlin.structure.Graph + */ +public interface GraphTemplate extends SemiStructuredTemplate { + + /** + * Deletes a {@link org.apache.tinkerpop.gremlin.structure.Vertex} + * + * @param id the id to be used in the query {@link org.apache.tinkerpop.gremlin.structure.T#id} + * @param the id type + * @throws NullPointerException when id is null + */ + void delete(T id); + + /** + * Deletes a {@link org.apache.tinkerpop.gremlin.structure.Edge} + * + * @param id the id to be used in the query {@link org.apache.tinkerpop.gremlin.structure.T#id} + * @param the id type + * @throws NullPointerException when either label and id are null + */ + void deleteEdge(T id); + + /** + * Deletes {@link org.apache.tinkerpop.gremlin.structure.Edge} instances + * + * @param ids the ids to be used in the query {@link org.apache.tinkerpop.gremlin.structure.T#id} + * @param the id type + * @throws NullPointerException when either label and id are null + */ + void deleteEdge(Iterable ids); + + /** + * Either find or create an Edge between this two entities. + * {@link org.apache.tinkerpop.gremlin.structure.Edge} + *
entityOUT ---label---> entityIN.
+ * + * @param incoming the incoming entity + * @param label the Edge label + * @param outgoing the outgoing entity + * @param the incoming type + * @param the outgoing type + * @return the {@link EdgeEntity} of these two entities + * @throws NullPointerException Either when any elements are null or the entity is null + */ + EdgeEntity edge(O outgoing, String label, I incoming); + + /** + * Either find or create an Edge between this two entities. + * {@link org.apache.tinkerpop.gremlin.structure.Edge} + *
entityOUT ---label---> entityIN.
+ * + * @param incoming the incoming entity + * @param label the Edge label + * @param outgoing the outgoing entity + * @param the incoming type + * @param the outgoing type + * @return the {@link EdgeEntity} of these two entities + * @throws NullPointerException Either when any elements are null or the entity is null + */ + default EdgeEntity edge(O outgoing, Supplier label, I incoming) { + Objects.requireNonNull(label, "supplier is required"); + return edge(outgoing, label.get(), incoming); + } + + /** + * Find an entity given {@link org.apache.tinkerpop.gremlin.structure.T#label} and + * {@link org.apache.tinkerpop.gremlin.structure.T#id} + * + * @param id the id to be used in the query {@link org.apache.tinkerpop.gremlin.structure.T#id} + * @param the entity type + * @param the id type + * @return the entity found otherwise {@link Optional#empty()} + * @throws NullPointerException when id is null + */ + Optional find(K id); + + /** + * returns the edges of from a vertex id + * + * @param id the id + * @param direction the direction + * @param labels the edge labels + * @param the K type + * @return the Edges + * @throws NullPointerException where there is any parameter null + */ + Collection edgesById(K id, Direction direction, String... labels); + + /** + * returns the edges of from a vertex id + * + * @param id the id + * @param direction the direction + * @param labels the edge labels + * @param the K type + * @return the Edges + * @throws NullPointerException where there is any parameter null + */ + @SuppressWarnings("unchecked") + Collection edgesById(K id, Direction direction, Supplier... labels); + + /** + * returns the edges of from a vertex id + * + * @param id the id + * @param direction the direction + * @param the K type + * @return the Edges + * @throws NullPointerException where there is any parameter null + */ + Collection edgesById(K id, Direction direction); + + /** + * returns the edges of from an entity + * + * @param entity the entity + * @param direction the direction + * @param labels the edge labels + * @param the entity type + * @return the Edges + * @throws NullPointerException where there is any parameter null + */ + Collection edges(T entity, Direction direction, String... labels); + + /** + * returns the edges of from an entity + * + * @param entity the entity + * @param direction the direction + * @param labels the edge labels + * @param the entity type + * @return the Edges + * @throws NullPointerException where there is any parameter null + */ + @SuppressWarnings("unchecked") + Collection edges(T entity, Direction direction, Supplier... labels); + + /** + * returns the edges of from an entity + * + * @param entity the entity + * @param direction the direction + * @param the entity type + * @return the Edges + * @throws NullPointerException where there is any parameter null + */ + Collection edges(T entity, Direction direction); + + /** + * Finds an {@link EdgeEntity} from the Edge Id + * + * @param edgeId the edge id + * @param the edge id type + * @return the {@link EdgeEntity} otherwise {@link Optional#empty()} + * @throws IllegalStateException when edgeId is null + */ + Optional edge(E edgeId); + + + /** + * Gets a {@link VertexTraversal} to run a query in the graph + * + * @param vertexIds get ids + * @return a {@link VertexTraversal} instance + * @throws IllegalStateException if any id element is null + */ + VertexTraversal traversalVertex(Object... vertexIds); + + + /** + * Gets a {@link EdgeTraversal} to run a query in the graph + * + * @param edgeIds get ids + * @return a {@link VertexTraversal} instance + * @throws IllegalStateException if any id element is null + */ + EdgeTraversal traversalEdge(Object... edgeIds); + + /** + * Gets the current transaction + * + * @return the current {@link Transaction} + */ + Transaction transaction(); + + /** + * Executes a Gremlin then bring the result as a {@link Stream} + * + * @param gremlin the query gremlin + * @param the entity type + * @return the result as {@link Stream} + * @throws NullPointerException when the gremlin is null + */ + Stream gremlin(String gremlin); + + /** + * Executes a Gremlin query then bring the result as a unique result + * + * @param gremlin the gremlin query + * @param the entity type + * @return the result as {@link List} + * @throws NullPointerException when the query is null + */ + Optional gremlinSingleResult(String gremlin); + + /** + * Creates a {@link PreparedStatement} from the query + * + * @param gremlin the gremlin query + * @return a {@link PreparedStatement} instance + * @throws NullPointerException when the query is null + */ + PreparedStatement gremlinPrepare(String gremlin); + +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducer.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducer.java new file mode 100644 index 000000000..37a4ee1c5 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducer.java @@ -0,0 +1,125 @@ +/* + * 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; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Vetoed; +import jakarta.inject.Inject; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.eclipse.jnosql.communication.semistructured.DatabaseManager; +import org.eclipse.jnosql.databases.tinkerpop.communication.GraphDatabaseManager; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; +import org.eclipse.jnosql.mapping.semistructured.EventPersistManager; + +import java.util.Objects; +import java.util.function.Function; + +/** + * An {@code ApplicationScoped} producer class responsible for creating instances of {@link GraphTemplate}. + * It implements the {@link Function} interface with {@link DatabaseManager} as input and {@link GraphTemplate} as output. + */ +@ApplicationScoped +public class GraphTemplateProducer implements Function { + + @Inject + private EntityConverter converter; + + @Inject + private EventPersistManager eventManager; + + @Inject + private EntitiesMetadata entities; + + @Inject + private Converters converters; + + + @Override + public GraphTemplate apply(Graph graph) { + Objects.requireNonNull(graph, "graph is required"); + return new ProducerGraphTemplate(converter, graph, + eventManager, entities, converters); + } + + @Vetoed + static class ProducerGraphTemplate extends AbstractGraphTemplate { + + private final EntityConverter converter; + + + private final EventPersistManager eventManager; + + private final EntitiesMetadata entities; + + private final Converters converters; + + private final Graph graph; + + private final GraphDatabaseManager manager; + + public ProducerGraphTemplate(EntityConverter converter, Graph graph, + EventPersistManager eventManager, + EntitiesMetadata entities, Converters converters) { + this.converter = converter; + this.graph = graph; + this.manager = GraphDatabaseManager.of(graph); + this.eventManager = eventManager; + this.entities = entities; + this.converters = converters; + } + + ProducerGraphTemplate() { + this(null, null, null, null, null); + } + + @Override + protected EntityConverter converter() { + return converter; + } + + @Override + protected GraphDatabaseManager manager() { + return manager; + } + + @Override + protected EventPersistManager eventManager() { + return eventManager; + } + + @Override + protected EntitiesMetadata entities() { + return entities; + } + + @Override + protected Converters converters() { + return converters; + } + + @Override + protected GraphTraversalSource traversal() { + return graph.traversal(); + } + + @Override + protected Graph graph() { + return graph; + } + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinExecutor.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinExecutor.java new file mode 100644 index 000000000..ce1eb449e --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinExecutor.java @@ -0,0 +1,85 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.jsr223.GremlinLangScriptEngine; +import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.databases.tinkerpop.communication.CommunicationEntityConverter; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; + +import javax.script.Bindings; +import javax.script.ScriptException; +import java.util.Collections; +import java.util.Map; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +final class GremlinExecutor { + private final EntityConverter converter; + + private static final GremlinScriptEngine ENGINE = new GremlinLangScriptEngine(); + + GremlinExecutor(EntityConverter converter) { + this.converter = converter; + } + + Stream executeGremlin(GraphTraversalSource traversalSource, String gremlin) { + return executeGremlin(traversalSource, gremlin, Collections.emptyMap()); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + Stream executeGremlin(GraphTraversalSource traversalSource, String gremlin, Map params) { + try { + Bindings bindings = ENGINE.createBindings(); + bindings.put("g", traversalSource); + + + String query = GremlinParamParser.INSTANCE.apply(gremlin, params); + Object eval = ENGINE.eval(query, bindings); + if (eval instanceof GraphTraversal graphTraversal) { + return convertToStream(graphTraversal.toStream()); + } + if (eval instanceof Iterable iterable) { + return convertToStream(StreamSupport.stream(iterable.spliterator(), false)); + } + if (eval instanceof Stream stream) { + return convertToStream(stream); + } + return Stream.of((T) eval); + } catch (ScriptException e) { + throw new GremlinQueryException("There is an error when executed the gremlin query: " + gremlin, e); + } + } + + @SuppressWarnings("unchecked") + private Stream convertToStream(Stream stream) { + return stream.map(this::getElement).map(e -> (T) e); + } + + private Object getElement(Object entity) { + if (entity instanceof Vertex vertex) { + return converter.toEntity(CommunicationEntityConverter.INSTANCE.apply(vertex)); + } + + if (entity instanceof Edge edge) { + return EdgeEntity.of(converter, edge); + } + return entity; + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinParamParser.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinParamParser.java new file mode 100644 index 000000000..a7f5641a7 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinParamParser.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 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.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiFunction; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * This singleton has the goal to interpolate params inside the Gremlin query. + * Thus, given the query: + * "g.V().hasLabel(@param)" where the params is {"param":"Otavio"} + * It should return the query to: g.V().hasLabel("Otavio") + * It should check the Gremlin query options: + * Gremlin.g4 + *

+ * Thus, given a current query with params it should convert to Gremlin compatible syntax. + */ +enum GremlinParamParser implements BiFunction, String> { + INSTANCE; + + private final Pattern pattern = Pattern.compile("@\\w+"); + + @Override + public String apply(String query, Map params) { + Objects.requireNonNull(query, "query is required"); + Objects.requireNonNull(query, "params is required"); + Matcher matcher = pattern.matcher(query); + List leftParams = new ArrayList<>(params.keySet()); + StringBuilder gremlin = new StringBuilder(); + while (matcher.find()) { + String param = matcher.group().substring(1); + leftParams.remove(param); + Object value = params.get(param); + if (value == null) { + throw new GremlinQueryException("The param is " + param + " is required on the query " + query); + } + matcher.appendReplacement(gremlin, toString(value)); + } + matcher.appendTail(gremlin); + if (leftParams.isEmpty()) { + return gremlin.toString(); + } + + throw new GremlinQueryException("There are params missing on the parser: " + leftParams + " on the query" + query); + } + + private String toString(Object value) { + if (value instanceof Number) { + return value.toString(); + } + return '\'' + value.toString() + '\''; + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinQueryException.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinQueryException.java new file mode 100644 index 000000000..05ac272fa --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinQueryException.java @@ -0,0 +1,43 @@ +/* + * 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; + + +import jakarta.data.exceptions.MappingException; + +/** + * An exception that provides information when executing Gremlin in the database. + */ +public class GremlinQueryException extends MappingException { + + /** + * A new instance with both the cause of the error and a message + * + * @param message the message + * @param cause the cause + */ + public GremlinQueryException(String message, Throwable cause) { + super(message, cause); + } + + /** + * A new instance with the message + * + * @param message the message + */ + public GremlinQueryException(String message) { + super(message); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/Transactional.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/Transactional.java new file mode 100644 index 000000000..9f3e8f496 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/Transactional.java @@ -0,0 +1,35 @@ +/* + * 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; + + +import jakarta.interceptor.InterceptorBinding; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * The Transactional annotation provides the application the ability to declaratively + * control transaction boundaries on CDI managed beans. {@link org.apache.tinkerpop.gremlin.structure.Transaction} + */ +@InterceptorBinding +@Target({METHOD, TYPE}) +@Retention(RUNTIME) +public @interface Transactional { +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TransactionalInterceptor.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TransactionalInterceptor.java new file mode 100644 index 000000000..8677f9af7 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TransactionalInterceptor.java @@ -0,0 +1,59 @@ +/* + * 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; + +import jakarta.annotation.Priority; +import jakarta.enterprise.inject.Alternative; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InvocationContext; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Transaction; +import org.eclipse.jnosql.databases.tinkerpop.communication.GraphTransactionUtil; + + +@Transactional +@Interceptor +@Alternative +@Priority(Interceptor.Priority.APPLICATION) +class TransactionalInterceptor { + + @Inject + private Instance graph; + + @AroundInvoke + public Object manageTransaction(InvocationContext context) throws Exception { + Transaction transaction = graph.get().tx(); + GraphTransactionUtil.lock(transaction); + if (!transaction.isOpen()) { + transaction.open(); + } + try { + Object proceed = context.proceed(); + transaction.commit(); + return proceed; + } catch (Exception exception) { + transaction.rollback(); + throw exception; + }finally { + GraphTransactionUtil.unlock(); + } + + } + + +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/ValueMapTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/ValueMapTraversal.java new file mode 100644 index 000000000..eff957311 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/ValueMapTraversal.java @@ -0,0 +1,71 @@ +/* + * 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; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * The Graph Traversal that maps {@link org.apache.tinkerpop.gremlin.structure.Element}. + * This Traversal is lazy, in other words, that just run after the + */ +public interface ValueMapTraversal { + + + /** + * Get all the result in the traversal as Stream + * + * @return the entity result as {@link Stream} + */ + Stream> stream(); + + /** + * Get the next n-number of results from the traversal. + * + * @param limit the limit to result + * @return the entity result as {@link Stream} + */ + Stream> next(int limit); + + /** + * Returns the next elements in the traversal. + * + * @return the map + */ + Map next(); + + /** + * Concludes the traversal that returns a single result + * + * @return the entity result otherwise {@link Optional#empty()} + */ + Optional> singleResult(); + + /** + * Concludes the traversal then returns the result as list. + * + * @return the maps result + */ + List> resultList(); + + /** + * Map the traversal next to its reduction as a sum of the elements + * + * @return the sum + */ + long count(); +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexConditionTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexConditionTraversal.java new file mode 100644 index 000000000..b8ae71997 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexConditionTraversal.java @@ -0,0 +1,236 @@ +/* + * 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; + + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.structure.T; + +import java.util.function.Supplier; + +import static java.util.Objects.requireNonNull; + +/** + * The base conditions to {@link VertexTraversal} and {@link VertexUntilTraversal} + */ +public interface VertexConditionTraversal { + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param value the value to the condition + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + VertexTraversal has(String propertyKey, Object value); + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when the propertyKey is null + */ + VertexTraversal has(String propertyKey); + + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param predicate the predicate condition + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when either key or predicate condition are null + */ + VertexTraversal has(String propertyKey, P predicate); + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param value the value to the condition + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + default VertexTraversal has(Supplier propertyKey, Object value) { + requireNonNull(propertyKey, "the supplier is required"); + return has(propertyKey.get(), value); + } + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param predicate the predicate condition + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when either key or predicate condition are null + */ + default VertexTraversal has(Supplier propertyKey, P predicate) { + requireNonNull(propertyKey, "the supplier is required"); + return has(propertyKey.get(), predicate); + } + + /** + * Adds a equals condition to a query + * + * @param accessor the key + * @param value the value to the condition + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + VertexTraversal has(T accessor, Object value); + + /** + * Adds a equals condition to a query + * + * @param accessor the key + * @param predicate the predicate condition + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + VertexTraversal has(T accessor, P predicate); + + + /** + * Defines Vertex has not a property + * + * @param propertyKey the property key + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when propertyKey is null + */ + VertexTraversal hasNot(String propertyKey); + + /** + * Defines Vertex has not a property + * + * @param propertyKey the property key + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when propertyKey is null + */ + default VertexTraversal hasNot(Supplier propertyKey) { + requireNonNull(propertyKey, "the supplier is required"); + return hasNot(propertyKey.get()); + } + + /** + * Map the {@link VertexTraversal} to its outgoing adjacent vertices given the edge labels. + * + * @param labels the edge labels to traverse + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + VertexTraversal out(String... labels); + + + /** + * Map the {@link VertexTraversal} to its adjacent vertices given the edge labels. + * + * @param labels the edge labels to traverse + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + VertexTraversal in(String... labels); + + + /** + * Map the {@link VertexTraversal} to its incoming adjacent vertices given the edge labels. + * + * @param labels the edge labels to traverse + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + VertexTraversal both(String... labels); + + /** + * Map the {@link VertexTraversal} to its outgoing adjacent vertices given the edge labels. + * + * @param label the edge labels to traverse + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + default VertexTraversal out(Supplier label) { + requireNonNull(label, "the supplier is required"); + return out(label.get()); + } + + /** + * Map the {@link VertexTraversal} to its adjacent vertices given the edge labels. + * + * @param label the edge labels to traverse + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + default VertexTraversal in(Supplier label) { + requireNonNull(label, "the supplier is required"); + return in(label.get()); + } + + + /** + * Map the {@link VertexTraversal} to its incoming adjacent vertices given the edge labels. + * + * @param label the edge labels to traverse + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + default VertexTraversal both(Supplier label) { + requireNonNull(label, "the supplier is required"); + return both(label.get()); + } + + /** + * Defines Vertex as label condition + * + * @param label the labels in the condition + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when label is null + */ + VertexTraversal hasLabel(String label); + + /** + * Defines Vertex as label condition + * + * @param the entity type + * @param type reads the {@link jakarta.nosql.Entity} annotation otherwise the {@link Class#getSimpleName()} + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when entityClazz is null + */ + VertexTraversal hasLabel(Class type); + + /** + * Defines Vertex as label condition + * + * @param the entity type + * @param predicate the predicate + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when predicate is null + */ + VertexTraversal hasLabel(P predicate); + + /** + * Defines Vertex as label condition + * + * @param label the labels in the condition + * @return a {@link VertexTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + default VertexTraversal hasLabel(Supplier label) { + requireNonNull(label, "the supplier is required"); + return hasLabel(label.get()); + } + + +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatStepTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatStepTraversal.java new file mode 100644 index 000000000..49b0cf5a3 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatStepTraversal.java @@ -0,0 +1,35 @@ +/* + * 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; + +/** + * After the {@link VertexRepeatTraversal} condition the next step is + * {@link VertexRepeatStepTraversal#times(int)} and {@link VertexRepeatStepTraversal#until()} + */ +public interface VertexRepeatStepTraversal { + + /** + * Starts a loop traversal n times + * @param times the repeat times that is required + * @return a {@link VertexTraversal} instance + */ + VertexTraversal times(int times); + + /** + * Define the loop traversal until a defined condition + * @return {@link VertexUntilTraversal} + */ + VertexUntilTraversal until(); +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatTraversal.java new file mode 100644 index 000000000..fae5edeb7 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatTraversal.java @@ -0,0 +1,148 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.structure.T; + +import java.util.function.Supplier; + +import static java.util.Objects.requireNonNull; + +/** + * The wrapper step to + * {@link org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#repeat(org.apache.tinkerpop.gremlin.process.traversal.Traversal)} + * in the Vertex type. + */ +public interface VertexRepeatTraversal { + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param value the value to the condition + * @return a {@link VertexRepeatStepTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + VertexRepeatStepTraversal has(String propertyKey, Object value); + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param predicate the predicate condition + * @return a {@link VertexRepeatStepTraversal} with the new condition + * @throws NullPointerException when either key or predicate condition are null + */ + VertexRepeatStepTraversal has(String propertyKey, P predicate); + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param value the value to the condition + * @return a {@link VertexRepeatStepTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + default VertexRepeatStepTraversal has(Supplier propertyKey, Object value){ + requireNonNull(propertyKey, "the supplier is required"); + return has(propertyKey.get(), value); + } + + /** + * Adds a equals condition to a query + * + * @param propertyKey the key + * @param predicate the predicate condition + * @return a {@link VertexRepeatStepTraversal} with the new condition + * @throws NullPointerException when either key or predicate condition are null + */ + default VertexRepeatStepTraversal has(Supplier propertyKey, P predicate){ + requireNonNull(propertyKey, "the supplier is required"); + return has(propertyKey.get(), predicate); + } + + /** + * Adds a equals condition to a query + * + * @param accessor the key + * @param value the value to the condition + * @return a {@link VertexRepeatStepTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + VertexRepeatStepTraversal has(T accessor, Object value); + + /** + * Adds a equals condition to a query + * + * @param accessor the key + * @param predicate the predicate condition + * @return a {@link VertexRepeatStepTraversal} with the new condition + * @throws NullPointerException when either key or value are null + */ + VertexRepeatStepTraversal has(T accessor, P predicate); + + + /** + * Defines Vertex has not a property + * + * @param propertyKey the property key + * @return a {@link VertexRepeatStepTraversal} with the new condition + * @throws NullPointerException when propertyKey is null + */ + VertexRepeatStepTraversal hasNot(String propertyKey); + + /** + * Defines Vertex has not a property + * + * @param propertyKey the property key + * @return a {@link VertexRepeatStepTraversal} with the new condition + * @throws NullPointerException when propertyKey is null + */ + default VertexRepeatStepTraversal hasNot(Supplier propertyKey){ + requireNonNull(propertyKey, "the supplier is required"); + return hasNot(propertyKey.get()); + } + + /** + * Map the {@link VertexTraversal} to its outgoing adjacent vertices given the edge labels. + * + * @param labels the edge labels to traverse + * @return a {@link VertexRepeatStepTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + VertexRepeatStepTraversal out(String... labels); + + + /** + * Map the {@link VertexTraversal} to its adjacent vertices given the edge labels. + * + * @param labels the edge labels to traverse + * @return a {@link VertexRepeatStepTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + VertexRepeatStepTraversal in(String... labels); + + + /** + * Map the {@link VertexTraversal} to its incoming adjacent vertices given the edge labels. + * + * @param labels the edge labels to traverse + * @return a {@link VertexRepeatStepTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + VertexRepeatStepTraversal both(String... labels); + +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversal.java new file mode 100644 index 000000000..c2156fc48 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversal.java @@ -0,0 +1,217 @@ +/* + * 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; + +import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DedupGlobalStep; + +import java.util.Optional; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static java.util.Objects.requireNonNull; + +/** + * The Graph Traversal that maps {@link org.apache.tinkerpop.gremlin.structure.Vertex}. + * This Traversal is lazy, in other words, that just run after any finalizing method. + */ +public interface VertexTraversal extends VertexConditionTraversal { + + + /** + * Does a filter predicate based + * + * @param predicate a predicate to apply to each element to determine if it should be included + * @param the type + * @return a {@link EdgeTraversal} with the Vertex predicate + * @throws NullPointerException when predicate is null + */ + VertexTraversal filter(Predicate predicate); + + /** + * Map the {@link EdgeTraversal} to its outgoing incident edges given the edge labels. + * + * @param edgeLabels the edge labels to traverse + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + EdgeTraversal outE(String... edgeLabels); + + + /** + * Map the {@link EdgeTraversal} to its incoming incident edges given the edge labels. + * + * @param edgeLabels the edge labels to traverse + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + EdgeTraversal inE(String... edgeLabels); + + /** + * Map the {@link EdgeTraversal} to its either incoming or outgoing incident edges given the edge labels. + * + * @param edgeLabels the edge labels to traverse + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + EdgeTraversal bothE(String... edgeLabels); + + /** + * Remove all duplicates in the traversal stream up to this point. + * + * @param labels if labels are provided, then the scoped object's labels determine de-duplication. No labels implies current object. + * @return the traversal with an appended {@link DedupGlobalStep}. + */ + VertexTraversal dedup(final String... labels); + + + /** + * Starts the loop traversal graph + * + * @return a {@link VertexRepeatTraversal} + */ + VertexRepeatTraversal repeat(); + + + /** + * Map the {@link EdgeTraversal} to its outgoing incident edges given the edge labels. + * + * @param label the edge labels to traverse + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + default EdgeTraversal outE(Supplier label) { + requireNonNull(label, "the supplier is required"); + return outE(label.get()); + } + + + /** + * Map the {@link EdgeTraversal} to its incoming incident edges given the edge labels. + * + * @param label the edge labels to traverse + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + default EdgeTraversal inE(Supplier label) { + requireNonNull(label, "the supplier is required"); + return inE(label.get()); + } + + + /** + * Map the {@link EdgeTraversal} to its either incoming or outgoing incident edges given the edge labels. + * + * @param label the edge labels to traverse + * @return a {@link EdgeTraversal} with the new condition + * @throws NullPointerException when has any null element + */ + default EdgeTraversal bothE(Supplier label) { + requireNonNull(label, "the supplier is required"); + return bothE(label.get()); + } + + + /** + * Filter the objects in the traversal by the number of them to pass through the next, where only the first + * {@code n} objects are allowed as defined by the {@code limit} argument. + * + * @param limit the number at which to end the next + * @return a VertexTraversal with the limit + */ + VertexTraversal limit(long limit); + + /** + * Filters out the first {@code n} objects in the traversal. + * + * @param skip the number of objects to skip + * @return a VertexTraversal with skip + */ + VertexTraversal skip(long skip); + + /** + * Returns a VertexTraversal with range defined + * + * @param start the start inclusive + * @param end the end exclusive + * @return a with the range set + */ + VertexTraversal range(long start, long end); + + /** + * Returns the next elements in the traversal. + * If the traversal is empty, then an {@link Optional#empty()} is returned. + * + * @param the entity type + * @return the entity result otherwise {@link Optional#empty()} + */ + Optional next(); + + /** + * Get all the result in the traversal as Stream + * + * @param the entity type + * @return the entity result as {@link Stream} + */ + Stream result(); + + /** + * Concludes the traversal that returns a single result + * + * @param the type + * @return the entity result otherwise {@link Optional#empty()} + */ + Optional singleResult(); + + + /** + * Get the next n-number of results from the traversal. + * + * @param the entity type + * @param limit the limit to result + * @return the entity result as {@link Stream} + */ + Stream next(int limit); + + /** + * Map the {@link org.apache.tinkerpop.gremlin.structure.Element} to a {@link java.util.Map} of the properties key'd according + * to their {@link org.apache.tinkerpop.gremlin.structure.Property#key}. + * If no property keys are provided, then all properties are retrieved. + * + * @param propertyKeys the properties to retrieve + * @return a {@link ValueMapTraversal} instance + */ + ValueMapTraversal valueMap(final String... propertyKeys); + + + /** + * Map the traversal next to its reduction as a sum of the elements + * + * @return the sum + */ + long count(); + + /** + * Defines the order of the Vertex, the property must have in all elements. + * Otherwise, it'll return an exception, as recommendation use + * {@link VertexTraversal#has(String)} before this method + * + * @param property the property to be order + * @return the {@link VertexTraversalOrder} to define the order way + * @throws NullPointerException when the property is null + * @throws IllegalStateException when there any Edge that does not have the property + */ + VertexTraversalOrder orderBy(String property); +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversalOrder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversalOrder.java new file mode 100644 index 000000000..cf3aa0e0e --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversalOrder.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * The traversal order to Vertex + */ +public interface VertexTraversalOrder { + + /** + * Defines the ascending order + * @return the {@link VertexTraversal} ordered ascending + */ + VertexTraversal asc(); + + /** + * Defines the descending order + * @return the {@link VertexTraversal} ordered descending + */ + VertexTraversal desc(); +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexUntilTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexUntilTraversal.java new file mode 100644 index 000000000..97e07f971 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexUntilTraversal.java @@ -0,0 +1,22 @@ +/* + * 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; + +/** + * The Vertex until wrapper of + * {@link org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#until(java.util.function.Predicate)} + */ +public interface VertexUntilTraversal extends VertexConditionTraversal { +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplier.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplier.java new file mode 100644 index 000000000..bfd9a1aa6 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplier.java @@ -0,0 +1,54 @@ +/* + * 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.configuration; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Disposes; +import jakarta.enterprise.inject.Produces; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.eclipse.jnosql.databases.tinkerpop.communication.GraphConfiguration; +import org.eclipse.jnosql.mapping.core.config.MicroProfileSettings; +import org.eclipse.jnosql.mapping.reflection.Reflections; + +import java.util.function.Supplier; +import java.util.logging.Logger; + +import static org.eclipse.jnosql.mapping.core.config.MappingConfigurations.GRAPH_PROVIDER; + + +@ApplicationScoped +class GraphSupplier implements Supplier { + + public static final Logger LOGGER = Logger.getLogger(GraphSupplier.class.getName()); + + @SuppressWarnings("unchecked") + @Override + @Produces + @ApplicationScoped + public Graph get(){ + var settings = MicroProfileSettings.INSTANCE; + LOGGER.fine("Loading the Graph configuration"); + var configuration = settings.get(GRAPH_PROVIDER, Class.class) + .filter(GraphConfiguration.class::isAssignableFrom) + .map(c -> (GraphConfiguration) Reflections.newInstance(c)).orElseGet(GraphConfiguration::getConfiguration); + LOGGER.fine("The Graph configuration loaded successfully with: " + configuration.getClass()); + return configuration.apply(settings); + } + + public void close(@Disposes Graph graph) throws Exception { + LOGGER.fine("Closing the Graph"); + graph.close(); + } +} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/RepositoryGraphBean.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/RepositoryGraphBean.java new file mode 100644 index 000000000..c7076db82 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/RepositoryGraphBean.java @@ -0,0 +1,115 @@ +/* + * 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.query; + +import jakarta.data.repository.DataRepository; +import jakarta.enterprise.context.spi.CreationalContext; +import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphTemplate; +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; + + +/** + * This class serves as a JNoSQL discovery bean for CDI extension, responsible for registering 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 RepositoryGraphBean> 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 RepositoryGraphBean(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; + } + + @Override + @SuppressWarnings("unchecked") + public T create(CreationalContext context) { + EntitiesMetadata entities = getInstance(EntitiesMetadata.class); + var template = provider.isEmpty() ? getInstance(GraphTemplate.class) : + getInstance(GraphTemplate.class, DatabaseQualifier.ofGraph(provider)); + + Converters converters = getInstance(Converters.class); + + var handler = new SemiStructuredRepositoryProxy<>(template, + entities, type, converters); + 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-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/CustomRepositoryGraphBean.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/CustomRepositoryGraphBean.java new file mode 100644 index 000000000..e32676b33 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/CustomRepositoryGraphBean.java @@ -0,0 +1,119 @@ +/* + * 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.GraphTemplate; +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(GraphTemplate.class) : + getInstance(GraphTemplate.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-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtension.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtension.java new file mode 100644 index 000000000..bd54b6267 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtension.java @@ -0,0 +1,83 @@ +/* + * 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.event.Observes; +import jakarta.enterprise.inject.spi.AfterBeanDiscovery; +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.mapping.DatabaseMetadata; +import org.eclipse.jnosql.mapping.Databases; +import org.eclipse.jnosql.mapping.metadata.ClassScanner; + +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Logger; + +import static org.eclipse.jnosql.mapping.DatabaseType.GRAPH; + +/** + * Extension to start up the GraphTemplate, Repository + * from the {@link org.eclipse.jnosql.mapping.Database} qualifier + */ +public class GraphExtension implements Extension { + + private static final Logger LOGGER = Logger.getLogger(GraphExtension.class.getName()); + + private final Set databases = new HashSet<>(); + + void observes(@Observes final ProcessProducer pp) { + Databases.addDatabase(pp, GRAPH, databases); + } + + void onAfterBeanDiscovery(@Observes final AfterBeanDiscovery afterBeanDiscovery) { + + ClassScanner scanner = ClassScanner.load(); + Set> crudTypes = scanner.repositoriesStandard(); + + + 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 -> { + if (!type.getProvider().isBlank()) { + final TemplateBean bean = new TemplateBean(type.getProvider()); + afterBeanDiscovery.addBean(bean); + } + }); + + + 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-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/TemplateBean.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/TemplateBean.java new file mode 100644 index 000000000..7e387c6b5 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/TemplateBean.java @@ -0,0 +1,83 @@ +/* + * 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 jakarta.nosql.Template; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphTemplate; +import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphTemplateProducer; +import org.eclipse.jnosql.mapping.DatabaseQualifier; +import org.eclipse.jnosql.mapping.DatabaseType; +import org.eclipse.jnosql.mapping.core.spi.AbstractBean; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.Set; + +class TemplateBean extends AbstractBean { + + private static final Set TYPES = Set.of(GraphTemplate.class, Template.class); + + private final String provider; + + private final Set qualifiers; + + /** + * Constructor + * + * @param provider the provider name, that must be a + */ + public TemplateBean(String provider) { + this.provider = provider; + this.qualifiers = Collections.singleton(DatabaseQualifier.ofGraph(provider)); + } + + @Override + public Class getBeanClass() { + return GraphTemplate.class; + } + + + @Override + public GraphTemplate create(CreationalContext context) { + + GraphTemplateProducer producer = getInstance(GraphTemplateProducer.class); + Graph graph = getGraph(); + return producer.apply(graph); + } + + private Graph getGraph() { + return getInstance(Graph.class, DatabaseQualifier.ofGraph(provider)); + } + + @Override + public Set getTypes() { + return TYPES; + } + + @Override + public Set getQualifiers() { + return qualifiers; + } + + + @Override + public String getId() { + return GraphTemplate.class.getName() + DatabaseType.GRAPH + "-" + provider; + } + +} diff --git a/jnosql-neo4j/src/main/resources/META-INF/beans.xml b/jnosql-neo4j/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..0340ecdbc --- /dev/null +++ b/jnosql-neo4j/src/main/resources/META-INF/beans.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/jnosql-neo4j/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension b/jnosql-neo4j/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension new file mode 100644 index 000000000..7464d6835 --- /dev/null +++ b/jnosql-neo4j/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension @@ -0,0 +1,15 @@ +# +# 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 +# +org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/communication/DefaultGraphDatabaseManagerTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/communication/DefaultGraphDatabaseManagerTest.java new file mode 100644 index 000000000..a5f5d954f --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/communication/DefaultGraphDatabaseManagerTest.java @@ -0,0 +1,465 @@ +/* + * 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.communication; + +import net.datafaker.Faker; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.assertj.core.api.Assertions; +import org.assertj.core.api.SoftAssertions; +import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; +import org.eclipse.jnosql.communication.semistructured.DeleteQuery; +import org.eclipse.jnosql.communication.semistructured.Element; +import org.eclipse.jnosql.communication.semistructured.Elements; +import org.eclipse.jnosql.communication.semistructured.SelectQuery; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.jnosql.communication.semistructured.DeleteQuery.delete; +import static org.eclipse.jnosql.communication.semistructured.SelectQuery.select; +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; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class DefaultGraphDatabaseManagerTest { + + public static final String COLLECTION_NAME = "person"; + + private GraphDatabaseManager entityManager; + + private final Faker faker = new Faker(); + + @BeforeEach + void setUp(){ + Graph graph = GraphSupplier.INSTANCE.get(); + this.entityManager = GraphDatabaseManager.of(graph); + } + + @BeforeEach + void beforeEach() { + delete().from(COLLECTION_NAME).delete(entityManager); + } + + @Test + void shouldInsertEntity(){ + String name = faker.name().fullName(); + var age = faker.number().randomDigit(); + var entity = CommunicationEntity.of("Person"); + entity.add("name", name); + entity.add("age", age); + var communicationEntity = entityManager.insert(entity); + assertNotNull(communicationEntity); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(communicationEntity.find("name", String.class)).get().isEqualTo(name); + softly.assertThat(communicationEntity.find("age", int.class)).get().isEqualTo(age); + softly.assertThat(communicationEntity.find(DefaultGraphDatabaseManager.ID_PROPERTY)).isPresent(); + }); + } + + @Test + void shouldInsertEntities(){ + String name = faker.name().fullName(); + var age = faker.number().randomDigit(); + var entity = CommunicationEntity.of("Person"); + entity.add("name", name); + entity.add("age", age); + + String name2 = faker.name().fullName(); + var age2 = faker.number().randomDigit(); + var entity2 = CommunicationEntity.of("Person"); + entity2.add("name", name2); + entity2.add("age", age2); + + var communicationEntities = StreamSupport + .stream(entityManager.insert(List.of(entity, entity2)).spliterator(), false).toList(); + + assertNotNull(communicationEntities); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(communicationEntities).hasSize(2); + softly.assertThat(communicationEntities.get(0).find("name", String.class)).get().isEqualTo(name); + softly.assertThat(communicationEntities.get(0).find("age", int.class)).get().isEqualTo(age); + softly.assertThat(communicationEntities.get(0).find(DefaultGraphDatabaseManager.ID_PROPERTY)).isPresent(); + + softly.assertThat(communicationEntities.get(1).find("name", String.class)).get().isEqualTo(name2); + softly.assertThat(communicationEntities.get(1).find("age", int.class)).get().isEqualTo(age2); + softly.assertThat(communicationEntities.get(1).find(DefaultGraphDatabaseManager.ID_PROPERTY)).isPresent(); + }); + + } + + @Test + void shouldInsert() { + var entity = getEntity(); + var documentEntity = entityManager.insert(entity); + assertTrue(documentEntity.elements().stream().map(Element::name).anyMatch(s -> s.equals("_id"))); + } + + @Test + void shouldThrowExceptionWhenInsertWithTTL() { + var entity = getEntity(); + var ttl = Duration.ofSeconds(10); + assertThrows(UnsupportedOperationException.class, () -> entityManager.insert(entity, ttl)); + } + + @Test + void shouldUpdate() { + var entity = entityManager.insert(getEntity()); + var newField = Elements.of("newField", "10"); + entity.add(newField); + var updated = entityManager.update(entity); + assertEquals(newField, updated.find("newField").orElseThrow()); + } + + @Test + void shouldRemoveEntity() { + var documentEntity = entityManager.insert(getEntity()); + + Optional id = documentEntity.find("_id"); + var query = select().from(COLLECTION_NAME) + .where("_id").eq(id.orElseThrow().get()) + .build(); + var deleteQuery = delete().from(COLLECTION_NAME).where("_id") + .eq(id.get().get()) + .build(); + + entityManager.delete(deleteQuery); + assertTrue(entityManager.select(query).findAny().isEmpty()); + } + + @Test + void shouldFindDocument() { + var entity = entityManager.insert(getEntity()); + Optional id = entity.find("_id"); + + var query = select().from(COLLECTION_NAME) + .where("_id").eq(id.orElseThrow().get()) + .build(); + + var entities = entityManager.select(query).collect(Collectors.toList()); + assertFalse(entities.isEmpty()); + assertThat(entities).contains(entity); + } + + @Test + void shouldFindDocument2() { + var entity = entityManager.insert(getEntity()); + Optional id = entity.find("_id"); + + var query = select().from(COLLECTION_NAME) + .where("name").eq("Poliana") + .and("city").eq("Salvador").and("_id").eq(id.orElseThrow().get()) + .build(); + + List entities = entityManager.select(query).collect(Collectors.toList()); + assertFalse(entities.isEmpty()); + assertThat(entities).contains(entity); + } + + @Test + void shouldFindDocument3() { + var entity = entityManager.insert(getEntity()); + Optional id = entity.find("_id"); + var query = select().from(COLLECTION_NAME) + .where("name").eq("Poliana") + .or("city").eq("Salvador") + .and(id.orElseThrow().name()).eq(id.get().get()) + .build(); + + List entities = entityManager.select(query).collect(Collectors.toList()); + assertFalse(entities.isEmpty()); + assertThat(entities).contains(entity); + } + + @Test + void shouldFindDocumentGreaterThan() { + DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); + entityManager.delete(deleteQuery); + Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); + List entities = StreamSupport.stream(entitiesSaved.spliterator(), false).toList(); + + var query = select().from(COLLECTION_NAME) + .where("age").gt(22) + .and("type").eq("V") + .build(); + + List entitiesFound = entityManager.select(query).collect(Collectors.toList()); + assertEquals(2, entitiesFound.size()); + assertThat(entitiesFound).isNotIn(entities.get(0)); + } + + @Test + void shouldFindDocumentGreaterEqualsThan() { + DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); + entityManager.delete(deleteQuery); + Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); + List entities = StreamSupport.stream(entitiesSaved.spliterator(), false).toList(); + + var query = select().from(COLLECTION_NAME) + .where("age").gte(23) + .and("type").eq("V") + .build(); + + List entitiesFound = entityManager.select(query).collect(Collectors.toList()); + assertEquals(2, entitiesFound.size()); + assertThat(entitiesFound).isNotIn(entities.get(0)); + } + + @Test + void shouldFindDocumentLesserThan() { + DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); + entityManager.delete(deleteQuery); + Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); + List entities = StreamSupport.stream(entitiesSaved.spliterator(), false) + .toList(); + + var query = select().from(COLLECTION_NAME) + .where("age").lt(23) + .and("type").eq("V") + .build(); + + List entitiesFound = entityManager.select(query).collect(Collectors.toList()); + assertEquals(1, entitiesFound.size()); + assertThat(entitiesFound).contains(entities.get(0)); + } + + @Test + void shouldFindDocumentLesserEqualsThan() { + DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); + entityManager.delete(deleteQuery); + Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); + List entities = StreamSupport.stream(entitiesSaved.spliterator(), false).toList(); + + SelectQuery query = select().from(COLLECTION_NAME) + .where("age").lte(23) + .and("type").eq("V") + .build(); + + List entitiesFound = entityManager.select(query).collect(Collectors.toList()); + assertEquals(2, entitiesFound.size()); + assertThat(entitiesFound).contains(entities.get(0), entities.get(2)); + } + + + @Test + void shouldFindDocumentIn() { + DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); + entityManager.delete(deleteQuery); + Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); + List entities = StreamSupport.stream(entitiesSaved.spliterator(), false).toList(); + + SelectQuery query = select().from(COLLECTION_NAME) + .where("location").in(asList("BR", "US")) + .and("type").eq("V") + .build(); + + Assertions.assertThat(entityManager.select(query).toList()).containsAll(entities); + } + + @Test + void shouldFindDocumentBetween() { + DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); + entityManager.delete(deleteQuery); + entityManager.insert(getEntitiesWithValues()); + + SelectQuery query = select().from(COLLECTION_NAME) + .where("age").between(22, 25) + .build(); + + + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(2); + softly.assertThat(entities).extracting(e -> e.find("age").orElseThrow().get(Integer.class)) + .contains(22, 23); + softly.assertThat(entities).extracting(e -> e.find("name").orElseThrow().get(String.class)) + .contains("Luna", "Lucas"); + }); + + + + } + + @Test + void shouldFindDocumentStart() { + Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); + List entities = StreamSupport.stream(entitiesSaved.spliterator(), false).toList(); + + SelectQuery query = select().from(COLLECTION_NAME) + .where("age").gt(22) + .and("type").eq("V") + .skip(1L) + .build(); + + List entitiesFound = entityManager.select(query).collect(Collectors.toList()); + assertEquals(1, entitiesFound.size()); + assertThat(entitiesFound).isNotIn(entities.get(0)); + + query = select().from(COLLECTION_NAME) + .where("age").gt(22) + .and("type").eq("V") + .skip(2L) + .build(); + + entitiesFound = entityManager.select(query).collect(Collectors.toList()); + assertTrue(entitiesFound.isEmpty()); + + } + + @Test + void shouldFindDocumentLimit() { + DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); + entityManager.delete(deleteQuery); + Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); + List entities = StreamSupport.stream(entitiesSaved.spliterator(), false).toList(); + + SelectQuery query = select().from(COLLECTION_NAME) + .where("age").gt(22) + .and("type").eq("V") + .limit(1L) + .build(); + + List entitiesFound = entityManager.select(query).collect(Collectors.toList()); + assertEquals(1, entitiesFound.size()); + assertThat(entitiesFound).isNotIn(entities.get(0)); + + query = select().from(COLLECTION_NAME) + .where("age").gt(22) + .and("type").eq("V") + .limit(2L) + .build(); + + entitiesFound = entityManager.select(query).collect(Collectors.toList()); + assertEquals(2, entitiesFound.size()); + + } + + @Test + void shouldFindDocumentSort() { + DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); + entityManager.delete(deleteQuery); + Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); + + SelectQuery query = select().from(COLLECTION_NAME) + .where("age").gt(22) + .and("type").eq("V") + .orderBy("age").asc() + .build(); + + List entitiesFound = entityManager.select(query).collect(Collectors.toList()); + assertEquals(2, entitiesFound.size()); + List ages = entitiesFound.stream() + .map(e -> e.find("age").orElseThrow().get(Integer.class)) + .collect(Collectors.toList()); + + assertThat(ages).contains(23, 25); + + query = select().from(COLLECTION_NAME) + .where("age").gt(22) + .and("type").eq("V") + .orderBy("age").desc() + .build(); + + entitiesFound = entityManager.select(query).toList(); + ages = entitiesFound.stream() + .map(e -> e.find("age").orElseThrow().get(Integer.class)) + .collect(Collectors.toList()); + assertEquals(2, entitiesFound.size()); + assertThat(ages).contains(25, 23); + + } + + @Test + void shouldFindAll() { + entityManager.insert(getEntity()); + SelectQuery query = select().from(COLLECTION_NAME).build(); + List entities = entityManager.select(query).toList(); + assertFalse(entities.isEmpty()); + } + + @Test + void shouldDeleteAll() { + entityManager.insert(getEntity()); + SelectQuery query = select().from(COLLECTION_NAME).build(); + List entities = entityManager.select(query).collect(Collectors.toList()); + assertFalse(entities.isEmpty()); + DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).build(); + entityManager.delete(deleteQuery); + entities = entityManager.select(query).toList(); + assertTrue(entities.isEmpty()); + } + + @Test + void shouldFindAllByFields() { + entityManager.insert(getEntity()); + SelectQuery query = select("name").from(COLLECTION_NAME).build(); + List entities = entityManager.select(query).toList(); + assertFalse(entities.isEmpty()); + final CommunicationEntity entity = entities.get(0); + assertEquals(3, entity.size()); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entity.find("name")).isPresent(); + softly.assertThat(entity.find("_id")).isPresent(); + softly.assertThat(entity.find("city")).isPresent(); + }); + } + + + + + private CommunicationEntity getEntity() { + CommunicationEntity entity = CommunicationEntity.of(COLLECTION_NAME); + Map map = new HashMap<>(); + map.put("name", "Poliana"); + map.put("city", "Salvador"); + List documents = Elements.of(map); + documents.forEach(entity::add); + return entity; + } + + private List getEntitiesWithValues() { + CommunicationEntity lucas = CommunicationEntity.of(COLLECTION_NAME); + lucas.add(Element.of("name", "Lucas")); + lucas.add(Element.of("age", 22)); + lucas.add(Element.of("location", "BR")); + lucas.add(Element.of("type", "V")); + + CommunicationEntity luna = CommunicationEntity.of(COLLECTION_NAME); + luna.add(Element.of("name", "Luna")); + luna.add(Element.of("age", 23)); + luna.add(Element.of("location", "US")); + luna.add(Element.of("type", "V")); + + CommunicationEntity otavio = CommunicationEntity.of(COLLECTION_NAME); + otavio.add(Element.of("name", "Otavio")); + otavio.add(Element.of("age", 25)); + otavio.add(Element.of("location", "BR")); + otavio.add(Element.of("type", "V")); + + + return asList(lucas, otavio, luna); + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphSupplier.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphSupplier.java new file mode 100644 index 000000000..dcf55743b --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphSupplier.java @@ -0,0 +1,45 @@ +/* + * 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.communication; + +import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph; +import org.apache.tinkerpop.gremlin.structure.Graph; + +import java.io.File; +import java.util.function.Supplier; +import java.util.logging.Logger; + +import static java.lang.System.currentTimeMillis; + +public enum GraphSupplier implements Supplier { + INSTANCE; + + private static final Logger LOGGER = Logger.getLogger(GraphSupplier.class.getName()); + + private final String directory; + + private final Graph graph; + + { + this.directory = new File("").getAbsolutePath() + "/target/jnosql-communication-graph/" + currentTimeMillis() + "/"; + graph = Neo4jGraph.open(directory); + } + + @Override + public Graph get() { + LOGGER.info("Starting Graph database at directory: " + directory); + return graph; + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplateTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplateTest.java new file mode 100644 index 000000000..c8cebef6d --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplateTest.java @@ -0,0 +1,546 @@ +/* + * 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; + +import jakarta.data.exceptions.EmptyResultException; +import jakarta.data.exceptions.NonUniqueResultException; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Transaction; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.assertj.core.api.SoftAssertions; +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.mapping.PreparedStatement; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.StreamSupport; + +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.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public abstract class AbstractGraphTemplateTest { + + protected abstract Graph getGraph(); + + protected abstract GraphTemplate getGraphTemplate(); + + @AfterEach + void after() { + getGraph().traversal().V().toList().forEach(Vertex::remove); + getGraph().traversal().E().toList().forEach(Edge::remove); + } + + @Test + void shouldReturnErrorWhenEntityIsNull() { + assertThrows(NullPointerException.class, () -> getGraphTemplate().insert(null)); + } + + @Test + void shouldInsertAnEntity() { + Human human = Human.builder().withAge() + .withName("Otavio").build(); + Human updated = getGraphTemplate().insert(human); + + getGraphTemplate().delete(updated.getId()); + } + + @Test + void shouldReturnErrorWhenInsertWithTTL() { + Human human = Human.builder().withAge() + .withName("Otavio").build(); + Assertions.assertThrows(UnsupportedOperationException.class, + () -> getGraphTemplate().insert(human, Duration.ZERO)); + } + + @Test + void shouldReturnErrorWhenInsertIterableWithTTL() { + Human human = Human.builder().withAge() + .withName("Otavio").build(); + Assertions.assertThrows(UnsupportedOperationException.class, + () -> getGraphTemplate().insert(Collections.singleton(human), Duration.ZERO)); + } + + @Test + void shouldInsertEntities() { + Human otavio = Human.builder().withAge() + .withName("Otavio").build(); + + Human poliana = Human.builder().withAge() + .withName("Poliana").build(); + + final Iterable people = getGraphTemplate() + .insert(Arrays.asList(otavio, poliana)); + + final boolean allHasId = StreamSupport.stream(people.spliterator(), false) + .map(Human::getId) + .allMatch(Objects::nonNull); + assertTrue(allHasId); + } + + @Test + void shouldMergeOnInsert() { + Human human = Human.builder().withAge() + .withName("Otavio").build(); + Human updated = getGraphTemplate().insert(human); + assertSame(human, updated); + } + + @Test + void shouldGetErrorWhenIdIsNullWhenUpdate() { + assertThrows(EmptyResultException.class, () -> { + Human human = Human.builder().withAge() + .withName("Otavio").build(); + getGraphTemplate().update(human); + }); + } + + @Test + void shouldGetErrorWhenEntityIsNotSavedYet() { + assertThrows(EmptyResultException.class, () -> { + Human human = Human.builder().withAge() + .withId(10L) + .withName("Otavio").build(); + + getGraphTemplate().update(human); + }); + } + + @Test + void shouldUpdate() { + Human human = Human.builder().withAge() + .withName("Otavio").build(); + Human updated = getGraphTemplate().insert(human); + Human newHuman = Human.builder() + .withAge() + .withId(updated.getId()) + .withName("Otavio Updated").build(); + + Human update = getGraphTemplate().update(newHuman); + + assertEquals(newHuman, update); + + getGraphTemplate().delete(update.getId()); + } + + @Test + void shouldUpdateEntities() { + Human otavio = Human.builder().withAge() + .withName("Otavio").build(); + + Human poliana = Human.builder().withAge() + .withName("Poliana").build(); + + final Iterable insertPeople = getGraphTemplate().insert(Arrays.asList(otavio, poliana)); + + final List newPeople = StreamSupport.stream(insertPeople.spliterator(), false) + .map(p -> Human.builder().withAge().withId(p.getId()).withName(p.getName() + " updated").build()) + .collect(toList()); + + final Iterable update = getGraphTemplate().update(newPeople); + + final boolean allUpdated = StreamSupport.stream(update.spliterator(), false) + .map(Human::getName).allMatch(name -> name.contains(" updated")); + + assertTrue(allUpdated); + } + + + @Test + void shouldMergeOnUpdate() { + Human human = Human.builder().withAge() + .withName("Otavio").build(); + Human updated = getGraphTemplate().insert(human); + Human newHuman = Human.builder() + .withAge() + .withId(updated.getId()) + .withName("Otavio Updated").build(); + + Human update = getGraphTemplate().update(newHuman); + + assertSame(update, newHuman); + } + + @Test + void shouldReturnErrorInFindWhenIdIsNull() { + assertThrows(NullPointerException.class, () -> getGraphTemplate().find(null)); + } + + @Test + void shouldFindAnEntity() { + Human human = Human.builder().withAge() + .withName("Otavio").build(); + Human updated = getGraphTemplate().insert(human); + Optional personFound = getGraphTemplate().find(updated.getId()); + + assertTrue(personFound.isPresent()); + assertEquals(updated, personFound.get()); + + getGraphTemplate().delete(updated.getId()); + } + + @Test + void shouldNotFindAnEntity() { + Optional personFound = getGraphTemplate().find(0L); + assertFalse(personFound.isPresent()); + } + + @Test + void shouldDeleteById() { + + Human human = getGraphTemplate().insert(Human.builder().withAge() + .withName("Otavio").build()); + + assertTrue(getGraphTemplate().find(human.getId()).isPresent()); + getGraphTemplate().delete(human.getId()); + assertFalse(getGraphTemplate().find(human.getId()).isPresent()); + } + + + @Test + void shouldDeleteAnEntityFromTemplate() { + + Human human = getGraphTemplate().insert(Human.builder().withAge() + .withName("Otavio").build()); + + assertTrue(getGraphTemplate().find(human.getId()).isPresent()); + getGraphTemplate().delete(Human.class, human.getId()); + assertFalse(getGraphTemplate().find(human.getId()).isPresent()); + } + + @Test + void shouldNotDeleteAnEntityFromTemplate() { + + Human human = getGraphTemplate().insert(Human.builder().withAge() + .withName("Otavio").build()); + + assertTrue(getGraphTemplate().find(human.getId()).isPresent()); + getGraphTemplate().delete(Magazine.class, human.getId()); + assertTrue(getGraphTemplate().find(human.getId()).isPresent()); + getGraphTemplate().delete(Human.class, human.getId()); + assertFalse(getGraphTemplate().find(human.getId()).isPresent()); + } + + + @Test + void shouldDeleteEntitiesById() { + + Human otavio = getGraphTemplate().insert(Human.builder().withAge() + .withName("Otavio").build()); + + Human poliana = getGraphTemplate().insert(Human.builder().withAge() + .withName("Poliana").build()); + + assertTrue(getGraphTemplate().find(otavio.getId()).isPresent()); + getGraphTemplate().delete(Arrays.asList(otavio.getId(), poliana.getId())); + assertFalse(getGraphTemplate().find(otavio.getId()).isPresent()); + assertFalse(getGraphTemplate().find(poliana.getId()).isPresent()); + } + + @Test + void shouldReturnErrorWhenGetEdgesIdHasNullId() { + assertThrows(NullPointerException.class, () -> getGraphTemplate().edgesById(null, Direction.BOTH)); + } + + @Test + void shouldReturnErrorWhenGetEdgesIdHasNullDirection() { + assertThrows(NullPointerException.class, () -> getGraphTemplate().edgesById(10, null)); + } + + @Test + void shouldReturnEmptyWhenVertexDoesNotExist() { + Collection edges = getGraphTemplate().edgesById(10, Direction.BOTH); + assertTrue(edges.isEmpty()); + } + + @Test + void shouldReturnEdgesById() { + Human otavio = getGraphTemplate().insert(Human.builder().withAge() + .withName("Otavio").build()); + + Creature dog = getGraphTemplate().insert(new Creature("dog")); + Magazine cleanCode = getGraphTemplate().insert(Magazine.builder().withName("Clean code").build()); + + EdgeEntity likes = getGraphTemplate().edge(otavio, "likes", dog); + EdgeEntity reads = getGraphTemplate().edge(otavio, "reads", cleanCode); + + Collection edgesById = getGraphTemplate().edgesById(otavio.getId(), Direction.BOTH); + Collection edgesById1 = getGraphTemplate().edgesById(otavio.getId(), Direction.BOTH, "reads"); + Collection edgesById2 = getGraphTemplate().edgesById(otavio.getId(), Direction.BOTH, () -> "likes"); + Collection edgesById3 = getGraphTemplate().edgesById(otavio.getId(), Direction.OUT); + Collection edgesById4 = getGraphTemplate().edgesById(cleanCode.getId(), Direction.IN); + + assertEquals(edgesById, edgesById3); + assertThat(edgesById).contains(likes, reads); + assertThat(edgesById1).contains(reads); + assertThat(edgesById2).contains(likes); + assertThat(edgesById4).contains(reads); + + } + + @Test + void shouldDeleteEdge() { + Human otavio = getGraphTemplate().insert(Human.builder().withAge() + .withName("Otavio").build()); + Creature dog = getGraphTemplate().insert(new Creature("Ada")); + + EdgeEntity likes = getGraphTemplate().edge(otavio, "likes", dog); + + final Optional edge = getGraphTemplate().edge(likes.id()); + Assertions.assertTrue(edge.isPresent()); + + getGraphTemplate().deleteEdge(likes.id()); + assertFalse(getGraphTemplate().edge(likes.id()).isPresent()); + } + + @Test + void shouldDeleteEdges() { + Human otavio = getGraphTemplate().insert(Human.builder().withAge() + .withName("Otavio").build()); + Creature dog = getGraphTemplate().insert(new Creature("Ada")); + Magazine cleanCode = getGraphTemplate().insert(Magazine.builder().withName("Clean code").build()); + + EdgeEntity likes = getGraphTemplate().edge(otavio, "likes", dog); + EdgeEntity reads = getGraphTemplate().edge(otavio, "reads", cleanCode); + + final Optional edge = getGraphTemplate().edge(likes.id()); + Assertions.assertTrue(edge.isPresent()); + + getGraphTemplate().deleteEdge(Arrays.asList(likes.id(), reads.id())); + assertFalse(getGraphTemplate().edge(likes.id()).isPresent()); + assertFalse(getGraphTemplate().edge(reads.id()).isPresent()); + } + + @Test + void shouldReturnErrorWhenGetEdgesHasNullId() { + assertThrows(NullPointerException.class, () -> getGraphTemplate().edges(null, Direction.BOTH)); + } + + @Test + void shouldReturnErrorWhenGetEdgesHasNullId2() { + Human otavio = Human.builder().withAge().withName("Otavio").build(); + Collection edges = getGraphTemplate().edges(otavio, Direction.BOTH); + assertThat(edges).isEmpty(); + } + + @Test + void shouldReturnErrorWhenGetEdgesHasNullDirection() { + assertThrows(NullPointerException.class, () -> { + Human otavio = getGraphTemplate().insert(Human.builder().withAge() + .withName("Otavio").build()); + getGraphTemplate().edges(otavio, null); + }); + } + + @Test + void shouldReturnEmptyWhenEntityDoesNotExist() { + Human otavio = Human.builder().withAge().withName("Otavio").withId(10L).build(); + Collection edges = getGraphTemplate().edges(otavio, Direction.BOTH); + assertTrue(edges.isEmpty()); + } + + + @Test + void shouldReturnEdges() { + Human otavio = getGraphTemplate().insert(Human.builder().withAge() + .withName("Otavio").build()); + + Creature dog = getGraphTemplate().insert(new Creature("dog")); + Magazine cleanCode = getGraphTemplate().insert(Magazine.builder().withName("Clean code").build()); + + EdgeEntity likes = getGraphTemplate().edge(otavio, "likes", dog); + EdgeEntity reads = getGraphTemplate().edge(otavio, "reads", cleanCode); + + Collection edgesById = getGraphTemplate().edges(otavio, Direction.BOTH); + Collection edgesById1 = getGraphTemplate().edges(otavio, Direction.BOTH, "reads"); + Collection edgesById2 = getGraphTemplate().edges(otavio, Direction.BOTH, () -> "likes"); + Collection edgesById3 = getGraphTemplate().edges(otavio, Direction.OUT); + Collection edgesById4 = getGraphTemplate().edges(cleanCode, Direction.IN); + + SoftAssertions.assertSoftly(soft ->{ + soft.assertThat(edgesById).contains(likes, reads); + soft.assertThat(edgesById1).contains(reads); + soft.assertThat(edgesById2).contains(likes); + soft.assertThat(edgesById3).contains(likes, reads); + soft.assertThat(edgesById4).contains(reads); + }); + } + + @Test + void shouldGetTransaction() { + Transaction transaction = getGraphTemplate().transaction(); + assertNotNull(transaction); + } + + @Test + void shouldExecuteQuery() { + Human human = Human.builder().withAge() + .withName("Otavio").build(); + getGraphTemplate().insert(human); + List people = getGraphTemplate() + .gremlin("g.V().hasLabel('Human')") + .toList(); + assertThat(people.stream().map(Human::getName).collect(toList())).contains("Otavio"); + } + + @Test + void shouldReturnEmpty() { + Optional person = getGraphTemplate().gremlinSingleResult("g.V().hasLabel('Person')"); + assertFalse(person.isPresent()); + } + + @Test + void shouldReturnOneElement() { + Human otavio = Human.builder().withAge() + .withName("Otavio").build(); + getGraphTemplate().insert(otavio); + Optional person = getGraphTemplate().gremlinSingleResult("g.V().hasLabel('Human')"); + assertTrue(person.isPresent()); + } + + @Test + void shouldReturnErrorWhenHasNoneThanOneElement() { + + getGraphTemplate().insert(Human.builder().withAge().withName("Otavio").build()); + getGraphTemplate().insert(Human.builder().withAge().withName("Poliana").build()); + assertThrows(NonUniqueResultException.class, () -> getGraphTemplate().gremlinSingleResult("g.V().hasLabel('Human')")); + } + + @Test + void shouldExecutePrepareStatement() { + getGraphTemplate().insert(Human.builder().withAge().withName("Otavio").build()); + PreparedStatement prepare = getGraphTemplate().gremlinPrepare("g.V().hasLabel(@param)"); + prepare.bind("param", "Human"); + List people = prepare.result().toList(); + assertThat(people.stream().map(Human::getName).collect(toList())).contains("Otavio"); + } + + @Test + void shouldExecutePrepareStatementSingleton() { + getGraphTemplate().insert(Human.builder().withAge().withName("Otavio").build()); + PreparedStatement prepare = getGraphTemplate().gremlinPrepare("g.V().hasLabel(@param)"); + prepare.bind("param", "Human"); + Optional otavio = prepare.singleResult(); + assertTrue(otavio.isPresent()); + } + + @Test + void shouldExecutePrepareStatementSingletonEmpty() { + PreparedStatement prepare = getGraphTemplate().gremlinPrepare("g.V().hasLabel(@param)"); + prepare.bind("param", "Person"); + Optional otavio = prepare.singleResult(); + assertFalse(otavio.isPresent()); + } + + @Test + void shouldExecutePrepareStatementWithErrorWhenThereIsMoreThanOneResult() { + getGraphTemplate().insert(Human.builder().withAge().withName("Otavio").build()); + getGraphTemplate().insert(Human.builder().withAge().withName("Poliana").build()); + PreparedStatement prepare = getGraphTemplate().gremlinPrepare("g.V().hasLabel(@param)"); + prepare.bind("param", "Human"); + assertThrows(NonUniqueResultException.class, prepare::singleResult); + } + + @Test + void shouldCount() { + getGraphTemplate().insert(Human.builder().withAge().withName("Otavio").build()); + getGraphTemplate().insert(Human.builder().withAge().withName("Poliana").build()); + assertEquals(2L, getGraphTemplate().count("Human")); + } + + @Test + void shouldCountFromEntity() { + getGraphTemplate().insert(Human.builder().withAge().withName("Otavio").build()); + getGraphTemplate().insert(Human.builder().withAge().withName("Poliana").build()); + assertEquals(2L, getGraphTemplate().count(Human.class)); + } + + + @Test + void shouldFindById() { + final Human otavio = getGraphTemplate().insert(Human.builder().withAge() + .withName("Otavio").build()); + + final Optional person = getGraphTemplate().find(Human.class, otavio.getId()); + assertNotNull(person); + assertTrue(person.isPresent()); + assertEquals(otavio.getName(), person.map(Human::getName).get()); + } + + @Test + void shouldFindAll() { + final Human otavio = getGraphTemplate().insert(Human.builder().withAge() + .withName("Otavio").build()); + List people = getGraphTemplate().findAll(Human.class).toList(); + + assertThat(people).hasSize(1) + .map(Human::getName) + .contains("Otavio"); + } + + @Test + void shouldDeleteAll() { + final Human otavio = getGraphTemplate().insert(Human.builder().withAge() + .withName("Otavio").build()); + List people = getGraphTemplate().findAll(Human.class).toList(); + + assertThat(people).hasSize(1) + .map(Human::getName) + .contains("Otavio"); + + getGraphTemplate().deleteAll(Human.class); + people = getGraphTemplate().findAll(Human.class).toList(); + + assertThat(people).isEmpty(); + } + + @Test + void shouldReturnEmptyWhenFindByIdNotFound() { + + final Optional person = getGraphTemplate().find(Human.class, -2L); + assertNotNull(person); + assertFalse(person.isPresent()); + } + + @Test + void shouldUpdateNullValues(){ + final Human otavio = getGraphTemplate().insert(Human.builder().withAge() + .withName("Otavio").build()); + + assertEquals("Otavio", otavio.getName()); + otavio.setName(null); + final Human human = getGraphTemplate().update(otavio); + assertNull(human.getName()); + + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTraversalTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTraversalTest.java new file mode 100644 index 000000000..425313bdd --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/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.databases.tinkerpop.mapping; + +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.databases.tinkerpop.mapping.entities.Human; +import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Magazine; +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 Human otavio; + protected Human poliana; + protected Human paulo; + + protected Magazine shack; + protected Magazine license; + protected Magazine 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(Human.builder().withAge(27) + .withName("Otavio").build()); + poliana = graphTemplate.insert(Human.builder().withAge(26) + .withName("Poliana").build()); + paulo = graphTemplate.insert(Human.builder().withAge(50) + .withName("Paulo").build()); + + shack = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + license = graphTemplate.insert(Magazine.builder().withAge(2013).withName("Software License").build()); + effectiveJava = graphTemplate.insert(Magazine.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-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalTest.java new file mode 100644 index 000000000..655358560 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/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.databases.tinkerpop.mapping; + +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.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.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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() + .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() + .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() + .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 magazines = graphTemplate.traversalVertex().outE(READS).inV().result().collect(toList()); + assertEquals(3, magazines.size()); + assertThat(magazines).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() { + Creature lion = graphTemplate.insert(new Creature("lion")); + Creature snake = graphTemplate.insert(new Creature("snake")); + Creature mouse = graphTemplate.insert(new Creature("mouse")); + Creature plant = graphTemplate.insert(new Creature("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() { + Creature lion = graphTemplate.insert(new Creature("lion")); + Creature snake = graphTemplate.insert(new Creature("snake")); + Creature mouse = graphTemplate.insert(new Creature("mouse")); + Creature plant = graphTemplate.insert(new Creature("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() { + Creature lion = graphTemplate.insert(new Creature("lion")); + Creature snake = graphTemplate.insert(new Creature("snake")); + Creature mouse = graphTemplate.insert(new Creature("mouse")); + Creature plant = graphTemplate.insert(new Creature("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() { + Creature lion = graphTemplate.insert(new Creature("lion")); + Creature snake = graphTemplate.insert(new Creature("snake")); + Creature mouse = graphTemplate.insert(new Creature("mouse")); + Creature plant = graphTemplate.insert(new Creature("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() { + Creature lion = graphTemplate.insert(new Creature("lion")); + Creature snake = graphTemplate.insert(new Creature("snake")); + Creature mouse = graphTemplate.insert(new Creature("mouse")); + Creature plant = graphTemplate.insert(new Creature("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 -> { + Creature incoming = result.incoming(); + Creature 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(Human.class) + .inE("knows").result() + .collect(Collectors.toList()); + + assertEquals(6, edges.size()); + + edges = graphTemplate.traversalVertex() + .hasLabel(Human.class) + .inE("knows") + .dedup() + .result() + .toList(); + + assertEquals(6, edges.size()); + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplateProducerTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplateProducerTest.java new file mode 100644 index 000000000..c7c93296a --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplateProducerTest.java @@ -0,0 +1,56 @@ +/* + * 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; + +import jakarta.inject.Inject; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + + +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) +@AddPackages(GraphProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) +class DefaultGraphTemplateProducerTest { + + @Inject + private GraphTemplateProducer producer; + + + @Test + void shouldReturnErrorWhenManagerNull() { + Assertions.assertThrows(NullPointerException.class, () -> producer.apply(null)); + } + + @Test + void shouldReturn() { + var graph = Mockito.mock(Graph.class); + var template = producer.apply(graph); + assertNotNull(template); + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTraversalSourceTemplateTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTraversalSourceTemplateTest.java new file mode 100644 index 000000000..1610a2984 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTraversalSourceTemplateTest.java @@ -0,0 +1,49 @@ +/* + * 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; + +import jakarta.inject.Inject; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, Transactional.class}) +@AddPackages({MagazineRepository.class, Reflections.class, GraphProducer.class}) +@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) +class DefaultGraphTraversalSourceTemplateTest extends AbstractGraphTemplateTest { + + @Inject + private GraphTemplate graphTemplate; + + @Inject + private Graph graph; + + @Override + protected Graph getGraph() { + return graph; + } + + @Override + protected GraphTemplate getGraphTemplate() { + return graphTemplate; + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversalTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversalTest.java new file mode 100644 index 000000000..222bd3c91 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/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.databases.tinkerpop.mapping; + +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.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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(Human.class).valueMap("name").count(); + assertEquals(3L, count); + } + + + @Test + void shouldReturnMapValues() { + List names = graphTemplate.traversalVertex() + .hasLabel(Human.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(Human.class).valueMap("name") + .stream(); + assertNotNull(stream); + assertEquals(3L, stream.count()); + } + + + @Test + void shouldReturnResultAsList() { + List> maps = graphTemplate.traversalVertex() + .hasLabel(Human.class).valueMap("name") + .resultList(); + assertEquals(3, maps.size()); + } + + @Test + void shouldReturnErrorWhenThereAreMoreThanOneInGetSingleResult() { + assertThrows(NonUniqueResultException.class, () -> graphTemplate.traversalVertex() + .hasLabel(Human.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("Human"). + has("name", name).valueMap("name").singleResult(); + assertEquals(name, poliana.map(m -> m.get("name")).orElse("")); + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalTest.java new file mode 100644 index 000000000..73faf1bea --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/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.databases.tinkerpop.mapping; + +import jakarta.data.exceptions.NonUniqueResultException; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.structure.T; +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.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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() + .toList(); + }); + } + + @Test + void shouldReturnErrorWhenHasKeyIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().has((String) null, + P.gt(26)) + .result() + .toList()); + } + + @Test + void shouldHaveLabel() { + List magazines = graphTemplate.traversalVertex().hasLabel("Magazine").result().collect(toList()); + assertEquals(3, magazines.size()); + assertThat(magazines).contains(shack, license, effectiveJava); + } + + @Test + void shouldHaveLabel2() { + + List entities = graphTemplate.traversalVertex() + .hasLabel(P.eq("Magazine").or(P.eq("Human"))) + .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().toList()); + } + + @Test + void shouldIn() { + List magazines = graphTemplate.traversalVertex().out(READS).result().collect(toList()); + assertEquals(3, magazines.size()); + assertThat(magazines).contains(shack, license, effectiveJava); + } + + @Test + void shouldReturnErrorWhenInIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().out((String) null).result().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().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().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().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("Human") + .valueMap("name").stream().toList(); + + assertFalse(maps.isEmpty()); + assertEquals(3, maps.size()); + + List names = new ArrayList<>(); + + maps.forEach(m -> names.add(m.get("name").toString())); + + assertThat(names).contains("Otavio", "Poliana", "Paulo"); + } + + @Test + void shouldMapValuesAsStreamLimit() { + List> maps = graphTemplate.traversalVertex().hasLabel("Human") + .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("Human") + .valueMap("name").next(); + + assertNotNull(map); + assertFalse(map.isEmpty()); + } + + + @Test + void shouldRepeatTimesTraversal() { + Creature lion = graphTemplate.insert(new Creature("lion")); + Creature snake = graphTemplate.insert(new Creature("snake")); + Creature mouse = graphTemplate.insert(new Creature("mouse")); + Creature plant = graphTemplate.insert(new Creature("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() { + Creature lion = graphTemplate.insert(new Creature("lion")); + Creature snake = graphTemplate.insert(new Creature("snake")); + Creature mouse = graphTemplate.insert(new Creature("mouse")); + Creature plant = graphTemplate.insert(new Creature("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() { + Creature lion = graphTemplate.insert(new Creature("lion")); + Creature snake = graphTemplate.insert(new Creature("snake")); + Creature mouse = graphTemplate.insert(new Creature("mouse")); + Creature plant = graphTemplate.insert(new Creature("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() { + Creature lion = graphTemplate.insert(new Creature("lion")); + Creature snake = graphTemplate.insert(new Creature("snake")); + Creature mouse = graphTemplate.insert(new Creature("mouse")); + Creature plant = graphTemplate.insert(new Creature("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("Magazine") + .has(property) + .orderBy(property) + .asc().result() + .map(Magazine::getName) + .collect(toList()); + + assertThat(properties).contains("Effective Java", "Software License", "The Shack"); + } + + @Test + void shouldOrderDesc() { + String property = "name"; + + List properties = graphTemplate.traversalVertex() + .hasLabel("Magazine") + .has(property) + .orderBy(property) + .desc().result() + .map(Magazine::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(Human.class::isInstance)); + assertTrue(graphTemplate.traversalVertex().hasLabel(() -> "Book").result().allMatch(Magazine.class::isInstance)); + assertTrue(graphTemplate.traversalVertex().hasLabel(Creature.class).result().allMatch(Creature.class::isInstance)); + } + + @Test + void shouldReturnResultAsList() { + List people = graphTemplate.traversalVertex().hasLabel("Human") + .result() + .toList(); + assertEquals(3, people.size()); + } + + @Test + void shouldReturnErrorWhenThereAreMoreThanOneInGetSingleResult() { + assertThrows(NonUniqueResultException.class, () -> graphTemplate.traversalVertex().hasLabel("Human").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("Human"). + has("name", name).singleResult(); + assertEquals(name, poliana.map(Human::getName).orElse("")); + } + + @Test + void shouldReturnErrorWhenPredicateIsNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().filter(null)); + } + + @Test + void shouldPredicate() { + long count = graphTemplate.traversalVertex() + .hasLabel(Human.class) + .filter(Human::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(Human.class) + .in("knows").result() + .collect(Collectors.toList()); + + assertEquals(6, people.size()); + + people = graphTemplate.traversalVertex() + .hasLabel(Human.class) + .in("knows").dedup().result() + .toList(); + + assertEquals(3, people.size()); + } + + +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntityTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntityTest.java new file mode 100644 index 000000000..8f944b2df --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntityTest.java @@ -0,0 +1,360 @@ +/* + * 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; + +import jakarta.data.exceptions.EmptyResultException; +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.Value; +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.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +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 EdgeEntityTest { + + + @Inject + private GraphTemplate graphTemplate; + + + @Test + void shouldReturnErrorWhenInboundIsNull() { + Assertions.assertThrows(NullPointerException.class, () -> { + Human human = Human.builder().withName("Poliana").withAge().build(); + Magazine magazine = null; + graphTemplate.edge(human, "reads", magazine); + }); + } + + @Test + void shouldReturnErrorWhenOutboundIsNull() { + Assertions.assertThrows(IllegalStateException.class, () -> { + Human human = Human.builder().withName("Poliana").withAge().build(); + Magazine magazine = Magazine.builder().withAge(2007).withName("The Shack").build(); + graphTemplate.edge(human, "reads", magazine); + }); + } + + @Test + void shouldReturnErrorWhenLabelIsNull() { + Assertions.assertThrows(NullPointerException.class, () -> { + Human human = Human.builder().withName("Poliana").withAge().build(); + Magazine magazine = Magazine.builder().withAge(2007).withName("The Shack").build(); + graphTemplate.edge(human, (String) null, magazine); + }); + } + + @Test + void shouldReturnNullWhenInboundIdIsNull() { + Assertions.assertThrows(EmptyResultException.class, () -> { + Human human = Human.builder().withId(-5).withName("Poliana").withAge().build(); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + graphTemplate.edge(human, "reads", magazine); + }); + + } + + @Test + void shouldReturnNullWhenOutboundIdIsNull() { + Assertions.assertThrows(IllegalStateException.class, () -> { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = Magazine.builder().withAge(2007).withName("The Shack").build(); + graphTemplate.edge(human, "reads", magazine); + }); + } + + @Test + void shouldReturnEntityNotFoundWhenOutBoundDidNotFound() { + Assertions.assertThrows( EmptyResultException.class, () -> { + Human human = Human.builder().withId(-10L).withName("Poliana").withAge().build(); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + graphTemplate.edge(human, "reads", magazine); + }); + } + + @Test + void shouldReturnEntityNotFoundWhenInBoundDidNotFound() { + Assertions.assertThrows( EmptyResultException.class, () -> { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = Magazine.builder().withId(10L).withAge(2007).withName("The Shack").build(); + graphTemplate.edge(human, "reads", magazine); + }); + } + + @Test + void shouldCreateAnEdge() { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); + + assertEquals("reads", edge.label()); + assertEquals(human, edge.outgoing()); + assertEquals(magazine, edge.incoming()); + assertTrue(edge.isEmpty()); + assertNotNull(edge.id()); + } + + @Test + void shouldGetId() { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); + + assertEquals("reads", edge.label()); + assertEquals(human, edge.outgoing()); + assertEquals(magazine, edge.incoming()); + assertTrue(edge.isEmpty()); + assertNotNull(edge.id()); + final Long id = edge.id(Long.class); + assertNotNull(id); + + assertEquals(id, edge.id(Integer.class).longValue()); + + } + + @Test + void shouldCreateAnEdgeWithSupplier() { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(human, () -> "reads", magazine); + + assertEquals("reads", edge.label()); + assertEquals(human, edge.outgoing()); + assertEquals(magazine, edge.incoming()); + assertTrue(edge.isEmpty()); + assertNotNull(edge.id()); + } + + @Test + void shouldUseAnEdge() { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); + + EdgeEntity sameEdge = graphTemplate.edge(human, "reads", magazine); + + assertEquals(edge.id(), sameEdge.id()); + assertEquals(edge, sameEdge); + } + + @Test + void shouldUseAnEdge2() { + Human poliana = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Human nilzete = graphTemplate.insert(Human.builder().withName("Nilzete").withAge().build()); + + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(poliana, "reads", magazine); + EdgeEntity edge1 = graphTemplate.edge(nilzete, "reads", magazine); + + EdgeEntity sameEdge = graphTemplate.edge(poliana, "reads", magazine); + EdgeEntity sameEdge1 = graphTemplate.edge(nilzete, "reads", magazine); + + assertEquals(edge.id(), sameEdge.id()); + assertEquals(edge, sameEdge); + + assertEquals(edge1.id(), sameEdge1.id()); + assertEquals(edge1, sameEdge1); + + } + + @Test + void shouldUseADifferentEdge() { + Human poliana = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Human nilzete = graphTemplate.insert(Human.builder().withName("Nilzete").withAge().build()); + + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(poliana, "reads", magazine); + EdgeEntity edge1 = graphTemplate.edge(nilzete, "reads", magazine); + + EdgeEntity sameEdge = graphTemplate.edge(poliana, "reads", magazine); + EdgeEntity sameEdge1 = graphTemplate.edge(nilzete, "reads", magazine); + + assertNotEquals(edge.id(), edge1.id()); + assertNotEquals(edge.id(), sameEdge1.id()); + + assertNotEquals(sameEdge1.id(), sameEdge.id()); + } + + @Test + void shouldReturnErrorWhenAddKeyIsNull() { + assertThrows(NullPointerException.class, () -> { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); + edge.add(null, "Brazil"); + }); + } + + @Test + void shouldReturnErrorWhenAddValueIsNull() { + + assertThrows(NullPointerException.class, () -> { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); + edge.add("where", null); + }); + } + + @Test + void shouldAddProperty() { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); + edge.add("where", "Brazil"); + + assertFalse(edge.isEmpty()); + assertEquals(1, edge.size()); + assertThat(edge.properties()).contains(Element.of("where", "Brazil")); + } + + @Test + void shouldAddPropertyWithValue() { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); + edge.add("where", Value.of("Brazil")); + + assertFalse(edge.isEmpty()); + assertEquals(1, edge.size()); + assertThat(edge.properties()).contains(Element.of("where", "Brazil")); + } + + + @Test + void shouldReturnErrorWhenRemoveNullKeyProperty() { + assertThrows(NullPointerException.class, () -> { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); + edge.add("where", "Brazil"); + + + assertFalse(edge.isEmpty()); + edge.remove(null); + }); + } + + @Test + void shouldRemoveProperty() { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); + edge.add("where", "Brazil"); + assertEquals(1, edge.size()); + assertFalse(edge.isEmpty()); + edge.remove("where"); + assertTrue(edge.isEmpty()); + assertEquals(0, edge.size()); + } + + @Test + void shouldFindProperty() { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); + edge.add("where", "Brazil"); + + Optional where = edge.get("where"); + assertTrue(where.isPresent()); + assertEquals("Brazil", where.get().get()); + assertFalse(edge.get("not").isPresent()); + + } + + @Test + void shouldDeleteAnEdge() { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); + edge.delete(); + + EdgeEntity newEdge = graphTemplate.edge(human, "reads", magazine); + assertNotEquals(edge.id(), newEdge.id()); + + graphTemplate.deleteEdge(newEdge.id()); + } + + @Test + void shouldReturnErrorWhenDeleteAnEdgeWithNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.delete((Iterable) null)); + } + + @Test + void shouldDeleteAnEdge2() { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + + EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); + + graphTemplate.deleteEdge(edge.id()); + + EdgeEntity newEdge = graphTemplate.edge(human, "reads", magazine); + assertNotEquals(edge.id(), newEdge.id()); + } + + + @Test + void shouldReturnErrorWhenFindEdgeWithNull() { + assertThrows(NullPointerException.class, () -> graphTemplate.edge(null)); + } + + + @Test + void shouldFindAnEdge() { + Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); + Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); + EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); + + Optional newEdge = graphTemplate.edge(edge.id()); + + assertTrue(newEdge.isPresent()); + assertEquals(edge.id(), newEdge.get().id()); + + graphTemplate.deleteEdge(edge.id()); + } + + @Test + void shouldNotFindAnEdge() { + Optional edgeEntity = graphTemplate.edge(-12L); + + assertFalse(edgeEntity.isPresent()); + } + +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphProducer.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphProducer.java new file mode 100644 index 000000000..e93101c3f --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphProducer.java @@ -0,0 +1,103 @@ +/* + * 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; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Alternative; +import jakarta.enterprise.inject.Disposes; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.Interceptor; +import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.mapping.Database; +import org.eclipse.jnosql.mapping.DatabaseType; +import org.mockito.Mockito; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.Comparator; +import java.util.function.Supplier; +import java.util.logging.Logger; + +import static java.lang.System.currentTimeMillis; +import static java.util.Collections.singleton; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ApplicationScoped +@Alternative +@Priority(Interceptor.Priority.APPLICATION) +public class GraphProducer implements Supplier { + + private static final Logger LOGGER = Logger.getLogger(GraphProducer.class.getName()); + + private Graph graph; + + private String directory; + + @PostConstruct + public void init() { + this.directory = new File("").getAbsolutePath() + "/target/jnosql-graph/" + currentTimeMillis() + "/"; + LOGGER.info("Starting Graph database at directory: " + directory); + this.graph = Neo4jGraph.open(directory); + LOGGER.info("Graph database created"); + } + + @Produces + @ApplicationScoped + @Override + public Graph get() { + return graph; + } + + + @Produces + @ApplicationScoped + @Database(value = DatabaseType.GRAPH, provider = "graphRepositoryMock") + public Graph getGraphMock() { + + Graph graphMock = mock(Graph.class); + Vertex vertex = mock(Vertex.class); + when(vertex.label()).thenReturn("Person"); + when(vertex.id()).thenReturn(10L); + when(graphMock.vertices(10L)).thenReturn(Collections.emptyIterator()); + when(vertex.keys()).thenReturn(singleton("name")); + when(vertex.value("name")).thenReturn("nameMock"); + when(graphMock.addVertex(Mockito.anyString())).thenReturn(vertex); + when(graphMock.vertices(Mockito.any())).thenReturn(Collections.emptyIterator()); + return graphMock; + } + + public void dispose(@Disposes Graph graph) throws Exception { + LOGGER.info("Graph database closing"); + graph.close(); + final Path path = Paths.get(directory); + if (Files.exists(path)) { + LOGGER.info("Removing directory graph database: " + directory); + Files.walk(path) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + LOGGER.info("Graph directory exists?: " + Files.exists(path)); + } + LOGGER.info("Graph Database closed"); + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducerTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducerTest.java new file mode 100644 index 000000000..da107b0fc --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducerTest.java @@ -0,0 +1,56 @@ +/* + * 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; + +import jakarta.inject.Inject; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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 org.mockito.Mockito; + +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 GraphTemplateProducerTest { + + @Inject + private GraphTemplateProducer producer; + + @Test + void shouldReturnErrorWhenManagerNull() { + assertThrows(NullPointerException.class, () -> producer.apply(null)); + } + + @Test + void shouldReturnGraphTemplateWhenGetGraph() { + Graph graph = Mockito.mock(Graph.class); + GraphTemplate template = producer.apply(graph); + assertNotNull(template); + } + + +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateTest.java new file mode 100644 index 000000000..ab810b047 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateTest.java @@ -0,0 +1,57 @@ +/* + * 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; + +import jakarta.inject.Inject; +import jakarta.nosql.Template; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.mapping.Database; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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.Assertions; +import org.junit.jupiter.api.Test; + +import static org.eclipse.jnosql.mapping.DatabaseType.GRAPH; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) +@AddPackages(GraphProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) +class GraphTemplateTest { + + @Inject + private Template template; + + @Inject + @Database(GRAPH) + private Template qualifier; + + + @Test + void shouldInjectTemplate() { + Assertions.assertNotNull(template); + } + + @Test + void shouldInjectQualifier() { + Assertions.assertNotNull(qualifier); + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineRepository.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineRepository.java new file mode 100644 index 000000000..33e597647 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineRepository.java @@ -0,0 +1,24 @@ +/* + * 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; + + +import jakarta.data.repository.BasicRepository; +import jakarta.data.repository.Repository; +import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Magazine; + +@Repository +public interface MagazineRepository extends BasicRepository { +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineTemplateTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineTemplateTest.java new file mode 100644 index 000000000..5a2272e6c --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineTemplateTest.java @@ -0,0 +1,97 @@ +/* + * 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; + +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.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.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) +@AddPackages(GraphProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) +class MagazineTemplateTest { + + @Inject + private MagazineTemplate template; + + @Inject + private Graph graph; + + @Test + void shouldSaveWithTransaction() { + AtomicReference status = new AtomicReference<>(); + + Magazine magazine = Magazine.builder().withName("The Book").build(); + Transaction transaction = graph.tx(); + transaction.addTransactionListener(status::set); + template.insert(magazine); + assertFalse(transaction.isOpen()); + assertEquals(COMMIT, status.get()); + } + + @Test + void shouldSaveWithRollback() { + AtomicReference status = new AtomicReference<>(); + + Magazine magazine = Magazine.builder().withName("The Book").build(); + Transaction transaction = graph.tx(); + transaction.addTransactionListener(status::set); + try { + template.insertException(magazine); + assert false; + }catch (Exception ignored){ + + } + + assertFalse(transaction.isOpen()); + assertEquals(ROLLBACK, status.get()); + } + + @Test + void shouldUseAutomaticNormalTransaction() { + AtomicReference status = new AtomicReference<>(); + + Magazine magazine = Magazine.builder().withName("The Book").build(); + Transaction transaction = graph.tx(); + transaction.addTransactionListener(status::set); + assertNull(status.get()); + template.normalInsertion(magazine); + assertEquals(COMMIT, status.get()); + assertFalse(transaction.isOpen()); + } +} + diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock.java new file mode 100644 index 000000000..d78c232f4 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock.java @@ -0,0 +1,83 @@ +/* + * 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.configuration; + +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Transaction; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.databases.tinkerpop.communication.GraphConfiguration; + +import java.util.Iterator; + +class GraphConfigurationMock implements GraphConfiguration { + + @Override + public Graph apply(Settings settings) { + return new GraphMock(settings); + } + + public record GraphMock(Settings settings) implements Graph { + + @Override + public Vertex addVertex(Object... keyValues) { + return null; + } + + @Override + public C compute(Class graphComputerClass) throws IllegalArgumentException { + return null; + } + + @Override + public GraphComputer compute() throws IllegalArgumentException { + return null; + } + + @Override + public Iterator vertices(Object... vertexIds) { + return null; + } + + @Override + public Iterator edges(Object... edgeIds) { + return null; + } + + @Override + public Transaction tx() { + return null; + } + + @Override + public void close() { + + } + + @Override + public Variables variables() { + return null; + } + + @Override + public Configuration configuration() { + return null; + } + + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock2.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock2.java new file mode 100644 index 000000000..c08c302f7 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock2.java @@ -0,0 +1,83 @@ +/* + * 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.configuration; + +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Transaction; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.databases.tinkerpop.communication.GraphConfiguration; + +import java.util.Iterator; + +public class GraphConfigurationMock2 implements GraphConfiguration { + + @Override + public Graph apply(Settings settings) { + return new GraphMock(settings); + } + + public record GraphMock(Settings settings) implements Graph { + + @Override + public Vertex addVertex(Object... keyValues) { + return null; + } + + @Override + public C compute(Class graphComputerClass) throws IllegalArgumentException { + return null; + } + + @Override + public GraphComputer compute() throws IllegalArgumentException { + return null; + } + + @Override + public Iterator vertices(Object... vertexIds) { + return null; + } + + @Override + public Iterator edges(Object... edgeIds) { + return null; + } + + @Override + public Transaction tx() { + return null; + } + + @Override + public void close() { + + } + + @Override + public Variables variables() { + return null; + } + + @Override + public Configuration configuration() { + return null; + } + + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplierTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplierTest.java new file mode 100644 index 000000000..aae0b7adc --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplierTest.java @@ -0,0 +1,82 @@ +/* + * 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.configuration; + +import jakarta.inject.Inject; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphProducer; +import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphTemplate; +import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.jnosql.mapping.core.config.MappingConfigurations.GRAPH_PROVIDER; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) +@AddPackages(GraphProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) +class GraphSupplierTest { + + @Inject + private GraphSupplier supplier; + + @BeforeEach + public void beforeEach(){ + System.clearProperty(GRAPH_PROVIDER.get()); + } + + @Test + void shouldGetGraph() { + System.setProperty(GRAPH_PROVIDER.get(), GraphConfigurationMock.class.getName()); + Graph graph = supplier.get(); + Assertions.assertNotNull(graph); + assertThat(graph).isInstanceOf(GraphConfigurationMock.GraphMock.class); + } + + + @Test + void shouldUseDefaultConfigurationWhenProviderIsWrong() { + System.setProperty(GRAPH_PROVIDER.get(), Integer.class.getName()); + Graph graph = supplier.get(); + Assertions.assertNotNull(graph); + assertThat(graph).isInstanceOf(GraphConfigurationMock2.GraphMock.class); + } + + @Test + void shouldUseDefaultConfiguration() { + Graph graph = supplier.get(); + Assertions.assertNotNull(graph); + assertThat(graph).isInstanceOf(GraphConfigurationMock2.GraphMock.class); + } + + @Test + void shouldClose() throws Exception { + Graph graph = Mockito.mock(Graph.class); + supplier.close(graph); + Mockito.verify(graph).close(); + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Creature.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Creature.java new file mode 100644 index 000000000..40fef8e77 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Creature.java @@ -0,0 +1,70 @@ +/* + * 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.entities; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.Objects; + +@Entity +public class Creature { + + + @Id + private Long id; + + @Column + private String name; + + Creature() { + } + + public Creature(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Creature creature)) { + return false; + } + return Objects.equals(id, creature.id) && + Objects.equals(name, creature.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + + @Override + public String toString() { + return "Creature{" + "name='" + name + '\'' + + '}'; + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Human.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Human.java new file mode 100644 index 000000000..dabc87030 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Human.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2023 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.entities; + + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; +import jakarta.nosql.MappedSuperclass; + +import java.util.List; +import java.util.Objects; + +@Entity +@MappedSuperclass +public class Human { + + @Id + private long id; + + @Column + private String name; + + @Column + private int age; + + @Column + private List phones; + + private String ignore; + + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public List getPhones() { + return phones; + } + + public String getIgnore() { + return ignore; + } + + public boolean isAdult() { + return age > 21; + } + + Human() { + } + + Human(long id, String name, int age, List phones, String ignore) { + this.id = id; + this.name = name; + this.age = age; + this.phones = phones; + this.ignore = ignore; + } + + @Override + public String toString() { + return "Person{" + "id=" + id + + ", name='" + name + '\'' + + ", age=" + age + + ", phones=" + phones + + ", ignore='" + ignore + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Human human = (Human) o; + return id == human.id && + age == human.age && + Objects.equals(name, human.name) && + Objects.equals(phones, human.phones); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, age, phones, ignore); + } + + public static HumanBuilder builder() { + return new HumanBuilder(); + } + + public void setName(String name){ + this.name = name; + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanBuilder.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanBuilder.java new file mode 100644 index 000000000..4d7ee50e8 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanBuilder.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 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.entities; + +import java.util.List; + +public class HumanBuilder { + private long id; + private String name; + private int age; + private List phones; + private String ignore; + + public HumanBuilder withId(long id) { + this.id = id; + return this; + } + + public HumanBuilder withName(String name) { + this.name = name; + return this; + } + + public HumanBuilder withAge() { + this.age = 10; + return this; + } + + public HumanBuilder withAge(int age) { + this.age = age; + return this; + } + + + public HumanBuilder withPhones(List phones) { + this.phones = phones; + return this; + } + + public HumanBuilder withIgnore() { + this.ignore = "Just Ignore"; + return this; + } + + public Human build() { + return new Human(id, name, age, phones, ignore); + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanRepository.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanRepository.java new file mode 100644 index 000000000..945d1f198 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanRepository.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 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.entities; + + +import jakarta.data.repository.BasicRepository; +import jakarta.data.repository.Repository; + +@Repository +public interface HumanRepository extends BasicRepository { +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Magazine.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Magazine.java new file mode 100644 index 000000000..07d03ed95 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Magazine.java @@ -0,0 +1,113 @@ +/* + * 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.entities; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.Objects; + +@Entity +public class Magazine { + + @Id + private Long id; + + @Column + private String name; + + @Column + private Integer age; + + + Magazine() { + } + + Magazine(Long id, String name, Integer age) { + this.id = id; + this.name = name; + this.age = age; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Integer getAge() { + return age; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Magazine magazine)) { + return false; + } + return Objects.equals(id, magazine.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + @Override + public String toString() { + return "Book{" + + "id=" + id + + ", name='" + name + '\'' + + ", age=" + age + + '}'; + } + + public static MagazineBuilder builder() { + return new MagazineBuilder(); + } + + public static class MagazineBuilder { + private String name; + private Integer age; + private Long id; + + private MagazineBuilder() { + } + + public MagazineBuilder withName(String name) { + this.name = name; + return this; + } + + public MagazineBuilder withAge(Integer age) { + this.age = age; + return this; + } + + public MagazineBuilder withId(Long id) { + this.id = id; + return this; + } + + public Magazine build() { + return new Magazine(id, name, age); + } + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/MagazineTemplate.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/MagazineTemplate.java new file mode 100644 index 000000000..b8f4a510c --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/MagazineTemplate.java @@ -0,0 +1,43 @@ +/* + * 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.entities; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphTemplate; +import org.eclipse.jnosql.databases.tinkerpop.mapping.Transactional; + +@ApplicationScoped +public class MagazineTemplate { + + @Inject + private GraphTemplate graphTemplate; + + @Transactional + public void insert(Magazine actor) { + graphTemplate.insert(actor); + } + + @Transactional + public void insertException(Magazine actor) { + graphTemplate.insert(actor); + throw new NullPointerException("should get a rollback"); + } + + public void normalInsertion(Magazine actor) { + graphTemplate.insert(actor); + } + +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/People.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/People.java new file mode 100644 index 000000000..1e47557cf --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/People.java @@ -0,0 +1,26 @@ +/* + * 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.entities; + +import jakarta.data.repository.Insert; +import jakarta.data.repository.Repository; + + +@Repository +public interface People { + + @Insert + Human insert(Human human); +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/WrongEntity.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/WrongEntity.java new file mode 100644 index 000000000..4ca2d506f --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/WrongEntity.java @@ -0,0 +1,56 @@ +/* + * 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.entities; + +import java.util.Objects; + +public class WrongEntity { + + private String name; + + WrongEntity() { + } + + public WrongEntity(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + WrongEntity animal = (WrongEntity) o; + return Objects.equals(name, animal.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } + + @Override + public String toString() { + return "Animal{" + "name='" + name + '\'' + + '}'; + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphCustomExtensionTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphCustomExtensionTest.java new file mode 100644 index 000000000..5bc45760d --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphCustomExtensionTest.java @@ -0,0 +1,77 @@ +/* + * 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.GraphTemplate; +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.core.spi.EntityMetadataExtension; +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 static org.junit.jupiter.api.Assertions.assertNotNull; + + +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) +@AddPackages(GraphProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.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-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtensionTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtensionTest.java new file mode 100644 index 000000000..9828f27b3 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtensionTest.java @@ -0,0 +1,84 @@ +/* + * 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.eclipse.jnosql.databases.tinkerpop.mapping.GraphProducer; +import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphTemplate; +import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Human; +import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.HumanRepository; +import org.eclipse.jnosql.mapping.Database; +import org.eclipse.jnosql.mapping.DatabaseType; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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 static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + + +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) +@AddPackages(GraphProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) +class GraphExtensionTest { + + + @Inject + @Database(value = DatabaseType.GRAPH) + private HumanRepository repository; + + @Inject + @Database(value = DatabaseType.GRAPH, provider = "graphRepositoryMock") + private HumanRepository repositoryMock; + + @Inject + @Database(value = DatabaseType.GRAPH, provider = "graphRepositoryMock") + private GraphTemplate templateMock; + + @Inject + private GraphTemplate template; + + + @Test + void shouldInitiate() { + assertNotNull(repository); + Human human = repository.save(Human.builder().build()); + assertNull(human.getName()); + } + + @Test + void shouldUseMock(){ + assertNotNull(repositoryMock); + } + + @Test + void shouldInjectTemplate() { + assertNotNull(templateMock); + assertNotNull(template); + } + + @Test + void shouldInjectRepository() { + assertNotNull(repository); + assertNotNull(repositoryMock); + } +} diff --git a/jnosql-neo4j/src/test/resources/META-INF/beans.xml b/jnosql-neo4j/src/test/resources/META-INF/beans.xml new file mode 100644 index 000000000..2a29afc00 --- /dev/null +++ b/jnosql-neo4j/src/test/resources/META-INF/beans.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/jnosql-neo4j/src/test/resources/META-INF/microprofile-config.properties b/jnosql-neo4j/src/test/resources/META-INF/microprofile-config.properties new file mode 100644 index 000000000..166d90edd --- /dev/null +++ b/jnosql-neo4j/src/test/resources/META-INF/microprofile-config.properties @@ -0,0 +1,4 @@ +graph=graph +graph.settings.key=value +graph.settings.key2=value2 +graph.provider=configuration.graph.mapping.org.eclipse.jnosql.databases.tinkerpop.GraphConfigurationMock diff --git a/jnosql-neo4j/src/test/resources/META-INF/services/org.eclipse.jnosql.databases.tinkerpop.communication.GraphConfiguration b/jnosql-neo4j/src/test/resources/META-INF/services/org.eclipse.jnosql.databases.tinkerpop.communication.GraphConfiguration new file mode 100644 index 000000000..eef0dd915 --- /dev/null +++ b/jnosql-neo4j/src/test/resources/META-INF/services/org.eclipse.jnosql.databases.tinkerpop.communication.GraphConfiguration @@ -0,0 +1 @@ +org.eclipse.jnosql.databases.tinkerpop.mapping.configuration.GraphConfigurationMock2 From a2d99d1c8fa96c019bbc74db6b7864cdcac5c0af Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 18:21:47 +0000 Subject: [PATCH 003/148] chore: remove antlr4 configuration to neo4j Signed-off-by: Otavio Santana --- .../eclipse/jnosql/query/grammar/data/JDQL.g4 | 160 ------------------ .../jnosql/query/grammar/method/Method.g4 | 40 ----- jnosql-neo4j/pom.xml | 53 +----- 3 files changed, 2 insertions(+), 251 deletions(-) delete mode 100644 jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 delete mode 100644 jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 diff --git a/jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 b/jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 deleted file mode 100644 index 2cdb92cb8..000000000 --- a/jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4 +++ /dev/null @@ -1,160 +0,0 @@ -grammar JDQL; - -statement : select_statement | update_statement | delete_statement; - -select_statement : select_clause? from_clause? where_clause? orderby_clause?; -update_statement : UPDATE entity_name set_clause where_clause?; -delete_statement : DELETE from_clause where_clause?; - -from_clause : FROM entity_name; - -where_clause : WHERE conditional_expression; - -set_clause : SET update_item (COMMA update_item)*; -update_item : state_field_path_expression EQ (scalar_expression | NULL); - -select_clause : SELECT select_list; -select_list - : state_field_path_expression (COMMA state_field_path_expression)* - | aggregate_expression - ; -aggregate_expression : COUNT '(' THIS ')'; - -orderby_clause : ORDER BY orderby_item (COMMA orderby_item)*; -orderby_item : state_field_path_expression (ASC | DESC)?; - -conditional_expression - // highest to lowest precedence - : LPAREN conditional_expression RPAREN - | null_comparison_expression - | in_expression - | between_expression - | like_expression - | comparison_expression - | NOT conditional_expression - | conditional_expression AND conditional_expression - | conditional_expression OR conditional_expression - ; - -comparison_expression : scalar_expression comparison_operator scalar_expression; -comparison_operator : EQ | GT | GTEQ | LT | LTEQ | NEQ; - -between_expression : scalar_expression NOT? BETWEEN scalar_expression AND scalar_expression; -like_expression : scalar_expression NOT? LIKE (STRING | input_parameter); - -in_expression : state_field_path_expression NOT? IN '(' in_item (',' in_item)* ')'; -in_item : literal | enum_literal | input_parameter; // could simplify to just literal - -null_comparison_expression : state_field_path_expression IS NOT? NULL; - -scalar_expression - // highest to lowest precedence - : LPAREN scalar_expression RPAREN - | primary_expression - | scalar_expression MUL scalar_expression - | scalar_expression DIV scalar_expression - | scalar_expression PLUS scalar_expression - | scalar_expression MINUS scalar_expression - | scalar_expression CONCAT scalar_expression - ; - -primary_expression - : function_expression - | special_expression - | state_field_path_expression - | enum_literal - | input_parameter - | literal - ; - -function_expression - : ('abs(' | 'ABS(') scalar_expression ')' - | ('length(' | 'LENGTH(') scalar_expression ')' - | ('lower(' | 'LOWER(') scalar_expression ')' - | ('upper(' | 'UPPER(') scalar_expression ')' - | ('left(' | 'LEFT(') scalar_expression ',' scalar_expression ')' - | ('right(' | 'RIGHT(') scalar_expression ',' scalar_expression ')' - ; - -special_expression - : LOCAL_DATE - | LOCAL_DATETIME - | LOCAL_TIME - | TRUE - | FALSE - ; - -state_field_path_expression : IDENTIFIER (DOT IDENTIFIER)* | FULLY_QUALIFIED_IDENTIFIER; - -entity_name : IDENTIFIER; // no ambiguity - -enum_literal : IDENTIFIER (DOT IDENTIFIER)* | FULLY_QUALIFIED_IDENTIFIER; // ambiguity with state_field_path_expression resolvable semantically - -input_parameter : COLON IDENTIFIER | QUESTION INTEGER; - -literal : STRING | INTEGER | DOUBLE; - -// Tokens defined to be case-insensitive using character classes -SELECT : [sS][eE][lL][eE][cC][tT]; -UPDATE : [uU][pP][dD][aA][tT][eE]; -DELETE : [dD][eE][lL][eE][tT][eE]; -FROM : [fF][rR][oO][mM]; -WHERE : [wW][hH][eE][rR][eE]; -SET : [sS][eE][tT]; -ORDER : [oO][rR][dD][eE][rR]; -BY : [bB][yY]; -NOT : [nN][oO][tT]; -IN : [iI][nN]; -IS : [iI][sS]; -NULL : [nN][uU][lL][lL]; -COUNT : [cC][oO][uU][nN][tT]; -TRUE : [tT][rR][uU][eE]; -FALSE : [fF][aA][lL][sS][eE]; -ASC : [aA][sS][cC]; -DESC : [dD][eE][sS][cC]; -AND : [aA][nN][dD]; -OR : [oO][rR]; -LOCAL_DATE : [lL][oO][cC][aA][lL] [dD][aA][tT][eE]; -LOCAL_DATETIME : [lL][oO][cC][aA][lL] [dD][aA][tT][eE][tT][iI][mM][eE]; -LOCAL_TIME : [lL][oO][cC][aA][lL] [tT][iI][mM][eE]; -BETWEEN : [bB][eE][tT][wW][eE][eE][nN]; -LIKE : [lL][iI][kK][eE]; -THIS : [tT][hH][iI][sS]; -LOCAL : [lL][oO][cC][aA][lL]; -DATE : [dD][aA][tT][eE]; -DATETIME : [dD][aA][tT][eE][tT][iI][mM][eE]; -TIME : [tT][iI][mM][eE]; - -// Operators -EQ : '='; -GT : '>'; -LT : '<'; -NEQ : '<>'; -GTEQ : '>='; -LTEQ : '<='; -PLUS : '+'; -MINUS : '-'; -MUL : '*'; -DIV : '/'; -CONCAT : '||'; - -// Special Characters -COMMA : ','; -DOT : '.'; -LPAREN : '('; -RPAREN : ')'; -COLON : ':'; -QUESTION : '?'; - -// Identifier and literals -FULLY_QUALIFIED_IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_]* (DOT [a-zA-Z_][a-zA-Z0-9_]*)+; -IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_]*; -STRING : '\'' ( ~('\'' | '\\') | '\\' . | '\'\'' )* '\'' // single quoted strings with embedded single quotes handled - | '"' ( ~["\\] | '\\' . )* '"' ; // double quoted strings -INTEGER : '-'?[0-9]+; -DOUBLE : '-'?[0-9]+'.'[0-9]* | '-'?'.'[0-9]+; - -// Whitespace and Comments -WS : [ \t\r\n]+ -> skip ; -LINE_COMMENT : '//' ~[\r\n]* -> skip; -BLOCK_COMMENT : '/*' .*? '*/' -> skip; diff --git a/jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 b/jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 deleted file mode 100644 index 21f5c25a7..000000000 --- a/jnosql-neo4j/antlr4/org/eclipse/jnosql/query/grammar/method/Method.g4 +++ /dev/null @@ -1,40 +0,0 @@ -grammar Method; -select: selectStart where? order? EOF; -deleteBy: 'deleteBy' where? EOF; - -selectStart: 'find' limit 'By' | 'findBy' | 'countAll' | 'countBy' | 'existsBy'; -where: condition (and condition| or condition)* ; -condition: eq | gt | gte | lt | lte | between | in | like | truth | untruth | nullable | contains | endsWith | startsWith; -order: 'OrderBy' orderName (orderName)*; -orderName: variable | variable asc | variable desc; -limit: firstLimit | firstOne; -firstLimit : 'First' limitNumber; -firstOne: 'First'; -and: 'And'; -or: 'Or'; -asc: 'Asc'; -desc: 'Desc'; -truth: variable 'True'; -untruth: variable 'False'; -eq: variable | variable ignoreCase? not? 'Equals'?; -gt: variable ignoreCase? not? 'GreaterThan'; -gte: variable ignoreCase? not? 'GreaterThanEqual'; -lt: variable ignoreCase? not? 'LessThan'; -lte: variable ignoreCase? not? 'LessThanEqual'; -between: variable ignoreCase? not? 'Between'; -in: variable ignoreCase? not? 'In'; -like: variable ignoreCase? not? 'Like'; -contains: variable ignoreCase? not? 'Contains'; -endsWith: variable ignoreCase? not? 'EndsWith'; -startsWith: variable ignoreCase? not? 'StartsWith'; -nullable: variable ignoreCase? not? 'Null'; -ignoreCase: 'IgnoreCase'; -not: 'Not'; -variable: ANY_NAME; -limitNumber: INT; -ANY_NAME: [a-zA-Z_.][a-zA-Z_.0-9-]*; -WS: [ \t\r\n]+ -> skip ; -INT: [0-9]+; -fragment ESC : '\\' (["\\/bfnrt] | UNICODE) ; -fragment UNICODE : 'u' HEX HEX HEX HEX ; -fragment HEX : [0-9a-fA-F] ; \ No newline at end of file diff --git a/jnosql-neo4j/pom.xml b/jnosql-neo4j/pom.xml index ea2855b4f..1be75ff45 100644 --- a/jnosql-neo4j/pom.xml +++ b/jnosql-neo4j/pom.xml @@ -23,15 +23,9 @@ 1.1.5-SNAPSHOT - jnosql-tinkerpop + jnosql-neo4j - 3.7.3 - 0.9-3.4.0 - 4.9.1 - - --add-opens java.base/java.lang=ALL-UNNAMED --add-exports java.base/sun.nio.ch=ALL-UNNAMED - @@ -39,50 +33,7 @@ org.eclipse.jnosql.mapping jnosql-mapping-semistructured ${project.version} - - - org.antlr - antlr4-runtime - - - - - org.apache.tinkerpop - gremlin-core - ${tinkerpop.version} - provided - - - org.apache.tinkerpop - neo4j-gremlin - ${tinkerpop.version} - test - - - org.neo4j - neo4j-tinkerpop-api-impl - ${neo4j.connector.version} - test - - - - - org.antlr - antlr4-maven-plugin - ${antlr4.version} - - antlr4 - - - - - antlr4 - - - - - - + From 12e117bcad14cd1be92f267495d28297475eb6e4 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 18:22:49 +0000 Subject: [PATCH 004/148] chore: update maven driver Signed-off-by: Otavio Santana --- jnosql-neo4j/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jnosql-neo4j/pom.xml b/jnosql-neo4j/pom.xml index 1be75ff45..6def0b8f1 100644 --- a/jnosql-neo4j/pom.xml +++ b/jnosql-neo4j/pom.xml @@ -26,6 +26,7 @@ jnosql-neo4j + 5.27.0 @@ -34,6 +35,11 @@ jnosql-mapping-semistructured ${project.version} + + org.neo4j.driver + neo4j-java-driver + ${neo4j.driver} + From bc21fcbe244bed1b9b237e8f2c33c61c5f8d643a Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 18:24:42 +0000 Subject: [PATCH 005/148] feat: remove mapping layer at neo4j Signed-off-by: Otavio Santana --- .../mapping/AbstractEdgeTraversal.java | 41 --- .../mapping/AbstractGraphTemplate.java | 327 ------------------ .../mapping/AbstractVertexTraversal.java | 40 --- .../tinkerpop/mapping/DefaultEdgeEntity.java | 163 --------- .../DefaultEdgeRepeatStepTraversal.java | 42 --- .../mapping/DefaultEdgeRepeatTraversal.java | 88 ----- .../mapping/DefaultEdgeTraversal.java | 186 ---------- .../mapping/DefaultEdgeTraversalOrder.java | 48 --- .../mapping/DefaultEdgeUntilTraversal.java | 84 ----- .../mapping/DefaultGraphTemplate.java | 98 ------ .../mapping/DefaultPreparedStatement.java | 82 ----- .../mapping/DefaultValueMapTraversal.java | 91 ----- .../DefaultVertexRepeatStepTraversal.java | 41 --- .../mapping/DefaultVertexRepeatTraversal.java | 99 ------ .../mapping/DefaultVertexTraversal.java | 237 ------------- .../mapping/DefaultVertexTraversalOrder.java | 46 --- .../mapping/DefaultVertexUntilTraversal.java | 130 ------- .../mapping/EdgeConditionTraversal.java | 127 ------- .../tinkerpop/mapping/EdgeEntity.java | 152 -------- .../mapping/EdgeRepeatStepTraversal.java | 36 -- .../mapping/EdgeRepeatTraversal.java | 129 ------- .../tinkerpop/mapping/EdgeTraversal.java | 157 --------- .../tinkerpop/mapping/EdgeTraversalOrder.java | 33 -- .../tinkerpop/mapping/EdgeUntilTraversal.java | 23 -- .../mapping/GraphEntityConverter.java | 30 -- .../tinkerpop/mapping/GraphTemplate.java | 250 ------------- .../mapping/GraphTemplateProducer.java | 125 ------- .../tinkerpop/mapping/GremlinExecutor.java | 85 ----- .../tinkerpop/mapping/GremlinParamParser.java | 70 ---- .../mapping/GremlinQueryException.java | 43 --- .../tinkerpop/mapping/Transactional.java | 35 -- .../mapping/TransactionalInterceptor.java | 59 ---- .../tinkerpop/mapping/ValueMapTraversal.java | 71 ---- .../mapping/VertexConditionTraversal.java | 236 ------------- .../mapping/VertexRepeatStepTraversal.java | 35 -- .../mapping/VertexRepeatTraversal.java | 148 -------- .../tinkerpop/mapping/VertexTraversal.java | 217 ------------ .../mapping/VertexTraversalOrder.java | 33 -- .../mapping/VertexUntilTraversal.java | 22 -- .../mapping/configuration/GraphSupplier.java | 54 --- .../mapping/query/RepositoryGraphBean.java | 115 ------ .../spi/CustomRepositoryGraphBean.java | 119 ------- .../tinkerpop/mapping/spi/GraphExtension.java | 83 ----- .../tinkerpop/mapping/spi/TemplateBean.java | 83 ----- 44 files changed, 4413 deletions(-) delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractEdgeTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplate.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractVertexTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeEntity.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatStepTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalOrder.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeUntilTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplate.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultPreparedStatement.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatStepTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalOrder.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexUntilTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeConditionTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntity.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatStepTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversalOrder.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeUntilTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphEntityConverter.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplate.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducer.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinExecutor.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinParamParser.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinQueryException.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/Transactional.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TransactionalInterceptor.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/ValueMapTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexConditionTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatStepTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversalOrder.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexUntilTraversal.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplier.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/RepositoryGraphBean.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/CustomRepositoryGraphBean.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtension.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/TemplateBean.java diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractEdgeTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractEdgeTraversal.java deleted file mode 100644 index 9e2a1b96e..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractEdgeTraversal.java +++ /dev/null @@ -1,41 +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; - -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import java.util.function.Function; -import java.util.function.Supplier; - -/** - * The base class to {@link EdgeTraversal} - */ -abstract class AbstractEdgeTraversal { - - protected final Supplier> supplier; - protected final Function, GraphTraversal> flow; - protected final EntityConverter converter; - - AbstractEdgeTraversal(Supplier> supplier, - Function, GraphTraversal> flow, - EntityConverter converter) { - this.supplier = supplier; - this.flow = flow; - this.converter = converter; - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplate.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplate.java deleted file mode 100644 index 2b49f6218..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplate.java +++ /dev/null @@ -1,327 +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; - -import jakarta.data.exceptions.EmptyResultException; -import jakarta.data.exceptions.NonUniqueResultException; -import org.apache.tinkerpop.gremlin.process.traversal.Traverser; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; -import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.Transaction; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.databases.tinkerpop.communication.CommunicationEntityConverter; -import org.eclipse.jnosql.databases.tinkerpop.communication.GraphDatabaseManager; -import org.eclipse.jnosql.databases.tinkerpop.communication.GraphTransactionUtil; -import org.eclipse.jnosql.mapping.IdNotFoundException; -import org.eclipse.jnosql.mapping.PreparedStatement; -import org.eclipse.jnosql.mapping.metadata.EntityMetadata; -import org.eclipse.jnosql.mapping.metadata.FieldMetadata; -import org.eclipse.jnosql.mapping.semistructured.AbstractSemiStructuredTemplate; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -import static java.util.Objects.isNull; -import static java.util.Objects.requireNonNull; -import static org.apache.tinkerpop.gremlin.structure.T.id; - -abstract class AbstractGraphTemplate extends AbstractSemiStructuredTemplate implements GraphTemplate { - - @SuppressWarnings("unchecked") - private static final Function, GraphTraversal> INITIAL_VERTEX = - g -> (GraphTraversal) g; - - @SuppressWarnings("unchecked") - private static final Function, GraphTraversal> INITIAL_EDGE = - g -> (GraphTraversal) g; - - - /** - * Retrieves the {@link GraphDatabaseManager} associated with this graph template. - * - * @return the {@link GraphDatabaseManager} associated with this graph template - */ - protected abstract GraphDatabaseManager manager(); - - /** - * Retrieves the {@link GraphTraversalSource} associated with this graph template. - * - * @return the {@link GraphTraversalSource} associated with this graph template - */ - protected abstract GraphTraversalSource traversal(); - - /** - * Retrieves the {@link Graph} associated with this graph template. - * - * @return the {@link Graph} associated with this graph template - */ - protected abstract Graph graph(); - - private GremlinExecutor gremlinExecutor; - - - @Override - public Optional find(K idValue) { - requireNonNull(idValue, "id is required"); - Optional vertex = traversal().V(idValue).tryNext(); - return vertex.map(v -> converter().toEntity(CommunicationEntityConverter.INSTANCE.apply(v))); - } - - @Override - public void delete(T id) { - requireNonNull(id, "id is required"); - traversal().V(id).toStream().forEach(Vertex::remove); - } - - @Override - public void deleteEdge(T id) { - requireNonNull(id, "id is required"); - traversal().E(id).toStream().forEach(Edge::remove); - } - - @Override - public void deleteEdge(Iterable ids) { - requireNonNull(ids, "ids is required"); - final Object[] edgeIds = StreamSupport.stream(ids.spliterator(), false).toArray(Object[]::new); - traversal().E(edgeIds).toStream().forEach(Edge::remove); - } - - @Override - public EdgeEntity edge(O outgoing, String label, I incoming) { - requireNonNull(incoming, "incoming is required"); - requireNonNull(label, "label is required"); - requireNonNull(outgoing, "outgoing is required"); - - checkId(outgoing); - checkId(incoming); - - if (isIdNull(outgoing)) { - throw new IllegalStateException("outgoing Id field is required"); - } - - if (isIdNull(incoming)) { - throw new IllegalStateException("incoming Id field is required"); - } - - Vertex outVertex = vertex(outgoing).orElseThrow(() -> new EmptyResultException("Outgoing entity does not found")); - Vertex inVertex = vertex(incoming).orElseThrow(() -> new EmptyResultException("Incoming entity does not found")); - - final Predicate> predicate = t -> { - Edge e = t.get(); - return e.inVertex().id().equals(inVertex.id()) - && e.outVertex().id().equals(outVertex.id()); - }; - - Optional edge = traversal().V(outVertex.id()) - .out(label).has(id, inVertex.id()).inE(label).filter(predicate).tryNext(); - - return edge.map(edge1 -> new DefaultEdgeEntity<>(edge1, incoming, outgoing)) - .orElseGet(() -> new DefaultEdgeEntity<>(getEdge(label, outVertex, inVertex), incoming, outgoing)); - } - - @Override - public Collection edgesById(K id, Direction direction, String... labels) { - requireNonNull(id, "id is required"); - requireNonNull(direction, "direction is required"); - - Iterator vertices = vertices(id); - if (vertices.hasNext()) { - List edges = new ArrayList<>(); - vertices.next().edges(direction, labels).forEachRemaining(edges::add); - return edges.stream().map(e ->EdgeEntity.of(converter(), e)).toList(); - } - return Collections.emptyList(); - } - - @SafeVarargs - @Override - public final Collection edgesById(K id, Direction direction, Supplier... labels) { - checkLabelsSupplier(labels); - return edgesByIdImpl(id, direction, Stream.of(labels).map(Supplier::get).toArray(String[]::new)); - } - - @Override - public Collection edgesById(K id, Direction direction) { - return edgesByIdImpl(id, direction); - } - - @Override - public Collection edges(T entity, Direction direction, String... labels) { - return edgesImpl(entity, direction, labels); - } - - @SafeVarargs - @Override - public final Collection edges(T entity, Direction direction, Supplier... labels) { - checkLabelsSupplier(labels); - return edgesImpl(entity, direction, Stream.of(labels).map(Supplier::get).toArray(String[]::new)); - } - - @Override - public Collection edges(T entity, Direction direction) { - return edgesImpl(entity, direction); - } - - @Override - public Optional edge(E edgeId) { - requireNonNull(edgeId, "edgeId is required"); - - Optional edgeOptional = traversal().E(edgeId).tryNext(); - - if (edgeOptional.isPresent()) { - Edge edge = edgeOptional.get(); - return Optional.of(EdgeEntity.of(converter(), edge)); - } - - return Optional.empty(); - } - - @Override - public VertexTraversal traversalVertex(Object... vertexIds) { - if (Stream.of(vertexIds).anyMatch(Objects::isNull)) { - throw new IllegalStateException("No one vertexId element cannot be null"); - } - return new DefaultVertexTraversal(() -> traversal().V(vertexIds), INITIAL_VERTEX, converter()); - } - - @Override - public EdgeTraversal traversalEdge(Object... edgeIds) { - if (Stream.of(edgeIds).anyMatch(Objects::isNull)) { - throw new IllegalStateException("No one edgeId element cannot be null"); - } - return new DefaultEdgeTraversal(() -> traversal().E(edgeIds), INITIAL_EDGE, converter()); - } - - @Override - public Transaction transaction() { - return graph().tx(); - } - - @Override - public Stream gremlin(String gremlin) { - requireNonNull(gremlin, "gremlin is required"); - return executor().executeGremlin(traversal(), gremlin); - } - - @Override - public Optional gremlinSingleResult(String gremlin) { - Stream entities = gremlin(gremlin); - final Iterator iterator = entities.iterator(); - if (!iterator.hasNext()) { - return Optional.empty(); - } - final T entity = iterator.next(); - if (!iterator.hasNext()) { - return Optional.ofNullable(entity); - } - throw new NonUniqueResultException("The gremlin query returns more than one result: " + gremlin); - } - - @Override - public PreparedStatement gremlinPrepare(String gremlin){ - requireNonNull(gremlin, "query is required"); - return new DefaultPreparedStatement(executor(), gremlin, traversal()); - } - - private void checkId(T entity) { - EntityMetadata entityMetadata = entities().get(entity.getClass()); - entityMetadata.id().orElseThrow(() -> IdNotFoundException.newInstance(entity.getClass())); - } - - private boolean isIdNull(T entity) { - EntityMetadata entityMetadata = entities().get(entity.getClass()); - FieldMetadata field = entityMetadata.id().orElseThrow(() -> IdNotFoundException.newInstance(entity.getClass())); - return isNull(field.read(entity)); - - } - - private Optional vertex(T entity) { - EntityMetadata entityMetadata = entities().get(entity.getClass()); - FieldMetadata field = entityMetadata.id().orElseThrow(() -> IdNotFoundException.newInstance(entity.getClass())); - Object id = field.read(entity); - Iterator vertices = vertices(id); - if (vertices.hasNext()) { - return Optional.of(vertices.next()); - } - return Optional.empty(); - } - - protected Iterator vertices(Object id) { - return graph().vertices(id); - } - - private Edge getEdge(String label, Vertex outVertex, Vertex inVertex) { - final Edge edge = outVertex.addEdge(label, inVertex); - GraphTransactionUtil.transaction(graph()); - return edge; - } - - private void checkLabelsSupplier(Supplier[] labels) { - if (Stream.of(labels).anyMatch(Objects::isNull)) { - throw new IllegalStateException("Item cannot be null"); - } - } - - private Collection edgesByIdImpl(K id, Direction direction, String... labels) { - - requireNonNull(id, "id is required"); - requireNonNull(direction, "direction is required"); - - Iterator vertices = vertices(id); - if (vertices.hasNext()) { - List edges = new ArrayList<>(); - vertices.next().edges(direction, labels).forEachRemaining(edges::add); - return edges.stream().map(e -> EdgeEntity.of(converter(), e)).toList(); - } - return Collections.emptyList(); - } - - private Collection edgesImpl(T entity, Direction direction, String... labels) { - requireNonNull(entity, "entity is required"); - - if (isIdNull(entity)) { - throw new IllegalStateException("Entity id is required"); - } - - Optional vertex = vertex(entity); - if (vertex.isEmpty()) { - return Collections.emptyList(); - } - - Object id = vertex.orElseThrow().id(); - return edgesByIdImpl(id, direction, labels); - } - - private GremlinExecutor executor() { - if (Objects.isNull(gremlinExecutor)) { - this.gremlinExecutor = new GremlinExecutor(converter()); - } - return gremlinExecutor; - } - -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractVertexTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractVertexTraversal.java deleted file mode 100644 index 4b2081081..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractVertexTraversal.java +++ /dev/null @@ -1,40 +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; - -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import java.util.function.Function; -import java.util.function.Supplier; - -/** - * The base class to {@link VertexTraversal} - */ -abstract class AbstractVertexTraversal { - - protected final Supplier> supplier; - protected final Function, GraphTraversal> flow; - protected final EntityConverter converter; - - AbstractVertexTraversal(Supplier> supplier, - Function, GraphTraversal> flow, - EntityConverter converter) { - this.supplier = supplier; - this.flow = flow; - this.converter = converter; - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeEntity.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeEntity.java deleted file mode 100644 index a016290bd..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeEntity.java +++ /dev/null @@ -1,163 +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; - -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Property; -import org.eclipse.jnosql.communication.Value; -import org.eclipse.jnosql.communication.semistructured.Element; -import org.eclipse.jnosql.databases.tinkerpop.communication.CommunicationEntityConverter; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.collectingAndThen; -import static java.util.stream.Collectors.toList; - -class DefaultEdgeEntity implements EdgeEntity { - - private final O outgoing; - - private final Edge edge; - - private final I incoming; - - DefaultEdgeEntity(Edge edge, I incoming, O outgoing) { - this.edge = edge; - this.incoming = incoming; - this.outgoing = outgoing; - } - - @Override - public Object id() { - return edge.id(); - } - - @Override - public T id(Class type) { - Objects.requireNonNull(type, "type is required"); - return Value.of(edge.id()).get(type); - } - - @Override - public String label() { - return edge.label(); - } - - @SuppressWarnings("unchecked") - @Override - public I incoming() { - return incoming; - } - - @SuppressWarnings("unchecked") - @Override - public O outgoing() { - return outgoing; - } - - @Override - public List properties() { - return edge.keys() - .stream() - .map(k -> Element.of(k, edge.value(k))) - .collect(collectingAndThen(toList(), Collections::unmodifiableList)); - } - - @Override - public void add(String key, Object value) { - requireNonNull(key, "key is required"); - requireNonNull(value, "value is required"); - edge.property(key, value); - - } - - @Override - public void add(String key, Value value) { - requireNonNull(key, "key is required"); - requireNonNull(value, "value is required"); - edge.property(key, value.get()); - } - - @Override - public void remove(String key) { - requireNonNull(key, "key is required"); - Property property = edge.property(key); - property.ifPresent(o -> property.remove()); - } - - @Override - public Optional get(String key) { - requireNonNull(key, "key is required"); - Property property = edge.property(key); - if (property.isPresent()) { - return Optional.of(Value.of(property.value())); - } - return Optional.empty(); - } - - @Override - public boolean isEmpty() { - return edge.keys().isEmpty(); - } - - @Override - public int size() { - return edge.keys().size(); - } - - @Override - public void delete() { - edge.remove(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - DefaultEdgeEntity that = (DefaultEdgeEntity) o; - return Objects.equals(edge.id(), that.edge.id()); - } - - @Override - public int hashCode() { - return Objects.hashCode(edge.id()); - } - - @Override - public String toString() { - return outgoing + - "---" + edge.label() + - " --->" + incoming; - } - - - public 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()))); - } - -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatStepTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatStepTraversal.java deleted file mode 100644 index ab7a585dc..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatStepTraversal.java +++ /dev/null @@ -1,42 +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; - -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import java.util.function.Function; -import java.util.function.Supplier; - -class DefaultEdgeRepeatStepTraversal extends AbstractEdgeTraversal implements EdgeRepeatStepTraversal { - - DefaultEdgeRepeatStepTraversal(Supplier> supplier, - Function, GraphTraversal> flow, - EntityConverter converter) { - super(supplier, flow, converter); - } - - @Override - public EdgeTraversal times(int times) { - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.times(times)), converter); - } - - @Override - public EdgeUntilTraversal until() { - return new DefaultEdgeUntilTraversal(supplier, flow, converter); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatTraversal.java deleted file mode 100644 index c42d26e67..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeRepeatTraversal.java +++ /dev/null @@ -1,88 +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; - -import org.apache.tinkerpop.gremlin.process.traversal.P; -import org.apache.tinkerpop.gremlin.process.traversal.Traversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.T; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import java.util.function.Function; -import java.util.function.Supplier; - -import static java.util.Objects.requireNonNull; - -class DefaultEdgeRepeatTraversal extends AbstractEdgeTraversal implements EdgeRepeatTraversal { - - - DefaultEdgeRepeatTraversal(Supplier> supplier, - Function, GraphTraversal> flow, - EntityConverter converter) { - super(supplier, flow, converter); - } - - @Override - public EdgeRepeatStepTraversal has(String propertyKey) { - requireNonNull(propertyKey, "propertyKey is required"); - - Traversal condition = __.has(propertyKey); - return new DefaultEdgeRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); - } - - @Override - public EdgeRepeatStepTraversal has(String propertyKey, Object value) { - requireNonNull(propertyKey, "propertyKey is required"); - requireNonNull(value, "value is required"); - Traversal condition = __.has(propertyKey, value); - return new DefaultEdgeRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); - } - - @Override - public EdgeRepeatStepTraversal has(String propertyKey, P predicate) { - requireNonNull(propertyKey, "propertyKey is required"); - requireNonNull(predicate, "predicate is required"); - Traversal condition = __.has(propertyKey, predicate); - return new DefaultEdgeRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); - } - - @Override - public EdgeRepeatStepTraversal has(T accessor, Object value) { - requireNonNull(accessor, "accessor is required"); - requireNonNull(value, "value is required"); - Traversal condition = __.has(accessor, value); - return new DefaultEdgeRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); - } - - @Override - public EdgeRepeatStepTraversal has(T accessor, P predicate) { - requireNonNull(accessor, "accessor is required"); - requireNonNull(predicate, "predicate is required"); - Traversal condition = __.has(accessor, predicate); - return new DefaultEdgeRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); - } - - @Override - public EdgeRepeatStepTraversal hasNot(String propertyKey) { - requireNonNull(propertyKey, "propertyKey is required"); - Traversal condition = __.hasNot(propertyKey); - return new DefaultEdgeRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); - } -} - - diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversal.java deleted file mode 100644 index be5c8c196..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversal.java +++ /dev/null @@ -1,186 +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; - -import jakarta.data.exceptions.NonUniqueResultException; -import org.apache.tinkerpop.gremlin.process.traversal.P; -import org.apache.tinkerpop.gremlin.process.traversal.Traverser; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.T; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import java.util.Iterator; -import java.util.Optional; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Stream; - -import static java.util.Objects.requireNonNull; - -class DefaultEdgeTraversal extends AbstractEdgeTraversal implements EdgeTraversal { - - - DefaultEdgeTraversal(Supplier> supplier, - Function, GraphTraversal> flow, - EntityConverter converter) { - super(supplier, flow, converter); - } - - @Override - public EdgeTraversal has(String propertyKey) { - requireNonNull(propertyKey, "propertyKey is required"); - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.has(propertyKey)), converter); - - } - - @Override - public EdgeTraversal has(String propertyKey, Object value) { - requireNonNull(propertyKey, "propertyKey is required"); - requireNonNull(value, "value is required"); - - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.has(propertyKey, value)), converter); - } - - @Override - public EdgeTraversal has(String propertyKey, P predicate) { - requireNonNull(propertyKey, "propertyKey is required"); - requireNonNull(predicate, "predicate is required"); - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.has(propertyKey, predicate)), converter); - } - - @Override - public EdgeTraversal has(T accessor, Object value) { - requireNonNull(accessor, "accessor is required"); - requireNonNull(value, "value is required"); - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.has(accessor, value)), converter); - } - - @Override - public EdgeTraversal has(T accessor, P predicate) { - requireNonNull(accessor, "accessor is required"); - requireNonNull(predicate, "predicate is required"); - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.has(accessor, predicate)), converter); - } - - @Override - public EdgeTraversal hasNot(String propertyKey) { - requireNonNull(propertyKey, "propertyKey is required"); - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.hasNot(propertyKey)), converter); - } - - @Override - public EdgeTraversal filter(Predicate predicate) { - requireNonNull(predicate, "predicate is required"); - - Predicate> p = e -> predicate.test(EdgeEntity.of(converter, e.get())); - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.filter(p)), converter); - } - - @Override - public EdgeTraversal limit(long limit) { - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.limit(limit)), converter); - } - - @Override - public EdgeTraversal range(long start, long end) { - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.range(start, end)), converter); - } - - @Override - public EdgeRepeatTraversal repeat() { - return new DefaultEdgeRepeatTraversal(supplier, flow, converter); - } - - - @Override - public VertexTraversal inV() { - return new DefaultVertexTraversal(supplier, flow.andThen(GraphTraversal::inV), converter); - } - - @Override - public VertexTraversal outV() { - return new DefaultVertexTraversal(supplier, flow.andThen(GraphTraversal::outV), converter); - } - - @Override - public VertexTraversal bothV() { - return new DefaultVertexTraversal(supplier, flow.andThen(GraphTraversal::bothV), converter); - } - - @Override - public EdgeTraversal dedup(String... labels) { - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.dedup(labels)), converter); - } - - - @Override - public Optional next() { - Optional edgeOptional = flow.apply(supplier.get()).tryNext(); - return edgeOptional.map(edge -> EdgeEntity.of(converter, edge)); - } - - @Override - public Optional singleResult() { - Stream result = result(); - final Iterator iterator = result.iterator(); - - if (!iterator.hasNext()) { - return Optional.empty(); - } - final EdgeEntity entity = iterator.next(); - if (!iterator.hasNext()) { - return Optional.of(entity); - } - throw new NonUniqueResultException("The Edge traversal query returns more than one result"); - } - - @Override - public Stream result() { - return stream(); - } - - @Override - public Stream stream() { - return flow.apply(supplier.get()).toList().stream() - .map(edge -> EdgeEntity.of(converter, edge)); - } - - @Override - public Stream next(int limit) { - return flow.apply(supplier.get()).next(limit).stream() - .map(edge -> EdgeEntity.of(converter, edge)); - } - - @Override - public ValueMapTraversal valueMap(String... propertyKeys) { - return new DefaultValueMapTraversal(supplier, flow.andThen(g -> g.elementMap(propertyKeys))); - } - - @Override - public EdgeTraversalOrder orderBy(String property) { - requireNonNull(property, "property is required"); - return new DefaultEdgeTraversalOrder(supplier, flow, converter, property); - } - - @Override - public long count() { - return flow.apply(supplier.get()).count().tryNext().orElse(0L); - } - - -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalOrder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalOrder.java deleted file mode 100644 index 0435df63b..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalOrder.java +++ /dev/null @@ -1,48 +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; - -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import java.util.function.Function; -import java.util.function.Supplier; - -import static org.apache.tinkerpop.gremlin.process.traversal.Order.asc; -import static org.apache.tinkerpop.gremlin.process.traversal.Order.desc; - -final class DefaultEdgeTraversalOrder extends AbstractEdgeTraversal implements EdgeTraversalOrder { - - - private final String property; - - DefaultEdgeTraversalOrder(Supplier> supplier, Function, - GraphTraversal> flow, EntityConverter converter, String property) { - super(supplier, flow, converter); - this.property = property; - } - - @Override - public EdgeTraversal asc() { - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.order().by(property, asc)), converter); - } - - @Override - public EdgeTraversal desc() { - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.order().by(property, desc)), converter); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeUntilTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeUntilTraversal.java deleted file mode 100644 index 00023d859..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeUntilTraversal.java +++ /dev/null @@ -1,84 +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; - -import org.apache.tinkerpop.gremlin.process.traversal.P; -import org.apache.tinkerpop.gremlin.process.traversal.Traversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.T; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import java.util.function.Function; -import java.util.function.Supplier; - -import static java.util.Objects.requireNonNull; - -class DefaultEdgeUntilTraversal extends AbstractEdgeTraversal implements EdgeUntilTraversal { - - DefaultEdgeUntilTraversal(Supplier> supplier, Function, - GraphTraversal> flow, EntityConverter converter) { - super(supplier, flow, converter); - } - - @Override - public EdgeTraversal has(String propertyKey) { - - requireNonNull(propertyKey, "propertyKey is required"); - Traversal condition = __.has(propertyKey); - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public EdgeTraversal has(String propertyKey, Object value) { - requireNonNull(propertyKey, "propertyKey is required"); - requireNonNull(value, "value is required"); - Traversal condition = __.has(propertyKey, value); - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public EdgeTraversal has(String propertyKey, P predicate) { - requireNonNull(propertyKey, "propertyKey is required"); - requireNonNull(predicate, "predicate is required"); - Traversal condition = __.has(propertyKey, predicate); - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public EdgeTraversal has(T accessor, Object value) { - requireNonNull(accessor, "accessor is required"); - requireNonNull(value, "value is required"); - Traversal condition = __.has(accessor, value); - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public EdgeTraversal has(T accessor, P predicate) { - requireNonNull(accessor, "accessor is required"); - requireNonNull(predicate, "predicate is required"); - Traversal condition = __.has(accessor, predicate); - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public EdgeTraversal hasNot(String propertyKey) { - requireNonNull(propertyKey, "propertyKey is required"); - Traversal condition = __.hasNot(propertyKey); - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplate.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplate.java deleted file mode 100644 index 8cb811ef3..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplate.java +++ /dev/null @@ -1,98 +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; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.Default; -import jakarta.inject.Inject; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.eclipse.jnosql.databases.tinkerpop.communication.GraphDatabaseManager; -import org.eclipse.jnosql.mapping.Database; -import org.eclipse.jnosql.mapping.core.Converters; -import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; -import org.eclipse.jnosql.mapping.semistructured.EventPersistManager; - -import static org.eclipse.jnosql.mapping.DatabaseType.GRAPH; - -@Default -@ApplicationScoped -@Database(GRAPH) -class DefaultGraphTemplate extends AbstractGraphTemplate { - - private final EntityConverter converter; - - private final GraphDatabaseManager manager; - - private final EventPersistManager eventManager; - - private final EntitiesMetadata entities; - - private final Converters converters; - private final Graph graph; - - - @Inject - DefaultGraphTemplate(EntityConverter converter, Graph graph, - EventPersistManager eventManager, - EntitiesMetadata entities, Converters converters) { - this.converter = converter; - this.graph = graph; - this.eventManager = eventManager; - this.entities = entities; - this.converters = converters; - this.manager = GraphDatabaseManager.of(graph); - } - - DefaultGraphTemplate() { - this(null, null, null, null, null); - } - - @Override - protected EntityConverter converter() { - return converter; - } - - @Override - protected GraphDatabaseManager manager() { - return manager; - } - - @Override - protected GraphTraversalSource traversal() { - return graph.traversal(); - } - - @Override - protected Graph graph() { - return graph; - } - - @Override - protected EventPersistManager eventManager() { - return eventManager; - } - - @Override - protected EntitiesMetadata entities() { - return entities; - } - - @Override - protected Converters converters() { - return converters; - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultPreparedStatement.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultPreparedStatement.java deleted file mode 100644 index 82336750b..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultPreparedStatement.java +++ /dev/null @@ -1,82 +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; - -import jakarta.data.exceptions.NonUniqueResultException; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; -import org.eclipse.jnosql.mapping.PreparedStatement; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Stream; - -import static java.util.Objects.requireNonNull; - -final class DefaultPreparedStatement implements PreparedStatement { - - private final GremlinExecutor executor; - - private final String gremlin; - - private final Map params = new HashMap<>(); - - private final GraphTraversalSource traversalSource; - - DefaultPreparedStatement(GremlinExecutor executor, String gremlin, GraphTraversalSource traversalSource) { - this.executor = executor; - this.gremlin = gremlin; - this.traversalSource = traversalSource; - } - - - @Override - public PreparedStatement bind(String name, Object value) { - requireNonNull(name, "name is required"); - requireNonNull(value, "value is required"); - params.put(name, value); - return this; - } - - @Override - public Stream result() { - return executor.executeGremlin(traversalSource, gremlin, params); - } - - @Override - public Optional singleResult() { - Stream entities = result(); - final Iterator iterator = entities.iterator(); - if (!iterator.hasNext()) { - return Optional.empty(); - } - final T entity = iterator.next(); - if (!iterator.hasNext()) { - return Optional.of(entity); - } - throw new NonUniqueResultException("There is more than one result found in the gremlin query: " + gremlin); - } - - @Override - public long count() { - throw new UnsupportedOperationException("There is no count operation in the graph database with Gremlin"); - } - - @Override - public boolean isCount() { - return false; - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversal.java deleted file mode 100644 index aae94ce98..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversal.java +++ /dev/null @@ -1,91 +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; - -import jakarta.data.exceptions.NonUniqueResultException; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.structure.Vertex; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static java.util.Collections.emptyMap; -import static java.util.stream.Collectors.toList; - -/** - * The default implementation of {@link ValueMapTraversal} - */ -class DefaultValueMapTraversal implements ValueMapTraversal { - - private final Supplier> supplier; - private final Function, GraphTraversal>> flow; - - DefaultValueMapTraversal(Supplier> supplier, Function, - GraphTraversal>> flow) { - this.supplier = supplier; - this.flow = flow; - } - - - @Override - public Stream> stream() { - return flow.apply(supplier.get()).toList().stream().map(toMap()); - } - - - @Override - public Stream> next(int limit) { - return flow.apply(supplier.get()).next(limit).stream().map(toMap()); - } - - @Override - public Map next() { - return flow.apply(supplier.get()).tryNext().map(toMap()).orElse(emptyMap()); - } - - @Override - public Optional> singleResult() { - List> result = resultList(); - if (result.isEmpty()) { - return Optional.empty(); - } - if (result.size() == 1) { - return Optional.of(result.get(0)); - } - throw new NonUniqueResultException("The Edge traversal query returns more than one result"); - } - - @Override - public List> resultList() { - return stream().collect(toList()); - } - - @Override - public long count() { - return flow.apply(supplier.get()).count().tryNext().orElse(0L); - } - - private Function, Map> toMap() { - return map -> map - .entrySet() - .stream() - .collect(Collectors.toMap(k -> k.getKey().toString(), Map.Entry::getValue)); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatStepTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatStepTraversal.java deleted file mode 100644 index efdfd966a..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatStepTraversal.java +++ /dev/null @@ -1,41 +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; - -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import java.util.function.Function; -import java.util.function.Supplier; - -class DefaultVertexRepeatStepTraversal extends AbstractVertexTraversal implements VertexRepeatStepTraversal { - - - DefaultVertexRepeatStepTraversal(Supplier> supplier, Function, - GraphTraversal> flow, EntityConverter converter) { - super(supplier, flow, converter); - } - - @Override - public VertexTraversal times(int times) { - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.times(times)), converter); - } - - @Override - public VertexUntilTraversal until() { - return new DefaultVertexUntilTraversal(supplier, flow, converter); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatTraversal.java deleted file mode 100644 index 2ce6512ae..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexRepeatTraversal.java +++ /dev/null @@ -1,99 +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; - -import org.apache.tinkerpop.gremlin.process.traversal.P; -import org.apache.tinkerpop.gremlin.process.traversal.Traversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; -import org.apache.tinkerpop.gremlin.structure.T; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import java.util.Objects; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Stream; - -import static java.util.Objects.requireNonNull; - -class DefaultVertexRepeatTraversal extends AbstractVertexTraversal implements VertexRepeatTraversal { - - DefaultVertexRepeatTraversal(Supplier> supplier, - Function, GraphTraversal> flow, - EntityConverter converter) { - super(supplier, flow, converter); - } - - @Override - public VertexRepeatStepTraversal has(String propertyKey, Object value) { - requireNonNull(propertyKey, "propertyKey is required"); - requireNonNull(value, "value is required"); - Traversal condition = __.has(propertyKey, value); - return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); - } - - @Override - public VertexRepeatStepTraversal has(String propertyKey, P predicate) { - requireNonNull(propertyKey, "propertyKey is required"); - requireNonNull(predicate, "predicate is required"); - Traversal condition = __.has(propertyKey, predicate); - return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); - } - - @Override - public VertexRepeatStepTraversal has(T accessor, Object value) { - requireNonNull(accessor, "accessor is required"); - requireNonNull(value, "value is required"); - Traversal condition = __.has(accessor, value); - return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); - } - - @Override - public VertexRepeatStepTraversal has(T accessor, P predicate) { - requireNonNull(accessor, "accessor is required"); - requireNonNull(predicate, "predicate is required"); - Traversal condition = __.has(accessor, predicate); - return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); - } - - @Override - public VertexRepeatStepTraversal hasNot(String propertyKey) { - requireNonNull(propertyKey, "propertyKey is required"); - Traversal condition = __.hasNot(propertyKey); - return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); - } - - @Override - public VertexRepeatStepTraversal out(String... labels) { - Stream.of(labels).forEach(l -> Objects.requireNonNull(l, "label is required")); - Traversal condition = __.out(labels); - return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); - } - - @Override - public VertexRepeatStepTraversal in(String... labels) { - Stream.of(labels).forEach(l -> Objects.requireNonNull(l, "label is required")); - Traversal condition = __.in(labels); - return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); - } - - @Override - public VertexRepeatStepTraversal both(String... labels) { - Stream.of(labels).forEach(l -> Objects.requireNonNull(l, "label is required")); - Traversal condition = __.both(labels); - return new DefaultVertexRepeatStepTraversal(supplier, flow.andThen(g -> g.repeat(condition)), converter); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversal.java deleted file mode 100644 index 36298e9fc..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversal.java +++ /dev/null @@ -1,237 +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; - -import jakarta.data.exceptions.NonUniqueResultException; -import jakarta.nosql.Entity; -import org.apache.tinkerpop.gremlin.process.traversal.P; -import org.apache.tinkerpop.gremlin.process.traversal.Traverser; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.structure.T; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.databases.tinkerpop.communication.CommunicationEntityConverter; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import java.util.Iterator; -import java.util.Optional; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Stream; - -import static java.util.Objects.requireNonNull; - -/** - * The default implementation of {@link VertexTraversal} - */ -class DefaultVertexTraversal extends AbstractVertexTraversal implements VertexTraversal { - - - private static final Predicate IS_EMPTY = String::isEmpty; - private static final Predicate NOT_EMPTY = IS_EMPTY.negate(); - - DefaultVertexTraversal(Supplier> supplier, - Function, GraphTraversal> flow, - EntityConverter converter) { - super(supplier, flow, converter); - } - - @Override - public VertexTraversal has(String propertyKey, Object value) { - requireNonNull(propertyKey, "propertyKey is required"); - requireNonNull(value, "value is required"); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.has(propertyKey, value)), converter); - } - - @Override - public VertexTraversal has(String propertyKey) { - requireNonNull(propertyKey, "propertyKey is required"); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.has(propertyKey)), converter); - } - - @Override - public VertexTraversal has(String propertyKey, P predicate) { - requireNonNull(propertyKey, "propertyKey is required"); - requireNonNull(predicate, "predicate is required"); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.has(propertyKey, predicate)), converter); - } - - @Override - public VertexTraversal has(T accessor, Object value) { - requireNonNull(accessor, "accessor is required"); - requireNonNull(value, "value is required"); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.has(accessor, value)), converter); - } - - @Override - public VertexTraversal has(T accessor, P predicate) { - requireNonNull(accessor, "accessor is required"); - requireNonNull(predicate, "predicate is required"); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.has(accessor, predicate)), converter); - } - - @Override - public VertexTraversal out(String... labels) { - Stream.of(labels).forEach(l -> requireNonNull(l, "label is required")); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.out(labels)), converter); - } - - @Override - public VertexTraversal filter(Predicate predicate) { - requireNonNull(predicate, "predicate is required"); - - Predicate> p = v -> predicate.test(GraphEntityConverter.INSTANCE.toEntity(converter, v.get())); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.filter(p)), converter); - } - - @Override - public EdgeTraversal outE(String... edgeLabels) { - Stream.of(edgeLabels).forEach(l -> requireNonNull(l, "label is required")); - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.outE(edgeLabels)), converter); - } - - @Override - public VertexTraversal in(String... labels) { - Stream.of(labels).forEach(l -> requireNonNull(l, "label is required")); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.in(labels)), converter); - } - - @Override - public EdgeTraversal inE(String... edgeLabels) { - Stream.of(edgeLabels).forEach(l -> requireNonNull(l, "edgeLabel is required")); - - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.inE(edgeLabels)), converter); - } - - @Override - public VertexTraversal both(String... labels) { - Stream.of(labels).forEach(l -> requireNonNull(l, "labels is required")); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.both(labels)), converter); - } - - @Override - public EdgeTraversal bothE(String... edgeLabels) { - Stream.of(edgeLabels).forEach(l -> requireNonNull(l, "edgeLabel is required")); - return new DefaultEdgeTraversal(supplier, flow.andThen(g -> g.bothE(edgeLabels)), converter); - } - - @Override - public VertexTraversal dedup(String... labels) { - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.dedup(labels)), converter); - } - - @Override - public VertexRepeatTraversal repeat() { - return new DefaultVertexRepeatTraversal(supplier, flow, converter); - } - - @Override - public VertexTraversal limit(long limit) { - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.limit(limit)), converter); - } - - @Override - public VertexTraversal skip(long skip) { - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.skip(skip)), converter); - } - - @Override - public VertexTraversal range(long start, long end) { - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.range(start, end)), converter); - } - - - @Override - public VertexTraversal hasLabel(String label) { - requireNonNull(label, "label is required"); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.hasLabel(label)), converter); - } - - @Override - public VertexTraversal hasLabel(Class type) { - requireNonNull(type, "type is required"); - Entity entity = type.getAnnotation(Entity.class); - String label = Optional.ofNullable(entity).map(Entity::value) - .filter(NOT_EMPTY) - .orElse(type.getSimpleName()); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.hasLabel(label)), converter); - } - - @Override - public VertexTraversal hasLabel(P predicate) { - requireNonNull(predicate, "predicate is required"); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.hasLabel(predicate)), converter); - } - - @Override - public VertexTraversal hasNot(String propertyKey) { - requireNonNull(propertyKey, "propertyKey is required"); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.hasNot(propertyKey)), converter); - } - - @Override - public Optional next() { - Optional vertex = flow.apply(supplier.get()).tryNext(); - return vertex.map(CommunicationEntityConverter.INSTANCE).map(converter::toEntity); - } - - @Override - public Stream result() { - return flow.apply(supplier.get()) - .toStream() - .map(CommunicationEntityConverter.INSTANCE) - .map(converter::toEntity); - } - - @Override - public Optional singleResult() { - final Stream stream = result(); - final Iterator iterator = stream.iterator(); - - if (!iterator.hasNext()) { - return Optional.empty(); - } - final T entity = iterator.next(); - if (!iterator.hasNext()) { - return Optional.of(entity); - } - throw new NonUniqueResultException("The Vertex traversal query returns more than one result"); - } - - @Override - public Stream next(int limit) { - return flow.apply(supplier.get()) - .next(limit) - .stream() - .map(CommunicationEntityConverter.INSTANCE) - .map(converter::toEntity); - } - - @Override - public ValueMapTraversal valueMap(String... propertyKeys) { - return new DefaultValueMapTraversal(supplier, flow.andThen(g -> g.elementMap(propertyKeys))); - } - - @Override - public long count() { - return flow.apply(supplier.get()).count().tryNext().orElse(0L); - } - - @Override - public VertexTraversalOrder orderBy(String property) { - requireNonNull(property, "property is required"); - return new DefaultVertexTraversalOrder(supplier, flow, converter, property); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalOrder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalOrder.java deleted file mode 100644 index 9c1d6fa99..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalOrder.java +++ /dev/null @@ -1,46 +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; - -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import java.util.function.Function; -import java.util.function.Supplier; - -import static org.apache.tinkerpop.gremlin.process.traversal.Order.asc; -import static org.apache.tinkerpop.gremlin.process.traversal.Order.desc; - -final class DefaultVertexTraversalOrder extends AbstractVertexTraversal implements VertexTraversalOrder { - - private final String property; - - DefaultVertexTraversalOrder(Supplier> supplier, Function, - GraphTraversal> flow, EntityConverter converter, String property) { - super(supplier, flow, converter); - this.property = property; - } - - @Override - public VertexTraversal asc() { - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.order().by(property, asc)), converter); - } - - @Override - public VertexTraversal desc() { - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.order().by(property, desc)), converter); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexUntilTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexUntilTraversal.java deleted file mode 100644 index bd151a1fd..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexUntilTraversal.java +++ /dev/null @@ -1,130 +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; - -import jakarta.nosql.Entity; -import org.apache.tinkerpop.gremlin.process.traversal.P; -import org.apache.tinkerpop.gremlin.process.traversal.Traversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; -import org.apache.tinkerpop.gremlin.structure.T; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import java.util.Objects; -import java.util.Optional; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Stream; - -import static java.util.Objects.requireNonNull; - -class DefaultVertexUntilTraversal extends AbstractVertexTraversal implements VertexUntilTraversal { - - - DefaultVertexUntilTraversal(Supplier> supplier, Function, - GraphTraversal> flow, EntityConverter converter) { - super(supplier, flow, converter); - } - - @Override - public VertexTraversal has(String propertyKey, Object value) { - - requireNonNull(propertyKey, "propertyKey is required"); - requireNonNull(value, "value is required"); - Traversal condition = __.has(propertyKey, value); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public VertexTraversal has(String propertyKey) { - requireNonNull(propertyKey, "propertyKey is required"); - Traversal condition = __.has(propertyKey); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public VertexTraversal has(String propertyKey, P predicate) { - requireNonNull(propertyKey, "propertyKey is required"); - requireNonNull(predicate, "predicate is required"); - Traversal condition = __.has(propertyKey, predicate); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public VertexTraversal has(T accessor, Object value) { - requireNonNull(accessor, "accessor is required"); - requireNonNull(value, "value is required"); - Traversal condition = __.has(accessor, value); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public VertexTraversal has(T accessor, P predicate) { - requireNonNull(accessor, "accessor is required"); - requireNonNull(predicate, "predicate is required"); - Traversal condition = __.has(accessor, predicate); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public VertexTraversal hasNot(String propertyKey) { - requireNonNull(propertyKey, "propertyKey is required"); - Traversal condition = __.hasNot(propertyKey); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public VertexTraversal out(String... labels) { - Stream.of(labels).forEach(l -> Objects.requireNonNull(l, "label is required")); - Traversal condition = __.out(labels); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public VertexTraversal in(String... labels) { - Stream.of(labels).forEach(l -> Objects.requireNonNull(l, "label is required")); - Traversal condition = __.in(labels); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public VertexTraversal both(String... labels) { - Stream.of(labels).forEach(l -> Objects.requireNonNull(l, "label is required")); - Traversal condition = __.both(labels); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public VertexTraversal hasLabel(String label) { - requireNonNull(label, "label is required"); - Traversal condition = __.hasLabel(label); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.until(condition)), converter); - } - - @Override - public VertexTraversal hasLabel(Class type) { - requireNonNull(type, "type is required"); - Entity entity = type.getAnnotation(Entity.class); - String label = Optional.ofNullable(entity).map(Entity::value).orElse(type.getName()); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.hasLabel(label)), converter); - } - - @Override - public VertexTraversal hasLabel(P predicate) { - requireNonNull(predicate, "predicate is required"); - return new DefaultVertexTraversal(supplier, flow.andThen(g -> g.hasLabel(predicate)), converter); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeConditionTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeConditionTraversal.java deleted file mode 100644 index 4cb8fb26b..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeConditionTraversal.java +++ /dev/null @@ -1,127 +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; - -import org.apache.tinkerpop.gremlin.process.traversal.P; -import org.apache.tinkerpop.gremlin.structure.T; - -import java.util.function.Supplier; - -import static java.util.Objects.requireNonNull; - -/** - * The base conditions to {@link EdgeTraversal} and {@link EdgeUntilTraversal} - */ -public interface EdgeConditionTraversal { - - /** - * Map the {@link org.apache.tinkerpop.gremlin.structure.Element} to the values of the associated properties given the provide property keys. - * If no property keys are provided, then all property values are emitted. - * - * @param propertyKey the key - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when propertyKey is null - */ - EdgeTraversal has(String propertyKey); - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param value the value to the condition - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - EdgeTraversal has(String propertyKey, Object value); - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param predicate the predicate condition - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when either key or predicate condition are null - */ - EdgeTraversal has(String propertyKey, P predicate); - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param value the value to the condition - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - default EdgeTraversal has(Supplier propertyKey, Object value) { - requireNonNull(propertyKey, "the supplier is required"); - return has(propertyKey.get(), value); - } - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param predicate the predicate condition - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when either key or predicate condition are null - */ - default EdgeTraversal has(Supplier propertyKey, P predicate) { - requireNonNull(propertyKey, "the supplier is required"); - return has(propertyKey.get(), predicate); - } - - /** - * Adds a equals condition to a query - * - * @param accessor the key - * @param value the value to the condition - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - EdgeTraversal has(T accessor, Object value); - - /** - * Adds a equals condition to a query - * - * @param accessor the key - * @param predicate the predicate condition - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - EdgeTraversal has(T accessor, P predicate); - - - /** - * Defines Vertex has not a property - * - * @param propertyKey the property key - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when propertyKey is null - */ - EdgeTraversal hasNot(String propertyKey); - - - /** - * Defines Vertex has not a property - * - * @param propertyKey the property key - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when propertyKey is null - */ - default EdgeTraversal hasNot(Supplier propertyKey) { - requireNonNull(propertyKey, "the supplier is required"); - return hasNot(propertyKey.get()); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntity.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntity.java deleted file mode 100644 index 222cd643e..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntity.java +++ /dev/null @@ -1,152 +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; - -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.eclipse.jnosql.communication.Value; -import org.eclipse.jnosql.communication.semistructured.Element; -import org.eclipse.jnosql.databases.tinkerpop.communication.CommunicationEntityConverter; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -/** - * It is a wrapper of {@link org.apache.tinkerpop.gremlin.structure.Edge} that links two Entity. - * Along with its Property objects, an Edge has both a Direction and a label. - * Any Change at the Edge is automatically continued in the database. However, any, change in the Entity will be ignored. - * {@link GraphTemplate#update(Object)} - * - *
outVertex ---label---> inVertex.
- */ -public interface EdgeEntity { - /** - * Returns the identifier of this edge. - * - * @return the identifier of this edge - */ - Object id(); - - /** - * Returns the identifier of this edge converted to the specified type. - * - * @param type the class type to convert the identifier to - * @param the type to convert the identifier to - * @return the identifier of this edge converted to the specified type - */ - T id(Class type); - - /** - * Returns the label of this edge. - * - * @return the label of this edge - */ - String label(); - - /** - * Retrieves the incoming entity connected to this edge. - * - * @param the type of the incoming entity - * @return the incoming entity connected to this edge - */ - T incoming(); - - /** - * Retrieves the outgoing entity connected to this edge. - * - * @param the type of the outgoing entity - * @return the outgoing entity connected to this edge - */ - T outgoing(); - - /** - * Returns the properties of this edge. - * - * @return the properties of this edge - */ - List properties(); - - /** - * Adds a new property to this edge with the specified key and value. - * - * @param key the key of the property - * @param value the value of the property - * @throws NullPointerException if either key or value is null - */ - void add(String key, Object value); - - /** - * Adds a new property to this edge with the specified key and value. - * - * @param key the key of the property - * @param value the value of the property - * @throws NullPointerException if either key or value is null - */ - void add(String key, Value value); - - /** - * Removes the property with the specified key from this edge. - * - * @param key the key of the property to be removed - * @throws NullPointerException if the key is null - */ - void remove(String key); - - /** - * Returns the property value associated with the specified key, if present. - * - * @param key the key of the property to retrieve - * @return the value associated with the specified key, or {@link Optional#empty()} if the key is not present - * @throws NullPointerException if the key is null - */ - Optional get(String key); - - /** - * Returns true if this edge contains no properties. - * - * @return true if this edge contains no properties - */ - boolean isEmpty(); - - /** - * Returns the number of properties in this edge. - * - * @return the number of properties in this edge - */ - int size(); - - /** - * Deletes this edge from the database. After this operation, any modification attempts such as adding or removing properties will result in an illegal state. - */ - void delete(); - - /** - * Creates an {@link EdgeEntity} instance from the provided {@link EntityConverter} and {@link Edge}. - * - * @param converter the entity converter to use - * @param edge the edge to create the entity from - * @return the created {@link EdgeEntity} instance - * @throws NullPointerException if either converter or edge is null - */ - 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.inVertex())), - converter.toEntity(entityConverter.apply(edge.outVertex()))); - } - -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatStepTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatStepTraversal.java deleted file mode 100644 index 087d74609..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatStepTraversal.java +++ /dev/null @@ -1,36 +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; - -/** - * After the {@link EdgeRepeatTraversal} condition the next step is - * {@link EdgeRepeatStepTraversal#times(int)} and {@link EdgeRepeatStepTraversal#until()} - */ -public interface EdgeRepeatStepTraversal { - - /** - * Starts a loop traversal n times - * @param times the repeat times that is required - * @return a {@link EdgeTraversal} instance - */ - EdgeTraversal times(int times); - - /** - * Define the loop traversal until a defined condition - * @return {@link EdgeUntilTraversal} - */ - EdgeUntilTraversal until(); - -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatTraversal.java deleted file mode 100644 index 38ba9338d..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeRepeatTraversal.java +++ /dev/null @@ -1,129 +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; - -import org.apache.tinkerpop.gremlin.process.traversal.P; -import org.apache.tinkerpop.gremlin.structure.T; - -import java.util.function.Supplier; - -import static java.util.Objects.requireNonNull; - -/** - * The wrapper step to - * {@link org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#repeat(org.apache.tinkerpop.gremlin.process.traversal.Traversal)} - * in the Edge type. - */ -public interface EdgeRepeatTraversal { - - - /** - * Map the {@link org.apache.tinkerpop.gremlin.structure.Element} to the values of the associated properties given the provide property keys. - * If no property keys are provided, then all property values are emitted. - * - * @param propertyKey the key - * @return a {@link EdgeRepeatStepTraversal} with the new condition - * @throws NullPointerException when propertyKey is null - */ - EdgeRepeatStepTraversal has(String propertyKey); - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param value the value to the condition - * @return a {@link EdgeRepeatStepTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - EdgeRepeatStepTraversal has(String propertyKey, Object value); - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param predicate the predicate condition - * @return a {@link EdgeRepeatStepTraversal} with the new condition - * @throws NullPointerException when either key or predicate condition are null - */ - EdgeRepeatStepTraversal has(String propertyKey, P predicate); - // - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param value the value to the condition - * @return a {@link EdgeRepeatStepTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - default EdgeRepeatStepTraversal has(Supplier propertyKey, Object value) { - requireNonNull(propertyKey, "the supplier is required"); - return has(propertyKey.get(), value); - } - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param predicate the predicate condition - * @return a {@link EdgeRepeatStepTraversal} with the new condition - * @throws NullPointerException when either key or predicate condition are null - */ - default EdgeRepeatStepTraversal has(Supplier propertyKey, P predicate) { - requireNonNull(propertyKey, "the supplier is required"); - return has(propertyKey.get(), predicate); - } - - /** - * Adds a equals condition to a query - * - * @param accessor the key - * @param value the value to the condition - * @return a {@link EdgeRepeatStepTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - EdgeRepeatStepTraversal has(T accessor, Object value); - - /** - * Adds a equals condition to a query - * - * @param accessor the key - * @param predicate the predicate condition - * @return a {@link EdgeRepeatStepTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - EdgeRepeatStepTraversal has(T accessor, P predicate); - - - /** - * Defines Vertex has not a property - * - * @param propertyKey the property key - * @return a {@link EdgeRepeatStepTraversal} with the new condition - * @throws NullPointerException when propertyKey is null - */ - EdgeRepeatStepTraversal hasNot(String propertyKey); - - /** - * Defines Vertex has not a property - * - * @param propertyKey the property key - * @return a {@link EdgeRepeatStepTraversal} with the new condition - * @throws NullPointerException when propertyKey is null - */ - default EdgeRepeatStepTraversal hasNot(Supplier propertyKey) { - requireNonNull(propertyKey, "the supplier is required"); - return hasNot(propertyKey.get()); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversal.java deleted file mode 100644 index 45bc12f64..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversal.java +++ /dev/null @@ -1,157 +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; - -import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DedupGlobalStep; - -import java.util.Optional; -import java.util.function.Predicate; -import java.util.stream.Stream; - -/** - * The Graph Traversal that maps {@link org.apache.tinkerpop.gremlin.structure.Edge}. - * This Traversal is lazy, in other words, that just run after any finalizing method. - */ -public interface EdgeTraversal extends EdgeConditionTraversal { - - /** - * Does a filter predicate based - * @param predicate a predicate to apply to each element to determine if it should be included - * @return a with the Vertex predicate - * @throws NullPointerException when predicate is null - */ - EdgeTraversal filter(Predicate predicate); - - /** - * Filter the objects in the traversal by the number of them to pass through the next, where only the first - * {@code n} objects are allowed as defined by the {@code limit} argument. - * - * @param limit the number at which to end the next - * @return a with the limit - */ - EdgeTraversal limit(long limit); - - /** - * Returns an EdgeTraversal with range defined - * - * @param start the start inclusive - * @param end the end exclusive - * @return a with the range set - */ - EdgeTraversal range(long start, long end); - - - /** - * Starts the loop traversal graph - * - * @return a {@link EdgeRepeatTraversal} - */ - EdgeRepeatTraversal repeat(); - - - /** - * Returns the next elements in the traversal. - * If the traversal is empty, then an {@link Optional#empty()} is returned. - * - * @return the EdgeEntity result otherwise {@link Optional#empty()} - */ - Optional next(); - - /** - * Concludes the traversal that returns a single {@link EdgeEntity} result - * @return the EdgeEntity result otherwise {@link Optional#empty()} - */ - Optional singleResult(); - - /** - * Concludes the traversal then returns the result as list. - * @return the entities result - */ - Stream result(); - - - /** - * Converts to vertex traversal taking the incoming Vertex - * - * @return {@link VertexTraversal} - */ - VertexTraversal inV(); - - /** - * Converts to vertex traversal taking the outgoing Vertex - * - * @return {@link VertexTraversal} - */ - VertexTraversal outV(); - - /** - * Converts to vertex traversal taking both incoming and outgoing Vertex - * - * @return {@link VertexTraversal} - */ - VertexTraversal bothV(); - - /** - * Remove all duplicates in the traversal stream up to this point. - * - * @param labels if labels are provided, then the scoped object's labels determine de-duplication. No labels implies current object. - * @return the traversal with an appended {@link DedupGlobalStep}. - */ - EdgeTraversal dedup(final String... labels); - - /** - * Get all the result in the traversal as Stream - * - * @return the entity result as {@link Stream} - */ - Stream stream(); - - /** - * Get the next n elements result as next, the number of elements is limit based - * - * @param limit the limit to result - * @return the entity result as {@link Stream} - */ - Stream next(int limit); - - /** - * Map the {@link org.apache.tinkerpop.gremlin.structure.Element} to a {@link java.util.Map} of the properties key'd according - * to their {@link org.apache.tinkerpop.gremlin.structure.Property#key}. - * If no property keys are provided, then all properties are retrieved. - * - * @param propertyKeys the properties to retrieve - * @return a {@link ValueMapTraversal} instance - */ - ValueMapTraversal valueMap(final String... propertyKeys); - - /** - * Defines the order of the Edge, the property must have in all elements. - * Otherwise, it'll return an exception. As recommendation use - * {@link EdgeTraversal#has(String)} before this method - * - * @param property the property to be order - * @return the {@link EdgeTraversalOrder} to define the order way - * @throws NullPointerException when the property is null - * @throws IllegalStateException when there any Edge that does not have the property - */ - EdgeTraversalOrder orderBy(String property); - - /** - * Map the traversal next to its reduction as a sum of the elements - * - * @return the sum - */ - long count(); -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversalOrder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversalOrder.java deleted file mode 100644 index e2ff71627..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeTraversalOrder.java +++ /dev/null @@ -1,33 +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; - -/** - * The traversal order to Edge - */ -public interface EdgeTraversalOrder { - - /** - * Defines the ascending order - * @return the {@link EdgeTraversal} ordered ascending - */ - EdgeTraversal asc(); - - /** - * Defines the descending order - * @return the {@link EdgeTraversal} ordered descending - */ - EdgeTraversal desc(); -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeUntilTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeUntilTraversal.java deleted file mode 100644 index 4cdb90005..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeUntilTraversal.java +++ /dev/null @@ -1,23 +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; - - -/** - * The Edge until wrapper of - * {@link org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#until(java.util.function.Predicate)} - */ -public interface EdgeUntilTraversal extends EdgeConditionTraversal { -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphEntityConverter.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphEntityConverter.java deleted file mode 100644 index 64870f375..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphEntityConverter.java +++ /dev/null @@ -1,30 +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; - -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.databases.tinkerpop.communication.CommunicationEntityConverter; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -enum GraphEntityConverter { - - INSTANCE; - - public T toEntity(EntityConverter converter, Vertex vertex) { - var entity = CommunicationEntityConverter.INSTANCE.apply(vertex); - return converter.toEntity(entity); - } - -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplate.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplate.java deleted file mode 100644 index 01880d341..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplate.java +++ /dev/null @@ -1,250 +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; - -import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Transaction; -import org.eclipse.jnosql.mapping.PreparedStatement; -import org.eclipse.jnosql.mapping.semistructured.SemiStructuredTemplate; - -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.function.Supplier; -import java.util.stream.Stream; - - -/** - * GraphTemplate is a helper class that increases productivity when performing common Graph operations. - * Includes integrated object mapping between documents and POJOs {@link org.apache.tinkerpop.gremlin.structure.Vertex} - * and {@link org.apache.tinkerpop.gremlin.structure.Edge}. - * It represents the common operation between an entity and {@link org.apache.tinkerpop.gremlin.structure.Graph} - * - * @see org.apache.tinkerpop.gremlin.structure.Graph - */ -public interface GraphTemplate extends SemiStructuredTemplate { - - /** - * Deletes a {@link org.apache.tinkerpop.gremlin.structure.Vertex} - * - * @param id the id to be used in the query {@link org.apache.tinkerpop.gremlin.structure.T#id} - * @param the id type - * @throws NullPointerException when id is null - */ - void delete(T id); - - /** - * Deletes a {@link org.apache.tinkerpop.gremlin.structure.Edge} - * - * @param id the id to be used in the query {@link org.apache.tinkerpop.gremlin.structure.T#id} - * @param the id type - * @throws NullPointerException when either label and id are null - */ - void deleteEdge(T id); - - /** - * Deletes {@link org.apache.tinkerpop.gremlin.structure.Edge} instances - * - * @param ids the ids to be used in the query {@link org.apache.tinkerpop.gremlin.structure.T#id} - * @param the id type - * @throws NullPointerException when either label and id are null - */ - void deleteEdge(Iterable ids); - - /** - * Either find or create an Edge between this two entities. - * {@link org.apache.tinkerpop.gremlin.structure.Edge} - *
entityOUT ---label---> entityIN.
- * - * @param incoming the incoming entity - * @param label the Edge label - * @param outgoing the outgoing entity - * @param the incoming type - * @param the outgoing type - * @return the {@link EdgeEntity} of these two entities - * @throws NullPointerException Either when any elements are null or the entity is null - */ - EdgeEntity edge(O outgoing, String label, I incoming); - - /** - * Either find or create an Edge between this two entities. - * {@link org.apache.tinkerpop.gremlin.structure.Edge} - *
entityOUT ---label---> entityIN.
- * - * @param incoming the incoming entity - * @param label the Edge label - * @param outgoing the outgoing entity - * @param the incoming type - * @param the outgoing type - * @return the {@link EdgeEntity} of these two entities - * @throws NullPointerException Either when any elements are null or the entity is null - */ - default EdgeEntity edge(O outgoing, Supplier label, I incoming) { - Objects.requireNonNull(label, "supplier is required"); - return edge(outgoing, label.get(), incoming); - } - - /** - * Find an entity given {@link org.apache.tinkerpop.gremlin.structure.T#label} and - * {@link org.apache.tinkerpop.gremlin.structure.T#id} - * - * @param id the id to be used in the query {@link org.apache.tinkerpop.gremlin.structure.T#id} - * @param the entity type - * @param the id type - * @return the entity found otherwise {@link Optional#empty()} - * @throws NullPointerException when id is null - */ - Optional find(K id); - - /** - * returns the edges of from a vertex id - * - * @param id the id - * @param direction the direction - * @param labels the edge labels - * @param the K type - * @return the Edges - * @throws NullPointerException where there is any parameter null - */ - Collection edgesById(K id, Direction direction, String... labels); - - /** - * returns the edges of from a vertex id - * - * @param id the id - * @param direction the direction - * @param labels the edge labels - * @param the K type - * @return the Edges - * @throws NullPointerException where there is any parameter null - */ - @SuppressWarnings("unchecked") - Collection edgesById(K id, Direction direction, Supplier... labels); - - /** - * returns the edges of from a vertex id - * - * @param id the id - * @param direction the direction - * @param the K type - * @return the Edges - * @throws NullPointerException where there is any parameter null - */ - Collection edgesById(K id, Direction direction); - - /** - * returns the edges of from an entity - * - * @param entity the entity - * @param direction the direction - * @param labels the edge labels - * @param the entity type - * @return the Edges - * @throws NullPointerException where there is any parameter null - */ - Collection edges(T entity, Direction direction, String... labels); - - /** - * returns the edges of from an entity - * - * @param entity the entity - * @param direction the direction - * @param labels the edge labels - * @param the entity type - * @return the Edges - * @throws NullPointerException where there is any parameter null - */ - @SuppressWarnings("unchecked") - Collection edges(T entity, Direction direction, Supplier... labels); - - /** - * returns the edges of from an entity - * - * @param entity the entity - * @param direction the direction - * @param the entity type - * @return the Edges - * @throws NullPointerException where there is any parameter null - */ - Collection edges(T entity, Direction direction); - - /** - * Finds an {@link EdgeEntity} from the Edge Id - * - * @param edgeId the edge id - * @param the edge id type - * @return the {@link EdgeEntity} otherwise {@link Optional#empty()} - * @throws IllegalStateException when edgeId is null - */ - Optional edge(E edgeId); - - - /** - * Gets a {@link VertexTraversal} to run a query in the graph - * - * @param vertexIds get ids - * @return a {@link VertexTraversal} instance - * @throws IllegalStateException if any id element is null - */ - VertexTraversal traversalVertex(Object... vertexIds); - - - /** - * Gets a {@link EdgeTraversal} to run a query in the graph - * - * @param edgeIds get ids - * @return a {@link VertexTraversal} instance - * @throws IllegalStateException if any id element is null - */ - EdgeTraversal traversalEdge(Object... edgeIds); - - /** - * Gets the current transaction - * - * @return the current {@link Transaction} - */ - Transaction transaction(); - - /** - * Executes a Gremlin then bring the result as a {@link Stream} - * - * @param gremlin the query gremlin - * @param the entity type - * @return the result as {@link Stream} - * @throws NullPointerException when the gremlin is null - */ - Stream gremlin(String gremlin); - - /** - * Executes a Gremlin query then bring the result as a unique result - * - * @param gremlin the gremlin query - * @param the entity type - * @return the result as {@link List} - * @throws NullPointerException when the query is null - */ - Optional gremlinSingleResult(String gremlin); - - /** - * Creates a {@link PreparedStatement} from the query - * - * @param gremlin the gremlin query - * @return a {@link PreparedStatement} instance - * @throws NullPointerException when the query is null - */ - PreparedStatement gremlinPrepare(String gremlin); - -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducer.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducer.java deleted file mode 100644 index 37a4ee1c5..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducer.java +++ /dev/null @@ -1,125 +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; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.Vetoed; -import jakarta.inject.Inject; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.eclipse.jnosql.communication.semistructured.DatabaseManager; -import org.eclipse.jnosql.databases.tinkerpop.communication.GraphDatabaseManager; -import org.eclipse.jnosql.mapping.core.Converters; -import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; -import org.eclipse.jnosql.mapping.semistructured.EventPersistManager; - -import java.util.Objects; -import java.util.function.Function; - -/** - * An {@code ApplicationScoped} producer class responsible for creating instances of {@link GraphTemplate}. - * It implements the {@link Function} interface with {@link DatabaseManager} as input and {@link GraphTemplate} as output. - */ -@ApplicationScoped -public class GraphTemplateProducer implements Function { - - @Inject - private EntityConverter converter; - - @Inject - private EventPersistManager eventManager; - - @Inject - private EntitiesMetadata entities; - - @Inject - private Converters converters; - - - @Override - public GraphTemplate apply(Graph graph) { - Objects.requireNonNull(graph, "graph is required"); - return new ProducerGraphTemplate(converter, graph, - eventManager, entities, converters); - } - - @Vetoed - static class ProducerGraphTemplate extends AbstractGraphTemplate { - - private final EntityConverter converter; - - - private final EventPersistManager eventManager; - - private final EntitiesMetadata entities; - - private final Converters converters; - - private final Graph graph; - - private final GraphDatabaseManager manager; - - public ProducerGraphTemplate(EntityConverter converter, Graph graph, - EventPersistManager eventManager, - EntitiesMetadata entities, Converters converters) { - this.converter = converter; - this.graph = graph; - this.manager = GraphDatabaseManager.of(graph); - this.eventManager = eventManager; - this.entities = entities; - this.converters = converters; - } - - ProducerGraphTemplate() { - this(null, null, null, null, null); - } - - @Override - protected EntityConverter converter() { - return converter; - } - - @Override - protected GraphDatabaseManager manager() { - return manager; - } - - @Override - protected EventPersistManager eventManager() { - return eventManager; - } - - @Override - protected EntitiesMetadata entities() { - return entities; - } - - @Override - protected Converters converters() { - return converters; - } - - @Override - protected GraphTraversalSource traversal() { - return graph.traversal(); - } - - @Override - protected Graph graph() { - return graph; - } - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinExecutor.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinExecutor.java deleted file mode 100644 index ce1eb449e..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinExecutor.java +++ /dev/null @@ -1,85 +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; - -import org.apache.tinkerpop.gremlin.jsr223.GremlinLangScriptEngine; -import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.databases.tinkerpop.communication.CommunicationEntityConverter; -import org.eclipse.jnosql.mapping.semistructured.EntityConverter; - -import javax.script.Bindings; -import javax.script.ScriptException; -import java.util.Collections; -import java.util.Map; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -final class GremlinExecutor { - private final EntityConverter converter; - - private static final GremlinScriptEngine ENGINE = new GremlinLangScriptEngine(); - - GremlinExecutor(EntityConverter converter) { - this.converter = converter; - } - - Stream executeGremlin(GraphTraversalSource traversalSource, String gremlin) { - return executeGremlin(traversalSource, gremlin, Collections.emptyMap()); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - Stream executeGremlin(GraphTraversalSource traversalSource, String gremlin, Map params) { - try { - Bindings bindings = ENGINE.createBindings(); - bindings.put("g", traversalSource); - - - String query = GremlinParamParser.INSTANCE.apply(gremlin, params); - Object eval = ENGINE.eval(query, bindings); - if (eval instanceof GraphTraversal graphTraversal) { - return convertToStream(graphTraversal.toStream()); - } - if (eval instanceof Iterable iterable) { - return convertToStream(StreamSupport.stream(iterable.spliterator(), false)); - } - if (eval instanceof Stream stream) { - return convertToStream(stream); - } - return Stream.of((T) eval); - } catch (ScriptException e) { - throw new GremlinQueryException("There is an error when executed the gremlin query: " + gremlin, e); - } - } - - @SuppressWarnings("unchecked") - private Stream convertToStream(Stream stream) { - return stream.map(this::getElement).map(e -> (T) e); - } - - private Object getElement(Object entity) { - if (entity instanceof Vertex vertex) { - return converter.toEntity(CommunicationEntityConverter.INSTANCE.apply(vertex)); - } - - if (entity instanceof Edge edge) { - return EdgeEntity.of(converter, edge); - } - return entity; - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinParamParser.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinParamParser.java deleted file mode 100644 index a7f5641a7..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinParamParser.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2023 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.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.BiFunction; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * This singleton has the goal to interpolate params inside the Gremlin query. - * Thus, given the query: - * "g.V().hasLabel(@param)" where the params is {"param":"Otavio"} - * It should return the query to: g.V().hasLabel("Otavio") - * It should check the Gremlin query options: - * Gremlin.g4 - *

- * Thus, given a current query with params it should convert to Gremlin compatible syntax. - */ -enum GremlinParamParser implements BiFunction, String> { - INSTANCE; - - private final Pattern pattern = Pattern.compile("@\\w+"); - - @Override - public String apply(String query, Map params) { - Objects.requireNonNull(query, "query is required"); - Objects.requireNonNull(query, "params is required"); - Matcher matcher = pattern.matcher(query); - List leftParams = new ArrayList<>(params.keySet()); - StringBuilder gremlin = new StringBuilder(); - while (matcher.find()) { - String param = matcher.group().substring(1); - leftParams.remove(param); - Object value = params.get(param); - if (value == null) { - throw new GremlinQueryException("The param is " + param + " is required on the query " + query); - } - matcher.appendReplacement(gremlin, toString(value)); - } - matcher.appendTail(gremlin); - if (leftParams.isEmpty()) { - return gremlin.toString(); - } - - throw new GremlinQueryException("There are params missing on the parser: " + leftParams + " on the query" + query); - } - - private String toString(Object value) { - if (value instanceof Number) { - return value.toString(); - } - return '\'' + value.toString() + '\''; - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinQueryException.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinQueryException.java deleted file mode 100644 index 05ac272fa..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GremlinQueryException.java +++ /dev/null @@ -1,43 +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; - - -import jakarta.data.exceptions.MappingException; - -/** - * An exception that provides information when executing Gremlin in the database. - */ -public class GremlinQueryException extends MappingException { - - /** - * A new instance with both the cause of the error and a message - * - * @param message the message - * @param cause the cause - */ - public GremlinQueryException(String message, Throwable cause) { - super(message, cause); - } - - /** - * A new instance with the message - * - * @param message the message - */ - public GremlinQueryException(String message) { - super(message); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/Transactional.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/Transactional.java deleted file mode 100644 index 9f3e8f496..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/Transactional.java +++ /dev/null @@ -1,35 +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; - - -import jakarta.interceptor.InterceptorBinding; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * The Transactional annotation provides the application the ability to declaratively - * control transaction boundaries on CDI managed beans. {@link org.apache.tinkerpop.gremlin.structure.Transaction} - */ -@InterceptorBinding -@Target({METHOD, TYPE}) -@Retention(RUNTIME) -public @interface Transactional { -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TransactionalInterceptor.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TransactionalInterceptor.java deleted file mode 100644 index 8677f9af7..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/TransactionalInterceptor.java +++ /dev/null @@ -1,59 +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; - -import jakarta.annotation.Priority; -import jakarta.enterprise.inject.Alternative; -import jakarta.enterprise.inject.Instance; -import jakarta.inject.Inject; -import jakarta.interceptor.AroundInvoke; -import jakarta.interceptor.Interceptor; -import jakarta.interceptor.InvocationContext; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.Transaction; -import org.eclipse.jnosql.databases.tinkerpop.communication.GraphTransactionUtil; - - -@Transactional -@Interceptor -@Alternative -@Priority(Interceptor.Priority.APPLICATION) -class TransactionalInterceptor { - - @Inject - private Instance graph; - - @AroundInvoke - public Object manageTransaction(InvocationContext context) throws Exception { - Transaction transaction = graph.get().tx(); - GraphTransactionUtil.lock(transaction); - if (!transaction.isOpen()) { - transaction.open(); - } - try { - Object proceed = context.proceed(); - transaction.commit(); - return proceed; - } catch (Exception exception) { - transaction.rollback(); - throw exception; - }finally { - GraphTransactionUtil.unlock(); - } - - } - - -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/ValueMapTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/ValueMapTraversal.java deleted file mode 100644 index eff957311..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/ValueMapTraversal.java +++ /dev/null @@ -1,71 +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; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Stream; - -/** - * The Graph Traversal that maps {@link org.apache.tinkerpop.gremlin.structure.Element}. - * This Traversal is lazy, in other words, that just run after the - */ -public interface ValueMapTraversal { - - - /** - * Get all the result in the traversal as Stream - * - * @return the entity result as {@link Stream} - */ - Stream> stream(); - - /** - * Get the next n-number of results from the traversal. - * - * @param limit the limit to result - * @return the entity result as {@link Stream} - */ - Stream> next(int limit); - - /** - * Returns the next elements in the traversal. - * - * @return the map - */ - Map next(); - - /** - * Concludes the traversal that returns a single result - * - * @return the entity result otherwise {@link Optional#empty()} - */ - Optional> singleResult(); - - /** - * Concludes the traversal then returns the result as list. - * - * @return the maps result - */ - List> resultList(); - - /** - * Map the traversal next to its reduction as a sum of the elements - * - * @return the sum - */ - long count(); -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexConditionTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexConditionTraversal.java deleted file mode 100644 index b8ae71997..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexConditionTraversal.java +++ /dev/null @@ -1,236 +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; - - -import org.apache.tinkerpop.gremlin.process.traversal.P; -import org.apache.tinkerpop.gremlin.structure.T; - -import java.util.function.Supplier; - -import static java.util.Objects.requireNonNull; - -/** - * The base conditions to {@link VertexTraversal} and {@link VertexUntilTraversal} - */ -public interface VertexConditionTraversal { - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param value the value to the condition - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - VertexTraversal has(String propertyKey, Object value); - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when the propertyKey is null - */ - VertexTraversal has(String propertyKey); - - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param predicate the predicate condition - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when either key or predicate condition are null - */ - VertexTraversal has(String propertyKey, P predicate); - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param value the value to the condition - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - default VertexTraversal has(Supplier propertyKey, Object value) { - requireNonNull(propertyKey, "the supplier is required"); - return has(propertyKey.get(), value); - } - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param predicate the predicate condition - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when either key or predicate condition are null - */ - default VertexTraversal has(Supplier propertyKey, P predicate) { - requireNonNull(propertyKey, "the supplier is required"); - return has(propertyKey.get(), predicate); - } - - /** - * Adds a equals condition to a query - * - * @param accessor the key - * @param value the value to the condition - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - VertexTraversal has(T accessor, Object value); - - /** - * Adds a equals condition to a query - * - * @param accessor the key - * @param predicate the predicate condition - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - VertexTraversal has(T accessor, P predicate); - - - /** - * Defines Vertex has not a property - * - * @param propertyKey the property key - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when propertyKey is null - */ - VertexTraversal hasNot(String propertyKey); - - /** - * Defines Vertex has not a property - * - * @param propertyKey the property key - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when propertyKey is null - */ - default VertexTraversal hasNot(Supplier propertyKey) { - requireNonNull(propertyKey, "the supplier is required"); - return hasNot(propertyKey.get()); - } - - /** - * Map the {@link VertexTraversal} to its outgoing adjacent vertices given the edge labels. - * - * @param labels the edge labels to traverse - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - VertexTraversal out(String... labels); - - - /** - * Map the {@link VertexTraversal} to its adjacent vertices given the edge labels. - * - * @param labels the edge labels to traverse - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - VertexTraversal in(String... labels); - - - /** - * Map the {@link VertexTraversal} to its incoming adjacent vertices given the edge labels. - * - * @param labels the edge labels to traverse - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - VertexTraversal both(String... labels); - - /** - * Map the {@link VertexTraversal} to its outgoing adjacent vertices given the edge labels. - * - * @param label the edge labels to traverse - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - default VertexTraversal out(Supplier label) { - requireNonNull(label, "the supplier is required"); - return out(label.get()); - } - - /** - * Map the {@link VertexTraversal} to its adjacent vertices given the edge labels. - * - * @param label the edge labels to traverse - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - default VertexTraversal in(Supplier label) { - requireNonNull(label, "the supplier is required"); - return in(label.get()); - } - - - /** - * Map the {@link VertexTraversal} to its incoming adjacent vertices given the edge labels. - * - * @param label the edge labels to traverse - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - default VertexTraversal both(Supplier label) { - requireNonNull(label, "the supplier is required"); - return both(label.get()); - } - - /** - * Defines Vertex as label condition - * - * @param label the labels in the condition - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when label is null - */ - VertexTraversal hasLabel(String label); - - /** - * Defines Vertex as label condition - * - * @param the entity type - * @param type reads the {@link jakarta.nosql.Entity} annotation otherwise the {@link Class#getSimpleName()} - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when entityClazz is null - */ - VertexTraversal hasLabel(Class type); - - /** - * Defines Vertex as label condition - * - * @param the entity type - * @param predicate the predicate - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when predicate is null - */ - VertexTraversal hasLabel(P predicate); - - /** - * Defines Vertex as label condition - * - * @param label the labels in the condition - * @return a {@link VertexTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - default VertexTraversal hasLabel(Supplier label) { - requireNonNull(label, "the supplier is required"); - return hasLabel(label.get()); - } - - -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatStepTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatStepTraversal.java deleted file mode 100644 index 49b0cf5a3..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatStepTraversal.java +++ /dev/null @@ -1,35 +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; - -/** - * After the {@link VertexRepeatTraversal} condition the next step is - * {@link VertexRepeatStepTraversal#times(int)} and {@link VertexRepeatStepTraversal#until()} - */ -public interface VertexRepeatStepTraversal { - - /** - * Starts a loop traversal n times - * @param times the repeat times that is required - * @return a {@link VertexTraversal} instance - */ - VertexTraversal times(int times); - - /** - * Define the loop traversal until a defined condition - * @return {@link VertexUntilTraversal} - */ - VertexUntilTraversal until(); -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatTraversal.java deleted file mode 100644 index fae5edeb7..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexRepeatTraversal.java +++ /dev/null @@ -1,148 +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; - -import org.apache.tinkerpop.gremlin.process.traversal.P; -import org.apache.tinkerpop.gremlin.structure.T; - -import java.util.function.Supplier; - -import static java.util.Objects.requireNonNull; - -/** - * The wrapper step to - * {@link org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#repeat(org.apache.tinkerpop.gremlin.process.traversal.Traversal)} - * in the Vertex type. - */ -public interface VertexRepeatTraversal { - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param value the value to the condition - * @return a {@link VertexRepeatStepTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - VertexRepeatStepTraversal has(String propertyKey, Object value); - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param predicate the predicate condition - * @return a {@link VertexRepeatStepTraversal} with the new condition - * @throws NullPointerException when either key or predicate condition are null - */ - VertexRepeatStepTraversal has(String propertyKey, P predicate); - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param value the value to the condition - * @return a {@link VertexRepeatStepTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - default VertexRepeatStepTraversal has(Supplier propertyKey, Object value){ - requireNonNull(propertyKey, "the supplier is required"); - return has(propertyKey.get(), value); - } - - /** - * Adds a equals condition to a query - * - * @param propertyKey the key - * @param predicate the predicate condition - * @return a {@link VertexRepeatStepTraversal} with the new condition - * @throws NullPointerException when either key or predicate condition are null - */ - default VertexRepeatStepTraversal has(Supplier propertyKey, P predicate){ - requireNonNull(propertyKey, "the supplier is required"); - return has(propertyKey.get(), predicate); - } - - /** - * Adds a equals condition to a query - * - * @param accessor the key - * @param value the value to the condition - * @return a {@link VertexRepeatStepTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - VertexRepeatStepTraversal has(T accessor, Object value); - - /** - * Adds a equals condition to a query - * - * @param accessor the key - * @param predicate the predicate condition - * @return a {@link VertexRepeatStepTraversal} with the new condition - * @throws NullPointerException when either key or value are null - */ - VertexRepeatStepTraversal has(T accessor, P predicate); - - - /** - * Defines Vertex has not a property - * - * @param propertyKey the property key - * @return a {@link VertexRepeatStepTraversal} with the new condition - * @throws NullPointerException when propertyKey is null - */ - VertexRepeatStepTraversal hasNot(String propertyKey); - - /** - * Defines Vertex has not a property - * - * @param propertyKey the property key - * @return a {@link VertexRepeatStepTraversal} with the new condition - * @throws NullPointerException when propertyKey is null - */ - default VertexRepeatStepTraversal hasNot(Supplier propertyKey){ - requireNonNull(propertyKey, "the supplier is required"); - return hasNot(propertyKey.get()); - } - - /** - * Map the {@link VertexTraversal} to its outgoing adjacent vertices given the edge labels. - * - * @param labels the edge labels to traverse - * @return a {@link VertexRepeatStepTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - VertexRepeatStepTraversal out(String... labels); - - - /** - * Map the {@link VertexTraversal} to its adjacent vertices given the edge labels. - * - * @param labels the edge labels to traverse - * @return a {@link VertexRepeatStepTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - VertexRepeatStepTraversal in(String... labels); - - - /** - * Map the {@link VertexTraversal} to its incoming adjacent vertices given the edge labels. - * - * @param labels the edge labels to traverse - * @return a {@link VertexRepeatStepTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - VertexRepeatStepTraversal both(String... labels); - -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversal.java deleted file mode 100644 index c2156fc48..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversal.java +++ /dev/null @@ -1,217 +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; - -import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DedupGlobalStep; - -import java.util.Optional; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Stream; - -import static java.util.Objects.requireNonNull; - -/** - * The Graph Traversal that maps {@link org.apache.tinkerpop.gremlin.structure.Vertex}. - * This Traversal is lazy, in other words, that just run after any finalizing method. - */ -public interface VertexTraversal extends VertexConditionTraversal { - - - /** - * Does a filter predicate based - * - * @param predicate a predicate to apply to each element to determine if it should be included - * @param the type - * @return a {@link EdgeTraversal} with the Vertex predicate - * @throws NullPointerException when predicate is null - */ - VertexTraversal filter(Predicate predicate); - - /** - * Map the {@link EdgeTraversal} to its outgoing incident edges given the edge labels. - * - * @param edgeLabels the edge labels to traverse - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - EdgeTraversal outE(String... edgeLabels); - - - /** - * Map the {@link EdgeTraversal} to its incoming incident edges given the edge labels. - * - * @param edgeLabels the edge labels to traverse - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - EdgeTraversal inE(String... edgeLabels); - - /** - * Map the {@link EdgeTraversal} to its either incoming or outgoing incident edges given the edge labels. - * - * @param edgeLabels the edge labels to traverse - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - EdgeTraversal bothE(String... edgeLabels); - - /** - * Remove all duplicates in the traversal stream up to this point. - * - * @param labels if labels are provided, then the scoped object's labels determine de-duplication. No labels implies current object. - * @return the traversal with an appended {@link DedupGlobalStep}. - */ - VertexTraversal dedup(final String... labels); - - - /** - * Starts the loop traversal graph - * - * @return a {@link VertexRepeatTraversal} - */ - VertexRepeatTraversal repeat(); - - - /** - * Map the {@link EdgeTraversal} to its outgoing incident edges given the edge labels. - * - * @param label the edge labels to traverse - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - default EdgeTraversal outE(Supplier label) { - requireNonNull(label, "the supplier is required"); - return outE(label.get()); - } - - - /** - * Map the {@link EdgeTraversal} to its incoming incident edges given the edge labels. - * - * @param label the edge labels to traverse - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - default EdgeTraversal inE(Supplier label) { - requireNonNull(label, "the supplier is required"); - return inE(label.get()); - } - - - /** - * Map the {@link EdgeTraversal} to its either incoming or outgoing incident edges given the edge labels. - * - * @param label the edge labels to traverse - * @return a {@link EdgeTraversal} with the new condition - * @throws NullPointerException when has any null element - */ - default EdgeTraversal bothE(Supplier label) { - requireNonNull(label, "the supplier is required"); - return bothE(label.get()); - } - - - /** - * Filter the objects in the traversal by the number of them to pass through the next, where only the first - * {@code n} objects are allowed as defined by the {@code limit} argument. - * - * @param limit the number at which to end the next - * @return a VertexTraversal with the limit - */ - VertexTraversal limit(long limit); - - /** - * Filters out the first {@code n} objects in the traversal. - * - * @param skip the number of objects to skip - * @return a VertexTraversal with skip - */ - VertexTraversal skip(long skip); - - /** - * Returns a VertexTraversal with range defined - * - * @param start the start inclusive - * @param end the end exclusive - * @return a with the range set - */ - VertexTraversal range(long start, long end); - - /** - * Returns the next elements in the traversal. - * If the traversal is empty, then an {@link Optional#empty()} is returned. - * - * @param the entity type - * @return the entity result otherwise {@link Optional#empty()} - */ - Optional next(); - - /** - * Get all the result in the traversal as Stream - * - * @param the entity type - * @return the entity result as {@link Stream} - */ - Stream result(); - - /** - * Concludes the traversal that returns a single result - * - * @param the type - * @return the entity result otherwise {@link Optional#empty()} - */ - Optional singleResult(); - - - /** - * Get the next n-number of results from the traversal. - * - * @param the entity type - * @param limit the limit to result - * @return the entity result as {@link Stream} - */ - Stream next(int limit); - - /** - * Map the {@link org.apache.tinkerpop.gremlin.structure.Element} to a {@link java.util.Map} of the properties key'd according - * to their {@link org.apache.tinkerpop.gremlin.structure.Property#key}. - * If no property keys are provided, then all properties are retrieved. - * - * @param propertyKeys the properties to retrieve - * @return a {@link ValueMapTraversal} instance - */ - ValueMapTraversal valueMap(final String... propertyKeys); - - - /** - * Map the traversal next to its reduction as a sum of the elements - * - * @return the sum - */ - long count(); - - /** - * Defines the order of the Vertex, the property must have in all elements. - * Otherwise, it'll return an exception, as recommendation use - * {@link VertexTraversal#has(String)} before this method - * - * @param property the property to be order - * @return the {@link VertexTraversalOrder} to define the order way - * @throws NullPointerException when the property is null - * @throws IllegalStateException when there any Edge that does not have the property - */ - VertexTraversalOrder orderBy(String property); -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversalOrder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversalOrder.java deleted file mode 100644 index cf3aa0e0e..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexTraversalOrder.java +++ /dev/null @@ -1,33 +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; - -/** - * The traversal order to Vertex - */ -public interface VertexTraversalOrder { - - /** - * Defines the ascending order - * @return the {@link VertexTraversal} ordered ascending - */ - VertexTraversal asc(); - - /** - * Defines the descending order - * @return the {@link VertexTraversal} ordered descending - */ - VertexTraversal desc(); -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexUntilTraversal.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexUntilTraversal.java deleted file mode 100644 index 97e07f971..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/VertexUntilTraversal.java +++ /dev/null @@ -1,22 +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; - -/** - * The Vertex until wrapper of - * {@link org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal#until(java.util.function.Predicate)} - */ -public interface VertexUntilTraversal extends VertexConditionTraversal { -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplier.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplier.java deleted file mode 100644 index bfd9a1aa6..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplier.java +++ /dev/null @@ -1,54 +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.configuration; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.Disposes; -import jakarta.enterprise.inject.Produces; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.eclipse.jnosql.databases.tinkerpop.communication.GraphConfiguration; -import org.eclipse.jnosql.mapping.core.config.MicroProfileSettings; -import org.eclipse.jnosql.mapping.reflection.Reflections; - -import java.util.function.Supplier; -import java.util.logging.Logger; - -import static org.eclipse.jnosql.mapping.core.config.MappingConfigurations.GRAPH_PROVIDER; - - -@ApplicationScoped -class GraphSupplier implements Supplier { - - public static final Logger LOGGER = Logger.getLogger(GraphSupplier.class.getName()); - - @SuppressWarnings("unchecked") - @Override - @Produces - @ApplicationScoped - public Graph get(){ - var settings = MicroProfileSettings.INSTANCE; - LOGGER.fine("Loading the Graph configuration"); - var configuration = settings.get(GRAPH_PROVIDER, Class.class) - .filter(GraphConfiguration.class::isAssignableFrom) - .map(c -> (GraphConfiguration) Reflections.newInstance(c)).orElseGet(GraphConfiguration::getConfiguration); - LOGGER.fine("The Graph configuration loaded successfully with: " + configuration.getClass()); - return configuration.apply(settings); - } - - public void close(@Disposes Graph graph) throws Exception { - LOGGER.fine("Closing the Graph"); - graph.close(); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/RepositoryGraphBean.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/RepositoryGraphBean.java deleted file mode 100644 index c7076db82..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/query/RepositoryGraphBean.java +++ /dev/null @@ -1,115 +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.query; - -import jakarta.data.repository.DataRepository; -import jakarta.enterprise.context.spi.CreationalContext; -import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphTemplate; -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; - - -/** - * This class serves as a JNoSQL discovery bean for CDI extension, responsible for registering 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 RepositoryGraphBean> 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 RepositoryGraphBean(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; - } - - @Override - @SuppressWarnings("unchecked") - public T create(CreationalContext context) { - EntitiesMetadata entities = getInstance(EntitiesMetadata.class); - var template = provider.isEmpty() ? getInstance(GraphTemplate.class) : - getInstance(GraphTemplate.class, DatabaseQualifier.ofGraph(provider)); - - Converters converters = getInstance(Converters.class); - - var handler = new SemiStructuredRepositoryProxy<>(template, - entities, type, converters); - 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-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/CustomRepositoryGraphBean.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/CustomRepositoryGraphBean.java deleted file mode 100644 index e32676b33..000000000 --- a/jnosql-neo4j/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.GraphTemplate; -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(GraphTemplate.class) : - getInstance(GraphTemplate.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-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtension.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtension.java deleted file mode 100644 index bd54b6267..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtension.java +++ /dev/null @@ -1,83 +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.event.Observes; -import jakarta.enterprise.inject.spi.AfterBeanDiscovery; -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.mapping.DatabaseMetadata; -import org.eclipse.jnosql.mapping.Databases; -import org.eclipse.jnosql.mapping.metadata.ClassScanner; - -import java.util.HashSet; -import java.util.Set; -import java.util.logging.Logger; - -import static org.eclipse.jnosql.mapping.DatabaseType.GRAPH; - -/** - * Extension to start up the GraphTemplate, Repository - * from the {@link org.eclipse.jnosql.mapping.Database} qualifier - */ -public class GraphExtension implements Extension { - - private static final Logger LOGGER = Logger.getLogger(GraphExtension.class.getName()); - - private final Set databases = new HashSet<>(); - - void observes(@Observes final ProcessProducer pp) { - Databases.addDatabase(pp, GRAPH, databases); - } - - void onAfterBeanDiscovery(@Observes final AfterBeanDiscovery afterBeanDiscovery) { - - ClassScanner scanner = ClassScanner.load(); - Set> crudTypes = scanner.repositoriesStandard(); - - - 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 -> { - if (!type.getProvider().isBlank()) { - final TemplateBean bean = new TemplateBean(type.getProvider()); - afterBeanDiscovery.addBean(bean); - } - }); - - - 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-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/TemplateBean.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/TemplateBean.java deleted file mode 100644 index 7e387c6b5..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/TemplateBean.java +++ /dev/null @@ -1,83 +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 jakarta.nosql.Template; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphTemplate; -import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphTemplateProducer; -import org.eclipse.jnosql.mapping.DatabaseQualifier; -import org.eclipse.jnosql.mapping.DatabaseType; -import org.eclipse.jnosql.mapping.core.spi.AbstractBean; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.Collections; -import java.util.Set; - -class TemplateBean extends AbstractBean { - - private static final Set TYPES = Set.of(GraphTemplate.class, Template.class); - - private final String provider; - - private final Set qualifiers; - - /** - * Constructor - * - * @param provider the provider name, that must be a - */ - public TemplateBean(String provider) { - this.provider = provider; - this.qualifiers = Collections.singleton(DatabaseQualifier.ofGraph(provider)); - } - - @Override - public Class getBeanClass() { - return GraphTemplate.class; - } - - - @Override - public GraphTemplate create(CreationalContext context) { - - GraphTemplateProducer producer = getInstance(GraphTemplateProducer.class); - Graph graph = getGraph(); - return producer.apply(graph); - } - - private Graph getGraph() { - return getInstance(Graph.class, DatabaseQualifier.ofGraph(provider)); - } - - @Override - public Set getTypes() { - return TYPES; - } - - @Override - public Set getQualifiers() { - return qualifiers; - } - - - @Override - public String getId() { - return GraphTemplate.class.getName() + DatabaseType.GRAPH + "-" + provider; - } - -} From 491c75eb5658478a4a0f5cb895d287c4412e0fe4 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 18:25:17 +0000 Subject: [PATCH 006/148] chore: remove sample code structure Signed-off-by: Otavio Santana --- .../jakarta.enterprise.inject.spi.Extension | 15 - .../mapping/AbstractGraphTemplateTest.java | 546 ------------------ .../mapping/AbstractTraversalTest.java | 96 --- .../mapping/DefaultEdgeTraversalTest.java | 540 ----------------- .../DefaultGraphTemplateProducerTest.java | 56 -- ...faultGraphTraversalSourceTemplateTest.java | 49 -- .../mapping/DefaultValueMapTraversalTest.java | 107 ---- .../mapping/DefaultVertexTraversalTest.java | 519 ----------------- .../tinkerpop/mapping/EdgeEntityTest.java | 360 ------------ .../tinkerpop/mapping/GraphProducer.java | 103 ---- .../mapping/GraphTemplateProducerTest.java | 56 -- .../tinkerpop/mapping/GraphTemplateTest.java | 57 -- .../tinkerpop/mapping/MagazineRepository.java | 24 - .../mapping/MagazineTemplateTest.java | 97 ---- .../configuration/GraphConfigurationMock.java | 83 --- .../GraphConfigurationMock2.java | 83 --- .../configuration/GraphSupplierTest.java | 82 --- .../tinkerpop/mapping/entities/Creature.java | 70 --- .../tinkerpop/mapping/entities/Human.java | 117 ---- .../mapping/entities/HumanBuilder.java | 60 -- .../mapping/entities/HumanRepository.java | 23 - .../tinkerpop/mapping/entities/Magazine.java | 113 ---- .../mapping/entities/MagazineTemplate.java | 43 -- .../tinkerpop/mapping/entities/People.java | 26 - .../mapping/entities/WrongEntity.java | 56 -- .../mapping/spi/GraphCustomExtensionTest.java | 77 --- .../mapping/spi/GraphExtensionTest.java | 84 --- 27 files changed, 3542 deletions(-) delete mode 100644 jnosql-neo4j/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplateTest.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTraversalTest.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalTest.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplateProducerTest.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTraversalSourceTemplateTest.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversalTest.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalTest.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntityTest.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphProducer.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducerTest.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateTest.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineRepository.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineTemplateTest.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock2.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplierTest.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Creature.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Human.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanBuilder.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanRepository.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Magazine.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/MagazineTemplate.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/People.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/WrongEntity.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphCustomExtensionTest.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtensionTest.java diff --git a/jnosql-neo4j/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension b/jnosql-neo4j/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension deleted file mode 100644 index 7464d6835..000000000 --- a/jnosql-neo4j/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension +++ /dev/null @@ -1,15 +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 -# -org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplateTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplateTest.java deleted file mode 100644 index c8cebef6d..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractGraphTemplateTest.java +++ /dev/null @@ -1,546 +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; - -import jakarta.data.exceptions.EmptyResultException; -import jakarta.data.exceptions.NonUniqueResultException; -import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.Transaction; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.assertj.core.api.SoftAssertions; -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.mapping.PreparedStatement; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.StreamSupport; - -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.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public abstract class AbstractGraphTemplateTest { - - protected abstract Graph getGraph(); - - protected abstract GraphTemplate getGraphTemplate(); - - @AfterEach - void after() { - getGraph().traversal().V().toList().forEach(Vertex::remove); - getGraph().traversal().E().toList().forEach(Edge::remove); - } - - @Test - void shouldReturnErrorWhenEntityIsNull() { - assertThrows(NullPointerException.class, () -> getGraphTemplate().insert(null)); - } - - @Test - void shouldInsertAnEntity() { - Human human = Human.builder().withAge() - .withName("Otavio").build(); - Human updated = getGraphTemplate().insert(human); - - getGraphTemplate().delete(updated.getId()); - } - - @Test - void shouldReturnErrorWhenInsertWithTTL() { - Human human = Human.builder().withAge() - .withName("Otavio").build(); - Assertions.assertThrows(UnsupportedOperationException.class, - () -> getGraphTemplate().insert(human, Duration.ZERO)); - } - - @Test - void shouldReturnErrorWhenInsertIterableWithTTL() { - Human human = Human.builder().withAge() - .withName("Otavio").build(); - Assertions.assertThrows(UnsupportedOperationException.class, - () -> getGraphTemplate().insert(Collections.singleton(human), Duration.ZERO)); - } - - @Test - void shouldInsertEntities() { - Human otavio = Human.builder().withAge() - .withName("Otavio").build(); - - Human poliana = Human.builder().withAge() - .withName("Poliana").build(); - - final Iterable people = getGraphTemplate() - .insert(Arrays.asList(otavio, poliana)); - - final boolean allHasId = StreamSupport.stream(people.spliterator(), false) - .map(Human::getId) - .allMatch(Objects::nonNull); - assertTrue(allHasId); - } - - @Test - void shouldMergeOnInsert() { - Human human = Human.builder().withAge() - .withName("Otavio").build(); - Human updated = getGraphTemplate().insert(human); - assertSame(human, updated); - } - - @Test - void shouldGetErrorWhenIdIsNullWhenUpdate() { - assertThrows(EmptyResultException.class, () -> { - Human human = Human.builder().withAge() - .withName("Otavio").build(); - getGraphTemplate().update(human); - }); - } - - @Test - void shouldGetErrorWhenEntityIsNotSavedYet() { - assertThrows(EmptyResultException.class, () -> { - Human human = Human.builder().withAge() - .withId(10L) - .withName("Otavio").build(); - - getGraphTemplate().update(human); - }); - } - - @Test - void shouldUpdate() { - Human human = Human.builder().withAge() - .withName("Otavio").build(); - Human updated = getGraphTemplate().insert(human); - Human newHuman = Human.builder() - .withAge() - .withId(updated.getId()) - .withName("Otavio Updated").build(); - - Human update = getGraphTemplate().update(newHuman); - - assertEquals(newHuman, update); - - getGraphTemplate().delete(update.getId()); - } - - @Test - void shouldUpdateEntities() { - Human otavio = Human.builder().withAge() - .withName("Otavio").build(); - - Human poliana = Human.builder().withAge() - .withName("Poliana").build(); - - final Iterable insertPeople = getGraphTemplate().insert(Arrays.asList(otavio, poliana)); - - final List newPeople = StreamSupport.stream(insertPeople.spliterator(), false) - .map(p -> Human.builder().withAge().withId(p.getId()).withName(p.getName() + " updated").build()) - .collect(toList()); - - final Iterable update = getGraphTemplate().update(newPeople); - - final boolean allUpdated = StreamSupport.stream(update.spliterator(), false) - .map(Human::getName).allMatch(name -> name.contains(" updated")); - - assertTrue(allUpdated); - } - - - @Test - void shouldMergeOnUpdate() { - Human human = Human.builder().withAge() - .withName("Otavio").build(); - Human updated = getGraphTemplate().insert(human); - Human newHuman = Human.builder() - .withAge() - .withId(updated.getId()) - .withName("Otavio Updated").build(); - - Human update = getGraphTemplate().update(newHuman); - - assertSame(update, newHuman); - } - - @Test - void shouldReturnErrorInFindWhenIdIsNull() { - assertThrows(NullPointerException.class, () -> getGraphTemplate().find(null)); - } - - @Test - void shouldFindAnEntity() { - Human human = Human.builder().withAge() - .withName("Otavio").build(); - Human updated = getGraphTemplate().insert(human); - Optional personFound = getGraphTemplate().find(updated.getId()); - - assertTrue(personFound.isPresent()); - assertEquals(updated, personFound.get()); - - getGraphTemplate().delete(updated.getId()); - } - - @Test - void shouldNotFindAnEntity() { - Optional personFound = getGraphTemplate().find(0L); - assertFalse(personFound.isPresent()); - } - - @Test - void shouldDeleteById() { - - Human human = getGraphTemplate().insert(Human.builder().withAge() - .withName("Otavio").build()); - - assertTrue(getGraphTemplate().find(human.getId()).isPresent()); - getGraphTemplate().delete(human.getId()); - assertFalse(getGraphTemplate().find(human.getId()).isPresent()); - } - - - @Test - void shouldDeleteAnEntityFromTemplate() { - - Human human = getGraphTemplate().insert(Human.builder().withAge() - .withName("Otavio").build()); - - assertTrue(getGraphTemplate().find(human.getId()).isPresent()); - getGraphTemplate().delete(Human.class, human.getId()); - assertFalse(getGraphTemplate().find(human.getId()).isPresent()); - } - - @Test - void shouldNotDeleteAnEntityFromTemplate() { - - Human human = getGraphTemplate().insert(Human.builder().withAge() - .withName("Otavio").build()); - - assertTrue(getGraphTemplate().find(human.getId()).isPresent()); - getGraphTemplate().delete(Magazine.class, human.getId()); - assertTrue(getGraphTemplate().find(human.getId()).isPresent()); - getGraphTemplate().delete(Human.class, human.getId()); - assertFalse(getGraphTemplate().find(human.getId()).isPresent()); - } - - - @Test - void shouldDeleteEntitiesById() { - - Human otavio = getGraphTemplate().insert(Human.builder().withAge() - .withName("Otavio").build()); - - Human poliana = getGraphTemplate().insert(Human.builder().withAge() - .withName("Poliana").build()); - - assertTrue(getGraphTemplate().find(otavio.getId()).isPresent()); - getGraphTemplate().delete(Arrays.asList(otavio.getId(), poliana.getId())); - assertFalse(getGraphTemplate().find(otavio.getId()).isPresent()); - assertFalse(getGraphTemplate().find(poliana.getId()).isPresent()); - } - - @Test - void shouldReturnErrorWhenGetEdgesIdHasNullId() { - assertThrows(NullPointerException.class, () -> getGraphTemplate().edgesById(null, Direction.BOTH)); - } - - @Test - void shouldReturnErrorWhenGetEdgesIdHasNullDirection() { - assertThrows(NullPointerException.class, () -> getGraphTemplate().edgesById(10, null)); - } - - @Test - void shouldReturnEmptyWhenVertexDoesNotExist() { - Collection edges = getGraphTemplate().edgesById(10, Direction.BOTH); - assertTrue(edges.isEmpty()); - } - - @Test - void shouldReturnEdgesById() { - Human otavio = getGraphTemplate().insert(Human.builder().withAge() - .withName("Otavio").build()); - - Creature dog = getGraphTemplate().insert(new Creature("dog")); - Magazine cleanCode = getGraphTemplate().insert(Magazine.builder().withName("Clean code").build()); - - EdgeEntity likes = getGraphTemplate().edge(otavio, "likes", dog); - EdgeEntity reads = getGraphTemplate().edge(otavio, "reads", cleanCode); - - Collection edgesById = getGraphTemplate().edgesById(otavio.getId(), Direction.BOTH); - Collection edgesById1 = getGraphTemplate().edgesById(otavio.getId(), Direction.BOTH, "reads"); - Collection edgesById2 = getGraphTemplate().edgesById(otavio.getId(), Direction.BOTH, () -> "likes"); - Collection edgesById3 = getGraphTemplate().edgesById(otavio.getId(), Direction.OUT); - Collection edgesById4 = getGraphTemplate().edgesById(cleanCode.getId(), Direction.IN); - - assertEquals(edgesById, edgesById3); - assertThat(edgesById).contains(likes, reads); - assertThat(edgesById1).contains(reads); - assertThat(edgesById2).contains(likes); - assertThat(edgesById4).contains(reads); - - } - - @Test - void shouldDeleteEdge() { - Human otavio = getGraphTemplate().insert(Human.builder().withAge() - .withName("Otavio").build()); - Creature dog = getGraphTemplate().insert(new Creature("Ada")); - - EdgeEntity likes = getGraphTemplate().edge(otavio, "likes", dog); - - final Optional edge = getGraphTemplate().edge(likes.id()); - Assertions.assertTrue(edge.isPresent()); - - getGraphTemplate().deleteEdge(likes.id()); - assertFalse(getGraphTemplate().edge(likes.id()).isPresent()); - } - - @Test - void shouldDeleteEdges() { - Human otavio = getGraphTemplate().insert(Human.builder().withAge() - .withName("Otavio").build()); - Creature dog = getGraphTemplate().insert(new Creature("Ada")); - Magazine cleanCode = getGraphTemplate().insert(Magazine.builder().withName("Clean code").build()); - - EdgeEntity likes = getGraphTemplate().edge(otavio, "likes", dog); - EdgeEntity reads = getGraphTemplate().edge(otavio, "reads", cleanCode); - - final Optional edge = getGraphTemplate().edge(likes.id()); - Assertions.assertTrue(edge.isPresent()); - - getGraphTemplate().deleteEdge(Arrays.asList(likes.id(), reads.id())); - assertFalse(getGraphTemplate().edge(likes.id()).isPresent()); - assertFalse(getGraphTemplate().edge(reads.id()).isPresent()); - } - - @Test - void shouldReturnErrorWhenGetEdgesHasNullId() { - assertThrows(NullPointerException.class, () -> getGraphTemplate().edges(null, Direction.BOTH)); - } - - @Test - void shouldReturnErrorWhenGetEdgesHasNullId2() { - Human otavio = Human.builder().withAge().withName("Otavio").build(); - Collection edges = getGraphTemplate().edges(otavio, Direction.BOTH); - assertThat(edges).isEmpty(); - } - - @Test - void shouldReturnErrorWhenGetEdgesHasNullDirection() { - assertThrows(NullPointerException.class, () -> { - Human otavio = getGraphTemplate().insert(Human.builder().withAge() - .withName("Otavio").build()); - getGraphTemplate().edges(otavio, null); - }); - } - - @Test - void shouldReturnEmptyWhenEntityDoesNotExist() { - Human otavio = Human.builder().withAge().withName("Otavio").withId(10L).build(); - Collection edges = getGraphTemplate().edges(otavio, Direction.BOTH); - assertTrue(edges.isEmpty()); - } - - - @Test - void shouldReturnEdges() { - Human otavio = getGraphTemplate().insert(Human.builder().withAge() - .withName("Otavio").build()); - - Creature dog = getGraphTemplate().insert(new Creature("dog")); - Magazine cleanCode = getGraphTemplate().insert(Magazine.builder().withName("Clean code").build()); - - EdgeEntity likes = getGraphTemplate().edge(otavio, "likes", dog); - EdgeEntity reads = getGraphTemplate().edge(otavio, "reads", cleanCode); - - Collection edgesById = getGraphTemplate().edges(otavio, Direction.BOTH); - Collection edgesById1 = getGraphTemplate().edges(otavio, Direction.BOTH, "reads"); - Collection edgesById2 = getGraphTemplate().edges(otavio, Direction.BOTH, () -> "likes"); - Collection edgesById3 = getGraphTemplate().edges(otavio, Direction.OUT); - Collection edgesById4 = getGraphTemplate().edges(cleanCode, Direction.IN); - - SoftAssertions.assertSoftly(soft ->{ - soft.assertThat(edgesById).contains(likes, reads); - soft.assertThat(edgesById1).contains(reads); - soft.assertThat(edgesById2).contains(likes); - soft.assertThat(edgesById3).contains(likes, reads); - soft.assertThat(edgesById4).contains(reads); - }); - } - - @Test - void shouldGetTransaction() { - Transaction transaction = getGraphTemplate().transaction(); - assertNotNull(transaction); - } - - @Test - void shouldExecuteQuery() { - Human human = Human.builder().withAge() - .withName("Otavio").build(); - getGraphTemplate().insert(human); - List people = getGraphTemplate() - .gremlin("g.V().hasLabel('Human')") - .toList(); - assertThat(people.stream().map(Human::getName).collect(toList())).contains("Otavio"); - } - - @Test - void shouldReturnEmpty() { - Optional person = getGraphTemplate().gremlinSingleResult("g.V().hasLabel('Person')"); - assertFalse(person.isPresent()); - } - - @Test - void shouldReturnOneElement() { - Human otavio = Human.builder().withAge() - .withName("Otavio").build(); - getGraphTemplate().insert(otavio); - Optional person = getGraphTemplate().gremlinSingleResult("g.V().hasLabel('Human')"); - assertTrue(person.isPresent()); - } - - @Test - void shouldReturnErrorWhenHasNoneThanOneElement() { - - getGraphTemplate().insert(Human.builder().withAge().withName("Otavio").build()); - getGraphTemplate().insert(Human.builder().withAge().withName("Poliana").build()); - assertThrows(NonUniqueResultException.class, () -> getGraphTemplate().gremlinSingleResult("g.V().hasLabel('Human')")); - } - - @Test - void shouldExecutePrepareStatement() { - getGraphTemplate().insert(Human.builder().withAge().withName("Otavio").build()); - PreparedStatement prepare = getGraphTemplate().gremlinPrepare("g.V().hasLabel(@param)"); - prepare.bind("param", "Human"); - List people = prepare.result().toList(); - assertThat(people.stream().map(Human::getName).collect(toList())).contains("Otavio"); - } - - @Test - void shouldExecutePrepareStatementSingleton() { - getGraphTemplate().insert(Human.builder().withAge().withName("Otavio").build()); - PreparedStatement prepare = getGraphTemplate().gremlinPrepare("g.V().hasLabel(@param)"); - prepare.bind("param", "Human"); - Optional otavio = prepare.singleResult(); - assertTrue(otavio.isPresent()); - } - - @Test - void shouldExecutePrepareStatementSingletonEmpty() { - PreparedStatement prepare = getGraphTemplate().gremlinPrepare("g.V().hasLabel(@param)"); - prepare.bind("param", "Person"); - Optional otavio = prepare.singleResult(); - assertFalse(otavio.isPresent()); - } - - @Test - void shouldExecutePrepareStatementWithErrorWhenThereIsMoreThanOneResult() { - getGraphTemplate().insert(Human.builder().withAge().withName("Otavio").build()); - getGraphTemplate().insert(Human.builder().withAge().withName("Poliana").build()); - PreparedStatement prepare = getGraphTemplate().gremlinPrepare("g.V().hasLabel(@param)"); - prepare.bind("param", "Human"); - assertThrows(NonUniqueResultException.class, prepare::singleResult); - } - - @Test - void shouldCount() { - getGraphTemplate().insert(Human.builder().withAge().withName("Otavio").build()); - getGraphTemplate().insert(Human.builder().withAge().withName("Poliana").build()); - assertEquals(2L, getGraphTemplate().count("Human")); - } - - @Test - void shouldCountFromEntity() { - getGraphTemplate().insert(Human.builder().withAge().withName("Otavio").build()); - getGraphTemplate().insert(Human.builder().withAge().withName("Poliana").build()); - assertEquals(2L, getGraphTemplate().count(Human.class)); - } - - - @Test - void shouldFindById() { - final Human otavio = getGraphTemplate().insert(Human.builder().withAge() - .withName("Otavio").build()); - - final Optional person = getGraphTemplate().find(Human.class, otavio.getId()); - assertNotNull(person); - assertTrue(person.isPresent()); - assertEquals(otavio.getName(), person.map(Human::getName).get()); - } - - @Test - void shouldFindAll() { - final Human otavio = getGraphTemplate().insert(Human.builder().withAge() - .withName("Otavio").build()); - List people = getGraphTemplate().findAll(Human.class).toList(); - - assertThat(people).hasSize(1) - .map(Human::getName) - .contains("Otavio"); - } - - @Test - void shouldDeleteAll() { - final Human otavio = getGraphTemplate().insert(Human.builder().withAge() - .withName("Otavio").build()); - List people = getGraphTemplate().findAll(Human.class).toList(); - - assertThat(people).hasSize(1) - .map(Human::getName) - .contains("Otavio"); - - getGraphTemplate().deleteAll(Human.class); - people = getGraphTemplate().findAll(Human.class).toList(); - - assertThat(people).isEmpty(); - } - - @Test - void shouldReturnEmptyWhenFindByIdNotFound() { - - final Optional person = getGraphTemplate().find(Human.class, -2L); - assertNotNull(person); - assertFalse(person.isPresent()); - } - - @Test - void shouldUpdateNullValues(){ - final Human otavio = getGraphTemplate().insert(Human.builder().withAge() - .withName("Otavio").build()); - - assertEquals("Otavio", otavio.getName()); - otavio.setName(null); - final Human human = getGraphTemplate().update(otavio); - assertNull(human.getName()); - - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTraversalTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTraversalTest.java deleted file mode 100644 index 425313bdd..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/AbstractTraversalTest.java +++ /dev/null @@ -1,96 +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; - -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.databases.tinkerpop.mapping.entities.Human; -import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Magazine; -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 Human otavio; - protected Human poliana; - protected Human paulo; - - protected Magazine shack; - protected Magazine license; - protected Magazine 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(Human.builder().withAge(27) - .withName("Otavio").build()); - poliana = graphTemplate.insert(Human.builder().withAge(26) - .withName("Poliana").build()); - paulo = graphTemplate.insert(Human.builder().withAge(50) - .withName("Paulo").build()); - - shack = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - license = graphTemplate.insert(Magazine.builder().withAge(2013).withName("Software License").build()); - effectiveJava = graphTemplate.insert(Magazine.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-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalTest.java deleted file mode 100644 index 655358560..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultEdgeTraversalTest.java +++ /dev/null @@ -1,540 +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; - -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.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.mapping.core.Converters; -import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; -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() - .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() - .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() - .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 magazines = graphTemplate.traversalVertex().outE(READS).inV().result().collect(toList()); - assertEquals(3, magazines.size()); - assertThat(magazines).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() { - Creature lion = graphTemplate.insert(new Creature("lion")); - Creature snake = graphTemplate.insert(new Creature("snake")); - Creature mouse = graphTemplate.insert(new Creature("mouse")); - Creature plant = graphTemplate.insert(new Creature("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() { - Creature lion = graphTemplate.insert(new Creature("lion")); - Creature snake = graphTemplate.insert(new Creature("snake")); - Creature mouse = graphTemplate.insert(new Creature("mouse")); - Creature plant = graphTemplate.insert(new Creature("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() { - Creature lion = graphTemplate.insert(new Creature("lion")); - Creature snake = graphTemplate.insert(new Creature("snake")); - Creature mouse = graphTemplate.insert(new Creature("mouse")); - Creature plant = graphTemplate.insert(new Creature("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() { - Creature lion = graphTemplate.insert(new Creature("lion")); - Creature snake = graphTemplate.insert(new Creature("snake")); - Creature mouse = graphTemplate.insert(new Creature("mouse")); - Creature plant = graphTemplate.insert(new Creature("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() { - Creature lion = graphTemplate.insert(new Creature("lion")); - Creature snake = graphTemplate.insert(new Creature("snake")); - Creature mouse = graphTemplate.insert(new Creature("mouse")); - Creature plant = graphTemplate.insert(new Creature("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 -> { - Creature incoming = result.incoming(); - Creature 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(Human.class) - .inE("knows").result() - .collect(Collectors.toList()); - - assertEquals(6, edges.size()); - - edges = graphTemplate.traversalVertex() - .hasLabel(Human.class) - .inE("knows") - .dedup() - .result() - .toList(); - - assertEquals(6, edges.size()); - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplateProducerTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplateProducerTest.java deleted file mode 100644 index c7c93296a..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTemplateProducerTest.java +++ /dev/null @@ -1,56 +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; - -import jakarta.inject.Inject; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; -import org.eclipse.jnosql.mapping.core.Converters; -import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; -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.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import static org.junit.jupiter.api.Assertions.assertNotNull; - - -@EnableAutoWeld -@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) -@AddPackages(GraphProducer.class) -@AddPackages(Reflections.class) -@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) -class DefaultGraphTemplateProducerTest { - - @Inject - private GraphTemplateProducer producer; - - - @Test - void shouldReturnErrorWhenManagerNull() { - Assertions.assertThrows(NullPointerException.class, () -> producer.apply(null)); - } - - @Test - void shouldReturn() { - var graph = Mockito.mock(Graph.class); - var template = producer.apply(graph); - assertNotNull(template); - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTraversalSourceTemplateTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTraversalSourceTemplateTest.java deleted file mode 100644 index 1610a2984..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultGraphTraversalSourceTemplateTest.java +++ /dev/null @@ -1,49 +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; - -import jakarta.inject.Inject; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; -import org.eclipse.jnosql.mapping.core.Converters; -import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; -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; - -@EnableAutoWeld -@AddPackages(value = {Converters.class, EntityConverter.class, Transactional.class}) -@AddPackages({MagazineRepository.class, Reflections.class, GraphProducer.class}) -@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) -class DefaultGraphTraversalSourceTemplateTest extends AbstractGraphTemplateTest { - - @Inject - private GraphTemplate graphTemplate; - - @Inject - private Graph graph; - - @Override - protected Graph getGraph() { - return graph; - } - - @Override - protected GraphTemplate getGraphTemplate() { - return graphTemplate; - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversalTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversalTest.java deleted file mode 100644 index 222bd3c91..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultValueMapTraversalTest.java +++ /dev/null @@ -1,107 +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; - -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.mapping.core.Converters; -import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; -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(Human.class).valueMap("name").count(); - assertEquals(3L, count); - } - - - @Test - void shouldReturnMapValues() { - List names = graphTemplate.traversalVertex() - .hasLabel(Human.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(Human.class).valueMap("name") - .stream(); - assertNotNull(stream); - assertEquals(3L, stream.count()); - } - - - @Test - void shouldReturnResultAsList() { - List> maps = graphTemplate.traversalVertex() - .hasLabel(Human.class).valueMap("name") - .resultList(); - assertEquals(3, maps.size()); - } - - @Test - void shouldReturnErrorWhenThereAreMoreThanOneInGetSingleResult() { - assertThrows(NonUniqueResultException.class, () -> graphTemplate.traversalVertex() - .hasLabel(Human.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("Human"). - has("name", name).valueMap("name").singleResult(); - assertEquals(name, poliana.map(m -> m.get("name")).orElse("")); - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalTest.java deleted file mode 100644 index 73faf1bea..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/DefaultVertexTraversalTest.java +++ /dev/null @@ -1,519 +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; - -import jakarta.data.exceptions.NonUniqueResultException; -import org.apache.tinkerpop.gremlin.process.traversal.P; -import org.apache.tinkerpop.gremlin.structure.T; -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.mapping.core.Converters; -import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; -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() - .toList(); - }); - } - - @Test - void shouldReturnErrorWhenHasKeyIsNull() { - assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().has((String) null, - P.gt(26)) - .result() - .toList()); - } - - @Test - void shouldHaveLabel() { - List magazines = graphTemplate.traversalVertex().hasLabel("Magazine").result().collect(toList()); - assertEquals(3, magazines.size()); - assertThat(magazines).contains(shack, license, effectiveJava); - } - - @Test - void shouldHaveLabel2() { - - List entities = graphTemplate.traversalVertex() - .hasLabel(P.eq("Magazine").or(P.eq("Human"))) - .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().toList()); - } - - @Test - void shouldIn() { - List magazines = graphTemplate.traversalVertex().out(READS).result().collect(toList()); - assertEquals(3, magazines.size()); - assertThat(magazines).contains(shack, license, effectiveJava); - } - - @Test - void shouldReturnErrorWhenInIsNull() { - assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().out((String) null).result().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().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().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().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("Human") - .valueMap("name").stream().toList(); - - assertFalse(maps.isEmpty()); - assertEquals(3, maps.size()); - - List names = new ArrayList<>(); - - maps.forEach(m -> names.add(m.get("name").toString())); - - assertThat(names).contains("Otavio", "Poliana", "Paulo"); - } - - @Test - void shouldMapValuesAsStreamLimit() { - List> maps = graphTemplate.traversalVertex().hasLabel("Human") - .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("Human") - .valueMap("name").next(); - - assertNotNull(map); - assertFalse(map.isEmpty()); - } - - - @Test - void shouldRepeatTimesTraversal() { - Creature lion = graphTemplate.insert(new Creature("lion")); - Creature snake = graphTemplate.insert(new Creature("snake")); - Creature mouse = graphTemplate.insert(new Creature("mouse")); - Creature plant = graphTemplate.insert(new Creature("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() { - Creature lion = graphTemplate.insert(new Creature("lion")); - Creature snake = graphTemplate.insert(new Creature("snake")); - Creature mouse = graphTemplate.insert(new Creature("mouse")); - Creature plant = graphTemplate.insert(new Creature("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() { - Creature lion = graphTemplate.insert(new Creature("lion")); - Creature snake = graphTemplate.insert(new Creature("snake")); - Creature mouse = graphTemplate.insert(new Creature("mouse")); - Creature plant = graphTemplate.insert(new Creature("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() { - Creature lion = graphTemplate.insert(new Creature("lion")); - Creature snake = graphTemplate.insert(new Creature("snake")); - Creature mouse = graphTemplate.insert(new Creature("mouse")); - Creature plant = graphTemplate.insert(new Creature("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("Magazine") - .has(property) - .orderBy(property) - .asc().result() - .map(Magazine::getName) - .collect(toList()); - - assertThat(properties).contains("Effective Java", "Software License", "The Shack"); - } - - @Test - void shouldOrderDesc() { - String property = "name"; - - List properties = graphTemplate.traversalVertex() - .hasLabel("Magazine") - .has(property) - .orderBy(property) - .desc().result() - .map(Magazine::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(Human.class::isInstance)); - assertTrue(graphTemplate.traversalVertex().hasLabel(() -> "Book").result().allMatch(Magazine.class::isInstance)); - assertTrue(graphTemplate.traversalVertex().hasLabel(Creature.class).result().allMatch(Creature.class::isInstance)); - } - - @Test - void shouldReturnResultAsList() { - List people = graphTemplate.traversalVertex().hasLabel("Human") - .result() - .toList(); - assertEquals(3, people.size()); - } - - @Test - void shouldReturnErrorWhenThereAreMoreThanOneInGetSingleResult() { - assertThrows(NonUniqueResultException.class, () -> graphTemplate.traversalVertex().hasLabel("Human").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("Human"). - has("name", name).singleResult(); - assertEquals(name, poliana.map(Human::getName).orElse("")); - } - - @Test - void shouldReturnErrorWhenPredicateIsNull() { - assertThrows(NullPointerException.class, () -> graphTemplate.traversalVertex().filter(null)); - } - - @Test - void shouldPredicate() { - long count = graphTemplate.traversalVertex() - .hasLabel(Human.class) - .filter(Human::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(Human.class) - .in("knows").result() - .collect(Collectors.toList()); - - assertEquals(6, people.size()); - - people = graphTemplate.traversalVertex() - .hasLabel(Human.class) - .in("knows").dedup().result() - .toList(); - - assertEquals(3, people.size()); - } - - -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntityTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntityTest.java deleted file mode 100644 index 8f944b2df..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/EdgeEntityTest.java +++ /dev/null @@ -1,360 +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; - -import jakarta.data.exceptions.EmptyResultException; -import jakarta.inject.Inject; -import org.eclipse.jnosql.communication.Value; -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.mapping.core.Converters; -import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; -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.Assertions; -import org.junit.jupiter.api.Test; - -import java.util.Optional; - -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 EdgeEntityTest { - - - @Inject - private GraphTemplate graphTemplate; - - - @Test - void shouldReturnErrorWhenInboundIsNull() { - Assertions.assertThrows(NullPointerException.class, () -> { - Human human = Human.builder().withName("Poliana").withAge().build(); - Magazine magazine = null; - graphTemplate.edge(human, "reads", magazine); - }); - } - - @Test - void shouldReturnErrorWhenOutboundIsNull() { - Assertions.assertThrows(IllegalStateException.class, () -> { - Human human = Human.builder().withName("Poliana").withAge().build(); - Magazine magazine = Magazine.builder().withAge(2007).withName("The Shack").build(); - graphTemplate.edge(human, "reads", magazine); - }); - } - - @Test - void shouldReturnErrorWhenLabelIsNull() { - Assertions.assertThrows(NullPointerException.class, () -> { - Human human = Human.builder().withName("Poliana").withAge().build(); - Magazine magazine = Magazine.builder().withAge(2007).withName("The Shack").build(); - graphTemplate.edge(human, (String) null, magazine); - }); - } - - @Test - void shouldReturnNullWhenInboundIdIsNull() { - Assertions.assertThrows(EmptyResultException.class, () -> { - Human human = Human.builder().withId(-5).withName("Poliana").withAge().build(); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - graphTemplate.edge(human, "reads", magazine); - }); - - } - - @Test - void shouldReturnNullWhenOutboundIdIsNull() { - Assertions.assertThrows(IllegalStateException.class, () -> { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = Magazine.builder().withAge(2007).withName("The Shack").build(); - graphTemplate.edge(human, "reads", magazine); - }); - } - - @Test - void shouldReturnEntityNotFoundWhenOutBoundDidNotFound() { - Assertions.assertThrows( EmptyResultException.class, () -> { - Human human = Human.builder().withId(-10L).withName("Poliana").withAge().build(); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - graphTemplate.edge(human, "reads", magazine); - }); - } - - @Test - void shouldReturnEntityNotFoundWhenInBoundDidNotFound() { - Assertions.assertThrows( EmptyResultException.class, () -> { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = Magazine.builder().withId(10L).withAge(2007).withName("The Shack").build(); - graphTemplate.edge(human, "reads", magazine); - }); - } - - @Test - void shouldCreateAnEdge() { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); - - assertEquals("reads", edge.label()); - assertEquals(human, edge.outgoing()); - assertEquals(magazine, edge.incoming()); - assertTrue(edge.isEmpty()); - assertNotNull(edge.id()); - } - - @Test - void shouldGetId() { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); - - assertEquals("reads", edge.label()); - assertEquals(human, edge.outgoing()); - assertEquals(magazine, edge.incoming()); - assertTrue(edge.isEmpty()); - assertNotNull(edge.id()); - final Long id = edge.id(Long.class); - assertNotNull(id); - - assertEquals(id, edge.id(Integer.class).longValue()); - - } - - @Test - void shouldCreateAnEdgeWithSupplier() { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(human, () -> "reads", magazine); - - assertEquals("reads", edge.label()); - assertEquals(human, edge.outgoing()); - assertEquals(magazine, edge.incoming()); - assertTrue(edge.isEmpty()); - assertNotNull(edge.id()); - } - - @Test - void shouldUseAnEdge() { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); - - EdgeEntity sameEdge = graphTemplate.edge(human, "reads", magazine); - - assertEquals(edge.id(), sameEdge.id()); - assertEquals(edge, sameEdge); - } - - @Test - void shouldUseAnEdge2() { - Human poliana = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Human nilzete = graphTemplate.insert(Human.builder().withName("Nilzete").withAge().build()); - - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(poliana, "reads", magazine); - EdgeEntity edge1 = graphTemplate.edge(nilzete, "reads", magazine); - - EdgeEntity sameEdge = graphTemplate.edge(poliana, "reads", magazine); - EdgeEntity sameEdge1 = graphTemplate.edge(nilzete, "reads", magazine); - - assertEquals(edge.id(), sameEdge.id()); - assertEquals(edge, sameEdge); - - assertEquals(edge1.id(), sameEdge1.id()); - assertEquals(edge1, sameEdge1); - - } - - @Test - void shouldUseADifferentEdge() { - Human poliana = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Human nilzete = graphTemplate.insert(Human.builder().withName("Nilzete").withAge().build()); - - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(poliana, "reads", magazine); - EdgeEntity edge1 = graphTemplate.edge(nilzete, "reads", magazine); - - EdgeEntity sameEdge = graphTemplate.edge(poliana, "reads", magazine); - EdgeEntity sameEdge1 = graphTemplate.edge(nilzete, "reads", magazine); - - assertNotEquals(edge.id(), edge1.id()); - assertNotEquals(edge.id(), sameEdge1.id()); - - assertNotEquals(sameEdge1.id(), sameEdge.id()); - } - - @Test - void shouldReturnErrorWhenAddKeyIsNull() { - assertThrows(NullPointerException.class, () -> { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); - edge.add(null, "Brazil"); - }); - } - - @Test - void shouldReturnErrorWhenAddValueIsNull() { - - assertThrows(NullPointerException.class, () -> { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); - edge.add("where", null); - }); - } - - @Test - void shouldAddProperty() { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); - edge.add("where", "Brazil"); - - assertFalse(edge.isEmpty()); - assertEquals(1, edge.size()); - assertThat(edge.properties()).contains(Element.of("where", "Brazil")); - } - - @Test - void shouldAddPropertyWithValue() { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); - edge.add("where", Value.of("Brazil")); - - assertFalse(edge.isEmpty()); - assertEquals(1, edge.size()); - assertThat(edge.properties()).contains(Element.of("where", "Brazil")); - } - - - @Test - void shouldReturnErrorWhenRemoveNullKeyProperty() { - assertThrows(NullPointerException.class, () -> { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); - edge.add("where", "Brazil"); - - - assertFalse(edge.isEmpty()); - edge.remove(null); - }); - } - - @Test - void shouldRemoveProperty() { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); - edge.add("where", "Brazil"); - assertEquals(1, edge.size()); - assertFalse(edge.isEmpty()); - edge.remove("where"); - assertTrue(edge.isEmpty()); - assertEquals(0, edge.size()); - } - - @Test - void shouldFindProperty() { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); - edge.add("where", "Brazil"); - - Optional where = edge.get("where"); - assertTrue(where.isPresent()); - assertEquals("Brazil", where.get().get()); - assertFalse(edge.get("not").isPresent()); - - } - - @Test - void shouldDeleteAnEdge() { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); - edge.delete(); - - EdgeEntity newEdge = graphTemplate.edge(human, "reads", magazine); - assertNotEquals(edge.id(), newEdge.id()); - - graphTemplate.deleteEdge(newEdge.id()); - } - - @Test - void shouldReturnErrorWhenDeleteAnEdgeWithNull() { - assertThrows(NullPointerException.class, () -> graphTemplate.delete((Iterable) null)); - } - - @Test - void shouldDeleteAnEdge2() { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - - EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); - - graphTemplate.deleteEdge(edge.id()); - - EdgeEntity newEdge = graphTemplate.edge(human, "reads", magazine); - assertNotEquals(edge.id(), newEdge.id()); - } - - - @Test - void shouldReturnErrorWhenFindEdgeWithNull() { - assertThrows(NullPointerException.class, () -> graphTemplate.edge(null)); - } - - - @Test - void shouldFindAnEdge() { - Human human = graphTemplate.insert(Human.builder().withName("Poliana").withAge().build()); - Magazine magazine = graphTemplate.insert(Magazine.builder().withAge(2007).withName("The Shack").build()); - EdgeEntity edge = graphTemplate.edge(human, "reads", magazine); - - Optional newEdge = graphTemplate.edge(edge.id()); - - assertTrue(newEdge.isPresent()); - assertEquals(edge.id(), newEdge.get().id()); - - graphTemplate.deleteEdge(edge.id()); - } - - @Test - void shouldNotFindAnEdge() { - Optional edgeEntity = graphTemplate.edge(-12L); - - assertFalse(edgeEntity.isPresent()); - } - -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphProducer.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphProducer.java deleted file mode 100644 index e93101c3f..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphProducer.java +++ /dev/null @@ -1,103 +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; - -import jakarta.annotation.PostConstruct; -import jakarta.annotation.Priority; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.Alternative; -import jakarta.enterprise.inject.Disposes; -import jakarta.enterprise.inject.Produces; -import jakarta.interceptor.Interceptor; -import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.mapping.Database; -import org.eclipse.jnosql.mapping.DatabaseType; -import org.mockito.Mockito; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collections; -import java.util.Comparator; -import java.util.function.Supplier; -import java.util.logging.Logger; - -import static java.lang.System.currentTimeMillis; -import static java.util.Collections.singleton; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@ApplicationScoped -@Alternative -@Priority(Interceptor.Priority.APPLICATION) -public class GraphProducer implements Supplier { - - private static final Logger LOGGER = Logger.getLogger(GraphProducer.class.getName()); - - private Graph graph; - - private String directory; - - @PostConstruct - public void init() { - this.directory = new File("").getAbsolutePath() + "/target/jnosql-graph/" + currentTimeMillis() + "/"; - LOGGER.info("Starting Graph database at directory: " + directory); - this.graph = Neo4jGraph.open(directory); - LOGGER.info("Graph database created"); - } - - @Produces - @ApplicationScoped - @Override - public Graph get() { - return graph; - } - - - @Produces - @ApplicationScoped - @Database(value = DatabaseType.GRAPH, provider = "graphRepositoryMock") - public Graph getGraphMock() { - - Graph graphMock = mock(Graph.class); - Vertex vertex = mock(Vertex.class); - when(vertex.label()).thenReturn("Person"); - when(vertex.id()).thenReturn(10L); - when(graphMock.vertices(10L)).thenReturn(Collections.emptyIterator()); - when(vertex.keys()).thenReturn(singleton("name")); - when(vertex.value("name")).thenReturn("nameMock"); - when(graphMock.addVertex(Mockito.anyString())).thenReturn(vertex); - when(graphMock.vertices(Mockito.any())).thenReturn(Collections.emptyIterator()); - return graphMock; - } - - public void dispose(@Disposes Graph graph) throws Exception { - LOGGER.info("Graph database closing"); - graph.close(); - final Path path = Paths.get(directory); - if (Files.exists(path)) { - LOGGER.info("Removing directory graph database: " + directory); - Files.walk(path) - .sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); - LOGGER.info("Graph directory exists?: " + Files.exists(path)); - } - LOGGER.info("Graph Database closed"); - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducerTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducerTest.java deleted file mode 100644 index da107b0fc..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateProducerTest.java +++ /dev/null @@ -1,56 +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; - -import jakarta.inject.Inject; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; -import org.eclipse.jnosql.mapping.core.Converters; -import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; -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 org.mockito.Mockito; - -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 GraphTemplateProducerTest { - - @Inject - private GraphTemplateProducer producer; - - @Test - void shouldReturnErrorWhenManagerNull() { - assertThrows(NullPointerException.class, () -> producer.apply(null)); - } - - @Test - void shouldReturnGraphTemplateWhenGetGraph() { - Graph graph = Mockito.mock(Graph.class); - GraphTemplate template = producer.apply(graph); - assertNotNull(template); - } - - -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateTest.java deleted file mode 100644 index ab810b047..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/GraphTemplateTest.java +++ /dev/null @@ -1,57 +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; - -import jakarta.inject.Inject; -import jakarta.nosql.Template; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; -import org.eclipse.jnosql.mapping.Database; -import org.eclipse.jnosql.mapping.core.Converters; -import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; -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.Assertions; -import org.junit.jupiter.api.Test; - -import static org.eclipse.jnosql.mapping.DatabaseType.GRAPH; - -@EnableAutoWeld -@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) -@AddPackages(GraphProducer.class) -@AddPackages(Reflections.class) -@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) -class GraphTemplateTest { - - @Inject - private Template template; - - @Inject - @Database(GRAPH) - private Template qualifier; - - - @Test - void shouldInjectTemplate() { - Assertions.assertNotNull(template); - } - - @Test - void shouldInjectQualifier() { - Assertions.assertNotNull(qualifier); - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineRepository.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineRepository.java deleted file mode 100644 index 33e597647..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineRepository.java +++ /dev/null @@ -1,24 +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; - - -import jakarta.data.repository.BasicRepository; -import jakarta.data.repository.Repository; -import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Magazine; - -@Repository -public interface MagazineRepository extends BasicRepository { -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineTemplateTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineTemplateTest.java deleted file mode 100644 index 5a2272e6c..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/MagazineTemplateTest.java +++ /dev/null @@ -1,97 +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; - -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.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.mapping.core.Converters; -import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; -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; - -@EnableAutoWeld -@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) -@AddPackages(GraphProducer.class) -@AddPackages(Reflections.class) -@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) -class MagazineTemplateTest { - - @Inject - private MagazineTemplate template; - - @Inject - private Graph graph; - - @Test - void shouldSaveWithTransaction() { - AtomicReference status = new AtomicReference<>(); - - Magazine magazine = Magazine.builder().withName("The Book").build(); - Transaction transaction = graph.tx(); - transaction.addTransactionListener(status::set); - template.insert(magazine); - assertFalse(transaction.isOpen()); - assertEquals(COMMIT, status.get()); - } - - @Test - void shouldSaveWithRollback() { - AtomicReference status = new AtomicReference<>(); - - Magazine magazine = Magazine.builder().withName("The Book").build(); - Transaction transaction = graph.tx(); - transaction.addTransactionListener(status::set); - try { - template.insertException(magazine); - assert false; - }catch (Exception ignored){ - - } - - assertFalse(transaction.isOpen()); - assertEquals(ROLLBACK, status.get()); - } - - @Test - void shouldUseAutomaticNormalTransaction() { - AtomicReference status = new AtomicReference<>(); - - Magazine magazine = Magazine.builder().withName("The Book").build(); - Transaction transaction = graph.tx(); - transaction.addTransactionListener(status::set); - assertNull(status.get()); - template.normalInsertion(magazine); - assertEquals(COMMIT, status.get()); - assertFalse(transaction.isOpen()); - } -} - diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock.java deleted file mode 100644 index d78c232f4..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock.java +++ /dev/null @@ -1,83 +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.configuration; - -import org.apache.commons.configuration2.Configuration; -import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.Transaction; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.communication.Settings; -import org.eclipse.jnosql.databases.tinkerpop.communication.GraphConfiguration; - -import java.util.Iterator; - -class GraphConfigurationMock implements GraphConfiguration { - - @Override - public Graph apply(Settings settings) { - return new GraphMock(settings); - } - - public record GraphMock(Settings settings) implements Graph { - - @Override - public Vertex addVertex(Object... keyValues) { - return null; - } - - @Override - public C compute(Class graphComputerClass) throws IllegalArgumentException { - return null; - } - - @Override - public GraphComputer compute() throws IllegalArgumentException { - return null; - } - - @Override - public Iterator vertices(Object... vertexIds) { - return null; - } - - @Override - public Iterator edges(Object... edgeIds) { - return null; - } - - @Override - public Transaction tx() { - return null; - } - - @Override - public void close() { - - } - - @Override - public Variables variables() { - return null; - } - - @Override - public Configuration configuration() { - return null; - } - - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock2.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock2.java deleted file mode 100644 index c08c302f7..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphConfigurationMock2.java +++ /dev/null @@ -1,83 +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.configuration; - -import org.apache.commons.configuration2.Configuration; -import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.Transaction; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.communication.Settings; -import org.eclipse.jnosql.databases.tinkerpop.communication.GraphConfiguration; - -import java.util.Iterator; - -public class GraphConfigurationMock2 implements GraphConfiguration { - - @Override - public Graph apply(Settings settings) { - return new GraphMock(settings); - } - - public record GraphMock(Settings settings) implements Graph { - - @Override - public Vertex addVertex(Object... keyValues) { - return null; - } - - @Override - public C compute(Class graphComputerClass) throws IllegalArgumentException { - return null; - } - - @Override - public GraphComputer compute() throws IllegalArgumentException { - return null; - } - - @Override - public Iterator vertices(Object... vertexIds) { - return null; - } - - @Override - public Iterator edges(Object... edgeIds) { - return null; - } - - @Override - public Transaction tx() { - return null; - } - - @Override - public void close() { - - } - - @Override - public Variables variables() { - return null; - } - - @Override - public Configuration configuration() { - return null; - } - - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplierTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplierTest.java deleted file mode 100644 index aae0b7adc..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/configuration/GraphSupplierTest.java +++ /dev/null @@ -1,82 +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.configuration; - -import jakarta.inject.Inject; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphProducer; -import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphTemplate; -import org.eclipse.jnosql.databases.tinkerpop.mapping.spi.GraphExtension; -import org.eclipse.jnosql.mapping.core.Converters; -import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; -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.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.jnosql.mapping.core.config.MappingConfigurations.GRAPH_PROVIDER; - -@EnableAutoWeld -@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) -@AddPackages(GraphProducer.class) -@AddPackages(Reflections.class) -@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) -class GraphSupplierTest { - - @Inject - private GraphSupplier supplier; - - @BeforeEach - public void beforeEach(){ - System.clearProperty(GRAPH_PROVIDER.get()); - } - - @Test - void shouldGetGraph() { - System.setProperty(GRAPH_PROVIDER.get(), GraphConfigurationMock.class.getName()); - Graph graph = supplier.get(); - Assertions.assertNotNull(graph); - assertThat(graph).isInstanceOf(GraphConfigurationMock.GraphMock.class); - } - - - @Test - void shouldUseDefaultConfigurationWhenProviderIsWrong() { - System.setProperty(GRAPH_PROVIDER.get(), Integer.class.getName()); - Graph graph = supplier.get(); - Assertions.assertNotNull(graph); - assertThat(graph).isInstanceOf(GraphConfigurationMock2.GraphMock.class); - } - - @Test - void shouldUseDefaultConfiguration() { - Graph graph = supplier.get(); - Assertions.assertNotNull(graph); - assertThat(graph).isInstanceOf(GraphConfigurationMock2.GraphMock.class); - } - - @Test - void shouldClose() throws Exception { - Graph graph = Mockito.mock(Graph.class); - supplier.close(graph); - Mockito.verify(graph).close(); - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Creature.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Creature.java deleted file mode 100644 index 40fef8e77..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Creature.java +++ /dev/null @@ -1,70 +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.entities; - -import jakarta.nosql.Column; -import jakarta.nosql.Entity; -import jakarta.nosql.Id; - -import java.util.Objects; - -@Entity -public class Creature { - - - @Id - private Long id; - - @Column - private String name; - - Creature() { - } - - public Creature(String name) { - this.name = name; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Creature creature)) { - return false; - } - return Objects.equals(id, creature.id) && - Objects.equals(name, creature.name); - } - - @Override - public int hashCode() { - return Objects.hash(id, name); - } - - @Override - public String toString() { - return "Creature{" + "name='" + name + '\'' + - '}'; - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Human.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Human.java deleted file mode 100644 index dabc87030..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Human.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2023 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.entities; - - -import jakarta.nosql.Column; -import jakarta.nosql.Entity; -import jakarta.nosql.Id; -import jakarta.nosql.MappedSuperclass; - -import java.util.List; -import java.util.Objects; - -@Entity -@MappedSuperclass -public class Human { - - @Id - private long id; - - @Column - private String name; - - @Column - private int age; - - @Column - private List phones; - - private String ignore; - - - public long getId() { - return id; - } - - public String getName() { - return name; - } - - public int getAge() { - return age; - } - - public List getPhones() { - return phones; - } - - public String getIgnore() { - return ignore; - } - - public boolean isAdult() { - return age > 21; - } - - Human() { - } - - Human(long id, String name, int age, List phones, String ignore) { - this.id = id; - this.name = name; - this.age = age; - this.phones = phones; - this.ignore = ignore; - } - - @Override - public String toString() { - return "Person{" + "id=" + id + - ", name='" + name + '\'' + - ", age=" + age + - ", phones=" + phones + - ", ignore='" + ignore + '\'' + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Human human = (Human) o; - return id == human.id && - age == human.age && - Objects.equals(name, human.name) && - Objects.equals(phones, human.phones); - } - - @Override - public int hashCode() { - return Objects.hash(id, name, age, phones, ignore); - } - - public static HumanBuilder builder() { - return new HumanBuilder(); - } - - public void setName(String name){ - this.name = name; - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanBuilder.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanBuilder.java deleted file mode 100644 index 4d7ee50e8..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanBuilder.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2023 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.entities; - -import java.util.List; - -public class HumanBuilder { - private long id; - private String name; - private int age; - private List phones; - private String ignore; - - public HumanBuilder withId(long id) { - this.id = id; - return this; - } - - public HumanBuilder withName(String name) { - this.name = name; - return this; - } - - public HumanBuilder withAge() { - this.age = 10; - return this; - } - - public HumanBuilder withAge(int age) { - this.age = age; - return this; - } - - - public HumanBuilder withPhones(List phones) { - this.phones = phones; - return this; - } - - public HumanBuilder withIgnore() { - this.ignore = "Just Ignore"; - return this; - } - - public Human build() { - return new Human(id, name, age, phones, ignore); - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanRepository.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanRepository.java deleted file mode 100644 index 945d1f198..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/HumanRepository.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023 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.entities; - - -import jakarta.data.repository.BasicRepository; -import jakarta.data.repository.Repository; - -@Repository -public interface HumanRepository extends BasicRepository { -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Magazine.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Magazine.java deleted file mode 100644 index 07d03ed95..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/Magazine.java +++ /dev/null @@ -1,113 +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.entities; - -import jakarta.nosql.Column; -import jakarta.nosql.Entity; -import jakarta.nosql.Id; - -import java.util.Objects; - -@Entity -public class Magazine { - - @Id - private Long id; - - @Column - private String name; - - @Column - private Integer age; - - - Magazine() { - } - - Magazine(Long id, String name, Integer age) { - this.id = id; - this.name = name; - this.age = age; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public Integer getAge() { - return age; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Magazine magazine)) { - return false; - } - return Objects.equals(id, magazine.id); - } - - @Override - public int hashCode() { - return Objects.hashCode(id); - } - - @Override - public String toString() { - return "Book{" + - "id=" + id + - ", name='" + name + '\'' + - ", age=" + age + - '}'; - } - - public static MagazineBuilder builder() { - return new MagazineBuilder(); - } - - public static class MagazineBuilder { - private String name; - private Integer age; - private Long id; - - private MagazineBuilder() { - } - - public MagazineBuilder withName(String name) { - this.name = name; - return this; - } - - public MagazineBuilder withAge(Integer age) { - this.age = age; - return this; - } - - public MagazineBuilder withId(Long id) { - this.id = id; - return this; - } - - public Magazine build() { - return new Magazine(id, name, age); - } - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/MagazineTemplate.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/MagazineTemplate.java deleted file mode 100644 index b8f4a510c..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/MagazineTemplate.java +++ /dev/null @@ -1,43 +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.entities; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphTemplate; -import org.eclipse.jnosql.databases.tinkerpop.mapping.Transactional; - -@ApplicationScoped -public class MagazineTemplate { - - @Inject - private GraphTemplate graphTemplate; - - @Transactional - public void insert(Magazine actor) { - graphTemplate.insert(actor); - } - - @Transactional - public void insertException(Magazine actor) { - graphTemplate.insert(actor); - throw new NullPointerException("should get a rollback"); - } - - public void normalInsertion(Magazine actor) { - graphTemplate.insert(actor); - } - -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/People.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/People.java deleted file mode 100644 index 1e47557cf..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/People.java +++ /dev/null @@ -1,26 +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.entities; - -import jakarta.data.repository.Insert; -import jakarta.data.repository.Repository; - - -@Repository -public interface People { - - @Insert - Human insert(Human human); -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/WrongEntity.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/WrongEntity.java deleted file mode 100644 index 4ca2d506f..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/entities/WrongEntity.java +++ /dev/null @@ -1,56 +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.entities; - -import java.util.Objects; - -public class WrongEntity { - - private String name; - - WrongEntity() { - } - - public WrongEntity(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - WrongEntity animal = (WrongEntity) o; - return Objects.equals(name, animal.name); - } - - @Override - public int hashCode() { - return Objects.hash(name); - } - - @Override - public String toString() { - return "Animal{" + "name='" + name + '\'' + - '}'; - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphCustomExtensionTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphCustomExtensionTest.java deleted file mode 100644 index 5bc45760d..000000000 --- a/jnosql-neo4j/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.GraphTemplate; -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.core.spi.EntityMetadataExtension; -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 static org.junit.jupiter.api.Assertions.assertNotNull; - - -@EnableAutoWeld -@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) -@AddPackages(GraphProducer.class) -@AddPackages(Reflections.class) -@AddExtensions({EntityMetadataExtension.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-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtensionTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtensionTest.java deleted file mode 100644 index 9828f27b3..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/mapping/spi/GraphExtensionTest.java +++ /dev/null @@ -1,84 +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.eclipse.jnosql.databases.tinkerpop.mapping.GraphProducer; -import org.eclipse.jnosql.databases.tinkerpop.mapping.GraphTemplate; -import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.Human; -import org.eclipse.jnosql.databases.tinkerpop.mapping.entities.HumanRepository; -import org.eclipse.jnosql.mapping.Database; -import org.eclipse.jnosql.mapping.DatabaseType; -import org.eclipse.jnosql.mapping.core.Converters; -import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; -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 static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - - -@EnableAutoWeld -@AddPackages(value = {Converters.class, EntityConverter.class, GraphTemplate.class}) -@AddPackages(GraphProducer.class) -@AddPackages(Reflections.class) -@AddExtensions({EntityMetadataExtension.class, GraphExtension.class}) -class GraphExtensionTest { - - - @Inject - @Database(value = DatabaseType.GRAPH) - private HumanRepository repository; - - @Inject - @Database(value = DatabaseType.GRAPH, provider = "graphRepositoryMock") - private HumanRepository repositoryMock; - - @Inject - @Database(value = DatabaseType.GRAPH, provider = "graphRepositoryMock") - private GraphTemplate templateMock; - - @Inject - private GraphTemplate template; - - - @Test - void shouldInitiate() { - assertNotNull(repository); - Human human = repository.save(Human.builder().build()); - assertNull(human.getName()); - } - - @Test - void shouldUseMock(){ - assertNotNull(repositoryMock); - } - - @Test - void shouldInjectTemplate() { - assertNotNull(templateMock); - assertNotNull(template); - } - - @Test - void shouldInjectRepository() { - assertNotNull(repository); - assertNotNull(repositoryMock); - } -} From 22109207af7d89c4f2c34d55f9af60ce124efce9 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 18:26:02 +0000 Subject: [PATCH 007/148] refactor: update package name to neo4j Signed-off-by: Otavio Santana --- .../communication/CommunicationEntityConverter.java | 2 +- .../communication/DefaultGraphDatabaseManager.java | 2 +- .../communication/GraphConfiguration.java | 2 +- .../communication/GraphDatabaseManager.java | 2 +- .../communication/GraphTransactionUtil.java | 2 +- .../{tinkerpop => neo4j}/communication/TraversalExecutor.java | 2 +- .../{tinkerpop => neo4j}/communication/package-info.java | 4 ++-- .../communication/DefaultGraphDatabaseManagerTest.java | 2 +- .../{tinkerpop => neo4j}/communication/GraphSupplier.java | 2 +- ...e.jnosql.databases.neo4j.communication.GraphConfiguration} | 0 10 files changed, 10 insertions(+), 10 deletions(-) rename jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/{tinkerpop => neo4j}/communication/CommunicationEntityConverter.java (95%) rename jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/{tinkerpop => neo4j}/communication/DefaultGraphDatabaseManager.java (99%) rename jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/{tinkerpop => neo4j}/communication/GraphConfiguration.java (97%) rename jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/{tinkerpop => neo4j}/communication/GraphDatabaseManager.java (97%) rename jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/{tinkerpop => neo4j}/communication/GraphTransactionUtil.java (98%) rename jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/{tinkerpop => neo4j}/communication/TraversalExecutor.java (98%) rename jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/{tinkerpop => neo4j}/communication/package-info.java (92%) rename jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/{tinkerpop => neo4j}/communication/DefaultGraphDatabaseManagerTest.java (99%) rename jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/{tinkerpop => neo4j}/communication/GraphSupplier.java (95%) rename jnosql-neo4j/src/test/resources/META-INF/services/{org.eclipse.jnosql.databases.tinkerpop.communication.GraphConfiguration => org.eclipse.jnosql.databases.neo4j.communication.GraphConfiguration} (100%) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/CommunicationEntityConverter.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/CommunicationEntityConverter.java similarity index 95% rename from jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/CommunicationEntityConverter.java rename to jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/CommunicationEntityConverter.java index 116c7f151..72f658c68 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/CommunicationEntityConverter.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/CommunicationEntityConverter.java @@ -12,7 +12,7 @@ * * Otavio Santana */ -package org.eclipse.jnosql.databases.tinkerpop.communication; +package org.eclipse.jnosql.databases.neo4j.communication; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/DefaultGraphDatabaseManager.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultGraphDatabaseManager.java similarity index 99% rename from jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/DefaultGraphDatabaseManager.java rename to jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultGraphDatabaseManager.java index d1eb5ee04..33b26fef3 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/DefaultGraphDatabaseManager.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultGraphDatabaseManager.java @@ -12,7 +12,7 @@ * * Otavio Santana */ -package org.eclipse.jnosql.databases.tinkerpop.communication; +package org.eclipse.jnosql.databases.neo4j.communication; import jakarta.data.exceptions.EmptyResultException; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphConfiguration.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphConfiguration.java similarity index 97% rename from jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphConfiguration.java rename to jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphConfiguration.java index c0fd5719c..44f635dcf 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphConfiguration.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphConfiguration.java @@ -12,7 +12,7 @@ * * Otavio Santana */ -package org.eclipse.jnosql.databases.tinkerpop.communication; +package org.eclipse.jnosql.databases.neo4j.communication; import org.apache.tinkerpop.gremlin.structure.Graph; import org.eclipse.jnosql.communication.CommunicationException; diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphDatabaseManager.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphDatabaseManager.java similarity index 97% rename from jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphDatabaseManager.java rename to jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphDatabaseManager.java index 19f9415ee..49ac43720 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphDatabaseManager.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphDatabaseManager.java @@ -12,7 +12,7 @@ * * Otavio Santana */ -package org.eclipse.jnosql.databases.tinkerpop.communication; +package org.eclipse.jnosql.databases.neo4j.communication; import org.apache.tinkerpop.gremlin.structure.Graph; import org.eclipse.jnosql.communication.semistructured.DatabaseManager; diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphTransactionUtil.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphTransactionUtil.java similarity index 98% rename from jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphTransactionUtil.java rename to jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphTransactionUtil.java index 13158306f..31a7198d7 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphTransactionUtil.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphTransactionUtil.java @@ -12,7 +12,7 @@ * * Otavio Santana */ -package org.eclipse.jnosql.databases.tinkerpop.communication; +package org.eclipse.jnosql.databases.neo4j.communication; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.Transaction; diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/TraversalExecutor.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/TraversalExecutor.java similarity index 98% rename from jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/TraversalExecutor.java rename to jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/TraversalExecutor.java index cf1085151..d58f19d1e 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/TraversalExecutor.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/TraversalExecutor.java @@ -12,7 +12,7 @@ * * Otavio Santana */ -package org.eclipse.jnosql.databases.tinkerpop.communication; +package org.eclipse.jnosql.databases.neo4j.communication; import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/package-info.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java similarity index 92% rename from jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/package-info.java rename to jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java index bcd663d8e..1936ed852 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/tinkerpop/communication/package-info.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java @@ -18,7 +18,7 @@ * in the JNoSQL project. It contains interfaces, classes, and utilities that enable developers to * communicate with and manage graph databases. *

- * The core interface in this package is {@link org.eclipse.jnosql.databases.tinkerpop.communication.GraphDatabaseManager}, + * The core interface in this package is {@link org.eclipse.jnosql.databases.neo4j.communication.GraphDatabaseManager}, * which extends {@link org.eclipse.jnosql.communication.semistructured.DatabaseManager} * and acts as a specialized extension for managing graph databases. Implementations of this interface * provide methods for interacting with the underlying graph database, executing graph traversals, @@ -28,4 +28,4 @@ * for working with different graph database technologies supported by JNoSQL. *

*/ -package org.eclipse.jnosql.databases.tinkerpop.communication; +package org.eclipse.jnosql.databases.neo4j.communication; diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/communication/DefaultGraphDatabaseManagerTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultGraphDatabaseManagerTest.java similarity index 99% rename from jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/communication/DefaultGraphDatabaseManagerTest.java rename to jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultGraphDatabaseManagerTest.java index a5f5d954f..063a18fb4 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/communication/DefaultGraphDatabaseManagerTest.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultGraphDatabaseManagerTest.java @@ -12,7 +12,7 @@ * * Otavio Santana */ -package org.eclipse.jnosql.databases.tinkerpop.communication; +package org.eclipse.jnosql.databases.neo4j.communication; import net.datafaker.Faker; import org.apache.tinkerpop.gremlin.structure.Graph; diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphSupplier.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/GraphSupplier.java similarity index 95% rename from jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphSupplier.java rename to jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/GraphSupplier.java index dcf55743b..afffb8e43 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/tinkerpop/communication/GraphSupplier.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/GraphSupplier.java @@ -12,7 +12,7 @@ * * Otavio Santana */ -package org.eclipse.jnosql.databases.tinkerpop.communication; +package org.eclipse.jnosql.databases.neo4j.communication; import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph; import org.apache.tinkerpop.gremlin.structure.Graph; diff --git a/jnosql-neo4j/src/test/resources/META-INF/services/org.eclipse.jnosql.databases.tinkerpop.communication.GraphConfiguration b/jnosql-neo4j/src/test/resources/META-INF/services/org.eclipse.jnosql.databases.neo4j.communication.GraphConfiguration similarity index 100% rename from jnosql-neo4j/src/test/resources/META-INF/services/org.eclipse.jnosql.databases.tinkerpop.communication.GraphConfiguration rename to jnosql-neo4j/src/test/resources/META-INF/services/org.eclipse.jnosql.databases.neo4j.communication.GraphConfiguration From fcd4a980106528697b10385842e96d3481bae58a Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 18:41:58 +0000 Subject: [PATCH 008/148] feat: remove extra code using neo4j Signed-off-by: Otavio Santana --- .../CommunicationEntityConverter.java | 33 -- .../DefaultGraphDatabaseManager.java | 170 ------- .../communication/GraphConfiguration.java | 60 --- .../communication/GraphDatabaseManager.java | 48 -- .../communication/GraphTransactionUtil.java | 94 ---- .../communication/TraversalExecutor.java | 86 ---- .../neo4j/communication/package-info.java | 31 -- .../DefaultGraphDatabaseManagerTest.java | 465 ------------------ .../neo4j/communication/GraphSupplier.java | 45 -- 9 files changed, 1032 deletions(-) delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/CommunicationEntityConverter.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultGraphDatabaseManager.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphConfiguration.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphDatabaseManager.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphTransactionUtil.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/TraversalExecutor.java delete mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultGraphDatabaseManagerTest.java delete mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/GraphSupplier.java diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/CommunicationEntityConverter.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/CommunicationEntityConverter.java deleted file mode 100644 index 72f658c68..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/CommunicationEntityConverter.java +++ /dev/null @@ -1,33 +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.neo4j.communication; - -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; - -import java.util.function.Function; - -public enum CommunicationEntityConverter implements Function{ - INSTANCE; - - - @Override - public CommunicationEntity apply(Vertex vertex) { - var entity = CommunicationEntity.of(vertex.label()); - vertex.properties().forEachRemaining(p -> entity.add(p.key(), p.value())); - entity.add(DefaultGraphDatabaseManager.ID_PROPERTY, vertex.id()); - return entity; - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultGraphDatabaseManager.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultGraphDatabaseManager.java deleted file mode 100644 index 33b26fef3..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultGraphDatabaseManager.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2024 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.communication; - -import jakarta.data.exceptions.EmptyResultException; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.communication.CommunicationException; -import org.eclipse.jnosql.communication.ValueUtil; -import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; -import org.eclipse.jnosql.communication.semistructured.DeleteQuery; -import org.eclipse.jnosql.communication.semistructured.SelectQuery; - -import java.time.Duration; -import java.util.Iterator; -import java.util.Objects; -import java.util.stream.Stream; - -import static org.apache.tinkerpop.gremlin.process.traversal.Order.asc; -import static org.apache.tinkerpop.gremlin.process.traversal.Order.desc; - -/** - * Default implementation of {@link GraphDatabaseManager} that serves as an adapter to the TinkerPop - * graph database provided by the Apache TinkerPop framework. - *

- * This implementation wraps a TinkerPop {@link Graph} instance and provides methods to interact with - * the underlying graph database, execute graph traversals, and perform other graph-related operations. - *

- *

- * Note that this implementation does not support certain operations such as insertions with a duration, - * as indicated by the UnsupportedOperationException thrown by those methods. - *

- */ -public class DefaultGraphDatabaseManager implements GraphDatabaseManager { - - public static final String ID_PROPERTY = "_id"; - private final Graph graph; - - DefaultGraphDatabaseManager(Graph graph) { - this.graph = graph; - } - - @Override - public Graph get() { - return graph; - } - - @Override - public String name() { - return "The tinkerpop graph database manager"; - } - - @Override - public CommunicationEntity insert(CommunicationEntity entity) { - - Objects.requireNonNull(entity, "entity is required"); - Vertex vertex = graph.addVertex(entity.name()); - 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; - } - - @Override - public CommunicationEntity insert(CommunicationEntity entity, Duration duration) { - throw new UnsupportedOperationException("There is no support to insert with duration"); - } - - @Override - public Iterable insert(Iterable entities) { - Objects.requireNonNull(entities, "entities is required"); - entities.forEach(this::insert); - return entities; - } - - @Override - public Iterable insert(Iterable iterable, Duration duration) { - throw new UnsupportedOperationException("There is no support to insert with duration"); - } - - @Override - public CommunicationEntity update(CommunicationEntity entity) { - Objects.requireNonNull(entity, "entity is required"); - entity.find(ID_PROPERTY).ifPresent(id -> { - Iterator vertices = graph.vertices(id.get()); - if(!vertices.hasNext()) { - throw new EmptyResultException("The entity does not exist with the id: " + id); - } - Vertex vertex = vertices.next(); - entity.elements().forEach(e -> vertex.property(e.name(), ValueUtil.convert(e.value()))); - }); - GraphTransactionUtil.transaction(graph); - return entity; - } - - @Override - public Iterable update(Iterable entities) { - Objects.requireNonNull(entities, "entities is required"); - Stream.of(entities).forEach(this::update); - return entities; - } - - @Override - public void delete(DeleteQuery query) { - Objects.requireNonNull(query, "delete is required"); - GraphTraversal traversal = graph.traversal().V().hasLabel(query.name()); - query.condition().ifPresent(c ->{ - GraphTraversal predicate = TraversalExecutor.getPredicate(c); - traversal.filter(predicate); - }); - - traversal.drop().iterate(); - GraphTransactionUtil.transaction(graph); - } - - @Override - public Stream select(SelectQuery query) { - Objects.requireNonNull(query, "query is required"); - GraphTraversal traversal = graph.traversal().V().hasLabel(query.name()); - query.condition().ifPresent(c ->{ - GraphTraversal predicate = TraversalExecutor.getPredicate(c); - traversal.filter(predicate); - }); - - if(query.limit()> 0) { - traversal.limit(query.limit()); - } else if(query.skip() > 0) { - traversal.skip(query.skip()); - } - query.sorts().forEach( - s -> { - if (s.isAscending()) { - traversal.order().by(s.property(), asc); - } else { - traversal.order().by(s.property(), desc); - } - }); - return traversal.toStream().map(CommunicationEntityConverter.INSTANCE); - } - - @Override - public long count(String entity) { - Objects.requireNonNull(entity, "entity is required"); - GraphTraversal count = graph.traversal().V().hasLabel(entity).count(); - return count.next(); - } - - @Override - public void close() { - try { - graph.close(); - } catch (Exception e) { - throw new CommunicationException("There is an issue when close the Graph connection", e); - } - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphConfiguration.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphConfiguration.java deleted file mode 100644 index 44f635dcf..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphConfiguration.java +++ /dev/null @@ -1,60 +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.neo4j.communication; - -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.eclipse.jnosql.communication.CommunicationException; -import org.eclipse.jnosql.communication.Settings; - -import java.util.ServiceLoader; -import java.util.function.Function; - -/** - * The Configuration that creates an instance of {@link Graph} that given a {@link Settings} make an {@link Graph} instance. - */ -public interface GraphConfiguration extends Function { - - - /** - * creates and returns a {@link GraphConfiguration} instance from {@link ServiceLoader} - * - * @param the configuration type - * @return {@link GraphConfiguration} instance - */ - @SuppressWarnings("unchecked") - static T getConfiguration() { - return (T) ServiceLoader.load(GraphConfiguration.class) - .stream() - .map(ServiceLoader.Provider::get) - .findFirst().orElseThrow(() -> new CommunicationException("It does not find GraphConfiguration")); - } - - /** - * creates and returns a {@link GraphConfiguration} instance from {@link ServiceLoader} - * for a particular provider implementation. - * - * @param the configuration type - * @param type the particular provider - * @return {@link GraphConfiguration} instance - */ - @SuppressWarnings("unchecked") - static T getConfiguration(Class type) { - return (T) ServiceLoader.load(GraphConfiguration.class) - .stream() - .map(ServiceLoader.Provider::get) - .filter(type::isInstance) - .findFirst().orElseThrow(() -> new CommunicationException("It does not find GraphConfiguration")); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphDatabaseManager.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphDatabaseManager.java deleted file mode 100644 index 49ac43720..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphDatabaseManager.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2024 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.communication; - -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.eclipse.jnosql.communication.semistructured.DatabaseManager; - -import java.util.Objects; -import java.util.function.Supplier; - -/** - * A specialized extension of {@link DatabaseManager} that provides access to a graph database represented by the - * {@link org.apache.tinkerpop.gremlin.structure.Graph} interface from Apache TinkerPop. - *

- * Implementations of this interface are expected to provide methods for interacting with the underlying graph - * database, such as retrieving vertices, edges, and properties, executing graph traversals, and performing other - * graph-related operations. - *

- * In addition to the functionality inherited from {@link DatabaseManager}, implementations of this interface - * also act as suppliers of the underlying {@link org.apache.tinkerpop.gremlin.structure.Graph} instance. - *

- */ -public interface GraphDatabaseManager extends DatabaseManager, Supplier { - - /** - * Creates a new instance of DefaultGraphDatabaseManager with the specified TinkerPop Graph. - * - * @param graph the TinkerPop Graph instance to be managed - * @return a new DefaultGraphDatabaseManager instance - * @throws NullPointerException if the graph parameter is null - */ - static GraphDatabaseManager of(Graph graph) { - Objects.requireNonNull(graph, "graph is required"); - return new DefaultGraphDatabaseManager(graph); - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphTransactionUtil.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphTransactionUtil.java deleted file mode 100644 index 31a7198d7..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/GraphTransactionUtil.java +++ /dev/null @@ -1,94 +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.neo4j.communication; - -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.Transaction; -import org.eclipse.jnosql.mapping.core.config.MicroProfileSettings; - -import java.util.Objects; -import java.util.logging.Logger; - -import static org.eclipse.jnosql.mapping.core.config.MappingConfigurations.GRAPH_TRANSACTION_AUTOMATIC; - -/** - * 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. - */ -public final class GraphTransactionUtil { - - private static final Logger LOGGER = Logger.getLogger(GraphTransactionUtil.class.getName()); - private static final ThreadLocal THREAD_LOCAL = new ThreadLocal<>(); - - private GraphTransactionUtil() { - } - - /** - * Locks the current transaction, preventing it from being committed. - * - * @param transaction the transaction to lock - */ - public static void lock(Transaction transaction) { - THREAD_LOCAL.set(transaction); - } - - /** - * Unlocks the current transaction. - * Allows the transaction to be committed. - */ - public static void unlock() { - THREAD_LOCAL.remove(); - } - - /** - * Automatically commits a transaction if enabled and not locked. - * - * @param graph the graph instance - */ - public synchronized static void transaction(Graph 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()); - } - - } - } - - /** - * Checks if automatic transaction management is enabled. - * - * @return true if automatic transaction management is enabled, false otherwise - */ - 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-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/TraversalExecutor.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/TraversalExecutor.java deleted file mode 100644 index d58f19d1e..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/TraversalExecutor.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2024 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.communication; - -import org.apache.tinkerpop.gremlin.process.traversal.P; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.eclipse.jnosql.communication.Condition; -import org.eclipse.jnosql.communication.TypeReference; -import org.eclipse.jnosql.communication.ValueUtil; -import org.eclipse.jnosql.communication.semistructured.CriteriaCondition; -import org.eclipse.jnosql.communication.semistructured.Element; - -import java.util.List; - -final class TraversalExecutor { - - private TraversalExecutor() { - } - - static GraphTraversal getPredicate(CriteriaCondition condition) { - Condition operator = condition.condition(); - Element element = condition.element(); - String name = element.name(); - var value = ValueUtil.convert(element.value()); - - switch (operator) { - case EQUALS -> { - return __.has(name, P.eq(value)); - } - case GREATER_THAN -> { - return __.has(name, P.gt(value)); - } - case GREATER_EQUALS_THAN -> { - return __.has(name, P.gte(value)); - } - case LESSER_THAN -> { - return __.has(name, P.lt(value)); - } - case LESSER_EQUALS_THAN -> { - return __.has(name, P.lte(value)); - } - case BETWEEN -> { - List values = ValueUtil.convertToList(element.value()); - if(values.size() == 2) { - return __.has(name, P.between(values.get(0), values.get(1))); - } - throw new IllegalStateException("The between condition requires two parameters"); - } - case IN -> { - return __.has(name, P.within(ValueUtil.convertToList(element.value()))); - } - case NOT -> { - var notCondition = element.value().get(CriteriaCondition.class); - return __.not(getPredicate(notCondition)); - } - case AND -> { - return condition.element().value().get(new TypeReference>() { - }).stream().map(TraversalExecutor::getPredicate) - .reduce(GraphTraversal::and) - .orElseThrow(() -> new UnsupportedOperationException("There is an inconsistency at the AND operator")); - } - case OR -> { - return condition.element().value().get(new TypeReference>() { - }).stream().map(TraversalExecutor::getPredicate) - .reduce(GraphTraversal::or) - .orElseThrow(() -> new UnsupportedOperationException("There is an inconsistency at the OR operator")); - } - default -> - throw new UnsupportedOperationException("There is not support to the type " + operator + " in graph"); - } - } -} diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java deleted file mode 100644 index 1936ed852..000000000 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2024 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 - */ - -/** - * This package provides communication facilities and abstractions for interacting with graph databases - * in the JNoSQL project. It contains interfaces, classes, and utilities that enable developers to - * communicate with and manage graph databases. - *

- * The core interface in this package is {@link org.eclipse.jnosql.databases.neo4j.communication.GraphDatabaseManager}, - * which extends {@link org.eclipse.jnosql.communication.semistructured.DatabaseManager} - * and acts as a specialized extension for managing graph databases. Implementations of this interface - * provide methods for interacting with the underlying graph database, executing graph traversals, - * and performing other graph-related operations. - *

- * Additionally, this package may contain specific implementations, adapters, or utilities tailored - * for working with different graph database technologies supported by JNoSQL. - *

- */ -package org.eclipse.jnosql.databases.neo4j.communication; diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultGraphDatabaseManagerTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultGraphDatabaseManagerTest.java deleted file mode 100644 index 063a18fb4..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultGraphDatabaseManagerTest.java +++ /dev/null @@ -1,465 +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.neo4j.communication; - -import net.datafaker.Faker; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.assertj.core.api.Assertions; -import org.assertj.core.api.SoftAssertions; -import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; -import org.eclipse.jnosql.communication.semistructured.DeleteQuery; -import org.eclipse.jnosql.communication.semistructured.Element; -import org.eclipse.jnosql.communication.semistructured.Elements; -import org.eclipse.jnosql.communication.semistructured.SelectQuery; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; - -import static java.util.Arrays.asList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.jnosql.communication.semistructured.DeleteQuery.delete; -import static org.eclipse.jnosql.communication.semistructured.SelectQuery.select; -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; -import static org.junit.jupiter.api.Assertions.assertTrue; - -class DefaultGraphDatabaseManagerTest { - - public static final String COLLECTION_NAME = "person"; - - private GraphDatabaseManager entityManager; - - private final Faker faker = new Faker(); - - @BeforeEach - void setUp(){ - Graph graph = GraphSupplier.INSTANCE.get(); - this.entityManager = GraphDatabaseManager.of(graph); - } - - @BeforeEach - void beforeEach() { - delete().from(COLLECTION_NAME).delete(entityManager); - } - - @Test - void shouldInsertEntity(){ - String name = faker.name().fullName(); - var age = faker.number().randomDigit(); - var entity = CommunicationEntity.of("Person"); - entity.add("name", name); - entity.add("age", age); - var communicationEntity = entityManager.insert(entity); - assertNotNull(communicationEntity); - - SoftAssertions.assertSoftly(softly -> { - softly.assertThat(communicationEntity.find("name", String.class)).get().isEqualTo(name); - softly.assertThat(communicationEntity.find("age", int.class)).get().isEqualTo(age); - softly.assertThat(communicationEntity.find(DefaultGraphDatabaseManager.ID_PROPERTY)).isPresent(); - }); - } - - @Test - void shouldInsertEntities(){ - String name = faker.name().fullName(); - var age = faker.number().randomDigit(); - var entity = CommunicationEntity.of("Person"); - entity.add("name", name); - entity.add("age", age); - - String name2 = faker.name().fullName(); - var age2 = faker.number().randomDigit(); - var entity2 = CommunicationEntity.of("Person"); - entity2.add("name", name2); - entity2.add("age", age2); - - var communicationEntities = StreamSupport - .stream(entityManager.insert(List.of(entity, entity2)).spliterator(), false).toList(); - - assertNotNull(communicationEntities); - SoftAssertions.assertSoftly(softly -> { - softly.assertThat(communicationEntities).hasSize(2); - softly.assertThat(communicationEntities.get(0).find("name", String.class)).get().isEqualTo(name); - softly.assertThat(communicationEntities.get(0).find("age", int.class)).get().isEqualTo(age); - softly.assertThat(communicationEntities.get(0).find(DefaultGraphDatabaseManager.ID_PROPERTY)).isPresent(); - - softly.assertThat(communicationEntities.get(1).find("name", String.class)).get().isEqualTo(name2); - softly.assertThat(communicationEntities.get(1).find("age", int.class)).get().isEqualTo(age2); - softly.assertThat(communicationEntities.get(1).find(DefaultGraphDatabaseManager.ID_PROPERTY)).isPresent(); - }); - - } - - @Test - void shouldInsert() { - var entity = getEntity(); - var documentEntity = entityManager.insert(entity); - assertTrue(documentEntity.elements().stream().map(Element::name).anyMatch(s -> s.equals("_id"))); - } - - @Test - void shouldThrowExceptionWhenInsertWithTTL() { - var entity = getEntity(); - var ttl = Duration.ofSeconds(10); - assertThrows(UnsupportedOperationException.class, () -> entityManager.insert(entity, ttl)); - } - - @Test - void shouldUpdate() { - var entity = entityManager.insert(getEntity()); - var newField = Elements.of("newField", "10"); - entity.add(newField); - var updated = entityManager.update(entity); - assertEquals(newField, updated.find("newField").orElseThrow()); - } - - @Test - void shouldRemoveEntity() { - var documentEntity = entityManager.insert(getEntity()); - - Optional id = documentEntity.find("_id"); - var query = select().from(COLLECTION_NAME) - .where("_id").eq(id.orElseThrow().get()) - .build(); - var deleteQuery = delete().from(COLLECTION_NAME).where("_id") - .eq(id.get().get()) - .build(); - - entityManager.delete(deleteQuery); - assertTrue(entityManager.select(query).findAny().isEmpty()); - } - - @Test - void shouldFindDocument() { - var entity = entityManager.insert(getEntity()); - Optional id = entity.find("_id"); - - var query = select().from(COLLECTION_NAME) - .where("_id").eq(id.orElseThrow().get()) - .build(); - - var entities = entityManager.select(query).collect(Collectors.toList()); - assertFalse(entities.isEmpty()); - assertThat(entities).contains(entity); - } - - @Test - void shouldFindDocument2() { - var entity = entityManager.insert(getEntity()); - Optional id = entity.find("_id"); - - var query = select().from(COLLECTION_NAME) - .where("name").eq("Poliana") - .and("city").eq("Salvador").and("_id").eq(id.orElseThrow().get()) - .build(); - - List entities = entityManager.select(query).collect(Collectors.toList()); - assertFalse(entities.isEmpty()); - assertThat(entities).contains(entity); - } - - @Test - void shouldFindDocument3() { - var entity = entityManager.insert(getEntity()); - Optional id = entity.find("_id"); - var query = select().from(COLLECTION_NAME) - .where("name").eq("Poliana") - .or("city").eq("Salvador") - .and(id.orElseThrow().name()).eq(id.get().get()) - .build(); - - List entities = entityManager.select(query).collect(Collectors.toList()); - assertFalse(entities.isEmpty()); - assertThat(entities).contains(entity); - } - - @Test - void shouldFindDocumentGreaterThan() { - DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); - entityManager.delete(deleteQuery); - Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); - List entities = StreamSupport.stream(entitiesSaved.spliterator(), false).toList(); - - var query = select().from(COLLECTION_NAME) - .where("age").gt(22) - .and("type").eq("V") - .build(); - - List entitiesFound = entityManager.select(query).collect(Collectors.toList()); - assertEquals(2, entitiesFound.size()); - assertThat(entitiesFound).isNotIn(entities.get(0)); - } - - @Test - void shouldFindDocumentGreaterEqualsThan() { - DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); - entityManager.delete(deleteQuery); - Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); - List entities = StreamSupport.stream(entitiesSaved.spliterator(), false).toList(); - - var query = select().from(COLLECTION_NAME) - .where("age").gte(23) - .and("type").eq("V") - .build(); - - List entitiesFound = entityManager.select(query).collect(Collectors.toList()); - assertEquals(2, entitiesFound.size()); - assertThat(entitiesFound).isNotIn(entities.get(0)); - } - - @Test - void shouldFindDocumentLesserThan() { - DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); - entityManager.delete(deleteQuery); - Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); - List entities = StreamSupport.stream(entitiesSaved.spliterator(), false) - .toList(); - - var query = select().from(COLLECTION_NAME) - .where("age").lt(23) - .and("type").eq("V") - .build(); - - List entitiesFound = entityManager.select(query).collect(Collectors.toList()); - assertEquals(1, entitiesFound.size()); - assertThat(entitiesFound).contains(entities.get(0)); - } - - @Test - void shouldFindDocumentLesserEqualsThan() { - DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); - entityManager.delete(deleteQuery); - Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); - List entities = StreamSupport.stream(entitiesSaved.spliterator(), false).toList(); - - SelectQuery query = select().from(COLLECTION_NAME) - .where("age").lte(23) - .and("type").eq("V") - .build(); - - List entitiesFound = entityManager.select(query).collect(Collectors.toList()); - assertEquals(2, entitiesFound.size()); - assertThat(entitiesFound).contains(entities.get(0), entities.get(2)); - } - - - @Test - void shouldFindDocumentIn() { - DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); - entityManager.delete(deleteQuery); - Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); - List entities = StreamSupport.stream(entitiesSaved.spliterator(), false).toList(); - - SelectQuery query = select().from(COLLECTION_NAME) - .where("location").in(asList("BR", "US")) - .and("type").eq("V") - .build(); - - Assertions.assertThat(entityManager.select(query).toList()).containsAll(entities); - } - - @Test - void shouldFindDocumentBetween() { - DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); - entityManager.delete(deleteQuery); - entityManager.insert(getEntitiesWithValues()); - - SelectQuery query = select().from(COLLECTION_NAME) - .where("age").between(22, 25) - .build(); - - - var entities = entityManager.select(query).toList(); - SoftAssertions.assertSoftly(softly -> { - softly.assertThat(entities).hasSize(2); - softly.assertThat(entities).extracting(e -> e.find("age").orElseThrow().get(Integer.class)) - .contains(22, 23); - softly.assertThat(entities).extracting(e -> e.find("name").orElseThrow().get(String.class)) - .contains("Luna", "Lucas"); - }); - - - - } - - @Test - void shouldFindDocumentStart() { - Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); - List entities = StreamSupport.stream(entitiesSaved.spliterator(), false).toList(); - - SelectQuery query = select().from(COLLECTION_NAME) - .where("age").gt(22) - .and("type").eq("V") - .skip(1L) - .build(); - - List entitiesFound = entityManager.select(query).collect(Collectors.toList()); - assertEquals(1, entitiesFound.size()); - assertThat(entitiesFound).isNotIn(entities.get(0)); - - query = select().from(COLLECTION_NAME) - .where("age").gt(22) - .and("type").eq("V") - .skip(2L) - .build(); - - entitiesFound = entityManager.select(query).collect(Collectors.toList()); - assertTrue(entitiesFound.isEmpty()); - - } - - @Test - void shouldFindDocumentLimit() { - DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); - entityManager.delete(deleteQuery); - Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); - List entities = StreamSupport.stream(entitiesSaved.spliterator(), false).toList(); - - SelectQuery query = select().from(COLLECTION_NAME) - .where("age").gt(22) - .and("type").eq("V") - .limit(1L) - .build(); - - List entitiesFound = entityManager.select(query).collect(Collectors.toList()); - assertEquals(1, entitiesFound.size()); - assertThat(entitiesFound).isNotIn(entities.get(0)); - - query = select().from(COLLECTION_NAME) - .where("age").gt(22) - .and("type").eq("V") - .limit(2L) - .build(); - - entitiesFound = entityManager.select(query).collect(Collectors.toList()); - assertEquals(2, entitiesFound.size()); - - } - - @Test - void shouldFindDocumentSort() { - DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).where("type").eq("V").build(); - entityManager.delete(deleteQuery); - Iterable entitiesSaved = entityManager.insert(getEntitiesWithValues()); - - SelectQuery query = select().from(COLLECTION_NAME) - .where("age").gt(22) - .and("type").eq("V") - .orderBy("age").asc() - .build(); - - List entitiesFound = entityManager.select(query).collect(Collectors.toList()); - assertEquals(2, entitiesFound.size()); - List ages = entitiesFound.stream() - .map(e -> e.find("age").orElseThrow().get(Integer.class)) - .collect(Collectors.toList()); - - assertThat(ages).contains(23, 25); - - query = select().from(COLLECTION_NAME) - .where("age").gt(22) - .and("type").eq("V") - .orderBy("age").desc() - .build(); - - entitiesFound = entityManager.select(query).toList(); - ages = entitiesFound.stream() - .map(e -> e.find("age").orElseThrow().get(Integer.class)) - .collect(Collectors.toList()); - assertEquals(2, entitiesFound.size()); - assertThat(ages).contains(25, 23); - - } - - @Test - void shouldFindAll() { - entityManager.insert(getEntity()); - SelectQuery query = select().from(COLLECTION_NAME).build(); - List entities = entityManager.select(query).toList(); - assertFalse(entities.isEmpty()); - } - - @Test - void shouldDeleteAll() { - entityManager.insert(getEntity()); - SelectQuery query = select().from(COLLECTION_NAME).build(); - List entities = entityManager.select(query).collect(Collectors.toList()); - assertFalse(entities.isEmpty()); - DeleteQuery deleteQuery = delete().from(COLLECTION_NAME).build(); - entityManager.delete(deleteQuery); - entities = entityManager.select(query).toList(); - assertTrue(entities.isEmpty()); - } - - @Test - void shouldFindAllByFields() { - entityManager.insert(getEntity()); - SelectQuery query = select("name").from(COLLECTION_NAME).build(); - List entities = entityManager.select(query).toList(); - assertFalse(entities.isEmpty()); - final CommunicationEntity entity = entities.get(0); - assertEquals(3, entity.size()); - SoftAssertions.assertSoftly(softly -> { - softly.assertThat(entity.find("name")).isPresent(); - softly.assertThat(entity.find("_id")).isPresent(); - softly.assertThat(entity.find("city")).isPresent(); - }); - } - - - - - private CommunicationEntity getEntity() { - CommunicationEntity entity = CommunicationEntity.of(COLLECTION_NAME); - Map map = new HashMap<>(); - map.put("name", "Poliana"); - map.put("city", "Salvador"); - List documents = Elements.of(map); - documents.forEach(entity::add); - return entity; - } - - private List getEntitiesWithValues() { - CommunicationEntity lucas = CommunicationEntity.of(COLLECTION_NAME); - lucas.add(Element.of("name", "Lucas")); - lucas.add(Element.of("age", 22)); - lucas.add(Element.of("location", "BR")); - lucas.add(Element.of("type", "V")); - - CommunicationEntity luna = CommunicationEntity.of(COLLECTION_NAME); - luna.add(Element.of("name", "Luna")); - luna.add(Element.of("age", 23)); - luna.add(Element.of("location", "US")); - luna.add(Element.of("type", "V")); - - CommunicationEntity otavio = CommunicationEntity.of(COLLECTION_NAME); - otavio.add(Element.of("name", "Otavio")); - otavio.add(Element.of("age", 25)); - otavio.add(Element.of("location", "BR")); - otavio.add(Element.of("type", "V")); - - - return asList(lucas, otavio, luna); - } -} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/GraphSupplier.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/GraphSupplier.java deleted file mode 100644 index afffb8e43..000000000 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/GraphSupplier.java +++ /dev/null @@ -1,45 +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.neo4j.communication; - -import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph; -import org.apache.tinkerpop.gremlin.structure.Graph; - -import java.io.File; -import java.util.function.Supplier; -import java.util.logging.Logger; - -import static java.lang.System.currentTimeMillis; - -public enum GraphSupplier implements Supplier { - INSTANCE; - - private static final Logger LOGGER = Logger.getLogger(GraphSupplier.class.getName()); - - private final String directory; - - private final Graph graph; - - { - this.directory = new File("").getAbsolutePath() + "/target/jnosql-communication-graph/" + currentTimeMillis() + "/"; - graph = Neo4jGraph.open(directory); - } - - @Override - public Graph get() { - LOGGER.info("Starting Graph database at directory: " + directory); - return graph; - } -} From b1396eaa3bd6aa414d8ad25f0f4654d86210e62a Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 18:43:06 +0000 Subject: [PATCH 009/148] feat: create initial structure to neo4j Signed-off-by: Otavio Santana --- .../communication/Neo4JConfigurations.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java new file mode 100644 index 000000000..72a9469fe --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java @@ -0,0 +1,20 @@ +/* + * + * 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.communication; + +public enum Neo4JConfigurations { +} From aa7453b7ff53cde841bac0cdd6c66e1edee9aa32 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 18:49:09 +0000 Subject: [PATCH 010/148] feat: create neo4jconfigurations Signed-off-by: Otavio Santana --- .../communication/Neo4JConfigurations.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java index 72a9469fe..ab67d608e 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java @@ -16,5 +16,20 @@ */ package org.eclipse.jnosql.databases.neo4j.communication; -public enum Neo4JConfigurations { +import java.util.function.Supplier; + +public enum Neo4JConfigurations implements Supplier { + + URI("jnosql.neo4j.uri"), USERNAME("jnosql.neo4j.username"), PASSWORD("jnosql.neo4j.password"); + + private final String value; + + Neo4JConfigurations(String value) { + this.value = value; + } + + @Override + public String get() { + return value; + } } From ba7cfa88d95e14627e9b9c954f36445a418ba1e2 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 18:53:38 +0000 Subject: [PATCH 011/148] docs: update documentation at neo4j Signed-off-by: Otavio Santana --- .../communication/Neo4JConfigurations.java | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java index ab67d608e..0810e3b36 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java @@ -18,9 +18,50 @@ import java.util.function.Supplier; +/** + * Enum Neo4JConfigurations + * This enum defines the configuration keys used in the Eclipse jNoSQL driver for Neo4j. + * Each configuration key is associated with a specific property required to connect to a Neo4j database. + * + *
    + *
  • URI: The connection URI for the Neo4j database (e.g., bolt://localhost:7687).
  • + *
  • USERNAME: The username used for authentication (e.g., "neo4j").
  • + *
  • PASSWORD: The password used for authentication (e.g., "password123").
  • + *
+ *

+ * This enum implements the Supplier interface, providing a method to retrieve the configuration key as a string. + * Usage example: + *

+ * String uri = Neo4JConfigurations.URI.get(); // Returns "jnosql.neo4j.uri"
+ * String username = Neo4JConfigurations.USERNAME.get(); // Returns "jnosql.neo4j.username"
+ * String password = Neo4JConfigurations.PASSWORD.get(); // Returns "jnosql.neo4j.password"
+ *
+ * // Example of configuring a connection:
+ * Map config = new HashMap<>();
+ * config.put(Neo4JConfigurations.URI.get(), "bolt://localhost:7687");
+ * config.put(Neo4JConfigurations.USERNAME.get(), "neo4j");
+ * config.put(Neo4JConfigurations.PASSWORD.get(), "password123");
+ * 
+ */ public enum Neo4JConfigurations implements Supplier { - URI("jnosql.neo4j.uri"), USERNAME("jnosql.neo4j.username"), PASSWORD("jnosql.neo4j.password"); + /** + * The URI of the Neo4j database. + * Example: bolt://localhost:7687 + */ + URI("jnosql.neo4j.uri"), + + /** + * The username for authentication. + * Example: "neo4j" + */ + USERNAME("jnosql.neo4j.username"), + + /** + * The password for authentication. + * Example: "password123" + */ + PASSWORD("jnosql.neo4j.password"); private final String value; From 1098377436deeca093ee372b97e64cdfdf87e3f4 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 19:06:51 +0000 Subject: [PATCH 012/148] feat: update neo4j configuration Signed-off-by: Otavio Santana --- .../communication/Neo4JConfiguration.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java new file mode 100644 index 000000000..e595e3c90 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java @@ -0,0 +1,33 @@ +/* + * + * 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.communication; + +import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration; +import org.eclipse.jnosql.communication.semistructured.DatabaseManagerFactory; + +import java.util.logging.Logger; + +public class Neo4JConfiguration implements DatabaseConfiguration { + + private static final Logger LOGGER = Logger.getLogger(Neo4JConfiguration.class.getName()); + + @Override + public DatabaseManagerFactory apply(Settings settings) { + return null; + } +} From ac84745ed9eab4c337974f9d95d4789c52f1632a Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 19:10:52 +0000 Subject: [PATCH 013/148] docs: enhance neo4j at configurations Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JConfigurations.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java index 0810e3b36..fb3edd256 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java @@ -37,10 +37,10 @@ * String password = Neo4JConfigurations.PASSWORD.get(); // Returns "jnosql.neo4j.password" * * // Example of configuring a connection: - * Map config = new HashMap<>(); - * config.put(Neo4JConfigurations.URI.get(), "bolt://localhost:7687"); - * config.put(Neo4JConfigurations.USERNAME.get(), "neo4j"); - * config.put(Neo4JConfigurations.PASSWORD.get(), "password123"); + * Settings settings = Settings.builder().put(Neo4JConfigurations.URI, "bolt://localhost:7687") + * .put(Neo4JConfigurations.USERNAME, "neo4j") + * .put(Neo4JConfigurations.PASSWORD, "password123") + * .build(); * */ public enum Neo4JConfigurations implements Supplier { From f836223eff0df95545323ce4d59cda8638d2ce9b Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 19:11:04 +0000 Subject: [PATCH 014/148] feat: create neo4j configuration Signed-off-by: Otavio Santana --- .../jnosql/databases/neo4j/communication/Neo4JConfiguration.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java index e595e3c90..f4b475c68 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java @@ -28,6 +28,7 @@ public class Neo4JConfiguration implements DatabaseConfiguration { @Override public DatabaseManagerFactory apply(Settings settings) { + Settings settings = Settings.builder().put("url", "bolt://localhost:7687").build(); return null; } } From f8b010cef02a5878b890215855866dbce82b23a1 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 19:13:26 +0000 Subject: [PATCH 015/148] feat: create neo4jproperty Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4Property.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4Property.java diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4Property.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4Property.java new file mode 100644 index 000000000..e429b74ad --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4Property.java @@ -0,0 +1,20 @@ +/* + * + * 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.communication; + +record Neo4Property(String uri, String user, String password) { +} From 40db18838845216fad87dff7fcc5959ee475c13d Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 19:19:47 +0000 Subject: [PATCH 016/148] feat: create neo4configuration Signed-off-by: Otavio Santana --- .../databases/neo4j/communication/Neo4JConfiguration.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java index f4b475c68..c8d9d1e42 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java @@ -19,16 +19,20 @@ import org.eclipse.jnosql.communication.Settings; import org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration; import org.eclipse.jnosql.communication.semistructured.DatabaseManagerFactory; - import java.util.logging.Logger; public class Neo4JConfiguration implements DatabaseConfiguration { private static final Logger LOGGER = Logger.getLogger(Neo4JConfiguration.class.getName()); + private static final String DEFAULT_BOLT = "bolt://localhost:7687"; + @Override public DatabaseManagerFactory apply(Settings settings) { - Settings settings = Settings.builder().put("url", "bolt://localhost:7687").build(); + var uri = settings.getOrDefault(Neo4JConfigurations.URI, DEFAULT_BOLT); + var user = settings.get(Neo4JConfigurations.USERNAME).or(null); + var password = settings.get(Neo4JConfigurations.PASSWORD).or(null); + LOGGER.info("Starting configuration to Neo4J database, the uri: " + uri); return null; } } From 8e6bb5855bc2f6ecaa83ed7c01573ea1b524e1c6 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 19:23:53 +0000 Subject: [PATCH 017/148] feat: enable database configuration Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JDatabaseManagerFactory.java | 4 ++++ ....jnosql.communication.semistructured.DatabaseConfiguration | 1 + 2 files changed, 5 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java create mode 100644 jnosql-neo4j/src/main/resources/META-INF/services/org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java new file mode 100644 index 000000000..c7cf3394a --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java @@ -0,0 +1,4 @@ +package org.eclipse.jnosql.databases.neo4j.communication; + +public class Neo4JDatabaseManagerFactory { +} diff --git a/jnosql-neo4j/src/main/resources/META-INF/services/org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration b/jnosql-neo4j/src/main/resources/META-INF/services/org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration new file mode 100644 index 000000000..2c3e81f80 --- /dev/null +++ b/jnosql-neo4j/src/main/resources/META-INF/services/org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration @@ -0,0 +1 @@ +org.eclipse.jnosql.databases.neo4j.communication.Neo4JConfiguration \ No newline at end of file From 5849c89db7a9937f42c8a76e431b959d8a8e5053 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 20 Jan 2025 19:24:07 +0000 Subject: [PATCH 018/148] feat: update neo4j configuration Signed-off-by: Otavio Santana --- .../databases/neo4j/communication/Neo4JConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java index c8d9d1e42..5ed0585e5 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java @@ -21,7 +21,7 @@ import org.eclipse.jnosql.communication.semistructured.DatabaseManagerFactory; import java.util.logging.Logger; -public class Neo4JConfiguration implements DatabaseConfiguration { +public final class Neo4JConfiguration implements DatabaseConfiguration { private static final Logger LOGGER = Logger.getLogger(Neo4JConfiguration.class.getName()); From d70d1f11710414cf360e3f5518702b36767b4423 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 21 Jan 2025 20:10:41 +0000 Subject: [PATCH 019/148] feat: create initial neo4propert structyre Signed-off-by: Otavio Santana --- .../databases/neo4j/communication/Neo4JConfiguration.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java index 5ed0585e5..f752cb599 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java @@ -30,9 +30,10 @@ public final class Neo4JConfiguration implements DatabaseConfiguration { @Override public DatabaseManagerFactory apply(Settings settings) { var uri = settings.getOrDefault(Neo4JConfigurations.URI, DEFAULT_BOLT); - var user = settings.get(Neo4JConfigurations.USERNAME).or(null); - var password = settings.get(Neo4JConfigurations.PASSWORD).or(null); + var user = settings.get(Neo4JConfigurations.USERNAME, String.class).orElse(null); + var password = settings.get(Neo4JConfigurations.PASSWORD, String.class).orElse(null); LOGGER.info("Starting configuration to Neo4J database, the uri: " + uri); + var neo4Property = new Neo4Property(uri, user, password); return null; } } From 7971ff17d1c802f340cd58727b9e0fce7981e39b Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 21 Jan 2025 20:13:58 +0000 Subject: [PATCH 020/148] docs: create documentation to neo4jconfiguration Signed-off-by: Otavio Santana --- .../communication/Neo4JConfiguration.java | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java index f752cb599..c577b375e 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java @@ -18,9 +18,34 @@ import org.eclipse.jnosql.communication.Settings; import org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration; -import org.eclipse.jnosql.communication.semistructured.DatabaseManagerFactory; import java.util.logging.Logger; +/** + * Class Neo4JConfiguration + * This class provides the configuration for the Neo4j database using the Eclipse jNoSQL framework. + * It implements the {@link DatabaseConfiguration} interface to set up a connection to a Neo4j database. + * + *

The configuration retrieves the following settings from the provided {@link Settings}: + *

    + *
  • {@link Neo4JConfigurations#URI}: The connection URI for the Neo4j database (e.g., "bolt://localhost:7687").
  • + *
  • {@link Neo4JConfigurations#USERNAME}: The username for authentication (optional).
  • + *
  • {@link Neo4JConfigurations#PASSWORD}: The password for authentication (optional).
  • + *
+ * + *

If no URI is provided, a default URI ("bolt://localhost:7687") is used. + * + *

Usage example: + *

+ * Settings settings = Settings.builder()
+ *         .put(Neo4JConfigurations.URI, "bolt://custom-host:7687")
+ *         .put(Neo4JConfigurations.USERNAME, "neo4j")
+ *         .put(Neo4JConfigurations.PASSWORD, "password123")
+ *         .build();
+ *
+ * Neo4JConfiguration configuration = new Neo4JConfiguration();
+ * Neo4JDatabaseManagerFactory factory = configuration.apply(settings);
+ * 
+ */ public final class Neo4JConfiguration implements DatabaseConfiguration { private static final Logger LOGGER = Logger.getLogger(Neo4JConfiguration.class.getName()); @@ -28,7 +53,7 @@ public final class Neo4JConfiguration implements DatabaseConfiguration { private static final String DEFAULT_BOLT = "bolt://localhost:7687"; @Override - public DatabaseManagerFactory apply(Settings settings) { + public Neo4JDatabaseManagerFactory apply(Settings settings) { var uri = settings.getOrDefault(Neo4JConfigurations.URI, DEFAULT_BOLT); var user = settings.get(Neo4JConfigurations.USERNAME, String.class).orElse(null); var password = settings.get(Neo4JConfigurations.PASSWORD, String.class).orElse(null); From 635501918a33a8dac1040dea18dfb9135fc5c5df Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 21 Jan 2025 20:15:12 +0000 Subject: [PATCH 021/148] feat: create initial structure to manager factory Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerFactory.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java index c7cf3394a..348669865 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java @@ -1,4 +1,26 @@ package org.eclipse.jnosql.databases.neo4j.communication; -public class Neo4JDatabaseManagerFactory { +import org.eclipse.jnosql.communication.semistructured.DatabaseManager; +import org.eclipse.jnosql.communication.semistructured.DatabaseManagerFactory; + +import java.util.Objects; + +public class Neo4JDatabaseManagerFactory implements DatabaseManagerFactory { + + private final Neo4Property property; + + Neo4JDatabaseManagerFactory(Neo4Property property) { + this.property = property; + } + + @Override + public void close() { + + } + + @Override + public DatabaseManager apply(String database) { + Objects.requireNonNull(database, "database is required"); + return null; + } } From 1b99f2bb23414aee926e99ae89530b586b39c251 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 21 Jan 2025 20:15:26 +0000 Subject: [PATCH 022/148] feat: updaste configuration returning diver Signed-off-by: Otavio Santana --- .../databases/neo4j/communication/Neo4JConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java index c577b375e..3a318b3b2 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java @@ -59,6 +59,6 @@ public Neo4JDatabaseManagerFactory apply(Settings settings) { var password = settings.get(Neo4JConfigurations.PASSWORD, String.class).orElse(null); LOGGER.info("Starting configuration to Neo4J database, the uri: " + uri); var neo4Property = new Neo4Property(uri, user, password); - return null; + return new Neo4JDatabaseManagerFactory(neo4Property); } } From 8dba6e311f50bd606050b91b22773e760fb19a4a Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 21 Jan 2025 20:16:13 +0000 Subject: [PATCH 023/148] feat: create initial structure for neo4jdatabase manager Signed-off-by: Otavio Santana --- .../communication/Neo4JDatabaseManager.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManager.java 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 new file mode 100644 index 000000000..ea7ee085a --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManager.java @@ -0,0 +1,20 @@ +/* + * + * 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.communication; + +public class Neo4JDatabaseManager { +} From a1bfe99afc538be39b690d20c933c9c0b79921ed Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 21 Jan 2025 20:16:31 +0000 Subject: [PATCH 024/148] feat: create header to neo4jdatabase manager factory Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerFactory.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java index 348669865..28a9fab71 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java @@ -1,3 +1,19 @@ +/* + * + * 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.communication; import org.eclipse.jnosql.communication.semistructured.DatabaseManager; From 7a5c95683fe07c253806ccbb52362251f7c82218 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 21 Jan 2025 20:25:51 +0000 Subject: [PATCH 025/148] feat: create and define neo4jconfiguration as statie method factory Signed-off-by: Otavio Santana --- .../databases/neo4j/communication/Neo4JConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java index 3a318b3b2..6c5851277 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java @@ -59,6 +59,6 @@ public Neo4JDatabaseManagerFactory apply(Settings settings) { var password = settings.get(Neo4JConfigurations.PASSWORD, String.class).orElse(null); LOGGER.info("Starting configuration to Neo4J database, the uri: " + uri); var neo4Property = new Neo4Property(uri, user, password); - return new Neo4JDatabaseManagerFactory(neo4Property); + return Neo4JDatabaseManagerFactory.of(neo4Property); } } From 25d27a98f13b556a87780bc72f39cdf9cf403281 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 21 Jan 2025 20:26:24 +0000 Subject: [PATCH 026/148] feat: create initial structure and the concept to neoj4 factory Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerFactory.java | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java index 28a9fab71..d9989b70c 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java @@ -18,25 +18,43 @@ import org.eclipse.jnosql.communication.semistructured.DatabaseManager; import org.eclipse.jnosql.communication.semistructured.DatabaseManagerFactory; +import org.neo4j.driver.AuthToken; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.Session; +import org.neo4j.driver.SessionConfig; import java.util.Objects; +import java.util.logging.Logger; public class Neo4JDatabaseManagerFactory implements DatabaseManagerFactory { - private final Neo4Property property; + private static final Logger LOGGER = Logger.getLogger(Neo4JDatabaseManagerFactory.class.getName()); - Neo4JDatabaseManagerFactory(Neo4Property property) { - this.property = property; + private final Driver driver; + + private Neo4JDatabaseManagerFactory(Driver driver) { + this.driver = driver; } @Override public void close() { - + this.driver.close(); } @Override - public DatabaseManager apply(String database) { + public Neo4JDatabaseManager apply(String database) { Objects.requireNonNull(database, "database is required"); - return null; + LOGGER.fine(() -> "Creating a new instance of Neo4JDatabaseManager with the database: " + database); + var session = driver.session(SessionConfig.builder().withDatabase(database).build()); + return new Neo4JDatabaseManager(session); + } + + static Neo4JDatabaseManagerFactory of(Neo4Property property) { + Objects.requireNonNull(property, "property is required"); + LOGGER.fine(() -> "Creating a new instance of Neo4JDatabaseManagerFactory with the uri: " + property.uri()); + AuthToken basic = AuthTokens.basic(property.user(), property.password()); + return new Neo4JDatabaseManagerFactory(GraphDatabase.driver(property.uri(), basic)); } } From bce5be55c5331b7e3fd617d69149ba56714a1fd0 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 21 Jan 2025 20:27:01 +0000 Subject: [PATCH 027/148] feat: include more logs at neo4jdatabase manager facatory Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JDatabaseManagerFactory.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java index d9989b70c..0496936e4 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java @@ -16,13 +16,11 @@ */ package org.eclipse.jnosql.databases.neo4j.communication; -import org.eclipse.jnosql.communication.semistructured.DatabaseManager; import org.eclipse.jnosql.communication.semistructured.DatabaseManagerFactory; import org.neo4j.driver.AuthToken; import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Driver; import org.neo4j.driver.GraphDatabase; -import org.neo4j.driver.Session; import org.neo4j.driver.SessionConfig; import java.util.Objects; @@ -40,6 +38,7 @@ private Neo4JDatabaseManagerFactory(Driver driver) { @Override public void close() { + LOGGER.info("Closing the Neo4J driver"); this.driver.close(); } From 30eab0ecc041d6b11fb31920517b70fa5d227035 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 21 Jan 2025 20:27:12 +0000 Subject: [PATCH 028/148] feat: create initial structure for neo4jdatabase manager Signed-off-by: Otavio Santana --- .../communication/Neo4JDatabaseManager.java | 70 ++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) 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 ea7ee085a..fc3b20712 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 @@ -16,5 +16,73 @@ */ package org.eclipse.jnosql.databases.neo4j.communication; -public class Neo4JDatabaseManager { +import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; +import org.eclipse.jnosql.communication.semistructured.DatabaseManager; +import org.eclipse.jnosql.communication.semistructured.DeleteQuery; +import org.eclipse.jnosql.communication.semistructured.SelectQuery; +import org.neo4j.driver.Session; + +import java.time.Duration; +import java.util.stream.Stream; + +public class Neo4JDatabaseManager implements DatabaseManager { + + public Neo4JDatabaseManager(Session session) { + + } + + @Override + public String name() { + return ""; + } + + @Override + public CommunicationEntity insert(CommunicationEntity entity) { + return null; + } + + @Override + public CommunicationEntity insert(CommunicationEntity entity, Duration ttl) { + return null; + } + + @Override + public Iterable insert(Iterable entities) { + return null; + } + + @Override + public Iterable insert(Iterable entities, Duration ttl) { + return null; + } + + @Override + public CommunicationEntity update(CommunicationEntity entity) { + return null; + } + + @Override + public Iterable update(Iterable entities) { + return null; + } + + @Override + public void delete(DeleteQuery query) { + + } + + @Override + public Stream select(SelectQuery query) { + return Stream.empty(); + } + + @Override + public long count(String entity) { + return 0; + } + + @Override + public void close() { + + } } From 968290bd3d020d5cc2ab8c1f535a1c08e02c37c6 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 21 Jan 2025 20:29:25 +0000 Subject: [PATCH 029/148] docs: create documentation for Neo4JDatabaseManagerFactory Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerFactory.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java index 0496936e4..4f44b7b82 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java @@ -26,6 +26,25 @@ import java.util.Objects; import java.util.logging.Logger; +/** + * This class provides a factory for creating and managing instances of {@link Neo4JDatabaseManager}, + * enabling communication with a Neo4j database. It implements the {@link DatabaseManagerFactory} interface + * and handles the lifecycle of the Neo4j {@link Driver}. + * + *

Main Responsibilities:

+ *
    + *
  • Establishing a connection to a Neo4j database using the provided URI, username, and password.
  • + *
  • Creating new instances of {@link Neo4JDatabaseManager} for a specific database.
  • + *
  • Managing the lifecycle of the Neo4j driver, including proper resource cleanup.
  • + *
+ * + * + *>Thread Safety: + * The factory is thread-safe as long as it is used properly. Ensure to close the factory + * when it is no longer needed to release resources. + * + */ + public class Neo4JDatabaseManagerFactory implements DatabaseManagerFactory { private static final Logger LOGGER = Logger.getLogger(Neo4JDatabaseManagerFactory.class.getName()); From 7c4fa7871e3a94d0d1c7c8e1d48d0dd9da0558bc Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 21 Jan 2025 20:32:17 +0000 Subject: [PATCH 030/148] docs: enhance documentation to neo4jdatabvase factory Signed-off-by: Otavio Santana --- .../communication/Neo4JDatabaseManagerFactory.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java index 4f44b7b82..3700d7cbe 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java @@ -37,14 +37,11 @@ *
  • Creating new instances of {@link Neo4JDatabaseManager} for a specific database.
  • *
  • Managing the lifecycle of the Neo4j driver, including proper resource cleanup.
  • * - * - * *>Thread Safety: * The factory is thread-safe as long as it is used properly. Ensure to close the factory * when it is no longer needed to release resources. - * + * At the close method, the factory closes the Neo4j {@link Driver} and releases resources. */ - public class Neo4JDatabaseManagerFactory implements DatabaseManagerFactory { private static final Logger LOGGER = Logger.getLogger(Neo4JDatabaseManagerFactory.class.getName()); @@ -55,6 +52,9 @@ private Neo4JDatabaseManagerFactory(Driver driver) { this.driver = driver; } + /** + * Closes the Neo4j driver and releases resources. + */ @Override public void close() { LOGGER.info("Closing the Neo4J driver"); @@ -66,7 +66,7 @@ public Neo4JDatabaseManager apply(String database) { Objects.requireNonNull(database, "database is required"); LOGGER.fine(() -> "Creating a new instance of Neo4JDatabaseManager with the database: " + database); var session = driver.session(SessionConfig.builder().withDatabase(database).build()); - return new Neo4JDatabaseManager(session); + return new Neo4JDatabaseManager(session, database); } static Neo4JDatabaseManagerFactory of(Neo4Property property) { From b73ccb0b479ccc0411ed86edf37b0fc2a745e58a Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Tue, 21 Jan 2025 20:33:41 +0000 Subject: [PATCH 031/148] feat: create initial structure for Neo4JDatabaseManager Signed-off-by: Otavio Santana --- .../communication/Neo4JDatabaseManager.java | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) 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 fc3b20712..4296bbb29 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 @@ -23,66 +23,82 @@ import org.neo4j.driver.Session; import java.time.Duration; +import java.util.Objects; +import java.util.logging.Logger; import java.util.stream.Stream; public class Neo4JDatabaseManager implements DatabaseManager { - public Neo4JDatabaseManager(Session session) { + private static final Logger LOGGER = Logger.getLogger(Neo4JDatabaseManager.class.getName()); + private final Session session; + private final String database; + + public Neo4JDatabaseManager(Session session, String database) { + this.session = session; + this.database = database; } @Override public String name() { - return ""; + return database; } @Override public CommunicationEntity insert(CommunicationEntity entity) { + Objects.requireNonNull(entity, "entity is required"); return null; } @Override public CommunicationEntity insert(CommunicationEntity entity, Duration ttl) { - return null; + throw new UnsupportedOperationException("This operation is not supported in Neo4J"); } @Override public Iterable insert(Iterable entities) { + Objects.requireNonNull(entities, "entities is required"); return null; } @Override public Iterable insert(Iterable entities, Duration ttl) { - return null; + throw new UnsupportedOperationException("This operation is not supported in Neo4J"); } @Override public CommunicationEntity update(CommunicationEntity entity) { + Objects.requireNonNull(entity, "entity is required"); return null; } @Override public Iterable update(Iterable entities) { + Objects.requireNonNull(entities, "entities is required"); return null; } @Override public void delete(DeleteQuery query) { + Objects.requireNonNull(query, "query is required"); } @Override public Stream select(SelectQuery query) { + Objects.requireNonNull(query, "query is required"); return Stream.empty(); } @Override public long count(String entity) { + Objects.requireNonNull(entity, "entity is required"); return 0; } @Override public void close() { - + LOGGER.fine("Closing the Neo4J session"); + this.session.close(); } } From 6ab24766ade6e2eaf6d774d2a7c50831f838c56f Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 19 Feb 2025 19:29:45 +0000 Subject: [PATCH 032/148] feat: create neo4jdatabase manager Signed-off-by: Otavio Santana --- .../communication/Neo4JDatabaseManager.java | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) 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 4296bbb29..85b6ed88e 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 @@ -16,13 +16,17 @@ */ package org.eclipse.jnosql.databases.neo4j.communication; +import org.eclipse.jnosql.communication.ValueUtil; import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; import org.eclipse.jnosql.communication.semistructured.DatabaseManager; import org.eclipse.jnosql.communication.semistructured.DeleteQuery; import org.eclipse.jnosql.communication.semistructured.SelectQuery; import org.neo4j.driver.Session; +import org.neo4j.driver.Transaction; +import org.neo4j.driver.Values; import java.time.Duration; +import java.util.Map; import java.util.Objects; import java.util.logging.Logger; import java.util.stream.Stream; @@ -44,10 +48,29 @@ public String name() { return database; } - @Override public CommunicationEntity insert(CommunicationEntity entity) { Objects.requireNonNull(entity, "entity is required"); - return null; + + Map entityMap = entity.toMap(); // Get all elements in a structured map + + try (Transaction tx = session.beginTransaction()) { + StringBuilder cypher = new StringBuilder("CREATE (e:"); + cypher.append(entity.name()).append(" {"); + + entityMap.keySet().forEach(key -> cypher.append(key).append(": $").append(key).append(", ")); + + // Remove the last comma and space, then close the bracket + if (!entityMap.isEmpty()) { + cypher.setLength(cypher.length() - 2); + } + cypher.append("})"); + + tx.run(cypher.toString(), Values.parameters(entityMap)); + tx.commit(); + } + + LOGGER.info("Inserted entity: " + entity.name()); + return entity; } @Override From ee4c0eec70766abc11bcbb3e7ee907ada3560d78 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Wed, 19 Feb 2025 19:33:01 +0000 Subject: [PATCH 033/148] feat: create neo4j database manager Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JDatabaseManager.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) 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 85b6ed88e..da3cbd3e4 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 @@ -28,6 +28,7 @@ import java.time.Duration; import java.util.Map; import java.util.Objects; +import java.util.UUID; import java.util.logging.Logger; import java.util.stream.Stream; @@ -51,7 +52,13 @@ public String name() { public CommunicationEntity insert(CommunicationEntity entity) { Objects.requireNonNull(entity, "entity is required"); - Map entityMap = entity.toMap(); // Get all elements in a structured map + if(!entity.contains("_id")) { + String generatedId = UUID.randomUUID().toString(); + LOGGER.fine("The entity does not contain an _id field. Generating one: " + generatedId); + entity.add("_id", generatedId); + } + + Map entityMap = entity.toMap(); try (Transaction tx = session.beginTransaction()) { StringBuilder cypher = new StringBuilder("CREATE (e:"); @@ -64,7 +71,7 @@ public CommunicationEntity insert(CommunicationEntity entity) { cypher.setLength(cypher.length() - 2); } cypher.append("})"); - + LOGGER.fine("Cypher: " + cypher); tx.run(cypher.toString(), Values.parameters(entityMap)); tx.commit(); } From 085c12afc2c3d441a66873202f94b31f6b3f711e Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 20 Feb 2025 08:01:00 +0000 Subject: [PATCH 034/148] feat: rename Neo4Jdatabase to Default Signed-off-by: Otavio Santana --- ...abaseManager.java => DefaultNeo4JDatabaseManager.java} | 7 +++---- .../neo4j/communication/Neo4JDatabaseManagerFactory.java | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) rename jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/{Neo4JDatabaseManager.java => DefaultNeo4JDatabaseManager.java} (93%) 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/DefaultNeo4JDatabaseManager.java similarity index 93% rename from jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManager.java rename to jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/DefaultNeo4JDatabaseManager.java index da3cbd3e4..3c7039cb3 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/DefaultNeo4JDatabaseManager.java @@ -16,7 +16,6 @@ */ package org.eclipse.jnosql.databases.neo4j.communication; -import org.eclipse.jnosql.communication.ValueUtil; import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; import org.eclipse.jnosql.communication.semistructured.DatabaseManager; import org.eclipse.jnosql.communication.semistructured.DeleteQuery; @@ -32,14 +31,14 @@ import java.util.logging.Logger; import java.util.stream.Stream; -public class Neo4JDatabaseManager implements DatabaseManager { +public class DefaultNeo4JDatabaseManager implements DatabaseManager { - private static final Logger LOGGER = Logger.getLogger(Neo4JDatabaseManager.class.getName()); + private static final Logger LOGGER = Logger.getLogger(DefaultNeo4JDatabaseManager.class.getName()); private final Session session; private final String database; - public Neo4JDatabaseManager(Session session, String database) { + public DefaultNeo4JDatabaseManager(Session session, String database) { this.session = session; this.database = database; } diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java index 3700d7cbe..4b88f7db8 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java @@ -27,14 +27,14 @@ import java.util.logging.Logger; /** - * This class provides a factory for creating and managing instances of {@link Neo4JDatabaseManager}, + * This class provides a factory for creating and managing instances of {@link DefaultNeo4JDatabaseManager}, * enabling communication with a Neo4j database. It implements the {@link DatabaseManagerFactory} interface * and handles the lifecycle of the Neo4j {@link Driver}. * *

    Main Responsibilities:

    *
      *
    • Establishing a connection to a Neo4j database using the provided URI, username, and password.
    • - *
    • Creating new instances of {@link Neo4JDatabaseManager} for a specific database.
    • + *
    • Creating new instances of {@link DefaultNeo4JDatabaseManager} for a specific database.
    • *
    • Managing the lifecycle of the Neo4j driver, including proper resource cleanup.
    • *
    *>Thread Safety: @@ -62,11 +62,11 @@ public void close() { } @Override - public Neo4JDatabaseManager apply(String database) { + public DefaultNeo4JDatabaseManager apply(String database) { Objects.requireNonNull(database, "database is required"); LOGGER.fine(() -> "Creating a new instance of Neo4JDatabaseManager with the database: " + database); var session = driver.session(SessionConfig.builder().withDatabase(database).build()); - return new Neo4JDatabaseManager(session, database); + return new DefaultNeo4JDatabaseManager(session, database); } static Neo4JDatabaseManagerFactory of(Neo4Property property) { From 79d654d5586ee290fca3e873bb52e0f18913e605 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 20 Feb 2025 08:02:03 +0000 Subject: [PATCH 035/148] feat: update Neo4jdatabase using the default interface Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 2 +- .../communication/Neo4JDatabaseManager.java | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManager.java 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 3c7039cb3..b074e31eb 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 @@ -31,7 +31,7 @@ import java.util.logging.Logger; import java.util.stream.Stream; -public class DefaultNeo4JDatabaseManager implements DatabaseManager { +public class DefaultNeo4JDatabaseManager implements Neo4JDatabaseManager { private static final Logger LOGGER = Logger.getLogger(DefaultNeo4JDatabaseManager.class.getName()); 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 new file mode 100644 index 000000000..2b6e4685d --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManager.java @@ -0,0 +1,22 @@ +/* + * + * 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.communication; + +import org.eclipse.jnosql.communication.semistructured.DatabaseManager; + +public interface Neo4JDatabaseManager extends DatabaseManager { +} From 145848a5f78cf9b47fb188dffac1d105f1c3602e Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 20 Feb 2025 08:14:14 +0000 Subject: [PATCH 036/148] docs: create javadoc for neo4j Signed-off-by: Otavio Santana --- .../communication/Neo4JDatabaseManager.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) 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 2b6e4685d..e73668162 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 @@ -18,5 +18,29 @@ import org.eclipse.jnosql.communication.semistructured.DatabaseManager; +/** + * This specialization of {@link DatabaseManager} is designed for Neo4j databases. + * + *

    Neo4j does not natively support Time-To-Live (TTL) operations, but TTL can be managed + * using the APOC library. Implementations of this interface should handle TTL-related methods + * accordingly—either by integrating APOC's TTL features or throwing {@link UnsupportedOperationException} + * if TTL is not supported.

    + * + *

    Usage Example:

    + *
    + * Neo4JDatabaseManager manager = ...; // Obtain an instance
    + * CommunicationEntity entity = CommunicationEntity.of("User");
    + * entity.add("name", "Alice");
    + * entity.add("age", 30);
    + *
    + * manager.insert(entity); // Insert into Neo4j
    + * 
    + * + *

    Implementation Notes:

    + *

    Ensure proper transaction and session management when implementing this interface. + * Unsupported TTL operations should result in an {@link UnsupportedOperationException}.

    + * + * @see DatabaseManager + */ public interface Neo4JDatabaseManager extends DatabaseManager { } From 2489800403a2a626bf3f738d66807ec3d02fda8b Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 20 Feb 2025 08:17:32 +0000 Subject: [PATCH 037/148] docs: include api note Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JDatabaseManager.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 e73668162..f867e85e5 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 @@ -26,6 +26,10 @@ * accordingly—either by integrating APOC's TTL features or throwing {@link UnsupportedOperationException} * if TTL is not supported.

    * + * @apiNote All write operations, including {@code insert} and {@code update}, will be executed within a transaction. + * When performing batch inserts using an iterable, the entire operation will be executed as a single transaction + * to ensure consistency. + * *

    Usage Example:

    *
      * Neo4JDatabaseManager manager = ...; // Obtain an instance
    @@ -36,9 +40,8 @@
      * manager.insert(entity); // Insert into Neo4j
      * 
    * - *

    Implementation Notes:

    - *

    Ensure proper transaction and session management when implementing this interface. - * Unsupported TTL operations should result in an {@link UnsupportedOperationException}.

    + * @apiNote Ensure proper transaction and session management when implementing this interface. + * Unsupported TTL operations should result in an {@link UnsupportedOperationException}. * * @see DatabaseManager */ From 10f738b71f10a76663ac96aa4aa796534380bc01 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 27 Feb 2025 19:42:15 +0000 Subject: [PATCH 038/148] chore: update neo4j on dependency Signed-off-by: Otavio Santana --- jnosql-neo4j/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jnosql-neo4j/pom.xml b/jnosql-neo4j/pom.xml index 6def0b8f1..f0558621f 100644 --- a/jnosql-neo4j/pom.xml +++ b/jnosql-neo4j/pom.xml @@ -30,6 +30,12 @@ + + org.testcontainers + neo4j + ${testcontainers.version} + test + org.eclipse.jnosql.mapping jnosql-mapping-semistructured From e99ec59c082fd599373a00a74e5c0862ed74c817 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 27 Feb 2025 19:43:53 +0000 Subject: [PATCH 039/148] chore: create database container Signed-off-by: Otavio Santana --- .../communication/DatabaseContainer.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java new file mode 100644 index 000000000..45d3ffadb --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 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.communication; + +public enum DatabaseContainer { +} From 6c652c0c996914f60f84a68a29f37d067d2be4ff Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 27 Feb 2025 19:58:25 +0000 Subject: [PATCH 040/148] feat: database container create Signed-off-by: Otavio Santana --- .../communication/DatabaseContainer.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java index 45d3ffadb..342dfd541 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java @@ -15,5 +15,30 @@ package org.eclipse.jnosql.databases.neo4j.communication; +import org.eclipse.jnosql.communication.Settings; +import org.testcontainers.containers.Neo4jContainer; +import org.testcontainers.utility.DockerImageName; + +import java.util.Objects; + public enum DatabaseContainer { + + INSTANCE; + + private final Neo4jContainer neo4jContainer = new Neo4jContainer<>(DockerImageName.parse("neo4j:lastest")) + .withoutAuthentication(); + + { + neo4jContainer.start(); + } + + + public Neo4JDatabaseManager get(String database) { + Objects.requireNonNull(database, "database is required"); + Settings settings = Settings.builder().put(Neo4JConfigurations.URI, neo4jContainer.getBoltUrl()).build(); + var configuration = new Neo4JConfiguration(); + var managerFactory = configuration.apply(settings); + return managerFactory.apply(database); + } + } From 7af128934ca51118922f1dd3251f0e4e82985888 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 27 Feb 2025 19:59:59 +0000 Subject: [PATCH 041/148] test: create structure databasemanager test Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerTest.java 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 new file mode 100644 index 000000000..80cdea094 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerTest.java @@ -0,0 +1,20 @@ +/* + * + * 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.communication; + +class Neo4JDatabaseManagerTest { +} From 7c4f6fc50d0ab7f8d8024ff4a92e7b4caa8e1a42 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 27 Feb 2025 20:04:39 +0000 Subject: [PATCH 042/148] test: create initial structure at neo4j Signed-off-by: Otavio Santana --- .../communication/DatabaseContainer.java | 2 +- .../Neo4JDatabaseManagerTest.java | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java index 342dfd541..f9ac2698e 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java @@ -25,7 +25,7 @@ public enum DatabaseContainer { INSTANCE; - private final Neo4jContainer neo4jContainer = new Neo4jContainer<>(DockerImageName.parse("neo4j:lastest")) + private final Neo4jContainer neo4jContainer = new Neo4jContainer<>(DockerImageName.parse("neo4j:5.26.3")) .withoutAuthentication(); { 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 80cdea094..3ae0d4e72 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 @@ -16,5 +16,53 @@ */ package org.eclipse.jnosql.databases.neo4j.communication; +import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; +import org.eclipse.jnosql.communication.semistructured.DatabaseManager; +import org.eclipse.jnosql.communication.semistructured.Element; +import org.eclipse.jnosql.communication.semistructured.Elements; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.eclipse.jnosql.communication.semistructured.DeleteQuery.delete; +import static org.junit.jupiter.api.Assertions.assertTrue; + class Neo4JDatabaseManagerTest { + + public static final String COLLECTION_NAME = "person"; + private static DatabaseManager entityManager; + + @BeforeAll + public static void setUp() { + entityManager = DatabaseContainer.INSTANCE.get("database"); + } + + @Test + void shouldInsert() { + var entity = getEntity(); + var documentEntity = entityManager.insert(entity); + assertTrue(documentEntity.elements().stream().map(Element::name).anyMatch(s -> s.equals("_id"))); + } + + @BeforeEach + void beforeEach() { + delete().from(COLLECTION_NAME).delete(entityManager); + } + + + + + private CommunicationEntity getEntity() { + CommunicationEntity entity = CommunicationEntity.of(COLLECTION_NAME); + Map map = new HashMap<>(); + map.put("name", "Poliana"); + map.put("city", "Salvador"); + List documents = Elements.of(map); + documents.forEach(entity::add); + return entity; + } } From 8b63f77f3af4b14e2cf705494b961c6977b034c7 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 27 Feb 2025 20:07:05 +0000 Subject: [PATCH 043/148] fix: remove the user and password be mandatory Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JDatabaseManagerFactory.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java index 4b88f7db8..3444aa575 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java @@ -72,7 +72,11 @@ public DefaultNeo4JDatabaseManager apply(String database) { static Neo4JDatabaseManagerFactory of(Neo4Property property) { Objects.requireNonNull(property, "property is required"); LOGGER.fine(() -> "Creating a new instance of Neo4JDatabaseManagerFactory with the uri: " + property.uri()); - AuthToken basic = AuthTokens.basic(property.user(), property.password()); + if(property.user() == null && property.password() == null) { + LOGGER.fine("Creating a new instance of Neo4JDatabaseManagerFactory without authentication"); + return new Neo4JDatabaseManagerFactory(GraphDatabase.driver(property.uri())); + } + var basic = AuthTokens.basic(property.user(), property.password()); return new Neo4JDatabaseManagerFactory(GraphDatabase.driver(property.uri(), basic)); } } From d67df0bc8fc2b5d977ad211072e1855e59d0e7ea Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 27 Feb 2025 20:14:13 +0000 Subject: [PATCH 044/148] feat: create neo4jdatabase manager factory to create automatically the graph Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerFactory.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java index 3444aa575..6b25e25ac 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java @@ -16,11 +16,14 @@ */ package org.eclipse.jnosql.databases.neo4j.communication; +import org.eclipse.jnosql.communication.CommunicationException; import org.eclipse.jnosql.communication.semistructured.DatabaseManagerFactory; import org.neo4j.driver.AuthToken; import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Driver; import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.Result; +import org.neo4j.driver.Session; import org.neo4j.driver.SessionConfig; import java.util.Objects; @@ -65,6 +68,12 @@ public void close() { public DefaultNeo4JDatabaseManager apply(String database) { Objects.requireNonNull(database, "database is required"); LOGGER.fine(() -> "Creating a new instance of Neo4JDatabaseManager with the database: " + database); + boolean databaseExists = databaseExists(database); + LOGGER.fine(() -> "Database " + database + " exists: " + databaseExists); + if (!databaseExists) { + LOGGER.fine(() -> "Database " + database + " does not exist. Creating it..."); + createDatabase(database); + } var session = driver.session(SessionConfig.builder().withDatabase(database).build()); return new DefaultNeo4JDatabaseManager(session, database); } @@ -79,4 +88,28 @@ static Neo4JDatabaseManagerFactory of(Neo4Property property) { var basic = AuthTokens.basic(property.user(), property.password()); return new Neo4JDatabaseManagerFactory(GraphDatabase.driver(property.uri(), basic)); } + + private boolean databaseExists(String database) { + try (Session session = driver.session()) { + Result result = session.run("SHOW DATABASES"); + while (result.hasNext()) { + if (database.equals(result.next().get("name").asString())) { + return true; + } + } + } catch (Exception e) { + LOGGER.severe("Failed to check database existence: " + e.getMessage()); + } + return false; + } + + private void createDatabase(String database) { + try (Session session = driver.session()) { + session.run("CREATE DATABASE " + database); + LOGGER.info("Database created: " + database); + } catch (Exception e) { + LOGGER.severe("Failed to create database: " + e.getMessage()); + throw new CommunicationException("Could not create database: " + database, e); + } + } } From 761117afbfba8c9128d7d55cfa35004ebad24302 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 27 Feb 2025 20:17:08 +0000 Subject: [PATCH 045/148] feat: defines neo4 database Signed-off-by: Otavio Santana --- .../databases/neo4j/communication/Neo4JDatabaseManagerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3ae0d4e72..b0a9c2a27 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 @@ -38,7 +38,7 @@ class Neo4JDatabaseManagerTest { @BeforeAll public static void setUp() { - entityManager = DatabaseContainer.INSTANCE.get("database"); + entityManager = DatabaseContainer.INSTANCE.get("neo4j"); } @Test From 66d71e0c8acc3b22ac7a8a5e9abbc497a8bab073 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 27 Feb 2025 20:21:33 +0000 Subject: [PATCH 046/148] feat: update the insert on neo4j Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) 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 b074e31eb..8aea95e17 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 @@ -34,6 +34,7 @@ public class DefaultNeo4JDatabaseManager implements Neo4JDatabaseManager { private static final Logger LOGGER = Logger.getLogger(DefaultNeo4JDatabaseManager.class.getName()); + public static final String ID = "_id"; private final Session session; private final String database; @@ -51,10 +52,10 @@ public String name() { public CommunicationEntity insert(CommunicationEntity entity) { Objects.requireNonNull(entity, "entity is required"); - if(!entity.contains("_id")) { + if (!entity.contains(ID)) { String generatedId = UUID.randomUUID().toString(); LOGGER.fine("The entity does not contain an _id field. Generating one: " + generatedId); - entity.add("_id", generatedId); + entity.add(ID, generatedId); } Map entityMap = entity.toMap(); @@ -65,17 +66,17 @@ public CommunicationEntity insert(CommunicationEntity entity) { entityMap.keySet().forEach(key -> cypher.append(key).append(": $").append(key).append(", ")); - // Remove the last comma and space, then close the bracket if (!entityMap.isEmpty()) { cypher.setLength(cypher.length() - 2); } cypher.append("})"); LOGGER.fine("Cypher: " + cypher); - tx.run(cypher.toString(), Values.parameters(entityMap)); + + tx.run(cypher.toString(), Values.parameters(flattenMap(entityMap))); tx.commit(); } - LOGGER.info("Inserted entity: " + entity.name()); + LOGGER.fine("Inserted entity: " + entity.name() + " with _id: " + entity.find(ID)); return entity; } @@ -130,4 +131,10 @@ public void close() { LOGGER.fine("Closing the Neo4J session"); this.session.close(); } + + private Object[] flattenMap(Map map) { + return map.entrySet().stream() + .flatMap(entry -> java.util.stream.Stream.of(entry.getKey(), entry.getValue())) + .toArray(); + } } From dc977f951988a436785fd5b16845a8ffc981dbcc Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 27 Feb 2025 20:24:18 +0000 Subject: [PATCH 047/148] feat: update structure on the class Signed-off-by: Otavio Santana --- .../communication/DefaultNeo4JDatabaseManager.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 8aea95e17..2432d0f95 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 @@ -80,11 +80,6 @@ public CommunicationEntity insert(CommunicationEntity entity) { return entity; } - @Override - public CommunicationEntity insert(CommunicationEntity entity, Duration ttl) { - throw new UnsupportedOperationException("This operation is not supported in Neo4J"); - } - @Override public Iterable insert(Iterable entities) { Objects.requireNonNull(entities, "entities is required"); @@ -137,4 +132,9 @@ private Object[] flattenMap(Map map) { .flatMap(entry -> java.util.stream.Stream.of(entry.getKey(), entry.getValue())) .toArray(); } + + @Override + public CommunicationEntity insert(CommunicationEntity entity, Duration ttl) { + throw new UnsupportedOperationException("This operation is not supported in Neo4J"); + } } From 38a123d76f7484fd490f01ff63350b73773bc087 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Thu, 27 Feb 2025 20:27:12 +0000 Subject: [PATCH 048/148] feat: implement on entity Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 71 +++++++++++-------- 1 file changed, 40 insertions(+), 31 deletions(-) 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 2432d0f95..48ed6213e 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,10 @@ import org.neo4j.driver.Values; import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; @@ -51,39 +55,13 @@ public String name() { public CommunicationEntity insert(CommunicationEntity entity) { Objects.requireNonNull(entity, "entity is required"); - - if (!entity.contains(ID)) { - String generatedId = UUID.randomUUID().toString(); - LOGGER.fine("The entity does not contain an _id field. Generating one: " + generatedId); - entity.add(ID, generatedId); - } - - Map entityMap = entity.toMap(); - - try (Transaction tx = session.beginTransaction()) { - StringBuilder cypher = new StringBuilder("CREATE (e:"); - cypher.append(entity.name()).append(" {"); - - entityMap.keySet().forEach(key -> cypher.append(key).append(": $").append(key).append(", ")); - - if (!entityMap.isEmpty()) { - cypher.setLength(cypher.length() - 2); - } - cypher.append("})"); - LOGGER.fine("Cypher: " + cypher); - - tx.run(cypher.toString(), Values.parameters(flattenMap(entityMap))); - tx.commit(); - } - - LOGGER.fine("Inserted entity: " + entity.name() + " with _id: " + entity.find(ID)); - return entity; + return insertEntities(Collections.singletonList(entity)).iterator().next(); } @Override public Iterable insert(Iterable entities) { Objects.requireNonNull(entities, "entities is required"); - return null; + return insertEntities(entities); } @Override @@ -91,6 +69,11 @@ public Iterable insert(Iterable entiti throw new UnsupportedOperationException("This operation is not supported in Neo4J"); } + @Override + public CommunicationEntity insert(CommunicationEntity entity, Duration ttl) { + throw new UnsupportedOperationException("This operation is not supported in Neo4J"); + } + @Override public CommunicationEntity update(CommunicationEntity entity) { Objects.requireNonNull(entity, "entity is required"); @@ -133,8 +116,34 @@ private Object[] flattenMap(Map map) { .toArray(); } - @Override - public CommunicationEntity insert(CommunicationEntity entity, Duration ttl) { - throw new UnsupportedOperationException("This operation is not supported in Neo4J"); + private Iterable insertEntities(Iterable entities) { + List entitiesResult = new ArrayList<>(); + try (Transaction tx = session.beginTransaction()) { + for (CommunicationEntity entity : entities) { + if (!entity.contains(ID)) { + String generatedId = UUID.randomUUID().toString(); + LOGGER.fine("The entity does not contain an _id field. Generating one: " + generatedId); + entity.add(ID, generatedId); + } + + Map properties = entity.toMap(); + StringBuilder cypher = new StringBuilder("CREATE (e:"); + cypher.append(entity.name()).append(" {"); + + properties.keySet().forEach(key -> cypher.append(key).append(": $").append(key).append(", ")); + + if (!properties.isEmpty()) { + cypher.setLength(cypher.length() - 2); + } + cypher.append("})"); + LOGGER.fine("Cypher: " + cypher); + + tx.run(cypher.toString(), Values.parameters(flattenMap(properties))); + entitiesResult.add(entity); + } + tx.commit(); + } + LOGGER.fine("Inserted entities: " + entitiesResult.size()); + return entitiesResult; } } From e8a01cfae0e89acccc92ca981c19198e29420511 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 05:29:59 +0000 Subject: [PATCH 049/148] feat: create count method Signed-off-by: Otavio Santana --- .../communication/DefaultNeo4JDatabaseManager.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) 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 48ed6213e..c85411220 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 @@ -16,6 +16,7 @@ */ package org.eclipse.jnosql.databases.neo4j.communication; +import org.eclipse.jnosql.communication.CommunicationException; import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; import org.eclipse.jnosql.communication.semistructured.DatabaseManager; import org.eclipse.jnosql.communication.semistructured.DeleteQuery; @@ -101,9 +102,16 @@ public Stream select(SelectQuery query) { @Override public long count(String entity) { Objects.requireNonNull(entity, "entity is required"); - return 0; + try (Transaction tx = session.beginTransaction()) { + String cypher = "MATCH (e:" + entity + ") RETURN count(e) AS count"; + long count = tx.run(cypher).single().get("count").asLong(); + tx.commit(); + return count; + } catch (Exception e) { + LOGGER.severe("Error executing count query: " + e.getMessage()); + throw new CommunicationException("Error executing count query", e); + } } - @Override public void close() { LOGGER.fine("Closing the Neo4J session"); From 84160a68696a27707e9fd05488ef701fbed4c798 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 05:30:18 +0000 Subject: [PATCH 050/148] test: create insert should insert entities scenario Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerTest.java | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) 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 b0a9c2a27..6f8fb2821 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 @@ -16,6 +16,8 @@ */ package org.eclipse.jnosql.databases.neo4j.communication; +import net.datafaker.Faker; +import org.assertj.core.api.SoftAssertions; import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; import org.eclipse.jnosql.communication.semistructured.DatabaseManager; import org.eclipse.jnosql.communication.semistructured.Element; @@ -44,8 +46,18 @@ public static void setUp() { @Test void shouldInsert() { var entity = getEntity(); - var documentEntity = entityManager.insert(entity); - assertTrue(documentEntity.elements().stream().map(Element::name).anyMatch(s -> s.equals("_id"))); + var communicationEntity = entityManager.insert(entity); + assertTrue(communicationEntity.elements().stream().map(Element::name).anyMatch(s -> s.equals("_id"))); + } + + @Test + void shouldInsertEntities() { + var entities = List.of(getEntity(), getEntity()); + var result = entityManager.insert(entities); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(result).hasSize(2); + softly.assertThat(result).allMatch(e -> e.elements().stream().map(Element::name).anyMatch(s -> s.equals("_id"))); + }); } @BeforeEach @@ -57,10 +69,13 @@ void beforeEach() { private CommunicationEntity getEntity() { + Faker faker = new Faker(); + CommunicationEntity entity = CommunicationEntity.of(COLLECTION_NAME); Map map = new HashMap<>(); - map.put("name", "Poliana"); - map.put("city", "Salvador"); + map.put("name", faker.name().fullName()); + map.put("city", faker.address().city()); + map.put("age", faker.number().randomNumber()); List documents = Elements.of(map); documents.forEach(entity::add); return entity; From 50a03e0d82c4b2741090583fb6897946cdb5d332 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 06:02:02 +0000 Subject: [PATCH 051/148] test: create scenario to account Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JDatabaseManagerTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 6f8fb2821..376203fc4 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 @@ -60,6 +60,14 @@ void shouldInsertEntities() { }); } + @Test + void shouldCount() { + var entity = getEntity(); + entityManager.insert(entity); + long count = entityManager.count(COLLECTION_NAME); + assertTrue(count > 0); + } + @BeforeEach void beforeEach() { delete().from(COLLECTION_NAME).delete(entityManager); From 2fa35b1ab8aee95c2836a6cbed614f99203c2cc0 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 06:05:16 +0000 Subject: [PATCH 052/148] feat: implement update Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) 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 c85411220..75d3cb364 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 @@ -78,7 +78,33 @@ public CommunicationEntity insert(CommunicationEntity entity, Duration ttl) { @Override public CommunicationEntity update(CommunicationEntity entity) { Objects.requireNonNull(entity, "entity is required"); - return null; + Objects.requireNonNull(entity, "entity is required"); + + if (!entity.contains(ID)) { + throw new CommunicationException("Cannot update entity without an _id field, entity: " + entity); + } + + Map entityMap = entity.toMap(); + StringBuilder cypher = new StringBuilder("MATCH (e:"); + cypher.append(entity.name()).append(" { ").append(ID).append(": $").append(ID).append(" }) SET "); + + entityMap.entrySet().stream() + .filter(entry -> !ID.equals(entry.getKey())) + .forEach(entry -> cypher.append("e.").append(entry.getKey()).append(" = $").append(entry.getKey()).append(", ")); + + if (cypher.toString().endsWith(", ")) { + cypher.setLength(cypher.length() - 2); + } + + LOGGER.fine("Cypher: " + cypher); + + try (Transaction tx = session.beginTransaction()) { + tx.run(cypher.toString(), Values.parameters(flattenMap(entityMap))); + tx.commit(); + } + + LOGGER.fine("Updated entity: " + entity.name() + " with _id: " + entity.find(ID).get().get(String.class)); + return entity; } @Override From 55cdee2fdf0a8a56a2c9e5e9151514b8116cb58a Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 06:09:42 +0000 Subject: [PATCH 053/148] feat: implement neo4j database mamanger Signed-off-by: Otavio Santana --- .../neo4j/communication/DefaultNeo4JDatabaseManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 75d3cb364..6a8f41121 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 @@ -103,13 +103,14 @@ public CommunicationEntity update(CommunicationEntity entity) { tx.commit(); } - LOGGER.fine("Updated entity: " + entity.name() + " with _id: " + entity.find(ID).get().get(String.class)); + LOGGER.fine("Updated entity: " + entity.name() + " with _id: " + entity.find(ID).orElseThrow().get(String.class)); return entity; } @Override public Iterable update(Iterable entities) { Objects.requireNonNull(entities, "entities is required"); + entities.forEach(this::update); return null; } From 087b19912fc294bbd88d674a9948561f5410e50c Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 06:09:55 +0000 Subject: [PATCH 054/148] test: create update scenario Signed-off-by: Otavio Santana --- .../communication/Neo4JDatabaseManagerTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) 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 376203fc4..710ae1fed 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 @@ -68,6 +68,23 @@ void shouldCount() { assertTrue(count > 0); } + @Test + void shouldUpdate() { + var entity = getEntity(); + var communicationEntity = entityManager.insert(entity); + var id = communicationEntity.find("_id").orElseThrow().get(); + var update = CommunicationEntity.of(COLLECTION_NAME); + update.add("_id", id); + update.add("name", "Lucas"); + + var result = entityManager.update(update); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(result).isNotNull(); + softly.assertThat(result.find("name").orElseThrow().get()).isEqualTo("Lucas"); + }); + assertTrue(result.find("name").isPresent()); + } + @BeforeEach void beforeEach() { delete().from(COLLECTION_NAME).delete(entityManager); From 65dc2c4b465f734b1997db3be2bfea30d5fa02bf Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 06:12:01 +0000 Subject: [PATCH 055/148] feat: implement update entities Signed-off-by: Otavio Santana --- .../neo4j/communication/DefaultNeo4JDatabaseManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6a8f41121..f5fdca623 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 @@ -111,7 +111,7 @@ public CommunicationEntity update(CommunicationEntity entity) { public Iterable update(Iterable entities) { Objects.requireNonNull(entities, "entities is required"); entities.forEach(this::update); - return null; + return entities; } @Override From 0e4b292e85fcd487cc299c3e16c6b4786ddd8857 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 06:12:17 +0000 Subject: [PATCH 056/148] test: update entities scenarios Signed-off-by: Otavio Santana --- .../communication/Neo4JDatabaseManagerTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) 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 710ae1fed..5e99fd8a8 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 @@ -85,6 +85,22 @@ void shouldUpdate() { assertTrue(result.find("name").isPresent()); } + @Test + void shouldUpdateEntities() { + var entity = getEntity(); + var communicationEntity = entityManager.insert(entity); + var id = communicationEntity.find("_id").orElseThrow().get(); + var update = CommunicationEntity.of(COLLECTION_NAME); + update.add("_id", id); + update.add("name", "Lucas"); + + var result = entityManager.update(List.of(update)); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(result).hasSize(1); + softly.assertThat(result).allMatch(e -> e.find("name").orElseThrow().get().equals("Lucas")); + }); + } + @BeforeEach void beforeEach() { delete().from(COLLECTION_NAME).delete(entityManager); From 61623d059a605998c582da2668cbf38783b8eb26 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 06:32:39 +0000 Subject: [PATCH 057/148] feat: update log with the database message Signed-off-by: Otavio Santana --- .../neo4j/communication/DefaultNeo4JDatabaseManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f5fdca623..bb3014676 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 @@ -141,7 +141,7 @@ public long count(String entity) { } @Override public void close() { - LOGGER.fine("Closing the Neo4J session"); + LOGGER.fine("Closing the Neo4J session, the database name is: " + database); this.session.close(); } From 62499eaee19cc2547d0aa4d05e3d1e8c6a612780 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 06:44:48 +0000 Subject: [PATCH 058/148] feat: enhance variable name at mongodb query conversor Signed-off-by: Otavio Santana --- .../mongodb/communication/DocumentQueryConversor.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jnosql-mongodb/src/main/java/org/eclipse/jnosql/databases/mongodb/communication/DocumentQueryConversor.java b/jnosql-mongodb/src/main/java/org/eclipse/jnosql/databases/mongodb/communication/DocumentQueryConversor.java index e64cbf787..21840cc2a 100644 --- a/jnosql-mongodb/src/main/java/org/eclipse/jnosql/databases/mongodb/communication/DocumentQueryConversor.java +++ b/jnosql-mongodb/src/main/java/org/eclipse/jnosql/databases/mongodb/communication/DocumentQueryConversor.java @@ -57,15 +57,15 @@ public static Bson convert(CriteriaCondition condition) { } case LIKE -> Filters.regex(document.name(), Pattern.compile(prepareRegexValue(value.toString()))); case AND -> { - List andList = condition.element().value().get(new TypeReference<>() { + List andConditions = condition.element().value().get(new TypeReference<>() { }); - yield Filters.and(andList.stream() + yield Filters.and(andConditions.stream() .map(DocumentQueryConversor::convert).toList()); } case OR -> { - List orList = condition.element().value().get(new TypeReference<>() { + List orConditions = condition.element().value().get(new TypeReference<>() { }); - yield Filters.or(orList.stream() + yield Filters.or(orConditions.stream() .map(DocumentQueryConversor::convert).toList()); } case BETWEEN -> { From 174108fe7452369a4b6b0948d39d9b7874f16b79 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 06:56:34 +0000 Subject: [PATCH 059/148] feat: create implementation on seqlec query Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 98 ++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) 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 bb3014676..e1af9a67b 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 @@ -17,9 +17,15 @@ package org.eclipse.jnosql.databases.neo4j.communication; import org.eclipse.jnosql.communication.CommunicationException; +import org.eclipse.jnosql.communication.Condition; +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.Value; +import org.eclipse.jnosql.communication.ValueUtil; import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; +import org.eclipse.jnosql.communication.semistructured.CriteriaCondition; import org.eclipse.jnosql.communication.semistructured.DatabaseManager; import org.eclipse.jnosql.communication.semistructured.DeleteQuery; +import org.eclipse.jnosql.communication.semistructured.Element; import org.eclipse.jnosql.communication.semistructured.SelectQuery; import org.neo4j.driver.Session; import org.neo4j.driver.Transaction; @@ -29,6 +35,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -123,9 +130,98 @@ public void delete(DeleteQuery query) { @Override public Stream select(SelectQuery query) { Objects.requireNonNull(query, "query is required"); - return Stream.empty(); + StringBuilder cypher = new StringBuilder("MATCH (e:"); + cypher.append(query.name()).append(")"); + + Map parameters = new HashMap<>(); + query.condition().ifPresent(c -> { + cypher.append(" WHERE "); + createWhereClause(cypher, c, parameters); + }); + + if (query.limit() > 0) { + cypher.append(" LIMIT ").append(query.limit()); + } + if (query.skip() > 0) { + cypher.append(" SKIP ").append(query.skip()); + } + + LOGGER.fine("Executing Cypher Query: " + cypher); + try (Transaction tx = session.beginTransaction()) { + return tx.run(cypher.toString(), Values.parameters(parameters)) + .list(record -> CommunicationEntity.of(query.name(), extractRecord(record))) + .stream(); + } } + private void createWhereClause(StringBuilder cypher, CriteriaCondition condition, Map parameters) { + Element element = condition.element(); + String fieldName = element.name(); + + switch (condition.condition()) { + case EQUALS: + case GREATER_THAN: + case GREATER_EQUALS_THAN: + case LESSER_THAN: + case LESSER_EQUALS_THAN: + case LIKE: + case IN: + parameters.put(fieldName, ValueUtil.convert(element.value())); + cypher.append("e.").append(fieldName).append(" ") + .append(getConditionOperator(condition.condition())) + .append(" $").append(fieldName); + break; + case BETWEEN: + List values = element.get(List.class); + parameters.put(fieldName + "_start", ValueUtil.convert(Value.of(values.get(0)))); + parameters.put(fieldName + "_end", ValueUtil.convert(Value.of(values.get(1)))); + cypher.append("e.").append(fieldName).append(" >= $").append(fieldName).append("_start AND e.") + .append(fieldName).append(" <= $").append(fieldName).append("_end"); + break; + case NOT: + cypher.append("NOT ("); + createWhereClause(cypher, element.get(CriteriaCondition.class), parameters); + cypher.append(")"); + break; + case AND: + case OR: + cypher.append("("); + List conditions = element.get(List.class); + for (int i = 0; i < conditions.size(); i++) { + if (i > 0) { + cypher.append(" ").append(getConditionOperator(condition.condition())).append(" "); + } + createWhereClause(cypher, conditions.get(i), parameters); + } + cypher.append(")"); + break; + default: + throw new CommunicationException("Unsupported condition: " + condition.condition()); + } + } + + private List extractRecord(org.neo4j.driver.Record record) { + List elements = new ArrayList<>(); + record.keys().forEach(key -> elements.add(Element.of(key, record.get(key).asObject()))); + return elements; + } + + private String getConditionOperator(Condition condition) { + switch (condition) { + case EQUALS: return "="; + case GREATER_THAN: return ">"; + case GREATER_EQUALS_THAN: return ">="; + case LESSER_THAN: return "<"; + case LESSER_EQUALS_THAN: return "<="; + case LIKE: return "CONTAINS"; + case IN: return "IN"; + case AND: return "AND"; + case OR: return "OR"; + default: throw new CommunicationException("Unsupported operator: " + condition); + } + } + + @Override public long count(String entity) { Objects.requireNonNull(entity, "entity is required"); From 62c253c855d124f38d86cf2bde1e598f5ea231f0 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 07:27:47 +0000 Subject: [PATCH 060/148] test: create id scenario Signed-off-by: Otavio Santana --- .../communication/Neo4JDatabaseManagerTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 5e99fd8a8..0789c2019 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 @@ -22,6 +22,7 @@ import org.eclipse.jnosql.communication.semistructured.DatabaseManager; import org.eclipse.jnosql.communication.semistructured.Element; import org.eclipse.jnosql.communication.semistructured.Elements; +import org.eclipse.jnosql.communication.semistructured.SelectQuery; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -29,6 +30,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import static org.eclipse.jnosql.communication.semistructured.DeleteQuery.delete; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -101,6 +103,19 @@ void shouldUpdateEntities() { }); } + @Test + void shouldSelectById() { + var entity = getEntity(); + var communicationEntity = entityManager.insert(entity); + var id = communicationEntity.find("_id").orElseThrow().get(); + var query = SelectQuery.select().from(COLLECTION_NAME).where("_id").eq(id).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(1); + softly.assertThat(entities).allMatch(e -> e.find("_id").isPresent()); + }); + } + @BeforeEach void beforeEach() { delete().from(COLLECTION_NAME).delete(entityManager); From 33f277319a9a216d34fd2ef0f9c7e53d8fbb2380 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 07:30:22 +0000 Subject: [PATCH 061/148] feat: implement the logic to find fields Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) 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 e1af9a67b..4490926ac 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 @@ -139,6 +139,13 @@ public Stream select(SelectQuery query) { createWhereClause(cypher, c, parameters); }); + cypher.append(" RETURN "); + List columns = query.columns(); + if (columns.isEmpty()) { + cypher.append("e "); + } else { + cypher.append(String.join(", ", columns)); + } if (query.limit() > 0) { cypher.append(" LIMIT ").append(query.limit()); } @@ -148,8 +155,8 @@ public Stream select(SelectQuery query) { LOGGER.fine("Executing Cypher Query: " + cypher); try (Transaction tx = session.beginTransaction()) { - return tx.run(cypher.toString(), Values.parameters(parameters)) - .list(record -> CommunicationEntity.of(query.name(), extractRecord(record))) + return tx.run(cypher.toString(), Values.parameters(flattenMap(parameters))) + .list(record -> extractEntity(query.name(), record, columns.isEmpty())) .stream(); } } @@ -200,25 +207,37 @@ private void createWhereClause(StringBuilder cypher, CriteriaCondition condition } } - private List extractRecord(org.neo4j.driver.Record record) { + private CommunicationEntity extractEntity(String entityName, org.neo4j.driver.Record record, boolean isFullNode) { List elements = new ArrayList<>(); - record.keys().forEach(key -> elements.add(Element.of(key, record.get(key).asObject()))); - return elements; + + for (String key : record.keys()) { + if (isFullNode && record.get(key).hasType(org.neo4j.driver.types.TypeSystem.getDefault().NODE())) { + record.get(key).asNode().asMap().forEach((k, v) -> elements.add(Element.of(k, v))); + } else { + elements.add(Element.of(key, record.get(key).asObject())); + } + } + + if (elements.isEmpty()) { + throw new CommunicationException("No valid entity found in the record"); + } + + return CommunicationEntity.of(entityName, elements); } private String getConditionOperator(Condition condition) { - switch (condition) { - case EQUALS: return "="; - case GREATER_THAN: return ">"; - case GREATER_EQUALS_THAN: return ">="; - case LESSER_THAN: return "<"; - case LESSER_EQUALS_THAN: return "<="; - case LIKE: return "CONTAINS"; - case IN: return "IN"; - case AND: return "AND"; - case OR: return "OR"; - default: throw new CommunicationException("Unsupported operator: " + condition); - } + return switch (condition) { + case EQUALS -> "="; + case GREATER_THAN -> ">"; + case GREATER_EQUALS_THAN -> ">="; + case LESSER_THAN -> "<"; + case LESSER_EQUALS_THAN -> "<="; + case LIKE -> "CONTAINS"; + case IN -> "IN"; + case AND -> "AND"; + case OR -> "OR"; + default -> throw new CommunicationException("Unsupported operator: " + condition); + }; } From 1163deddde0235f2326ec8802e7051747a17f742 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 07:30:30 +0000 Subject: [PATCH 062/148] test: create query by feilds Signed-off-by: Otavio Santana --- .../communication/Neo4JDatabaseManagerTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) 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 0789c2019..4ddcd22e4 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 @@ -116,6 +116,19 @@ void shouldSelectById() { }); } + @Test + void shouldSelectByIdWithName() { + var entity = getEntity(); + var communicationEntity = entityManager.insert(entity); + var id = communicationEntity.find("_id").orElseThrow().get(); + var query = SelectQuery.select("name").from(COLLECTION_NAME).where("_id").eq(id).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(1); + softly.assertThat(entities).allMatch(e -> e.find("_id").isPresent()); + }); + } + @BeforeEach void beforeEach() { delete().from(COLLECTION_NAME).delete(entityManager); From bb09d45b2a9d0ac00d4cd9644a36a6a75faec7be Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 08:30:14 +0000 Subject: [PATCH 063/148] feat: generate test to field Signed-off-by: Otavio Santana --- .../communication/DefaultNeo4JDatabaseManager.java | 12 ++++-------- .../communication/Neo4JDatabaseManagerTest.java | 4 ++-- 2 files changed, 6 insertions(+), 10 deletions(-) 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 4490926ac..5e06fde7d 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 @@ -41,6 +41,7 @@ import java.util.Objects; import java.util.UUID; import java.util.logging.Logger; +import java.util.stream.Collectors; import java.util.stream.Stream; public class DefaultNeo4JDatabaseManager implements Neo4JDatabaseManager { @@ -144,7 +145,7 @@ public Stream select(SelectQuery query) { if (columns.isEmpty()) { cypher.append("e "); } else { - cypher.append(String.join(", ", columns)); + cypher.append(columns.stream().map(col -> "e." + col).collect(Collectors.joining(", "))); } if (query.limit() > 0) { cypher.append(" LIMIT ").append(query.limit()); @@ -211,20 +212,15 @@ private CommunicationEntity extractEntity(String entityName, org.neo4j.driver.Re List elements = new ArrayList<>(); for (String key : record.keys()) { + String fieldName = key.contains(".") ? key.substring(key.indexOf('.') + 1) : key; if (isFullNode && record.get(key).hasType(org.neo4j.driver.types.TypeSystem.getDefault().NODE())) { record.get(key).asNode().asMap().forEach((k, v) -> elements.add(Element.of(k, v))); } else { - elements.add(Element.of(key, record.get(key).asObject())); + elements.add(Element.of(fieldName, record.get(key).asObject())); } } - - if (elements.isEmpty()) { - throw new CommunicationException("No valid entity found in the record"); - } - return CommunicationEntity.of(entityName, elements); } - private String getConditionOperator(Condition condition) { return switch (condition) { case EQUALS -> "="; 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 4ddcd22e4..9f0405999 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 @@ -121,11 +121,11 @@ void shouldSelectByIdWithName() { var entity = getEntity(); var communicationEntity = entityManager.insert(entity); var id = communicationEntity.find("_id").orElseThrow().get(); - var query = SelectQuery.select("name").from(COLLECTION_NAME).where("_id").eq(id).build(); + var query = SelectQuery.select("name", "city").from(COLLECTION_NAME).where("_id").eq(id).build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(1); - softly.assertThat(entities).allMatch(e -> e.find("_id").isPresent()); + softly.assertThat(entities.get(0).elements()).hasSize(2); }); } From 90edc74b277a1da3fdb26db8ea18f72f05d8bd96 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 08:43:02 +0000 Subject: [PATCH 064/148] feat: update skip and limit order Signed-off-by: Otavio Santana --- .../neo4j/communication/DefaultNeo4JDatabaseManager.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 5e06fde7d..3cbc12fae 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 @@ -147,12 +147,12 @@ public Stream select(SelectQuery query) { } else { cypher.append(columns.stream().map(col -> "e." + col).collect(Collectors.joining(", "))); } - if (query.limit() > 0) { - cypher.append(" LIMIT ").append(query.limit()); - } if (query.skip() > 0) { cypher.append(" SKIP ").append(query.skip()); } + if (query.limit() > 0) { + cypher.append(" LIMIT ").append(query.limit()); + } LOGGER.fine("Executing Cypher Query: " + cypher); try (Transaction tx = session.beginTransaction()) { From 4cc1af5246b3fb0b98227300347d2009f98397d4 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 08:43:17 +0000 Subject: [PATCH 065/148] feat: create test scenario to skip and limit Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) 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 9f0405999..1a8c6da87 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 @@ -27,6 +27,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -126,6 +127,44 @@ void shouldSelectByIdWithName() { SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(1); softly.assertThat(entities.get(0).elements()).hasSize(2); + softly.assertThat(entities.get(0).contains("name")).isTrue(); + softly.assertThat(entities.get(0).contains("city")).isTrue(); + }); + } + + @Test + void shouldLimit() { + for (int index = 0; index < 10; index++) { + entityManager.insert(getEntity()); + } + var query = SelectQuery.select().from(COLLECTION_NAME).limit(5).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(5); + }); + } + + @Test + void shouldStart() { + for (int index = 0; index < 10; index++) { + entityManager.insert(getEntity()); + } + var query = SelectQuery.select().from(COLLECTION_NAME).skip(5).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(5); + }); + } + + @Test + void shouldStartAndLimit() { + for (int index = 0; index < 10; index++) { + entityManager.insert(getEntity()); + } + var query = SelectQuery.select().from(COLLECTION_NAME).skip(5).limit(2).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(2); }); } From 0032a14338181a583816cf3cedadd4dea5e1a19e Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 08:47:54 +0000 Subject: [PATCH 066/148] feat: update the order logic Signed-off-by: Otavio Santana --- .../neo4j/communication/DefaultNeo4JDatabaseManager.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 3cbc12fae..83476faab 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 @@ -16,6 +16,7 @@ */ package org.eclipse.jnosql.databases.neo4j.communication; +import jakarta.data.Sort; import org.eclipse.jnosql.communication.CommunicationException; import org.eclipse.jnosql.communication.Condition; import org.eclipse.jnosql.communication.TypeReference; @@ -153,6 +154,13 @@ public Stream select(SelectQuery query) { if (query.limit() > 0) { cypher.append(" LIMIT ").append(query.limit()); } + List> sorts = query.sorts(); + if (!sorts.isEmpty()) { + cypher.append(" ORDER BY "); + cypher.append(sorts.stream() + .map(order -> "n." + order.property() + " " + (order.isAscending() ? "ASC" : "DESC")) + .collect(Collectors.joining(", "))); + } LOGGER.fine("Executing Cypher Query: " + cypher); try (Transaction tx = session.beginTransaction()) { From eb03b314a175e8e8c75f50833707de10b14f1295 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 08:52:00 +0000 Subject: [PATCH 067/148] test: generate scenario to asc order Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 2 +- .../Neo4JDatabaseManagerTest.java | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) 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 83476faab..09b601c00 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 @@ -158,7 +158,7 @@ public Stream select(SelectQuery query) { if (!sorts.isEmpty()) { cypher.append(" ORDER BY "); cypher.append(sorts.stream() - .map(order -> "n." + order.property() + " " + (order.isAscending() ? "ASC" : "DESC")) + .map(order -> "e." + order.property() + " " + (order.isAscending() ? "ASC" : "DESC")) .collect(Collectors.joining(", "))); } 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 1a8c6da87..142995e3a 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 @@ -168,6 +168,24 @@ void shouldStartAndLimit() { }); } + @Test + void shouldTOrderAsc(){ + for (int index = 0; index < 10; index++) { + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + } + + var query = SelectQuery.select().from(COLLECTION_NAME).orderBy("index").asc().build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(10); + softly.assertThat(entities).allMatch(e -> e.find("index").isPresent()); + softly.assertThat(entities).extracting(e -> e.find("index").orElseThrow().get()) + .containsExactly(0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L); + }); + } + @BeforeEach void beforeEach() { delete().from(COLLECTION_NAME).delete(entityManager); From ef51b1b78f872a0ff00f0044dd518f79a85acf66 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Fri, 28 Feb 2025 08:53:07 +0000 Subject: [PATCH 068/148] test: generate order desc Signed-off-by: Otavio Santana --- .../communication/Neo4JDatabaseManagerTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) 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 142995e3a..47419936e 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 @@ -186,6 +186,23 @@ void shouldTOrderAsc(){ }); } + @Test + void shouldOrderDesc() { + for (int index = 0; index < 10; index++) { + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + } + var query = SelectQuery.select().from(COLLECTION_NAME).orderBy("index").desc().build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(10); + softly.assertThat(entities).allMatch(e -> e.find("index").isPresent()); + softly.assertThat(entities).extracting(e -> e.find("index").orElseThrow().get()) + .containsExactly(9L, 8L, 7L, 6L, 5L, 4L, 3L, 2L, 1L, 0L); + }); + } + @BeforeEach void beforeEach() { delete().from(COLLECTION_NAME).delete(entityManager); From 82b58a5b11800a2dfbdbd5c479d0f7402a1ae90d Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 03:28:11 +0000 Subject: [PATCH 069/148] test: update order desc Signed-off-by: Otavio Santana --- .../communication/Neo4JDatabaseManagerTest.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) 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 47419936e..1fc5285e1 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 @@ -46,6 +46,11 @@ public static void setUp() { entityManager = DatabaseContainer.INSTANCE.get("neo4j"); } + @BeforeEach + void beforeEach() { + delete().from(COLLECTION_NAME).delete(entityManager); + } + @Test void shouldInsert() { var entity = getEntity(); @@ -203,14 +208,6 @@ void shouldOrderDesc() { }); } - @BeforeEach - void beforeEach() { - delete().from(COLLECTION_NAME).delete(entityManager); - } - - - - private CommunicationEntity getEntity() { Faker faker = new Faker(); From 032504dcfe356c660b0ea4b9ed076dbf1ff188ce Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 03:34:41 +0000 Subject: [PATCH 070/148] test: create equals and not equals scenarions Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerTest.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) 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 1fc5285e1..64afc431b 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 @@ -208,6 +208,35 @@ void shouldOrderDesc() { }); } + @Test + void shouldFindEquals() { + var entity = getEntity(); + entityManager.insert(entity); + var query = SelectQuery.select().from(COLLECTION_NAME).where("name").eq(entity.find("name").orElseThrow().get()).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(1); + softly.assertThat(entities).allMatch(e -> e.find("name").isPresent()); + }); + } + + @Test + void shouldFindNotEquals(){ + var entity = getEntity(); + entityManager.insert(entity); + entityManager.insert(getEntity()); + Object name = entity.find("name").orElseThrow().get(); + var query = SelectQuery.select().from(COLLECTION_NAME).where("name").not() + .eq(name).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(1); + softly.assertThat(entities).allMatch(e -> !e.find("name").orElseThrow().get().equals(name)); + }); + } + + + private CommunicationEntity getEntity() { Faker faker = new Faker(); From 80dbb136acba873a74d6aa94c8de7b211af9c27c Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 03:49:59 +0000 Subject: [PATCH 071/148] test: create lesser scenarion Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerTest.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) 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 64afc431b..263275f80 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 @@ -235,6 +235,69 @@ void shouldFindNotEquals(){ }); } + @Test + void shouldGreaterThan() { + for (int index = 0; index < 10; index++) { + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + } + var index = 4; + var query = SelectQuery.select().from(COLLECTION_NAME).where("index").gt(index).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(5); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)> index); + }); + } + + @Test + void shouldGreaterThanEqual() { + for (int index = 0; index < 10; index++) { + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + } + var index = 4; + var query = SelectQuery.select().from(COLLECTION_NAME).where("index").gte(index).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(6); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)>= index); + }); + } + + @Test + void shouldLesserThan() { + for (int index = 0; index < 10; index++) { + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + } + var index = 4; + var query = SelectQuery.select().from(COLLECTION_NAME).where("index").lt(index).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(4); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class) < index); + }); + } + + @Test + void shouldLesserThanEqual() { + for (int index = 0; index < 10; index++) { + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + } + var index = 4; + var query = SelectQuery.select().from(COLLECTION_NAME).where("index").lte(index).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(5); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)<= index); + }); + } private CommunicationEntity getEntity() { From 7e5a108f17c2db566032b1939f8a3e818517e047 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 03:53:14 +0000 Subject: [PATCH 072/148] test: create like Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerTest.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) 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 263275f80..4f4acef21 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 @@ -299,6 +299,35 @@ void shouldLesserThanEqual() { }); } + @Test + void shouldFindIn() { + for (int index = 0; index < 10; index++) { + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + } + var query = SelectQuery.select().from(COLLECTION_NAME).where("index").in(List.of(1, 2, 3)).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(3); + softly.assertThat(entities).allMatch(e -> Stream.of(1, 2, 3) + .anyMatch(i -> i.equals(e.find("index").orElseThrow().get(Integer.class)))); + }); + } + + @Test + void shouldLike() { + var entity = getEntity(); + entity.add("name", "Ada Lovelace"); + entityManager.insert(entity); + var query = SelectQuery.select().from(COLLECTION_NAME).where("name").like("Love").build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(1); + softly.assertThat(entities).allMatch(e -> e.find("name").orElseThrow().get().toString().contains("Love")); + }); + } + private CommunicationEntity getEntity() { Faker faker = new Faker(); From 0b43a900a7b3895817416cf9f80bc27b21f4871a Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 04:03:00 +0000 Subject: [PATCH 073/148] test: update test scenarion Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerTest.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) 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 4f4acef21..d5adc7dfe 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 @@ -31,6 +31,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Predicate; import java.util.stream.Stream; import static org.eclipse.jnosql.communication.semistructured.DeleteQuery.delete; @@ -328,6 +329,53 @@ void shouldLike() { }); } + @Test + void shouldAnd() { + for (int index = 0; index < 10; index++) { + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + } + var index = 4; + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + var query = SelectQuery.select().from(COLLECTION_NAME).where("index") + .gte(index).and("name") + .eq(entity.find("name") + .orElseThrow().get()).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(1); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)<= index); + }); + } + + @Test + void shouldOr() { + for (int index = 0; index < 10; index++) { + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + } + var index = 4; + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + var name = entity.find("name") + .orElseThrow().get(String.class); + var query = SelectQuery.select().from(COLLECTION_NAME).where("index") + .gte(index).or("name") + .eq(name).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(7); + Predicate get = e -> e.find("index").orElseThrow().get(Integer.class) >= index; + Predicate eq = e -> e.find("name").orElseThrow().get(String.class).equals(name); + softly.assertThat(entities).allMatch(get.or(eq)); + }); + } + private CommunicationEntity getEntity() { Faker faker = new Faker(); From 924c9bc231b017876db7d35d266f4db7afa260fb Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 04:05:45 +0000 Subject: [PATCH 074/148] feat: implement delete query Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 09b601c00..d70bf490c 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 @@ -126,7 +126,22 @@ public Iterable update(Iterable entiti @Override public void delete(DeleteQuery query) { Objects.requireNonNull(query, "query is required"); + StringBuilder cypher = new StringBuilder("MATCH (e:"); + cypher.append(query.name()).append(")"); + + Map parameters = new HashMap<>(); + query.condition().ifPresent(c -> { + cypher.append(" WHERE "); + createWhereClause(cypher, c, parameters); + }); + + cypher.append(" DELETE e"); + LOGGER.fine("Executing Delete Cypher Query: " + cypher); + try (Transaction tx = session.beginTransaction()) { + tx.run(cypher.toString(), Values.parameters(flattenMap(parameters))); + tx.commit(); + } } @Override From a76d27ba2f33458c20fbab6b476adc14ba5c9079 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 04:07:14 +0000 Subject: [PATCH 075/148] feat: implement neo4j Signed-off-by: Otavio Santana --- .../neo4j/communication/DefaultNeo4JDatabaseManager.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 d70bf490c..c2a67c4e5 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 @@ -135,7 +135,13 @@ public void delete(DeleteQuery query) { createWhereClause(cypher, c, parameters); }); - cypher.append(" DELETE e"); + List columns = query.columns(); + if (!columns.isEmpty()) { + cypher.append(" SET "); + cypher.append(columns.stream().map(col -> "e." + col + " = NULL").collect(Collectors.joining(", "))); + } else { + cypher.append(" DELETE e"); + } LOGGER.fine("Executing Delete Cypher Query: " + cypher); try (Transaction tx = session.beginTransaction()) { From 8c1d7f6ec815bec35695d047d8f6cafb7f8d0684 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 04:10:54 +0000 Subject: [PATCH 076/148] feat: create neo4jbuilder structure Signed-off-by: Otavio Santana --- .../communication/Neo4JQueryBuilder.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java new file mode 100644 index 000000000..476a8958b --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java @@ -0,0 +1,20 @@ +/* + * + * 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.communication; + +public class Neo4JQueryBuilder { +} From 14bc0e0563d96999915f8eca798309864dfe19e8 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 04:13:52 +0000 Subject: [PATCH 077/148] feat: update neo4 query builder Signed-off-by: Otavio Santana --- .../communication/Neo4JQueryBuilder.java | 112 +++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java index 476a8958b..679eb530c 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java @@ -16,5 +16,113 @@ */ package org.eclipse.jnosql.databases.neo4j.communication; -public class Neo4JQueryBuilder { -} + +import org.eclipse.jnosql.communication.CommunicationException; +import org.eclipse.jnosql.communication.Condition; +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.semistructured.CriteriaCondition; +import org.eclipse.jnosql.communication.semistructured.DeleteQuery; +import org.eclipse.jnosql.communication.semistructured.Element; +import org.eclipse.jnosql.communication.semistructured.SelectQuery; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public enum Neo4JQueryBuilder { + INSTANCE; + + public String buildQuery(DeleteQuery query, Map parameters) { + StringBuilder cypher = new StringBuilder("MATCH (e:"); + cypher.append(query.name()).append(")"); + + query.condition().ifPresent(c -> { + cypher.append(" WHERE "); + createWhereClause(cypher, c, parameters); + }); + + List columns = query.columns(); + if (!columns.isEmpty()) { + cypher.append(" SET "); + cypher.append(columns.stream().map(col -> "e." + col + " = NULL").collect(Collectors.joining(", "))); + } else { + cypher.append(" DELETE e"); + } + + return cypher.toString(); + } + + public String buildQuery(SelectQuery query, Map parameters) { + StringBuilder cypher = new StringBuilder("MATCH (e:"); + cypher.append(query.name()).append(")"); + + query.condition().ifPresent(c -> { + cypher.append(" WHERE "); + createWhereClause(cypher, c, parameters); + }); + + cypher.append(" RETURN "); + List columns = query.columns(); + if (columns.isEmpty()) { + cypher.append("e"); + } else { + cypher.append(columns.stream().map(col -> "e." + col).collect(Collectors.joining(", "))); + } + + return cypher.toString(); + } + + private void createWhereClause(StringBuilder cypher, CriteriaCondition condition, Map parameters) { + Element element = condition.element(); + String fieldName = element.name(); + + switch (condition.condition()) { + case EQUALS: + case GREATER_THAN: + case GREATER_EQUALS_THAN: + case LESSER_THAN: + case LESSER_EQUALS_THAN: + case LIKE: + case IN: + parameters.put(fieldName, element.get()); + cypher.append("e.").append(fieldName).append(" ") + .append(getConditionOperator(condition.condition())) + .append(" $").append(fieldName); + break; + case NOT: + cypher.append("NOT ("); + createWhereClause(cypher, element.get(CriteriaCondition.class), parameters); + cypher.append(")"); + break; + case AND: + case OR: + cypher.append("("); + List conditions = element.get(new TypeReference<>() {}); + for (int index = 0; index < conditions.size(); index++) { + if (index > 0) { + cypher.append(" ").append(getConditionOperator(condition.condition())).append(" "); + } + createWhereClause(cypher, conditions.get(index), parameters); + } + cypher.append(")"); + break; + default: + throw new CommunicationException("Unsupported condition: " + condition.condition()); + } + } + + private String getConditionOperator(Condition condition) { + return switch (condition) { + case EQUALS -> "="; + case GREATER_THAN -> ">"; + case GREATER_EQUALS_THAN -> ">="; + case LESSER_THAN -> "<"; + case LESSER_EQUALS_THAN -> "<="; + case LIKE -> "CONTAINS"; + case IN -> "IN"; + case AND -> "AND"; + case OR -> "OR"; + default -> throw new CommunicationException("Unsupported operator: " + condition); + }; + } +} \ No newline at end of file From bd4e410443045a1a13369db4fb8cc79a8c7b4fd8 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 04:15:39 +0000 Subject: [PATCH 078/148] test: create neo4j query builder scenarion Signed-off-by: Otavio Santana --- .../communication/Neo4JQueryBuilderTest.java | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilderTest.java diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilderTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilderTest.java new file mode 100644 index 000000000..12eb3bf14 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilderTest.java @@ -0,0 +1,94 @@ +/* + * + * 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.communication; + +import org.eclipse.jnosql.communication.semistructured.CriteriaCondition; +import org.eclipse.jnosql.communication.semistructured.DeleteQuery; +import org.eclipse.jnosql.communication.semistructured.SelectQuery; +import org.eclipse.jnosql.communication.semistructured.Element; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class Neo4JQueryBuilderTest { + + + @Test + void shouldBuildDeleteQueryForNode() { + DeleteQuery query = mock(DeleteQuery.class); + when(query.name()).thenReturn("Person"); + when(query.condition()).thenReturn(java.util.Optional.empty()); + when(query.columns()).thenReturn(List.of()); + + Map parameters = new HashMap<>(); + String cypher = Neo4JQueryBuilder.INSTANCE.buildQuery(query, parameters); + + assertThat(cypher).isEqualTo("MATCH (e:Person) DELETE e"); + } + + @Test + void shouldBuildDeleteQueryForSpecificColumns() { + DeleteQuery query = mock(DeleteQuery.class); + when(query.name()).thenReturn("Person"); + when(query.condition()).thenReturn(java.util.Optional.empty()); + when(query.columns()).thenReturn(List.of("name", "age")); + + Map parameters = new HashMap<>(); + String cypher = Neo4JQueryBuilder.INSTANCE.buildQuery(query, parameters); + + assertThat(cypher).isEqualTo("MATCH (e:Person) SET e.name = NULL, e.age = NULL"); + } + + @Test + void shouldBuildSelectQueryWithCondition() { + SelectQuery query = mock(SelectQuery.class); + when(query.name()).thenReturn("Person"); + CriteriaCondition condition = mock(CriteriaCondition.class); + Element element = mock(Element.class); + when(condition.element()).thenReturn(element); + when(element.name()).thenReturn("age"); + when(element.get()).thenReturn(30); + when(condition.condition()).thenReturn(org.eclipse.jnosql.communication.Condition.EQUALS); + when(query.condition()).thenReturn(java.util.Optional.of(condition)); + when(query.columns()).thenReturn(List.of("name", "age")); + + Map parameters = new HashMap<>(); + String cypher = Neo4JQueryBuilder.INSTANCE.buildQuery(query, parameters); + + assertThat(cypher).isEqualTo("MATCH (e:Person) WHERE e.age = $age RETURN e.name, e.age"); + assertThat(parameters).containsEntry("age", 30); + } + + @Test + void shouldBuildSelectQueryWithoutCondition() { + SelectQuery query = mock(SelectQuery.class); + when(query.name()).thenReturn("Person"); + when(query.condition()).thenReturn(java.util.Optional.empty()); + when(query.columns()).thenReturn(List.of("name", "age")); + + Map parameters = new HashMap<>(); + String cypher = Neo4JQueryBuilder.INSTANCE.buildQuery(query, parameters); + + assertThat(cypher).isEqualTo("MATCH (e:Person) RETURN e.name, e.age"); + } +} \ No newline at end of file From 56ce964483dfe25a255fc2dc08f4a04122f11c80 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 04:41:15 +0000 Subject: [PATCH 079/148] feat: create neo4j database with builder of cypher Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 120 +----------------- 1 file changed, 5 insertions(+), 115 deletions(-) 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 c2a67c4e5..686671d37 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 @@ -16,15 +16,8 @@ */ package org.eclipse.jnosql.databases.neo4j.communication; -import jakarta.data.Sort; import org.eclipse.jnosql.communication.CommunicationException; -import org.eclipse.jnosql.communication.Condition; -import org.eclipse.jnosql.communication.TypeReference; -import org.eclipse.jnosql.communication.Value; -import org.eclipse.jnosql.communication.ValueUtil; import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; -import org.eclipse.jnosql.communication.semistructured.CriteriaCondition; -import org.eclipse.jnosql.communication.semistructured.DatabaseManager; import org.eclipse.jnosql.communication.semistructured.DeleteQuery; import org.eclipse.jnosql.communication.semistructured.Element; import org.eclipse.jnosql.communication.semistructured.SelectQuery; @@ -34,7 +27,6 @@ import java.time.Duration; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -42,7 +34,6 @@ import java.util.Objects; import java.util.UUID; import java.util.logging.Logger; -import java.util.stream.Collectors; import java.util.stream.Stream; public class DefaultNeo4JDatabaseManager implements Neo4JDatabaseManager { @@ -126,26 +117,12 @@ public Iterable update(Iterable entiti @Override public void delete(DeleteQuery query) { Objects.requireNonNull(query, "query is required"); - StringBuilder cypher = new StringBuilder("MATCH (e:"); - cypher.append(query.name()).append(")"); - Map parameters = new HashMap<>(); - query.condition().ifPresent(c -> { - cypher.append(" WHERE "); - createWhereClause(cypher, c, parameters); - }); - - List columns = query.columns(); - if (!columns.isEmpty()) { - cypher.append(" SET "); - cypher.append(columns.stream().map(col -> "e." + col + " = NULL").collect(Collectors.joining(", "))); - } else { - cypher.append(" DELETE e"); - } + String cypher = Neo4JQueryBuilder.INSTANCE.buildQuery(query, parameters); LOGGER.fine("Executing Delete Cypher Query: " + cypher); try (Transaction tx = session.beginTransaction()) { - tx.run(cypher.toString(), Values.parameters(flattenMap(parameters))); + tx.run(cypher, Values.parameters(flattenMap(parameters))); tx.commit(); } } @@ -153,89 +130,17 @@ public void delete(DeleteQuery query) { @Override public Stream select(SelectQuery query) { Objects.requireNonNull(query, "query is required"); - StringBuilder cypher = new StringBuilder("MATCH (e:"); - cypher.append(query.name()).append(")"); - Map parameters = new HashMap<>(); - query.condition().ifPresent(c -> { - cypher.append(" WHERE "); - createWhereClause(cypher, c, parameters); - }); - - cypher.append(" RETURN "); - List columns = query.columns(); - if (columns.isEmpty()) { - cypher.append("e "); - } else { - cypher.append(columns.stream().map(col -> "e." + col).collect(Collectors.joining(", "))); - } - if (query.skip() > 0) { - cypher.append(" SKIP ").append(query.skip()); - } - if (query.limit() > 0) { - cypher.append(" LIMIT ").append(query.limit()); - } - List> sorts = query.sorts(); - if (!sorts.isEmpty()) { - cypher.append(" ORDER BY "); - cypher.append(sorts.stream() - .map(order -> "e." + order.property() + " " + (order.isAscending() ? "ASC" : "DESC")) - .collect(Collectors.joining(", "))); - } + String cypher = Neo4JQueryBuilder.INSTANCE.buildQuery(query, parameters); LOGGER.fine("Executing Cypher Query: " + cypher); try (Transaction tx = session.beginTransaction()) { - return tx.run(cypher.toString(), Values.parameters(flattenMap(parameters))) - .list(record -> extractEntity(query.name(), record, columns.isEmpty())) + return tx.run(cypher, Values.parameters(flattenMap(parameters))) + .list(record -> extractEntity(query.name(), record, query.columns().isEmpty())) .stream(); } } - private void createWhereClause(StringBuilder cypher, CriteriaCondition condition, Map parameters) { - Element element = condition.element(); - String fieldName = element.name(); - - switch (condition.condition()) { - case EQUALS: - case GREATER_THAN: - case GREATER_EQUALS_THAN: - case LESSER_THAN: - case LESSER_EQUALS_THAN: - case LIKE: - case IN: - parameters.put(fieldName, ValueUtil.convert(element.value())); - cypher.append("e.").append(fieldName).append(" ") - .append(getConditionOperator(condition.condition())) - .append(" $").append(fieldName); - break; - case BETWEEN: - List values = element.get(List.class); - parameters.put(fieldName + "_start", ValueUtil.convert(Value.of(values.get(0)))); - parameters.put(fieldName + "_end", ValueUtil.convert(Value.of(values.get(1)))); - cypher.append("e.").append(fieldName).append(" >= $").append(fieldName).append("_start AND e.") - .append(fieldName).append(" <= $").append(fieldName).append("_end"); - break; - case NOT: - cypher.append("NOT ("); - createWhereClause(cypher, element.get(CriteriaCondition.class), parameters); - cypher.append(")"); - break; - case AND: - case OR: - cypher.append("("); - List conditions = element.get(List.class); - for (int i = 0; i < conditions.size(); i++) { - if (i > 0) { - cypher.append(" ").append(getConditionOperator(condition.condition())).append(" "); - } - createWhereClause(cypher, conditions.get(i), parameters); - } - cypher.append(")"); - break; - default: - throw new CommunicationException("Unsupported condition: " + condition.condition()); - } - } private CommunicationEntity extractEntity(String entityName, org.neo4j.driver.Record record, boolean isFullNode) { List elements = new ArrayList<>(); @@ -250,21 +155,6 @@ private CommunicationEntity extractEntity(String entityName, org.neo4j.driver.Re } return CommunicationEntity.of(entityName, elements); } - private String getConditionOperator(Condition condition) { - return switch (condition) { - case EQUALS -> "="; - case GREATER_THAN -> ">"; - case GREATER_EQUALS_THAN -> ">="; - case LESSER_THAN -> "<"; - case LESSER_EQUALS_THAN -> "<="; - case LIKE -> "CONTAINS"; - case IN -> "IN"; - case AND -> "AND"; - case OR -> "OR"; - default -> throw new CommunicationException("Unsupported operator: " + condition); - }; - } - @Override public long count(String entity) { From c2c5a6405a3020da146511e858f532f9d51774c6 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 04:43:36 +0000 Subject: [PATCH 080/148] feat: update neo4j test scenarion Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerTest.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) 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 d5adc7dfe..b5058e8b9 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 @@ -139,7 +139,7 @@ void shouldSelectByIdWithName() { } @Test - void shouldLimit() { + void shouldSelectLimit() { for (int index = 0; index < 10; index++) { entityManager.insert(getEntity()); } @@ -151,7 +151,7 @@ void shouldLimit() { } @Test - void shouldStart() { + void shouldSelectStart() { for (int index = 0; index < 10; index++) { entityManager.insert(getEntity()); } @@ -163,7 +163,7 @@ void shouldStart() { } @Test - void shouldStartAndLimit() { + void shouldSelectStartAndLimit() { for (int index = 0; index < 10; index++) { entityManager.insert(getEntity()); } @@ -175,7 +175,7 @@ void shouldStartAndLimit() { } @Test - void shouldTOrderAsc(){ + void shouldSelectOrderAsc(){ for (int index = 0; index < 10; index++) { var entity = getEntity(); entity.add("index", index); @@ -193,7 +193,7 @@ void shouldTOrderAsc(){ } @Test - void shouldOrderDesc() { + void shouldSelectOrderDesc() { for (int index = 0; index < 10; index++) { var entity = getEntity(); entity.add("index", index); @@ -210,7 +210,7 @@ void shouldOrderDesc() { } @Test - void shouldFindEquals() { + void shouldSelectFindEquals() { var entity = getEntity(); entityManager.insert(entity); var query = SelectQuery.select().from(COLLECTION_NAME).where("name").eq(entity.find("name").orElseThrow().get()).build(); @@ -222,7 +222,7 @@ void shouldFindEquals() { } @Test - void shouldFindNotEquals(){ + void shouldSelectFindNotEquals(){ var entity = getEntity(); entityManager.insert(entity); entityManager.insert(getEntity()); @@ -237,7 +237,7 @@ void shouldFindNotEquals(){ } @Test - void shouldGreaterThan() { + void shouldSelectGreaterThan() { for (int index = 0; index < 10; index++) { var entity = getEntity(); entity.add("index", index); @@ -253,7 +253,7 @@ void shouldGreaterThan() { } @Test - void shouldGreaterThanEqual() { + void shouldSelectGreaterThanEqual() { for (int index = 0; index < 10; index++) { var entity = getEntity(); entity.add("index", index); @@ -269,7 +269,7 @@ void shouldGreaterThanEqual() { } @Test - void shouldLesserThan() { + void shouldSelectLesserThan() { for (int index = 0; index < 10; index++) { var entity = getEntity(); entity.add("index", index); @@ -285,7 +285,7 @@ void shouldLesserThan() { } @Test - void shouldLesserThanEqual() { + void shouldSelectLesserThanEqual() { for (int index = 0; index < 10; index++) { var entity = getEntity(); entity.add("index", index); @@ -301,7 +301,7 @@ void shouldLesserThanEqual() { } @Test - void shouldFindIn() { + void shouldSelectFindIn() { for (int index = 0; index < 10; index++) { var entity = getEntity(); entity.add("index", index); @@ -317,7 +317,7 @@ void shouldFindIn() { } @Test - void shouldLike() { + void shouldSelectLike() { var entity = getEntity(); entity.add("name", "Ada Lovelace"); entityManager.insert(entity); @@ -330,7 +330,7 @@ void shouldLike() { } @Test - void shouldAnd() { + void shouldSelectAnd() { for (int index = 0; index < 10; index++) { var entity = getEntity(); entity.add("index", index); @@ -352,7 +352,7 @@ void shouldAnd() { } @Test - void shouldOr() { + void shouldSelectOr() { for (int index = 0; index < 10; index++) { var entity = getEntity(); entity.add("index", index); From 64868b562b2fa2d5f8a07f0093f4b5dfdb6a10f0 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 04:58:40 +0000 Subject: [PATCH 081/148] test: create neo4j Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerTest.java | 87 +++++++++++++++---- 1 file changed, 70 insertions(+), 17 deletions(-) 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 b5058e8b9..bc8aa513b 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 @@ -20,6 +20,7 @@ import org.assertj.core.api.SoftAssertions; import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; import org.eclipse.jnosql.communication.semistructured.DatabaseManager; +import org.eclipse.jnosql.communication.semistructured.DeleteQuery; import org.eclipse.jnosql.communication.semistructured.Element; import org.eclipse.jnosql.communication.semistructured.Elements; import org.eclipse.jnosql.communication.semistructured.SelectQuery; @@ -35,6 +36,7 @@ import java.util.stream.Stream; import static org.eclipse.jnosql.communication.semistructured.DeleteQuery.delete; +import static org.eclipse.jnosql.communication.semistructured.SelectQuery.select; import static org.junit.jupiter.api.Assertions.assertTrue; class Neo4JDatabaseManagerTest { @@ -115,7 +117,7 @@ void shouldSelectById() { var entity = getEntity(); var communicationEntity = entityManager.insert(entity); var id = communicationEntity.find("_id").orElseThrow().get(); - var query = SelectQuery.select().from(COLLECTION_NAME).where("_id").eq(id).build(); + var query = select().from(COLLECTION_NAME).where("_id").eq(id).build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(1); @@ -128,7 +130,7 @@ void shouldSelectByIdWithName() { var entity = getEntity(); var communicationEntity = entityManager.insert(entity); var id = communicationEntity.find("_id").orElseThrow().get(); - var query = SelectQuery.select("name", "city").from(COLLECTION_NAME).where("_id").eq(id).build(); + var query = select("name", "city").from(COLLECTION_NAME).where("_id").eq(id).build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(1); @@ -143,7 +145,7 @@ void shouldSelectLimit() { for (int index = 0; index < 10; index++) { entityManager.insert(getEntity()); } - var query = SelectQuery.select().from(COLLECTION_NAME).limit(5).build(); + var query = select().from(COLLECTION_NAME).limit(5).build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(5); @@ -155,7 +157,7 @@ void shouldSelectStart() { for (int index = 0; index < 10; index++) { entityManager.insert(getEntity()); } - var query = SelectQuery.select().from(COLLECTION_NAME).skip(5).build(); + var query = select().from(COLLECTION_NAME).skip(5).build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(5); @@ -167,7 +169,7 @@ void shouldSelectStartAndLimit() { for (int index = 0; index < 10; index++) { entityManager.insert(getEntity()); } - var query = SelectQuery.select().from(COLLECTION_NAME).skip(5).limit(2).build(); + var query = select().from(COLLECTION_NAME).skip(5).limit(2).build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(2); @@ -182,7 +184,7 @@ void shouldSelectOrderAsc(){ entityManager.insert(entity); } - var query = SelectQuery.select().from(COLLECTION_NAME).orderBy("index").asc().build(); + var query = select().from(COLLECTION_NAME).orderBy("index").asc().build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(10); @@ -199,7 +201,7 @@ void shouldSelectOrderDesc() { entity.add("index", index); entityManager.insert(entity); } - var query = SelectQuery.select().from(COLLECTION_NAME).orderBy("index").desc().build(); + var query = select().from(COLLECTION_NAME).orderBy("index").desc().build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(10); @@ -213,7 +215,7 @@ void shouldSelectOrderDesc() { void shouldSelectFindEquals() { var entity = getEntity(); entityManager.insert(entity); - var query = SelectQuery.select().from(COLLECTION_NAME).where("name").eq(entity.find("name").orElseThrow().get()).build(); + var query = select().from(COLLECTION_NAME).where("name").eq(entity.find("name").orElseThrow().get()).build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(1); @@ -227,7 +229,7 @@ void shouldSelectFindNotEquals(){ entityManager.insert(entity); entityManager.insert(getEntity()); Object name = entity.find("name").orElseThrow().get(); - var query = SelectQuery.select().from(COLLECTION_NAME).where("name").not() + var query = select().from(COLLECTION_NAME).where("name").not() .eq(name).build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { @@ -244,7 +246,7 @@ void shouldSelectGreaterThan() { entityManager.insert(entity); } var index = 4; - var query = SelectQuery.select().from(COLLECTION_NAME).where("index").gt(index).build(); + var query = select().from(COLLECTION_NAME).where("index").gt(index).build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(5); @@ -260,7 +262,7 @@ void shouldSelectGreaterThanEqual() { entityManager.insert(entity); } var index = 4; - var query = SelectQuery.select().from(COLLECTION_NAME).where("index").gte(index).build(); + var query = select().from(COLLECTION_NAME).where("index").gte(index).build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(6); @@ -276,7 +278,7 @@ void shouldSelectLesserThan() { entityManager.insert(entity); } var index = 4; - var query = SelectQuery.select().from(COLLECTION_NAME).where("index").lt(index).build(); + var query = select().from(COLLECTION_NAME).where("index").lt(index).build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(4); @@ -292,7 +294,7 @@ void shouldSelectLesserThanEqual() { entityManager.insert(entity); } var index = 4; - var query = SelectQuery.select().from(COLLECTION_NAME).where("index").lte(index).build(); + var query = select().from(COLLECTION_NAME).where("index").lte(index).build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(5); @@ -307,7 +309,7 @@ void shouldSelectFindIn() { entity.add("index", index); entityManager.insert(entity); } - var query = SelectQuery.select().from(COLLECTION_NAME).where("index").in(List.of(1, 2, 3)).build(); + var query = select().from(COLLECTION_NAME).where("index").in(List.of(1, 2, 3)).build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(3); @@ -321,7 +323,7 @@ void shouldSelectLike() { var entity = getEntity(); entity.add("name", "Ada Lovelace"); entityManager.insert(entity); - var query = SelectQuery.select().from(COLLECTION_NAME).where("name").like("Love").build(); + var query = select().from(COLLECTION_NAME).where("name").like("Love").build(); var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(1); @@ -340,7 +342,7 @@ void shouldSelectAnd() { var entity = getEntity(); entity.add("index", index); entityManager.insert(entity); - var query = SelectQuery.select().from(COLLECTION_NAME).where("index") + var query = select().from(COLLECTION_NAME).where("index") .gte(index).and("name") .eq(entity.find("name") .orElseThrow().get()).build(); @@ -364,7 +366,7 @@ void shouldSelectOr() { entityManager.insert(entity); var name = entity.find("name") .orElseThrow().get(String.class); - var query = SelectQuery.select().from(COLLECTION_NAME).where("index") + var query = select().from(COLLECTION_NAME).where("index") .gte(index).or("name") .eq(name).build(); var entities = entityManager.select(query).toList(); @@ -376,6 +378,57 @@ void shouldSelectOr() { }); } + @Test + void shouldDeleteById() { + var entity = getEntity(); + var communicationEntity = entityManager.insert(entity); + var id = communicationEntity.find("_id").orElseThrow().get(); + var deleteQuery = delete().from(COLLECTION_NAME).where("_id").eq(id).build(); + entityManager.delete(deleteQuery); + var query = select().from(COLLECTION_NAME).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).isEmpty(); + }); + } + + @Test + void shouldDeleteGreaterThan() { + for (int index = 0; index < 10; index++) { + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + } + var index = 4; + var deleteQuery = delete().from(COLLECTION_NAME).where("index").gt(index).build(); + entityManager.delete(deleteQuery); + var query = select().from(COLLECTION_NAME).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(5); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)<= index); + }); + } + + @Test + void shouldDeleteGreaterThanEqual() { + for (int index = 0; index < 10; index++) { + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + } + var index = 4; + var deleteQuery = delete().from(COLLECTION_NAME).where("index").gte(index).build(); + entityManager.delete(deleteQuery); + var query = select().from(COLLECTION_NAME).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(4); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)< index); + }); + } + + private CommunicationEntity getEntity() { Faker faker = new Faker(); From 48d1b391dd72eab3bd1932c3084c130c60574c1e Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 05:00:48 +0000 Subject: [PATCH 082/148] test: update scenarions to test Signed-off-by: Otavio Santana --- .../Neo4JDatabaseManagerTest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) 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 bc8aa513b..bd6262a2f 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 @@ -428,6 +428,41 @@ void shouldDeleteGreaterThanEqual() { }); } + @Test + void shouldDeleteLesserThan() { + for (int index = 0; index < 10; index++) { + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + } + var index = 4; + var deleteQuery = delete().from(COLLECTION_NAME).where("index").lt(index).build(); + entityManager.delete(deleteQuery); + var query = select().from(COLLECTION_NAME).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(6); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)>= index); + }); + } + + @Test + void shouldDeleteLesserThanEqual() { + for (int index = 0; index < 10; index++) { + var entity = getEntity(); + entity.add("index", index); + entityManager.insert(entity); + } + var index = 4; + var deleteQuery = delete().from(COLLECTION_NAME).where("index").lte(index).build(); + entityManager.delete(deleteQuery); + var query = select().from(COLLECTION_NAME).build(); + var entities = entityManager.select(query).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(entities).hasSize(5); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)> index); + }); + } private CommunicationEntity getEntity() { From dda1a92294b23aa76e29eb063484c0aaaadbba0f Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 05:05:37 +0000 Subject: [PATCH 083/148] feat: update neo4j support Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JQueryBuilder.java | 11 +++++++++++ .../neo4j/communication/Neo4JDatabaseManagerTest.java | 3 --- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java index 679eb530c..0db995838 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java @@ -61,6 +61,17 @@ public String buildQuery(SelectQuery query, Map parameters) { createWhereClause(cypher, c, parameters); }); + query.sorts().forEach(sort -> cypher.append(" ORDER BY e.").append(sort.property()).append(" ") + .append(sort.isAscending() ? "ASC" : "DESC")); + + if (query.skip() > 0) { + cypher.append(" SKIP ").append(query.skip()); + } + + if (query.limit() > 0) { + cypher.append(" LIMIT ").append(query.limit()); + } + cypher.append(" RETURN "); List columns = query.columns(); if (columns.isEmpty()) { 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 bd6262a2f..d1b23013b 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 @@ -20,15 +20,12 @@ import org.assertj.core.api.SoftAssertions; import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; import org.eclipse.jnosql.communication.semistructured.DatabaseManager; -import org.eclipse.jnosql.communication.semistructured.DeleteQuery; import org.eclipse.jnosql.communication.semistructured.Element; import org.eclipse.jnosql.communication.semistructured.Elements; -import org.eclipse.jnosql.communication.semistructured.SelectQuery; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; From a38aae29ef51bb8be480c7f35e37f52740d71aba Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 05:16:40 +0000 Subject: [PATCH 084/148] docs: update changelog Signed-off-by: Otavio Santana --- CHANGELOG.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index b49b05b75..c2a1b1cf6 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -11,6 +11,7 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version === Added - Include TCK tests +- Include support to Neo4J === Changed From 69dfb2e5ebfab2326e2a08e0881bf016b7ef86e9 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 05:33:06 +0000 Subject: [PATCH 085/148] feat: create new resources to neo4j Signed-off-by: Otavio Santana --- .../communication/Neo4JDatabaseManager.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) 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 f867e85e5..1c9311c1a 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 @@ -16,8 +16,12 @@ */ package org.eclipse.jnosql.databases.neo4j.communication; +import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; import org.eclipse.jnosql.communication.semistructured.DatabaseManager; +import java.util.Map; +import java.util.stream.Stream; + /** * This specialization of {@link DatabaseManager} is designed for Neo4j databases. * @@ -46,4 +50,35 @@ * @see DatabaseManager */ public interface Neo4JDatabaseManager extends DatabaseManager { + + /** + * A specialized {@link DatabaseManager + + /** + * Executes a custom Cypher query with parameters and returns a stream of {@link CommunicationEntity}. + * + * @param cypher the Cypher query to execute. + * @param parameters the parameters to bind to the query. + * @return a stream of {@link CommunicationEntity} matching the query result. + */ + Stream executeQuery(String cypher, Map parameters); + + /** + * Traverses the graph starting from a node and follows the specified relationship type up to a given depth. + * + * @param startNodeId the ID of the starting node. + * @param relationship the type of relationship to traverse. + * @param depth the traversal depth limit. + * @return a stream of {@link CommunicationEntity} representing related nodes. + */ + Stream traverse(String startNodeId, String relationship, int depth); + + /** + * Creates a relationship between two {@link CommunicationEntity} nodes. + * + * @param source the source entity. + * @param target the target entity. + * @param relationshipType the type of relationship to create. + */ + void edge(CommunicationEntity source, CommunicationEntity target, String relationshipType); } From b0919166e241316126c1c2a25de4e9b622ddc7d9 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 08:02:43 +0000 Subject: [PATCH 086/148] docs: enhance documentation to neo4j databse manager Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JDatabaseManager.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 1c9311c1a..9399e9076 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 @@ -56,10 +56,11 @@ public interface Neo4JDatabaseManager extends DatabaseManager { /** * Executes a custom Cypher query with parameters and returns a stream of {@link CommunicationEntity}. - * * @param cypher the Cypher query to execute. * @param parameters the parameters to bind to the query. * @return a stream of {@link CommunicationEntity} matching the query result. + * @throws NullPointerException if any of the arguments are {@code null}. + * @throws CypherException if there is an issue with the Cypher syntax. */ Stream executeQuery(String cypher, Map parameters); @@ -70,6 +71,7 @@ public interface Neo4JDatabaseManager extends DatabaseManager { * @param relationship the type of relationship to traverse. * @param depth the traversal depth limit. * @return a stream of {@link CommunicationEntity} representing related nodes. + * @throws NullPointerException if any of the arguments are {@code null}. */ Stream traverse(String startNodeId, String relationship, int depth); @@ -79,6 +81,8 @@ public interface Neo4JDatabaseManager extends DatabaseManager { * @param source the source entity. * @param target the target entity. * @param relationshipType the type of relationship to create. + * @throws EdgeCommunicationException if either the source or target entity does not exist in the database. + * @throws NullPointerException if any of the arguments are {@code null}. */ - void edge(CommunicationEntity source, CommunicationEntity target, String relationshipType); + void edge(CommunicationEntity source, String relationshipType, CommunicationEntity target); } From 086355ec42be9f8e0d30683e0439f605591b2372 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 08:03:46 +0000 Subject: [PATCH 087/148] feat: create neo4j exception Signed-off-by: Otavio Santana --- .../Neo4JCommunicationException.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java new file mode 100644 index 000000000..9c24fba26 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java @@ -0,0 +1,30 @@ +/* + * + * 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.communication; + +import org.eclipse.jnosql.communication.CommunicationException; + +public class Neo4JCommunicationException extends CommunicationException { + + public Neo4JCommunicationException(String message) { + super(message); + } + + public Neo4JCommunicationException(String message, Throwable exception) { + super(message, exception); + } +} From 6803159eb0d8e0d8f54ec669250c962aa01b41c2 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 08:04:26 +0000 Subject: [PATCH 088/148] feat: create edge communication exception Signed-off-by: Otavio Santana --- .../EdgeCommunicationException.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java new file mode 100644 index 000000000..d8bd5b94d --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java @@ -0,0 +1,28 @@ +/* + * + * 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.communication; + +public class EdgeCommunicationException extends Neo4JCommunicationException{ + + public EdgeCommunicationException(String message) { + super(message); + } + + public EdgeCommunicationException(String message, Throwable exception) { + super(message, exception); + } +} From 661e03be80213de7c556874fe17022fbbfc2d1ef Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 08:05:41 +0000 Subject: [PATCH 089/148] docs: create edgae communication Signed-off-by: Otavio Santana --- .../communication/EdgeCommunicationException.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java index d8bd5b94d..917408ea8 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java @@ -16,6 +16,20 @@ */ package org.eclipse.jnosql.databases.neo4j.communication; +/** + * Exception thrown when an issue occurs with edge (relationship) operations in Neo4j. + *

    + * This exception is raised in cases where an edge cannot be created or removed + * due to missing nodes, constraint violations, or other graph-related inconsistencies. + *

    + * + *

    Common Scenarios:

    + *
      + *
    • Attempting to create an edge where either the source or target entity does not exist.
    • + *
    • Removing an edge that is not found in the graph.
    • + *
    • Trying to enforce an invalid relationship constraint.
    • + *
    + */ public class EdgeCommunicationException extends Neo4JCommunicationException{ public EdgeCommunicationException(String message) { From 218c9c6b660083de839e1042ac09998cfeb0d3a3 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 08:07:09 +0000 Subject: [PATCH 090/148] docs: generate documentation to Neo4j Signed-off-by: Otavio Santana --- .../Neo4JCommunicationException.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java index 9c24fba26..527f3b500 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java @@ -18,6 +18,21 @@ import org.eclipse.jnosql.communication.CommunicationException; +/** + * Exception representing general communication errors when interacting with a Neo4j database. + *

    + * This exception is used as a base for more specific exceptions related to Neo4j operations, + * such as Cypher syntax errors, transaction failures, or connectivity issues. + *

    + * + *

    Common Causes:

    + *
      + *
    • Invalid Cypher query syntax.
    • + *
    • Transaction failures during read or write operations.
    • + *
    • Connection timeouts or authentication failures.
    • + *
    • Inconsistent data constraints within the graph.
    • + *
    + */ public class Neo4JCommunicationException extends CommunicationException { public Neo4JCommunicationException(String message) { From 70415a1e87f8bd19e09d50fadcbfa88444617215 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 08:07:20 +0000 Subject: [PATCH 091/148] feat: generate new method on neo4j database Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JDatabaseManager.java | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 9399e9076..960b615d4 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 @@ -85,4 +85,15 @@ public interface Neo4JDatabaseManager extends DatabaseManager { * @throws NullPointerException if any of the arguments are {@code null}. */ void edge(CommunicationEntity source, String relationshipType, CommunicationEntity target); + + /** + * Removes an existing relationship (edge) between two {@link CommunicationEntity} nodes. + * + * @param source the source entity, which must already exist in the database. + * @param target the target entity, which must already exist in the database. + * @param relationshipType the type of relationship to remove. + * @throws EdgeCommunicationException if either the source or target entity does not exist in the database. + * @throws NullPointerException if any of the arguments are {@code null}. + */ + void remove(CommunicationEntity source, String relationshipType, CommunicationEntity target); } From d412253cabc0ca7d7d64d6302b7c38a4b1c40cdd Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 08:11:56 +0000 Subject: [PATCH 092/148] feat: generate query logs to all database operations Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) 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 686671d37..9ac7eb393 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 @@ -96,7 +96,7 @@ public CommunicationEntity update(CommunicationEntity entity) { cypher.setLength(cypher.length() - 2); } - LOGGER.fine("Cypher: " + cypher); + LOGGER.finest(() -> "Executing Cyphyer to update entities: " + cypher); try (Transaction tx = session.beginTransaction()) { tx.run(cypher.toString(), Values.parameters(flattenMap(entityMap))); @@ -133,7 +133,7 @@ public Stream select(SelectQuery query) { Map parameters = new HashMap<>(); String cypher = Neo4JQueryBuilder.INSTANCE.buildQuery(query, parameters); - LOGGER.fine("Executing Cypher Query: " + cypher); + LOGGER.fine("Executing Cypher Query for select entities: " + cypher); try (Transaction tx = session.beginTransaction()) { return tx.run(cypher, Values.parameters(flattenMap(parameters))) .list(record -> extractEntity(query.name(), record, query.columns().isEmpty())) @@ -141,26 +141,12 @@ public Stream select(SelectQuery query) { } } - - private CommunicationEntity extractEntity(String entityName, org.neo4j.driver.Record record, boolean isFullNode) { - List elements = new ArrayList<>(); - - for (String key : record.keys()) { - String fieldName = key.contains(".") ? key.substring(key.indexOf('.') + 1) : key; - if (isFullNode && record.get(key).hasType(org.neo4j.driver.types.TypeSystem.getDefault().NODE())) { - record.get(key).asNode().asMap().forEach((k, v) -> elements.add(Element.of(k, v))); - } else { - elements.add(Element.of(fieldName, record.get(key).asObject())); - } - } - return CommunicationEntity.of(entityName, elements); - } - @Override public long count(String entity) { Objects.requireNonNull(entity, "entity is required"); try (Transaction tx = session.beginTransaction()) { String cypher = "MATCH (e:" + entity + ") RETURN count(e) AS count"; + LOGGER.fine("Executing Cypher Query for counting: " + cypher); long count = tx.run(cypher).single().get("count").asLong(); tx.commit(); return count; @@ -169,6 +155,8 @@ public long count(String entity) { throw new CommunicationException("Error executing count query", e); } } + + @Override public void close() { LOGGER.fine("Closing the Neo4J session, the database name is: " + database); @@ -201,7 +189,7 @@ private Iterable insertEntities(Iterable insertEntities(Iterable elements = new ArrayList<>(); + + for (String key : record.keys()) { + String fieldName = key.contains(".") ? key.substring(key.indexOf('.') + 1) : key; + if (isFullNode && record.get(key).hasType(org.neo4j.driver.types.TypeSystem.getDefault().NODE())) { + record.get(key).asNode().asMap().forEach((k, v) -> elements.add(Element.of(k, v))); + } else { + elements.add(Element.of(fieldName, record.get(key).asObject())); + } + } + return CommunicationEntity.of(entityName, elements); + } } From 74820fdbe79f4fdc4dd2204db5fdb7331df3ea08 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 08:16:03 +0000 Subject: [PATCH 093/148] feat: update Neo4jdatabase with the new methods Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) 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 9ac7eb393..bc1534d3d 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 @@ -156,6 +156,72 @@ public long count(String entity) { } } + @Override + public Stream executeQuery(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(); + LOGGER.fine("Executed Cypher query: " + cypher); + tx.commit(); + return result; + } catch (Exception e) { + throw new CommunicationException("Error executing Cypher query", e); + } + } + + @Override + public Stream traverse(String startNodeId, String relationship, int depth) { + Objects.requireNonNull(startNodeId, "Start node ID is required"); + Objects.requireNonNull(relationship, "Relationship type is required"); + + String cypher = "MATCH (startNode { _id: $_id })-[r:" + relationship + "*1.." + depth + "]-(endNode) RETURN endNode"; + + try (Transaction tx = session.beginTransaction()) { + Stream result = tx.run(cypher, Values.parameters("_id", startNodeId)) + .list(record -> extractEntity("TraversalResult", record, false)) + .stream(); + LOGGER.fine("Executed traversal query: " + cypher); + tx.commit(); + return result; + } + } + + @Override + public void edge(CommunicationEntity source, String relationshipType, CommunicationEntity target) { + Objects.requireNonNull(source, "Source entity is required"); + Objects.requireNonNull(target, "Target entity is required"); + Objects.requireNonNull(relationshipType, "Relationship type is required"); + + String cypher = "MATCH (s { _id: $_id_source }), (t { _id: $_id_target }) CREATE (s)-[:" + relationshipType + "]->(t)"; + + try (Transaction tx = session.beginTransaction()) { + tx.run(cypher, Values.parameters("_id_source", source.find("_id").orElseThrow(), + "_id_target", target.find("_id").orElseThrow())); + LOGGER.fine("Created edge: " + cypher); + tx.commit(); + } + } + + @Override + public void remove(CommunicationEntity source, String relationshipType, CommunicationEntity target) { + Objects.requireNonNull(source, "Source entity is required"); + Objects.requireNonNull(target, "Target entity is required"); + Objects.requireNonNull(relationshipType, "Relationship type is required"); + + String cypher = "MATCH (s { _id: $_id_source })-[r:" + relationshipType + "]-(t { _id: $_id_target }) DELETE r"; + + try (Transaction tx = session.beginTransaction()) { + tx.run(cypher, Values.parameters("_id_source", source.find("_id").orElseThrow(), + "_id_target", target.find("_id").orElseThrow())); + LOGGER.fine("Removed edge: " + cypher); + tx.commit(); + } + } + @Override public void close() { From 6ab7963a9fb56907bbcb023d563d1799227f1a9e Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 08:33:03 +0000 Subject: [PATCH 094/148] feat: create test scenario to neo4j Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 12 +- .../Neo4JDatabaseManagerTest.java | 116 ++++++++++++++++-- 2 files changed, 113 insertions(+), 15 deletions(-) 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 bc1534d3d..32474c6d7 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 @@ -199,8 +199,10 @@ public void edge(CommunicationEntity source, String relationshipType, Communicat String cypher = "MATCH (s { _id: $_id_source }), (t { _id: $_id_target }) CREATE (s)-[:" + relationshipType + "]->(t)"; try (Transaction tx = session.beginTransaction()) { - tx.run(cypher, Values.parameters("_id_source", source.find("_id").orElseThrow(), - "_id_target", target.find("_id").orElseThrow())); + var sourceId = source.find(ID).orElseThrow(() -> new EdgeCommunicationException("The source entity should have the " +ID + " property")).get(); + var targetId = target.find(ID).orElseThrow(() -> new EdgeCommunicationException("The target entity should have the " +ID + " property")).get(); + tx.run(cypher, Values.parameters("_id_source", sourceId, + "_id_target", targetId)); LOGGER.fine("Created edge: " + cypher); tx.commit(); } @@ -214,9 +216,11 @@ public void remove(CommunicationEntity source, String relationshipType, Communic String cypher = "MATCH (s { _id: $_id_source })-[r:" + relationshipType + "]-(t { _id: $_id_target }) DELETE r"; + var sourceId = source.find(ID).orElseThrow(() -> new EdgeCommunicationException("The source entity should have the " +ID + " property")).get(); + var targetId = target.find(ID).orElseThrow(() -> new EdgeCommunicationException("The target entity should have the " +ID + " property")).get(); + try (Transaction tx = session.beginTransaction()) { - tx.run(cypher, Values.parameters("_id_source", source.find("_id").orElseThrow(), - "_id_target", target.find("_id").orElseThrow())); + tx.run(cypher, Values.parameters("_id_source", sourceId, "_id_target", targetId)); LOGGER.fine("Removed edge: " + cypher); tx.commit(); } 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 d1b23013b..89b5c9791 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 @@ -39,7 +39,7 @@ class Neo4JDatabaseManagerTest { public static final String COLLECTION_NAME = "person"; - private static DatabaseManager entityManager; + private static Neo4JDatabaseManager entityManager; @BeforeAll public static void setUp() { @@ -48,6 +48,7 @@ public static void setUp() { @BeforeEach void beforeEach() { + removeAllEdges(); delete().from(COLLECTION_NAME).delete(entityManager); } @@ -174,7 +175,7 @@ void shouldSelectStartAndLimit() { } @Test - void shouldSelectOrderAsc(){ + void shouldSelectOrderAsc() { for (int index = 0; index < 10; index++) { var entity = getEntity(); entity.add("index", index); @@ -221,7 +222,7 @@ void shouldSelectFindEquals() { } @Test - void shouldSelectFindNotEquals(){ + void shouldSelectFindNotEquals() { var entity = getEntity(); entityManager.insert(entity); entityManager.insert(getEntity()); @@ -247,7 +248,7 @@ void shouldSelectGreaterThan() { var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(5); - softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)> index); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class) > index); }); } @@ -263,7 +264,7 @@ void shouldSelectGreaterThanEqual() { var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(6); - softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)>= index); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class) >= index); }); } @@ -295,7 +296,7 @@ void shouldSelectLesserThanEqual() { var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(5); - softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)<= index); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class) <= index); }); } @@ -346,7 +347,7 @@ void shouldSelectAnd() { var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(1); - softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)<= index); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class) <= index); }); } @@ -403,7 +404,7 @@ void shouldDeleteGreaterThan() { var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(5); - softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)<= index); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class) <= index); }); } @@ -421,7 +422,7 @@ void shouldDeleteGreaterThanEqual() { var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(4); - softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)< index); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class) < index); }); } @@ -439,7 +440,7 @@ void shouldDeleteLesserThan() { var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(6); - softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)>= index); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class) >= index); }); } @@ -457,7 +458,90 @@ void shouldDeleteLesserThanEqual() { var entities = entityManager.select(query).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(entities).hasSize(5); - softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class)> index); + softly.assertThat(entities).allMatch(e -> e.find("index").orElseThrow().get(Integer.class) > index); + }); + } + + @Test + void shouldExecuteCustomQuery() { + var entity = getEntity(); + entityManager.insert(entity); + + String cypher = "MATCH (e:person) RETURN e"; + var result = entityManager.executeQuery(cypher, new HashMap<>()).toList(); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(result).isNotEmpty(); + softly.assertThat(result.get(0).find("name")).isPresent(); + }); + } + + @Test + void shouldTraverseGraph() { + var person1 = getEntity(); + var person2 = getEntity(); + var person3 = getEntity(); + + entityManager.insert(person1); + entityManager.insert(person2); + entityManager.insert(person3); + + entityManager.edge(person1, "FRIEND", person2); + entityManager.edge(person2, "FRIEND", person3); + + var startNodeId = person1.find("_id").orElseThrow().get(); + var result = entityManager.traverse(startNodeId.toString(), "FRIEND", 2).toList(); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(result).hasSize(2); + softly.assertThat(result).allMatch(e -> e.find("_id").isPresent()); + }); + entityManager.remove(person1, "FRIEND", person2); + entityManager.remove(person2, "FRIEND", person3); + } + + @Test + void shouldCreateEdge() { + var person1 = getEntity(); + var person2 = getEntity(); + + entityManager.insert(person1); + entityManager.insert(person2); + + entityManager.edge(person1, "FRIEND", person2); + + String cypher = "MATCH (p1:person { _id: $_id1 })-[r:FRIEND]-(p2:person { _id: $_id2 }) RETURN r"; + Map parameters = Map.of( + "_id1", person1.find("_id").orElseThrow().get(), + "_id2", person2.find("_id").orElseThrow().get() + ); + + var result = entityManager.executeQuery(cypher, parameters).toList(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(result).isNotEmpty(); + }); + entityManager.remove(person1, "FRIEND", person2); + } + + @Test + void shouldRemoveEdge() { + var person1 = getEntity(); + var person2 = getEntity(); + + entityManager.insert(person1); + entityManager.insert(person2); + + entityManager.edge(person1, "FRIEND", person2); + + var startNodeId = person1.find("_id").orElseThrow().get(); + var targetNodeId = person2.find("_id").orElseThrow().get(); + + 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(); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(result).isEmpty(); }); } @@ -474,4 +558,14 @@ private CommunicationEntity getEntity() { documents.forEach(entity::add); return entity; } + + private void removeAllEdges() { + String cypher = "MATCH ()-[r]-() DELETE r"; + + try { + entityManager.executeQuery(cypher, new HashMap<>()).toList(); + } catch (Exception e) { + throw new RuntimeException("Failed to remove edges before node deletion", e); + } + } } From eaa29695b6a962a8b9c29ee284294b90f5e5cedd Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 16:39:08 +0000 Subject: [PATCH 095/148] feat: update query on neo4j Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 17 +++++----- .../communication/Neo4JQueryBuilder.java | 31 +++++++++++++------ .../Neo4JDatabaseManagerTest.java | 26 +++++++++++----- 3 files changed, 50 insertions(+), 24 deletions(-) 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 32474c6d7..82d8bf470 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 @@ -40,6 +40,7 @@ public class DefaultNeo4JDatabaseManager implements Neo4JDatabaseManager { private static final Logger LOGGER = Logger.getLogger(DefaultNeo4JDatabaseManager.class.getName()); public static final String ID = "_id"; + public static final String NEO4J_NATIVE_ID = "nodeId"; private final Session session; private final String database; @@ -243,11 +244,6 @@ private Iterable insertEntities(Iterable entitiesResult = new ArrayList<>(); try (Transaction tx = session.beginTransaction()) { for (CommunicationEntity entity : entities) { - if (!entity.contains(ID)) { - String generatedId = UUID.randomUUID().toString(); - LOGGER.fine("The entity does not contain an _id field. Generating one: " + generatedId); - entity.add(ID, generatedId); - } Map properties = entity.toMap(); StringBuilder cypher = new StringBuilder("CREATE (e:"); @@ -258,10 +254,13 @@ private Iterable insertEntities(Iterable elements.add(Element.of(k, v))); + var node = record.get(key).asNode(); + node.asMap().forEach((k, v) -> elements.add(Element.of(k, v))); + elements.add(Element.of(ID, node.elementId())); } else { elements.add(Element.of(fieldName, record.get(key).asObject())); } diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java index 0db995838..7db432f0f 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java @@ -28,10 +28,12 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; - public enum Neo4JQueryBuilder { INSTANCE; + private static final String INTERNAL_ID = "_id"; + private static final String NEO4J_ID = "elementId(e)"; + public String buildQuery(DeleteQuery query, Map parameters) { StringBuilder cypher = new StringBuilder("MATCH (e:"); cypher.append(query.name()).append(")"); @@ -44,7 +46,9 @@ public String buildQuery(DeleteQuery query, Map parameters) { List columns = query.columns(); if (!columns.isEmpty()) { cypher.append(" SET "); - cypher.append(columns.stream().map(col -> "e." + col + " = NULL").collect(Collectors.joining(", "))); + cypher.append(columns.stream() + .map(col -> "e." + translateField(col) + " = NULL") + .collect(Collectors.joining(", "))); } else { cypher.append(" DELETE e"); } @@ -61,8 +65,9 @@ public String buildQuery(SelectQuery query, Map parameters) { createWhereClause(cypher, c, parameters); }); - query.sorts().forEach(sort -> cypher.append(" ORDER BY e.").append(sort.property()).append(" ") - .append(sort.isAscending() ? "ASC" : "DESC")); + query.sorts().forEach(sort -> cypher.append(" ORDER BY e.") + .append(translateField(sort.property())) + .append(" ").append(sort.isAscending() ? "ASC" : "DESC")); if (query.skip() > 0) { cypher.append(" SKIP ").append(query.skip()); @@ -77,7 +82,9 @@ public String buildQuery(SelectQuery query, Map parameters) { if (columns.isEmpty()) { cypher.append("e"); } else { - cypher.append(columns.stream().map(col -> "e." + col).collect(Collectors.joining(", "))); + cypher.append(columns.stream() + .map(this::translateField) + .collect(Collectors.joining(", "))); } return cypher.toString(); @@ -86,6 +93,7 @@ public String buildQuery(SelectQuery query, Map parameters) { private void createWhereClause(StringBuilder cypher, CriteriaCondition condition, Map parameters) { Element element = condition.element(); String fieldName = element.name(); + String queryField = translateField(fieldName); // Correct field translation switch (condition.condition()) { case EQUALS: @@ -95,10 +103,11 @@ private void createWhereClause(StringBuilder cypher, CriteriaCondition condition case LESSER_EQUALS_THAN: case LIKE: case IN: - parameters.put(fieldName, element.get()); - cypher.append("e.").append(fieldName).append(" ") + String paramName = INTERNAL_ID.equals(fieldName) ? "id" : fieldName; // Ensure valid parameter name + parameters.put(paramName, element.get()); + cypher.append(queryField).append(" ") .append(getConditionOperator(condition.condition())) - .append(" $").append(fieldName); + .append(" $").append(paramName); break; case NOT: cypher.append("NOT ("); @@ -122,6 +131,10 @@ private void createWhereClause(StringBuilder cypher, CriteriaCondition condition } } + private String translateField(String field) { + return INTERNAL_ID.equals(field) ? "elementId(e)" : "e." + field; + } + private String getConditionOperator(Condition condition) { return switch (condition) { case EQUALS -> "="; @@ -136,4 +149,4 @@ private String getConditionOperator(Condition condition) { default -> throw new CommunicationException("Unsupported operator: " + condition); }; } -} \ No newline at end of file +} 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 89b5c9791..59057be31 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 @@ -502,24 +502,36 @@ void shouldTraverseGraph() { @Test void shouldCreateEdge() { - var person1 = getEntity(); - var person2 = getEntity(); + var person1 = entityManager.insert(getEntity()); + var person2 = entityManager.insert(getEntity()); - entityManager.insert(person1); - entityManager.insert(person2); + String person1Id = entityManager.select(select().from(COLLECTION_NAME) + .where("_id").eq(person1.find("_id").orElseThrow().get()).build()) + .findFirst().orElseThrow().find("_id").orElseThrow().get(String.class); + + String person2Id = entityManager.select(select().from(COLLECTION_NAME) + .where("_id").eq(person2.find("_id").orElseThrow().get()).build()) + .findFirst().orElseThrow().find("_id").orElseThrow().get(String.class); + + person1.add("_id", person1Id); + person2.add("_id", person2Id); entityManager.edge(person1, "FRIEND", person2); - String cypher = "MATCH (p1:person { _id: $_id1 })-[r:FRIEND]-(p2:person { _id: $_id2 }) RETURN r"; + String cypher = "MATCH (p1:person) WHERE elementId(p1) = $id1 " + + "MATCH (p2:person) WHERE elementId(p2) = $id2 " + + "MATCH (p1)-[r:FRIEND]-(p2) RETURN r"; + Map parameters = Map.of( - "_id1", person1.find("_id").orElseThrow().get(), - "_id2", person2.find("_id").orElseThrow().get() + "id1", person1Id, + "id2", person2Id ); var result = entityManager.executeQuery(cypher, parameters).toList(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(result).isNotEmpty(); }); + entityManager.remove(person1, "FRIEND", person2); } From ea6b86b4395bc4c2d5cb3547b32eb26444256f8d Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 16:40:37 +0000 Subject: [PATCH 096/148] feat: update query to relationship Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) 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 82d8bf470..aa89eabd3 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 @@ -179,10 +179,12 @@ public Stream traverse(String startNodeId, String relations Objects.requireNonNull(startNodeId, "Start node ID is required"); Objects.requireNonNull(relationship, "Relationship type is required"); - String cypher = "MATCH (startNode { _id: $_id })-[r:" + relationship + "*1.." + depth + "]-(endNode) RETURN endNode"; + String cypher = "MATCH (startNode) WHERE elementId(startNode) = $elementId " + + "MATCH (startNode)-[r:" + relationship + "*1.." + depth + "]-(endNode) " + + "RETURN endNode"; try (Transaction tx = session.beginTransaction()) { - Stream result = tx.run(cypher, Values.parameters("_id", startNodeId)) + Stream result = tx.run(cypher, Values.parameters("elementId", startNodeId)) .list(record -> extractEntity("TraversalResult", record, false)) .stream(); LOGGER.fine("Executed traversal query: " + cypher); @@ -197,13 +199,21 @@ public void edge(CommunicationEntity source, String relationshipType, Communicat Objects.requireNonNull(target, "Target entity is required"); Objects.requireNonNull(relationshipType, "Relationship type is required"); - String cypher = "MATCH (s { _id: $_id_source }), (t { _id: $_id_target }) CREATE (s)-[:" + relationshipType + "]->(t)"; + String cypher = "MATCH (s) WHERE elementId(s) = $sourceElementId " + + "MATCH (t) WHERE elementId(t) = $targetElementId " + + "CREATE (s)-[r:" + relationshipType + "]->(t)"; try (Transaction tx = session.beginTransaction()) { - var sourceId = source.find(ID).orElseThrow(() -> new EdgeCommunicationException("The source entity should have the " +ID + " property")).get(); - var targetId = target.find(ID).orElseThrow(() -> new EdgeCommunicationException("The target entity should have the " +ID + " property")).get(); - tx.run(cypher, Values.parameters("_id_source", sourceId, - "_id_target", targetId)); + var sourceId = source.find(ID).orElseThrow(() -> + new EdgeCommunicationException("The source entity should have the " + ID + " property")).get(); + var targetId = target.find(ID).orElseThrow(() -> + new EdgeCommunicationException("The target entity should have the " + ID + " property")).get(); + + tx.run(cypher, Values.parameters( + "sourceElementId", sourceId, + "targetElementId", targetId + )); + LOGGER.fine("Created edge: " + cypher); tx.commit(); } @@ -215,19 +225,27 @@ public void remove(CommunicationEntity source, String relationshipType, Communic Objects.requireNonNull(target, "Target entity is required"); Objects.requireNonNull(relationshipType, "Relationship type is required"); - String cypher = "MATCH (s { _id: $_id_source })-[r:" + relationshipType + "]-(t { _id: $_id_target }) DELETE r"; + String cypher = "MATCH (s) WHERE elementId(s) = $sourceElementId " + + "MATCH (t) WHERE elementId(t) = $targetElementId " + + "MATCH (s)-[r:" + relationshipType + "]-(t) DELETE r"; - var sourceId = source.find(ID).orElseThrow(() -> new EdgeCommunicationException("The source entity should have the " +ID + " property")).get(); - var targetId = target.find(ID).orElseThrow(() -> new EdgeCommunicationException("The target entity should have the " +ID + " property")).get(); + var sourceId = source.find(ID).orElseThrow(() -> + new EdgeCommunicationException("The source entity should have the " + ID + " property")).get(); + var targetId = target.find(ID).orElseThrow(() -> + new EdgeCommunicationException("The target entity should have the " + ID + " property")).get(); try (Transaction tx = session.beginTransaction()) { - tx.run(cypher, Values.parameters("_id_source", sourceId, "_id_target", targetId)); + tx.run(cypher, Values.parameters( + "sourceElementId", sourceId, + "targetElementId", targetId + )); LOGGER.fine("Removed edge: " + cypher); tx.commit(); } } + @Override public void close() { LOGGER.fine("Closing the Neo4J session, the database name is: " + database); From d9e8f78a131ab3bdb519150bd328916b5f8799ce Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 16:41:46 +0000 Subject: [PATCH 097/148] feat: update the query Signed-off-by: Otavio Santana --- .../DefaultNeo4JDatabaseManager.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) 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 aa89eabd3..5f7d1a6b4 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 @@ -79,15 +79,13 @@ public CommunicationEntity insert(CommunicationEntity entity, Duration ttl) { @Override public CommunicationEntity update(CommunicationEntity entity) { Objects.requireNonNull(entity, "entity is required"); - Objects.requireNonNull(entity, "entity is required"); if (!entity.contains(ID)) { - throw new CommunicationException("Cannot update entity without an _id field, entity: " + entity); + throw new Neo4JCommunicationException("Cannot update entity without an _id field, entity: " + entity); } Map entityMap = entity.toMap(); - StringBuilder cypher = new StringBuilder("MATCH (e:"); - cypher.append(entity.name()).append(" { ").append(ID).append(": $").append(ID).append(" }) SET "); + StringBuilder cypher = new StringBuilder("MATCH (e) WHERE elementId(e) = $elementId SET "); entityMap.entrySet().stream() .filter(entry -> !ID.equals(entry.getKey())) @@ -97,17 +95,25 @@ public CommunicationEntity update(CommunicationEntity entity) { cypher.setLength(cypher.length() - 2); } - LOGGER.finest(() -> "Executing Cyphyer to update entities: " + cypher); + LOGGER.finest(() -> "Executing Cypher query to update entity: " + cypher); try (Transaction tx = session.beginTransaction()) { - tx.run(cypher.toString(), Values.parameters(flattenMap(entityMap))); + var elementId = entity.find(ID) + .orElseThrow(() -> new CommunicationException("Entity must have an ID")) + .get(String.class); + + Map params = new HashMap<>(entityMap); + params.put("elementId", elementId); + + tx.run(cypher.toString(), Values.parameters(flattenMap(params))); tx.commit(); } - LOGGER.fine("Updated entity: " + entity.name() + " with _id: " + entity.find(ID).orElseThrow().get(String.class)); + LOGGER.fine("Updated entity: " + entity.name() + " with elementId: " + entity.find(ID).orElseThrow().get(String.class)); return entity; } + @Override public Iterable update(Iterable entities) { Objects.requireNonNull(entities, "entities is required"); From 239ef92c63763f87b451f214b584ef917a95cba6 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 16:46:59 +0000 Subject: [PATCH 098/148] feat: fix serliazation at neo4j Signed-off-by: Otavio Santana --- .../communication/DefaultNeo4JDatabaseManager.java | 12 +++++++----- .../communication/Neo4JDatabaseManagerTest.java | 2 ++ 2 files changed, 9 insertions(+), 5 deletions(-) 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 5f7d1a6b4..823ae6cab 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 @@ -297,15 +297,17 @@ private CommunicationEntity extractEntity(String entityName, org.neo4j.driver.Re List elements = new ArrayList<>(); for (String key : record.keys()) { - String fieldName = key.contains(".") ? key.substring(key.indexOf('.') + 1) : key; - if (isFullNode && record.get(key).hasType(org.neo4j.driver.types.TypeSystem.getDefault().NODE())) { - var node = record.get(key).asNode(); + var value = record.get(key); + + if (value.hasType(org.neo4j.driver.types.TypeSystem.getDefault().NODE())) { + var node = value.asNode(); node.asMap().forEach((k, v) -> elements.add(Element.of(k, v))); - elements.add(Element.of(ID, node.elementId())); + elements.add(Element.of("_id", node.elementId())); // Ensuring correct ID retrieval } else { - elements.add(Element.of(fieldName, record.get(key).asObject())); + elements.add(Element.of(key, value.asObject())); // Handle normal properties } } + return CommunicationEntity.of(entityName, elements); } } 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 59057be31..07ce76fd6 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 @@ -473,6 +473,8 @@ void shouldExecuteCustomQuery() { SoftAssertions.assertSoftly(softly -> { softly.assertThat(result).isNotEmpty(); 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 }); } From 602783b5010661c853e0bfa3836ad6fabfa0b281 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 16:50:44 +0000 Subject: [PATCH 099/148] feat: update Signed-off-by: Otavio Santana --- .../communication/DefaultNeo4JDatabaseManager.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 823ae6cab..a8ee0bfcf 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 @@ -295,19 +295,19 @@ var record = result.hasNext() ? result.next() : null; private CommunicationEntity extractEntity(String entityName, 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())) { + String fieldName = key.contains(".") ? key.substring(key.indexOf('.') + 1) : key; + + if (isFullNode && value.hasType(org.neo4j.driver.types.TypeSystem.getDefault().NODE())) { var node = value.asNode(); node.asMap().forEach((k, v) -> elements.add(Element.of(k, v))); - elements.add(Element.of("_id", node.elementId())); // Ensuring correct ID retrieval + elements.add(Element.of("_id", node.elementId())); // Store Neo4j ID } else { - elements.add(Element.of(key, value.asObject())); // Handle normal properties + elements.add(Element.of(fieldName, value.asObject())); // Proper field extraction } } - return CommunicationEntity.of(entityName, elements); } } From 0d51fa536478393dca6fb60af4fd765f3552329f Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 16:58:55 +0000 Subject: [PATCH 100/148] feat: fix serialization Signed-off-by: Otavio Santana --- .../communication/DefaultNeo4JDatabaseManager.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) 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 a8ee0bfcf..c90fb8cc2 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 @@ -295,19 +295,20 @@ var record = result.hasNext() ? result.next() : null; private CommunicationEntity extractEntity(String entityName, org.neo4j.driver.Record record, boolean isFullNode) { List elements = new ArrayList<>(); + for (String key : record.keys()) { var value = record.get(key); - String fieldName = key.contains(".") ? key.substring(key.indexOf('.') + 1) : key; - - if (isFullNode && value.hasType(org.neo4j.driver.types.TypeSystem.getDefault().NODE())) { + if (value.hasType(org.neo4j.driver.types.TypeSystem.getDefault().NODE())) { var node = value.asNode(); - node.asMap().forEach((k, v) -> elements.add(Element.of(k, v))); - elements.add(Element.of("_id", node.elementId())); // Store Neo4j ID + node.asMap().forEach((k, v) -> elements.add(Element.of(k, v))); // Extract properties + elements.add(Element.of(ID, node.elementId())); } else { - elements.add(Element.of(fieldName, value.asObject())); // Proper field extraction + String fieldName = key.contains(".") ? key.substring(key.indexOf('.') + 1) : key; + elements.add(Element.of(fieldName, value.asObject())); } } + return CommunicationEntity.of(entityName, elements); } } From fc1d23ed3f4c9aa416a73c6e3cae6b84830bb626 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 17:03:32 +0000 Subject: [PATCH 101/148] fix order to query builder Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JQueryBuilder.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java index 7db432f0f..49ec8b5ea 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java @@ -65,10 +65,12 @@ public String buildQuery(SelectQuery query, Map parameters) { createWhereClause(cypher, c, parameters); }); - query.sorts().forEach(sort -> cypher.append(" ORDER BY e.") - .append(translateField(sort.property())) - .append(" ").append(sort.isAscending() ? "ASC" : "DESC")); - + if (!query.sorts().isEmpty()) { + cypher.append(" ORDER BY "); + cypher.append(query.sorts().stream() + .map(sort -> "e." + sort.property() + (sort.isAscending() ? " ASC" : " DESC")) + .collect(Collectors.joining(", "))); // Fix double "e." + } if (query.skip() > 0) { cypher.append(" SKIP ").append(query.skip()); } From 3b8b977e0de6ced793dd3a725724af13020c17b7 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 17:08:09 +0000 Subject: [PATCH 102/148] feat: enhance imports Signed-off-by: Otavio Santana --- .../jnosql/databases/neo4j/communication/package-info.java | 1 + .../databases/neo4j/communication/Neo4JDatabaseManagerTest.java | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java new file mode 100644 index 000000000..d43c80de3 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java @@ -0,0 +1 @@ +package org.eclipse.jnosql.databases.neo4j.communication; \ No newline at end of file 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 07ce76fd6..640193845 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 @@ -19,7 +19,6 @@ import net.datafaker.Faker; import org.assertj.core.api.SoftAssertions; import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; -import org.eclipse.jnosql.communication.semistructured.DatabaseManager; import org.eclipse.jnosql.communication.semistructured.Element; import org.eclipse.jnosql.communication.semistructured.Elements; import org.junit.jupiter.api.BeforeAll; From 356825de59b2313f52619927f7bb5272cca55f5a Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 17:10:17 +0000 Subject: [PATCH 103/148] docs: create documentation to neo4j Signed-off-by: Otavio Santana --- .../neo4j/communication/package-info.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java index d43c80de3..a4626be6c 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/package-info.java @@ -1 +1,25 @@ +/* + * + * 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 + * + */ + +/** + * This package contains the communication layer for Neo4j integration with Eclipse JNoSQL. + + * It provides implementations for managing communication with a Neo4j database, + * including inserting, updating, deleting, and querying entities. Additionally, + * it supports handling relationships (edges) between nodes and executing custom Cypher queries. + */ package org.eclipse.jnosql.databases.neo4j.communication; \ No newline at end of file From ef5ae8b6eb8d8631a3791f998ce8b037f67ede6c Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 17:16:13 +0000 Subject: [PATCH 104/148] feat: create Neo4Template Signed-off-by: Otavio Santana --- .../neo4j/mapping/Neo4JTemplate.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplate.java 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 new file mode 100644 index 000000000..0ff254ff2 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplate.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.neo4j.mapping; + +import org.eclipse.jnosql.mapping.semistructured.SemiStructuredTemplate; + +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Stream; + +public interface Neo4JTemplate extends SemiStructuredTemplate { + + Stream executeQuery(String cypher, Map parameters); + + Stream traverse(String startNodeId, String relationship, int depth); + + Stream traverse(String startNodeId, Supplier relationship, int depth); + + void edge(T source, String relationshipType, E target); + + void edge(T source, Supplier relationship, E target); + + void remove(T source, String relationshipType, E target); + + void remove(T source, Supplier relationship, E target); +} From bd56fa73dba46eda05956a9feb0b3c7a40ab31fd Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 17:21:49 +0000 Subject: [PATCH 105/148] docs: enhance documentaiton Neo4Jtemplate Signed-off-by: Otavio Santana --- .../neo4j/mapping/Neo4JTemplate.java | 77 ++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) 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 0ff254ff2..d57235d76 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 @@ -22,19 +22,94 @@ import java.util.function.Supplier; import java.util.stream.Stream; +/** + * A template interface for executing queries, traversing relationships, + * and managing edges in a Neo4J database. + * This interface provides methods for executing Cypher queries, + * traversing relationships, and handling edges between entities. + * + */ public interface Neo4JTemplate extends SemiStructuredTemplate { - + /** + * Executes a Cypher query and returns a stream of results mapped to the given entity type. + * + * @param cypher The Cypher query string. + * @param parameters The query parameters. + * @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} or {@code parameters} is null. + */ Stream executeQuery(String cypher, Map parameters); + /** + * Traverses relationships from a given start node up to a specified depth. + * + * @param startNodeId The unique identifier of the starting node. + * @param relationship The relationship type to traverse. + * @param depth The depth of traversal. + * @param The entity type representing nodes or relationships within the graph database. + * @return A stream of entities resulting from the traversal. + * @throws NullPointerException if {@code startNodeId} or {@code relationship} is null. + */ Stream traverse(String startNodeId, String relationship, int depth); + /** + * Traverses relationships dynamically using a relationship supplier. + * + * @param startNodeId The unique identifier of the starting node. + * @param relationship A supplier providing the relationship type dynamically. + * @param depth The depth of traversal. + * @param The entity type representing nodes or relationships within the graph database. + * @return A stream of entities resulting from the traversal. + * @throws NullPointerException if {@code startNodeId} or {@code relationship} is null. + */ Stream traverse(String startNodeId, Supplier relationship, int depth); + /** + * Creates an edge between two entities with the specified relationship type. + * + * @param source The source entity. + * @param relationshipType The relationship type to establish. + * @param target The target entity. + * @param The entity type representing the source node. + * @param The entity type representing the target node. + * @throws NullPointerException if {@code source}, {@code relationshipType}, or {@code target} is null. + */ void edge(T source, String relationshipType, E target); + /** + * Creates an edge between two entities using a dynamically provided relationship type. + * + * @param source The source entity. + * @param relationship A supplier providing the relationship type dynamically. + * @param target The target entity. + * @param The entity type representing the source node. + * @param The entity type representing the target node. + * @throws NullPointerException if {@code source}, {@code relationship}, or {@code target} is null. + */ void edge(T source, Supplier relationship, E target); + /** + * Removes an edge between two entities with the specified relationship type. + * + * @param source The source entity. + * @param relationshipType The relationship type to remove. + * @param target The target entity. + * @param The entity type representing the source node. + * @param The entity type representing the target node. + * @throws NullPointerException if {@code source}, {@code relationshipType}, or {@code target} is null. + */ void remove(T source, String relationshipType, E target); + /** + * Removes an edge between two entities using a dynamically provided relationship type. + * + * @param source The source entity. + * @param relationship A supplier providing the relationship type dynamically. + * @param target The target entity. + * @param The entity type representing the source node. + * @param The entity type representing the target node. + * @throws NullPointerException if {@code source}, {@code relationship}, or {@code target} is null. + */ void remove(T source, Supplier relationship, E target); } From ed67bde938fb427186970b0158c52e2691f23205 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 17:24:23 +0000 Subject: [PATCH 106/148] feat: start the implementation on neo4j Signed-off-by: Otavio Santana --- .../neo4j/mapping/DefaultNeo4JTemplate.java | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/DefaultNeo4JTemplate.java 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 new file mode 100644 index 000000000..3a55f9a50 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/DefaultNeo4JTemplate.java @@ -0,0 +1,106 @@ +package org.eclipse.jnosql.databases.neo4j.mapping; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.Typed; +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.semistructured.DatabaseManager; +import org.eclipse.jnosql.databases.neo4j.communication.Neo4JDatabaseManager; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.semistructured.AbstractSemiStructuredTemplate; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; +import org.eclipse.jnosql.mapping.semistructured.EventPersistManager; + +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Stream; + +@ApplicationScoped +@Typed(Neo4JTemplate.class) +public class DefaultNeo4JTe extends AbstractSemiStructuredTemplate implements Neo4JTemplate { + + private Instance manager; + + private EntityConverter converter; + + private EntitiesMetadata entities; + + private Converters converters; + + private EventPersistManager persistManager; + + + @Inject + DefaultNeo4JTemplate(Instance manager, + EntityConverter converter, + EntitiesMetadata entities, + Converters converters, + EventPersistManager persistManager) { + this.manager = manager; + this.converter = converter; + this.entities = entities; + this.converters = converters; + this.persistManager = persistManager; + } + + @Override + public Stream executeQuery(String cypher, Map parameters) { + return Stream.empty(); + } + + @Override + public Stream traverse(String startNodeId, String relationship, int depth) { + return Stream.empty(); + } + + @Override + public Stream traverse(String startNodeId, Supplier relationship, int depth) { + return Stream.empty(); + } + + @Override + public void edge(T source, String relationshipType, E target) { + + } + + @Override + public void edge(T source, Supplier relationship, E target) { + + } + + @Override + public void remove(T source, String relationshipType, E target) { + + } + + @Override + public void remove(T source, Supplier relationship, E target) { + + } + + @Override + protected EntityConverter converter() { + return converter; + } + + @Override + protected DatabaseManager manager() { + return manager.get(); + } + + @Override + protected EventPersistManager eventManager() { + return persistManager; + } + + @Override + protected EntitiesMetadata entities() { + return entities; + } + + @Override + protected Converters converters() { + return converters; + } +} From 7f023398cdaca0cbd373db4a4eee49b1d80ad507 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 17:24:59 +0000 Subject: [PATCH 107/148] feat: create a logger neo4j template Signed-off-by: Otavio Santana --- .../jnosql/databases/neo4j/mapping/DefaultNeo4JTemplate.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 3a55f9a50..50e7b9d69 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 @@ -14,11 +14,14 @@ import java.util.Map; import java.util.function.Supplier; +import java.util.logging.Logger; import java.util.stream.Stream; @ApplicationScoped @Typed(Neo4JTemplate.class) -public class DefaultNeo4JTe extends AbstractSemiStructuredTemplate implements Neo4JTemplate { +public class DefaultNeo4JTemplate extends AbstractSemiStructuredTemplate implements Neo4JTemplate { + + private static final Logger LOGGER = Logger.getLogger(DefaultNeo4JTemplate.class.getName()); private Instance manager; From 9d65ea8ecf96e14733f0a21d64673bb334e1a89f Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 17:36:09 +0000 Subject: [PATCH 108/148] feat: create Edge representation Signed-off-by: Otavio Santana --- .../neo4j/mapping/DefaultNeo4JTemplate.java | 39 ++++++++-- .../jnosql/databases/neo4j/mapping/Edge.java | 71 +++++++++++++++++++ 2 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Edge.java 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 50e7b9d69..e1eee44fb 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 @@ -13,6 +13,8 @@ import org.eclipse.jnosql.mapping.semistructured.EventPersistManager; import java.util.Map; +import java.util.Objects; +import java.util.Optional; import java.util.function.Supplier; import java.util.logging.Logger; import java.util.stream.Stream; @@ -47,31 +49,54 @@ public class DefaultNeo4JTemplate extends AbstractSemiStructuredTemplate impleme this.persistManager = persistManager; } + @SuppressWarnings("unchecked") @Override public Stream executeQuery(String cypher, Map parameters) { - return Stream.empty(); + Objects.requireNonNull(cypher, "cypher is required"); + Objects.requireNonNull(parameters, "parameters is required"); + return manager.get().executeQuery(cypher, parameters) + .map(e -> (T) converter.toEntity(e)); } + @Override + public Stream traverse(String startNodeId, Supplier relationship, int depth) { + Objects.requireNonNull(startNodeId, "startNodeId is required"); + Objects.requireNonNull(relationship, "relationship is required"); + return traverse(startNodeId, relationship.get(), depth); + } + + @Override public Stream traverse(String startNodeId, String relationship, int depth) { - return Stream.empty(); + Objects.requireNonNull(startNodeId, "startNodeId is required"); + Objects.requireNonNull(relationship, "relationship is required"); + return manager.get().traverse(startNodeId, relationship, depth) + .map(e -> (T) converter.toEntity(e)); } @Override - public Stream traverse(String startNodeId, Supplier relationship, int depth) { - return Stream.empty(); + public void edge(T source, Supplier relationship, E target) { + Objects.requireNonNull(source, "source is required"); + Objects.requireNonNull(relationship, "relationship is required"); + Objects.requireNonNull(target, "target is required"); + edge(source, relationship.get(), target); } + @Override public void edge(T source, String relationshipType, E target) { + Objects.requireNonNull(relationshipType, "relationshipType is required"); + Objects.requireNonNull(source, "source is required"); + Objects.requireNonNull(target, "target is required"); - } + this.find(source.getClass(), source); + this.find(target.getClass(), target); - @Override - public void edge(T source, Supplier relationship, E target) { } + + @Override public void remove(T source, String relationshipType, E target) { diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Edge.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Edge.java new file mode 100644 index 000000000..60e1c00ad --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Edge.java @@ -0,0 +1,71 @@ +/* + * + * 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 java.util.Objects; + +public class Edge { + + private final T source; + private final E target; + private final String relationship; + + private Edge(T source, E target, String relationship) { + this.source = source; + this.target = target; + this.relationship = relationship; + } + + public T source() { + return source; + } + + public E target() { + return target; + } + + public String relationship() { + return relationship; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + Edge edge = (Edge) o; + return Objects.equals(source, edge.source) && Objects.equals(target, edge.target) && Objects.equals(relationship, edge.relationship); + } + + @Override + public int hashCode() { + return Objects.hash(source, target, relationship); + } + + @Override + public String toString() { + return "Edge{" + + "source=" + source + + ", target=" + target + + ", relationship='" + relationship + '\'' + + '}'; + } + + static Edge of(T source, E target, String relationship) { + return new Edge<>(source, target, relationship); + } +} From 551dd8dad446320414a27c9a27553536c0e3acf6 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 17:39:17 +0000 Subject: [PATCH 109/148] docs: enhance documentation at edge Signed-off-by: Otavio Santana --- .../jnosql/databases/neo4j/mapping/Edge.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Edge.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Edge.java index 60e1c00ad..65840c024 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Edge.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Edge.java @@ -18,6 +18,18 @@ import java.util.Objects; +/** + * Represents an edge in a graph database, linking a source entity to a target entity + * through a specified relationship type. + * + *

    This class models relationships between nodes in a Neo4J database, where each + * edge is defined by a source node, a target node, and a relationship type.

    + * + *

    Edges are immutable and ensure that a valid relationship exists between two entities.

    + * + * @param The entity type representing the source node. + * @param The entity type representing the target node. + */ public class Edge { private final T source; @@ -30,14 +42,29 @@ private Edge(T source, E target, String relationship) { this.relationship = relationship; } + /** + * Retrieves the source entity of the edge. + * + * @return The source entity. + */ public T source() { return source; } + /** + * Retrieves the target entity of the edge. + * + * @return The target entity. + */ public E target() { return target; } + /** + * Retrieves the relationship type of the edge. + * + * @return The relationship type. + */ public String relationship() { return relationship; } From 7f4ed539056b8c3a115338402b673732f6a09fbf Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 17:59:59 +0000 Subject: [PATCH 110/148] feat: implement Neo4j template Signed-off-by: Otavio Santana --- .../neo4j/mapping/DefaultNeo4JTemplate.java | 58 +++++++++++++++---- .../jnosql/databases/neo4j/mapping/Edge.java | 2 +- .../neo4j/mapping/Neo4JTemplate.java | 6 +- 3 files changed, 51 insertions(+), 15 deletions(-) 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 e1eee44fb..0332fb659 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 @@ -14,7 +14,6 @@ import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.function.Supplier; import java.util.logging.Logger; import java.util.stream.Stream; @@ -66,6 +65,7 @@ public Stream traverse(String startNodeId, Supplier relationship, } + @SuppressWarnings("unchecked") @Override public Stream traverse(String startNodeId, String relationship, int depth) { Objects.requireNonNull(startNodeId, "startNodeId is required"); @@ -75,35 +75,69 @@ public Stream traverse(String startNodeId, String relationship, int depth } @Override - public void edge(T source, Supplier relationship, E target) { + public Edge edge(T source, Supplier relationship, E target) { Objects.requireNonNull(source, "source is required"); Objects.requireNonNull(relationship, "relationship is required"); Objects.requireNonNull(target, "target is required"); - edge(source, relationship.get(), target); + return edge(source, relationship.get(), target); } - + @SuppressWarnings("unchecked") @Override - public void edge(T source, String relationshipType, E target) { + public Edge edge(T source, String relationshipType, E target) { Objects.requireNonNull(relationshipType, "relationshipType is required"); Objects.requireNonNull(source, "source is required"); Objects.requireNonNull(target, "target is required"); - this.find(source.getClass(), source); - this.find(target.getClass(), target); - - + T findSource = this.find((Class)source.getClass(), source).orElseGet(() ->{ + LOGGER.fine("There is not entity to source: " + source + " inserting the entity"); + return this.insert(source); + }); + + E findTarget = this.find((Class)target.getClass(), source).orElseGet(() ->{ + LOGGER.fine("There is not entity to target: " + target + " inserting the entity"); + return this.insert(target); + }); + + var sourceCommunication = this.converter.toCommunication(findSource); + var targetCommunication = this.converter.toCommunication(findTarget); + LOGGER.fine(() -> "creating an edge from " + sourceCommunication + " to " + targetCommunication + " with the relationship: " + relationshipType); + manager.get().edge(sourceCommunication, relationshipType, targetCommunication); + return Edge.of(findSource, relationshipType, findTarget); } @Override - public void remove(T source, String relationshipType, E target) { - + public void remove(T source, Supplier relationship, E target) { + Objects.requireNonNull(source, "source is required"); + Objects.requireNonNull(relationship, "relationship is required"); + Objects.requireNonNull(target, "target is required"); + this.remove(source, relationship.get(), target); } + @SuppressWarnings("unchecked") @Override - public void remove(T source, Supplier relationship, E target) { + public void remove(T source, String relationshipType, E target) { + Objects.requireNonNull(source, "source is required"); + Objects.requireNonNull(relationshipType, "relationshipType is required"); + Objects.requireNonNull(target, "target is required"); + + T findSource = this.find((Class)source.getClass(), source).orElseGet(() ->{ + LOGGER.fine("There is not entity to source: " + source + " inserting the entity"); + return this.insert(source); + }); + + E findTarget = this.find((Class)target.getClass(), source).orElseGet(() ->{ + LOGGER.fine("There is not entity to target: " + target + " inserting the entity"); + return this.insert(target); + }); + + var sourceCommunication = this.converter.toCommunication(findSource); + var targetCommunication = this.converter.toCommunication(findTarget); + + LOGGER.fine(() -> "removing an edge from " + sourceCommunication + " to " + targetCommunication + " with the relationship: " + relationshipType); + manager.get().remove(sourceCommunication, relationshipType, targetCommunication); } diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Edge.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Edge.java index 65840c024..ba61cdf51 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Edge.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Edge.java @@ -92,7 +92,7 @@ public String toString() { '}'; } - static Edge of(T source, E target, String relationship) { + static Edge of(T source, String relationship, E target) { return new Edge<>(source, target, relationship); } } 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 d57235d76..80bdc8497 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 @@ -73,9 +73,10 @@ public interface Neo4JTemplate extends SemiStructuredTemplate { * @param target The target entity. * @param The entity type representing the source node. * @param The entity type representing the target node. + * @return The created {@link Edge} representing the relationship. * @throws NullPointerException if {@code source}, {@code relationshipType}, or {@code target} is null. */ - void edge(T source, String relationshipType, E target); + Edge edge(T source, String relationshipType, E target); /** * Creates an edge between two entities using a dynamically provided relationship type. @@ -85,9 +86,10 @@ public interface Neo4JTemplate extends SemiStructuredTemplate { * @param target The target entity. * @param The entity type representing the source node. * @param The entity type representing the target node. + * @return The created {@link Edge} representing the relationship. * @throws NullPointerException if {@code source}, {@code relationship}, or {@code target} is null. */ - void edge(T source, Supplier relationship, E target); + Edge edge(T source, Supplier relationship, E target); /** * Removes an edge between two entities with the specified relationship type. From 08f77d682a438fdaf96c6ea093ec53c1ae87cc7c Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 18:00:37 +0000 Subject: [PATCH 111/148] test: create initial neo4template test structure Signed-off-by: Otavio Santana --- .../databases/neo4j/mapping/DefaultNeo4JTemplateTest.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/DefaultNeo4JTemplateTest.java 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 new file mode 100644 index 000000000..de7eb6458 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/DefaultNeo4JTemplateTest.java @@ -0,0 +1,7 @@ +package org.eclipse.jnosql.databases.neo4j.mapping; + +import static org.junit.jupiter.api.Assertions.*; + +class DefaultNeo4JTemplateTest { + +} \ No newline at end of file From b561592b53ab9c6834924f404ee9ab110adb23b8 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 18:00:54 +0000 Subject: [PATCH 112/148] feat: include header to neo4 tempalte Signed-off-by: Otavio Santana --- .../neo4j/mapping/DefaultNeo4JTemplate.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) 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 0332fb659..adb03e33d 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 @@ -1,3 +1,19 @@ +/* + * + * 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.enterprise.context.ApplicationScoped; From bfc4080e2c630d5ab21e4a9df18598f11eb2b501 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 18:01:08 +0000 Subject: [PATCH 113/148] feat: create struture Signed-off-by: Otavio Santana --- .../neo4j/mapping/DefaultNeo4JTemplateTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) 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 de7eb6458..0790c912b 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 @@ -1,3 +1,19 @@ +/* + * + * 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 static org.junit.jupiter.api.Assertions.*; From 4c5364350e3d876de5a52572b73f65239dfe169a Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 18:05:02 +0000 Subject: [PATCH 114/148] test: create initial structure Signed-off-by: Otavio Santana --- .../databases/neo4j/mapping/Birthday.java | 70 +++++++++++++++++++ .../jnosql/databases/neo4j/mapping/Music.java | 66 +++++++++++++++++ .../neo4j/mapping/Neo4JTemplateTest.java | 31 ++++++++ 3 files changed, 167 insertions(+) create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Birthday.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Music.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplateTest.java diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Birthday.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Birthday.java new file mode 100644 index 000000000..6ce88cc85 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Birthday.java @@ -0,0 +1,70 @@ +/* + * 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.neo4j.mapping; + + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.Objects; + +@Entity +public class Birthday { + + @Id + private String name; + + @Column + private Integer age; + + + public String getName() { + return name; + } + + public Integer getAge() { + return age; + } + + public Birthday(String name, Integer age) { + this.name = name; + this.age = age; + } + + public Birthday() { + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Birthday birthday = (Birthday) o; + return Objects.equals(name, birthday.name) && + Objects.equals(age, birthday.age); + } + + @Override + public int hashCode() { + return Objects.hash(name, age); + } + + @Override + public String toString() { + return "Person{" + "name='" + name + '\'' + + ", age=" + age + + '}'; + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Music.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Music.java new file mode 100644 index 000000000..24ab933a6 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Music.java @@ -0,0 +1,66 @@ +/* + * 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.neo4j.mapping; + + +import jakarta.nosql.Column; +import jakarta.nosql.Convert; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +@Entity +public class Music { + + @Id + private String id; + + @Column + private String name; + + @Column + private int year; + + Music() { + } + + + Music(String id, String name, int year) { + this.id = id; + this.name = name; + this.year = year; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public int getYear() { + return year; + } + + @Override + public String toString() { + return "Music{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", year=" + year + + '}'; + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplateTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplateTest.java new file mode 100644 index 000000000..d656ec70d --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplateTest.java @@ -0,0 +1,31 @@ +package org.eclipse.jnosql.databases.neo4j.mapping; + +import jakarta.inject.Inject; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + + +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, Neo4JTemplate.class}) +@AddPackages(Music.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class}) +class Neo4JTemplateTest { + + @Inject + private Neo4JTemplate template; + + @Test + void shouldInjectMongoDBTemplate() { + Assertions.assertNotNull(template); + } +} \ No newline at end of file From 06364a445628196ce24b815f8b360d9005451eac Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 18:12:33 +0000 Subject: [PATCH 115/148] test: create test scenarion on neo4 templat Signed-off-by: Otavio Santana --- .../mapping/DefaultNeo4JTemplateTest.java | 129 ++++++++++++++++++ .../neo4j/mapping/Neo4JTemplateTest.java | 2 - 2 files changed, 129 insertions(+), 2 deletions(-) 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 0790c912b..1d8fd6f6e 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 @@ -16,8 +16,137 @@ */ package org.eclipse.jnosql.databases.neo4j.mapping; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; +import jakarta.nosql.tck.entities.Person; +import net.datafaker.Faker; +import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; +import org.eclipse.jnosql.communication.semistructured.Element; +import org.eclipse.jnosql.databases.neo4j.communication.Neo4JDatabaseManager; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; +import org.eclipse.jnosql.mapping.semistructured.EventPersistManager; +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.Collections; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Stream; + import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, EntityConverter.class, Neo4JTemplate.class}) +@AddPackages(Music.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class}) class DefaultNeo4JTemplateTest { + @Inject + private EntityConverter converter; + + @Inject + private EventPersistManager persistManager; + + @Inject + private EntitiesMetadata entities; + + @Inject + private Converters converters; + + private DefaultNeo4JTemplate template; + + private Neo4JDatabaseManager manager; + + @BeforeEach + void setUp() { + this.manager = mock(Neo4JDatabaseManager.class); + Instance instance = mock(Instance.class); + when(instance.get()).thenReturn(manager); + template = new DefaultNeo4JTemplate(instance, converter, entities, converters, persistManager); + } + + @Test + void shouldExecuteQuery() { + String cypher = "MATCH (n:Person) RETURN n"; + Map parameters = Collections.emptyMap(); + CommunicationEntity entity = CommunicationEntity.of("Person"); + entity.add(Element.of("name", "Ada")); + when(manager.executeQuery(cypher, parameters)).thenReturn(Stream.of(entity)); + + Stream result = template.executeQuery(cypher, parameters); + assertNotNull(result); + assertTrue(result.findFirst().isPresent()); + } + + @Test + void shouldThrowExceptionWhenQueryIsNull() { + assertThrows(NullPointerException.class, () -> template.executeQuery(null, Collections.emptyMap())); + assertThrows(NullPointerException.class, () -> template.executeQuery("MATCH (n) RETURN n", null)); + } + + @Test + void shouldCreateEdge() { + var faker = new Faker(); + Person source = Person.of(faker); + Person target = Person.of(faker); + String relationshipType = "FRIENDS"; + Edge edge = Edge.of(source, relationshipType, target); + + doNothing().when(manager).edge(any(), anyString(), any()); + + Edge result = template.edge(source, relationshipType, target); + + assertNotNull(result); + assertEquals(edge, result); + verify(manager).edge(any(), eq(relationshipType), any()); + } + + @Test + void shouldThrowExceptionWhenCreatingEdgeWithNullValues() { + var faker = new Faker(); + Person source = Person.of(faker); + Person target = Person.of(faker); + assertThrows(NullPointerException.class, () -> template.edge(null, "FRIENDS", target)); + assertThrows(NullPointerException.class, () -> template.edge(source, (Supplier) null, target)); + assertThrows(NullPointerException.class, () -> template.edge(source, "FRIENDS", null)); + } + + @Test + void shouldRemoveEdge() { + var faker = new Faker(); + Person source = Person.of(faker); + Person target = Person.of(faker); + String relationshipType = "FRIENDS"; + + doNothing().when(manager).remove(any(), anyString(), any()); + + template.remove(source, relationshipType, target); + verify(manager).remove(any(), eq(relationshipType), any()); + } + + @Test + void shouldThrowExceptionWhenRemovingEdgeWithNullValues() { + var faker = new Faker(); + Person source = Person.of(faker); + Person target = Person.of(faker); + assertThrows(NullPointerException.class, () -> template.remove(null, "FRIENDS", target)); + assertThrows(NullPointerException.class, () -> template.remove(source, (String) null, target)); + assertThrows(NullPointerException.class, () -> template.remove(source, "FRIENDS", null)); + } } \ No newline at end of file diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplateTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplateTest.java index d656ec70d..711423c5d 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplateTest.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplateTest.java @@ -11,8 +11,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - @EnableAutoWeld @AddPackages(value = {Converters.class, EntityConverter.class, Neo4JTemplate.class}) From 202acd541d4d72c494f88c8baf832892e6545dd9 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 18:17:40 +0000 Subject: [PATCH 116/148] feat: create test on neodj# Signed-off-by: Otavio Santana --- .../databases/neo4j/mapping/DefaultNeo4JTemplateTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 1d8fd6f6e..fdd8ad6a5 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 @@ -34,6 +34,7 @@ import org.jboss.weld.junit5.auto.EnableAutoWeld; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import java.util.Collections; import java.util.Map; @@ -108,7 +109,8 @@ void shouldCreateEdge() { String relationshipType = "FRIENDS"; Edge edge = Edge.of(source, relationshipType, target); - doNothing().when(manager).edge(any(), anyString(), any()); + + Mockito.when(manager.insert(Mockito.any(CommunicationEntity.class))).thenAnswer(invocation -> invocation.getArgument(0)); Edge result = template.edge(source, relationshipType, target); @@ -135,7 +137,7 @@ void shouldRemoveEdge() { String relationshipType = "FRIENDS"; doNothing().when(manager).remove(any(), anyString(), any()); - + Mockito.when(manager.insert(Mockito.any(CommunicationEntity.class))).thenAnswer(invocation -> invocation.getArgument(0)); template.remove(source, relationshipType, target); verify(manager).remove(any(), eq(relationshipType), any()); } From e3dadc218768bd4b0bce4a010f1f5026841db116 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 18:23:56 +0000 Subject: [PATCH 117/148] chore: organize pom.xml Signed-off-by: Otavio Santana --- jnosql-neo4j/pom.xml | 23 +++++++++++-------- .../integration/TemplateIntegrationTest.java | 4 ++++ 2 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/TemplateIntegrationTest.java diff --git a/jnosql-neo4j/pom.xml b/jnosql-neo4j/pom.xml index f0558621f..74c6599ef 100644 --- a/jnosql-neo4j/pom.xml +++ b/jnosql-neo4j/pom.xml @@ -31,10 +31,14 @@ - org.testcontainers - neo4j - ${testcontainers.version} - test + org.neo4j.driver + neo4j-java-driver + ${neo4j.driver} + + + ${project.groupId} + jnosql-database-commons + ${project.version} org.eclipse.jnosql.mapping @@ -42,10 +46,11 @@ ${project.version} - org.neo4j.driver - neo4j-java-driver - ${neo4j.driver} - + org.testcontainers + neo4j + ${testcontainers.version} + test +
    - + 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 new file mode 100644 index 000000000..ee215091a --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/TemplateIntegrationTest.java @@ -0,0 +1,4 @@ +package org.eclipse.jnosql.databases.neo4j.integration; + +public class TemplateIntegrationTest { +} From 79b25e31386a60668aa9db99d1898b00741901d2 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 18:29:05 +0000 Subject: [PATCH 118/148] feat: create magazine record Signed-off-by: Otavio Santana --- .../databases/neo4j/integration/Magazine.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/Magazine.java diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/Magazine.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/Magazine.java new file mode 100644 index 000000000..cf3cd78b5 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/Magazine.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 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.integration; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +@Entity +public record Magazine(@Id String id, @Column("title") String title, @Column("edition") int edition) { + + +} From da3a07d03d16a686f40582dcd6527a895927a39a Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 18:29:25 +0000 Subject: [PATCH 119/148] feat: create neo4jconfigurations Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JConfigurations.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java index fb3edd256..a4ee35362 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfigurations.java @@ -61,7 +61,13 @@ public enum Neo4JConfigurations implements Supplier { * The password for authentication. * Example: "password123" */ - PASSWORD("jnosql.neo4j.password"); + PASSWORD("jnosql.neo4j.password"), + + /** + * The database name. + * Example: "library" + */ + DATABASE("jnosql.neo4j.database"); private final String value; From f353648712555fb27f6c8e4f2b5ccf1b86fb0ae1 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 18:29:39 +0000 Subject: [PATCH 120/148] feat: define neo4j test scenarions Signed-off-by: Otavio Santana --- .../communication/DatabaseContainer.java | 3 + .../Neo4JDatabaseManagerTest.java | 4 + .../integration/TemplateIntegrationTest.java | 102 ++++++++++++++++++ 3 files changed, 109 insertions(+) diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java index f9ac2698e..022012da1 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/DatabaseContainer.java @@ -33,6 +33,9 @@ public enum DatabaseContainer { } + public String host() { + return neo4jContainer.getBoltUrl(); + } public Neo4JDatabaseManager get(String database) { Objects.requireNonNull(database, "database is required"); Settings settings = Settings.builder().put(Neo4JConfigurations.URI, neo4jContainer.getBoltUrl()).build(); 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 640193845..b3c866d69 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 @@ -24,6 +24,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import java.util.HashMap; import java.util.List; @@ -31,10 +32,13 @@ import java.util.function.Predicate; import java.util.stream.Stream; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; import static org.eclipse.jnosql.communication.semistructured.DeleteQuery.delete; import static org.eclipse.jnosql.communication.semistructured.SelectQuery.select; import static org.junit.jupiter.api.Assertions.assertTrue; +@EnabledIfSystemProperty(named = NAMED, matches = MATCHES) class Neo4JDatabaseManagerTest { public static final String COLLECTION_NAME = "person"; 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 ee215091a..0daee3679 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 @@ -1,4 +1,106 @@ package org.eclipse.jnosql.databases.neo4j.integration; + +import jakarta.inject.Inject; +import org.eclipse.jnosql.databases.neo4j.communication.DatabaseContainer; +import org.eclipse.jnosql.databases.neo4j.communication.Neo4JConfigurations; +import org.eclipse.jnosql.databases.neo4j.mapping.Neo4JTemplate; +import org.eclipse.jnosql.mapping.Database; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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 org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import java.util.Optional; + +import static java.util.UUID.randomUUID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; + +@EnableAutoWeld +@AddPackages(value = {Database.class, EntityConverter.class, Neo4JTemplate.class}) +@AddPackages(Magazine.class) +@AddPackages(Reflections.class) +@AddPackages(Converters.class) +@AddExtensions({EntityMetadataExtension.class}) +@EnabledIfSystemProperty(named = NAMED, matches = MATCHES) public class TemplateIntegrationTest { + + static { + DatabaseContainer.INSTANCE.host(); + System.setProperty(Neo4JConfigurations.URI.get(), DatabaseContainer.INSTANCE.host()); + System.setProperty(Neo4JConfigurations.DATABASE.get(), "neo4j"); + } + + @Inject + private Neo4JTemplate template; + + @Test + void shouldFindById() { + Magazine magazine = new Magazine(randomUUID().toString(), "Effective Java", 1); + assertThat(template.insert(magazine)) + .isNotNull() + .isEqualTo(magazine); + + assertThat(template.find(Magazine.class, magazine.id())) + .isNotNull().get().isEqualTo(magazine); + } + + @Test + void shouldInsert() { + Magazine magazine = new Magazine(randomUUID().toString(), "Effective Java", 1); + template.insert(magazine); + Optional optional = template.find(Magazine.class, magazine.id()); + assertThat(optional).isNotNull().isNotEmpty() + .get().isEqualTo(magazine); + } + + @Test + void shouldUpdate() { + Magazine magazine = new Magazine(randomUUID().toString(), "Effective Java", 1); + assertThat(template.insert(magazine)) + .isNotNull() + .isEqualTo(magazine); + + Magazine updated = new Magazine(magazine.id(), magazine.title() + " updated", 2); + + assertThat(template.update(updated)) + .isNotNull() + .isNotEqualTo(magazine); + + assertThat(template.find(Magazine.class, magazine.id())) + .isNotNull().get().isEqualTo(updated); + + } + + @Test + void shouldDeleteById() { + Magazine magazine = new Magazine(randomUUID().toString(), "Effective Java", 1); + assertThat(template.insert(magazine)) + .isNotNull() + .isEqualTo(magazine); + + template.delete(Magazine.class, magazine.id()); + assertThat(template.find(Magazine.class, magazine.id())) + .isNotNull().isEmpty(); + } + + @Test + void shouldDeleteAll(){ + for (int index = 0; index < 20; index++) { + Magazine magazine = new Magazine(randomUUID().toString(), "Effective Java", 1); + assertThat(template.insert(magazine)) + .isNotNull() + .isEqualTo(magazine); + } + + template.delete(Magazine.class).execute(); + assertThat(template.select(Magazine.class).result()).isEmpty(); + } } From acfff74b4f905ed7303ef741f602841ab23d95c9 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 18:55:23 +0000 Subject: [PATCH 121/148] feat: define configuration to neo4 Signed-off-by: Otavio Santana --- .../neo4j/mapping/GraphManagerSupplier.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/GraphManagerSupplier.java diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/GraphManagerSupplier.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/GraphManagerSupplier.java new file mode 100644 index 000000000..ea51a8230 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/GraphManagerSupplier.java @@ -0,0 +1,54 @@ +/* + * 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.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Disposes; +import jakarta.enterprise.inject.Produces; +import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.databases.neo4j.communication.Neo4JConfiguration; +import org.eclipse.jnosql.databases.neo4j.communication.Neo4JDatabaseManager; +import org.eclipse.jnosql.databases.neo4j.communication.Neo4JDatabaseManagerFactory; +import org.eclipse.jnosql.mapping.core.config.MicroProfileSettings; + +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; + +@ApplicationScoped +class GraphManagerSupplier implements Supplier { + + private static final String DATABASE_DEFAULT = "neo4j"; + + private static final Logger LOGGER = Logger.getLogger(GraphManagerSupplier.class.getName()); + + @Override + @Produces + @ApplicationScoped + public Neo4JDatabaseManager get() { + LOGGER.fine(() -> "Creating a Neo4JDatabaseManager bean"); + Settings settings = MicroProfileSettings.INSTANCE; + var configuration = new Neo4JConfiguration(); + Neo4JDatabaseManagerFactory managerFactory = configuration.apply(settings); + var database = settings.getOrDefault("database", DATABASE_DEFAULT); + LOGGER.fine(() -> "Creating a Neo4JDatabaseManager bean with database: " + database); + return managerFactory.apply(database); + } + + public void close(@Disposes Neo4JDatabaseManager manager) { + LOGGER.log(Level.FINEST, "Closing Neo4JDatabaseManager resource, database name: " + manager.name()); + manager.close(); + } +} From be2eecf7ce4de6530a50ab93cf3e2e966981b0f6 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 19:07:40 +0000 Subject: [PATCH 122/148] test: enahnce integration test Signed-off-by: Otavio Santana --- .../integration/TemplateIntegrationTest.java | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) 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 0daee3679..3afe8473a 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 @@ -13,6 +13,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; @@ -41,12 +42,14 @@ public class TemplateIntegrationTest { @Inject private Neo4JTemplate template; + @BeforeEach + void setUp() { + template.delete(Magazine.class).execute(); + } + @Test void shouldFindById() { - Magazine magazine = new Magazine(randomUUID().toString(), "Effective Java", 1); - assertThat(template.insert(magazine)) - .isNotNull() - .isEqualTo(magazine); + Magazine magazine = template.insert(new Magazine(null, "Effective Java", 1)); assertThat(template.find(Magazine.class, magazine.id())) .isNotNull().get().isEqualTo(magazine); @@ -54,8 +57,8 @@ void shouldFindById() { @Test void shouldInsert() { - Magazine magazine = new Magazine(randomUUID().toString(), "Effective Java", 1); - template.insert(magazine); + Magazine magazine = template.insert(new Magazine(null, "Effective Java", 1)); + Optional optional = template.find(Magazine.class, magazine.id()); assertThat(optional).isNotNull().isNotEmpty() .get().isEqualTo(magazine); @@ -63,10 +66,7 @@ void shouldInsert() { @Test void shouldUpdate() { - Magazine magazine = new Magazine(randomUUID().toString(), "Effective Java", 1); - assertThat(template.insert(magazine)) - .isNotNull() - .isEqualTo(magazine); + Magazine magazine = template.insert(new Magazine(null, "Effective Java", 1)); Magazine updated = new Magazine(magazine.id(), magazine.title() + " updated", 2); @@ -81,10 +81,7 @@ void shouldUpdate() { @Test void shouldDeleteById() { - Magazine magazine = new Magazine(randomUUID().toString(), "Effective Java", 1); - assertThat(template.insert(magazine)) - .isNotNull() - .isEqualTo(magazine); + Magazine magazine = template.insert(new Magazine(null, "Effective Java", 1)); template.delete(Magazine.class, magazine.id()); assertThat(template.find(Magazine.class, magazine.id())) @@ -94,10 +91,8 @@ void shouldDeleteById() { @Test void shouldDeleteAll(){ for (int index = 0; index < 20; index++) { - Magazine magazine = new Magazine(randomUUID().toString(), "Effective Java", 1); - assertThat(template.insert(magazine)) - .isNotNull() - .isEqualTo(magazine); + Magazine magazine = template.insert(new Magazine(null, "Effective Java", 1)); + assertThat(magazine).isNotNull(); } template.delete(Magazine.class).execute(); From ab394c57e67393eb7ff4615bd0f82907a5a7c64c Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 19:07:51 +0000 Subject: [PATCH 123/148] test: fix integratin test Signed-off-by: Otavio Santana --- .../databases/neo4j/integration/TemplateIntegrationTest.java | 1 - 1 file changed, 1 deletion(-) 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 3afe8473a..812d493bc 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 @@ -19,7 +19,6 @@ import java.util.Optional; -import static java.util.UUID.randomUUID; import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; From 327c3556b2b36047322d190c710b21fa8bbd4f05 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 19:10:39 +0000 Subject: [PATCH 124/148] feat: update Cypher neoej4 Signed-off-by: Otavio Santana --- .../databases/neo4j/mapping/Cypher.java | 29 +++++++++++++++++++ .../neo4j/mapping/Neo4JRepository.java | 28 ++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepository.java diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java new file mode 100644 index 000000000..6b0ea97bd --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java @@ -0,0 +1,29 @@ +/* + * 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.neo4j.mapping; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Cypher { + + + String value(); +} \ No newline at end of file diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepository.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepository.java new file mode 100644 index 000000000..428f3a417 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepository.java @@ -0,0 +1,28 @@ +/* + * 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.neo4j.mapping; + + +import org.eclipse.jnosql.mapping.NoSQLRepository; + +/** + * The Cassandra {@link NoSQLRepository} + * + * @param the entity type + * @param the id entity type + */ +public interface Neo4JRepository extends NoSQLRepository { + +} From 015a3c424c8a35e8d33fc50ea59fb83f71564729 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 19:11:01 +0000 Subject: [PATCH 125/148] feat: update header Signed-off-by: Otavio Santana --- .../java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java | 2 +- .../eclipse/jnosql/databases/neo4j/mapping/Neo4JRepository.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java index 6b0ea97bd..e4eb4262e 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Contributors to the Eclipse Foundation + * 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. diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepository.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepository.java index 428f3a417..201d92d71 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepository.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepository.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Contributors to the Eclipse Foundation + * 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. From 56ecd350777f1dde8cc25e252201e166d0442570 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 19:11:55 +0000 Subject: [PATCH 126/148] docs: generat documentatino to cypher annotation Signed-off-by: Otavio Santana --- .../databases/neo4j/mapping/Cypher.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java index e4eb4262e..ec502b8e8 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java @@ -20,10 +20,40 @@ import java.lang.annotation.Target; +/** + * Annotation for defining Cypher queries in Neo4J repositories. + *

    + * This annotation allows users to specify a Cypher query directly on repository methods, + * enabling custom query execution within {@code Neo4JRepository}. + *

    + * + *

    + * Example usage: + *

    + * {@code
    + * @Cypher("MATCH (n:Person) WHERE n.name = $name RETURN n")
    + * List findByName(@Param("name") String name);
    + * }
    + * 
    + *

    + * + *

    + * The {@code value} attribute should contain a valid Cypher query. Query parameters + * can be defined using the {@code $parameterName} syntax, which will be replaced by + * method parameters annotated with {@code @Param}. + *

    + * + * @see Neo4JRepository + */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Cypher { + /** + * The Cypher query to be executed. + * + * @return The Cypher query string. + */ String value(); } \ No newline at end of file From 92b45cda6d7a5a10cc6312edf64a17b85e387fed Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 19:16:54 +0000 Subject: [PATCH 127/148] docs: enhance neo4j repository documentation Signed-off-by: Otavio Santana --- .../neo4j/mapping/Neo4JRepository.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepository.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepository.java index 201d92d71..1ab4244d4 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepository.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepository.java @@ -18,10 +18,30 @@ import org.eclipse.jnosql.mapping.NoSQLRepository; /** - * The Cassandra {@link NoSQLRepository} + * A repository interface for interacting with Neo4J databases using the Jakarta Data API. + *

    + * This interface extends {@link NoSQLRepository}, providing + * generic CRUD operations for entities stored in a Neo4J database. + * It also allows defining custom Cypher queries using the {@link Cypher} annotation. + *

    * - * @param the entity type - * @param the id entity type + *

    Example usage:

    + *
    + * {@code
    + * @Repository
    + * public interface PersonRepository extends Neo4JRepository {
    + *
    + *     @Cypher("MATCH (p:Person) WHERE p.name = $name RETURN p")
    + *     List findByName(@Param("name") String name);
    + * }
    + * }
    + * 
    + * + * @param the entity type representing nodes in the Neo4J database. + * @param the entity ID type, typically a {@link String} corresponding to the element ID. + * + * @see NoSQLRepository + * @see Cypher */ public interface Neo4JRepository extends NoSQLRepository { From 01dce5a8efae720eabfda6950ac0c4f9f5e9f352 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 19:19:14 +0000 Subject: [PATCH 128/148] feat: implement neo4j proxy repository Signed-off-by: Otavio Santana --- .../neo4j/mapping/Neo4JRepositoryProxy.java | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepositoryProxy.java 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 new file mode 100644 index 000000000..c501fbccd --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepositoryProxy.java @@ -0,0 +1,110 @@ +/* + * 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 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.Map; +import java.util.Objects; +import java.util.stream.Stream; + +import static org.eclipse.jnosql.mapping.core.repository.DynamicReturn.toSingleResult; + +class Neo4JRepositoryProxy extends AbstractSemiStructuredRepositoryProxy { + + private final Class typeClass; + + private final Neo4JTemplate template; + + private final AbstractRepository repository; + + private final Converters converters; + + private final EntityMetadata entityMetadata; + + private final Class repositoryType; + + Neo4JRepositoryProxy(Neo4JTemplate 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 Neo4JTemplate template() { + return template; + } + + @Override + public Object invoke(Object instance, Method method, Object[] args) throws Throwable { + + Cypher cql = method.getAnnotation(Cypher.class); + if (Objects.nonNull(cql)) { + + Stream result; + Map values = CQLObjectUtil.getValues(args, method); + if (!values.isEmpty()) { + result = template.cql(cql.value(), values); + } else if (args == null || args.length == 0) { + result = template.cql(cql.value()); + } else { + result = template.cql(cql.value(), args); + } + return DynamicReturn.builder() + .classSource(typeClass) + .methodSource(method) + .result(() -> result) + .singleResult(toSingleResult(method).apply(() -> result)) + .build().execute(); + } + + return super.invoke(instance, method, args); + } +} From e4ecb9a3c2d75da21586c3d9c33aa7b72f81fd6d Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 20:17:21 +0000 Subject: [PATCH 129/148] feat: create new resource to neoj4 Signed-off-by: Otavio Santana --- .../neo4j/mapping/DefaultNeo4JTemplate.java | 2 +- .../neo4j/mapping/Neo4JRepositoryProxy.java | 10 ++-- .../neo4j/mapping/Neo4JTemplate.java | 2 +- .../neo4j/mapping/ParamConverterUtils.java | 50 +++++++++++++++++++ .../mapping/DefaultNeo4JTemplateTest.java | 8 +-- 5 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/ParamConverterUtils.java 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 adb03e33d..9106d4182 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,7 @@ public class DefaultNeo4JTemplate extends AbstractSemiStructuredTemplate impleme @SuppressWarnings("unchecked") @Override - public Stream executeQuery(String cypher, Map parameters) { + 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) 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 c501fbccd..c6dc39218 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 @@ -24,6 +24,7 @@ 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; @@ -89,14 +90,13 @@ public Object invoke(Object instance, Method method, Object[] args) throws Throw if (Objects.nonNull(cql)) { Stream result; - Map values = CQLObjectUtil.getValues(args, method); + Map values = ParamConverterUtils.getValues(args, method); if (!values.isEmpty()) { - result = template.cql(cql.value(), values); - } else if (args == null || args.length == 0) { - result = template.cql(cql.value()); + result = template.cypher(cql.value(), values); } else { - result = template.cql(cql.value(), args); + result = template.cypher(cql.value(), Collections.emptyMap()); } + return DynamicReturn.builder() .classSource(typeClass) .methodSource(method) 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 80bdc8497..084ca0355 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 @@ -39,7 +39,7 @@ public interface Neo4JTemplate extends SemiStructuredTemplate { * @return A stream of entities 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); /** * Traverses relationships from a given start node up to a specified depth. diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/ParamConverterUtils.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/ParamConverterUtils.java new file mode 100644 index 000000000..846f04f36 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/ParamConverterUtils.java @@ -0,0 +1,50 @@ +/* + * 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.neo4j.mapping; + +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-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 fdd8ad6a5..023a6aa41 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 @@ -83,22 +83,22 @@ void setUp() { } @Test - void shouldExecuteQuery() { + void shouldCypher() { String cypher = "MATCH (n:Person) RETURN n"; Map parameters = Collections.emptyMap(); CommunicationEntity entity = CommunicationEntity.of("Person"); entity.add(Element.of("name", "Ada")); when(manager.executeQuery(cypher, parameters)).thenReturn(Stream.of(entity)); - Stream result = template.executeQuery(cypher, parameters); + Stream result = template.cypher(cypher, parameters); assertNotNull(result); assertTrue(result.findFirst().isPresent()); } @Test void shouldThrowExceptionWhenQueryIsNull() { - assertThrows(NullPointerException.class, () -> template.executeQuery(null, Collections.emptyMap())); - assertThrows(NullPointerException.class, () -> template.executeQuery("MATCH (n) RETURN n", null)); + assertThrows(NullPointerException.class, () -> template.cypher(null, Collections.emptyMap())); + assertThrows(NullPointerException.class, () -> template.cypher("MATCH (n) RETURN n", null)); } @Test From 65d848a568e86af1c1bc890054c575a9d57fc2d5 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 20:19:12 +0000 Subject: [PATCH 130/148] feat: neo4j repository Signed-off-by: Otavio Santana --- .../neo4j/mapping/Neo4JRepositoryBean.java | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepositoryBean.java diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepositoryBean.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepositoryBean.java new file mode 100644 index 000000000..7957cc277 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JRepositoryBean.java @@ -0,0 +1,79 @@ +/* + * 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.neo4j.mapping; + +import jakarta.enterprise.context.spi.CreationalContext; +import jakarta.enterprise.inject.Default; +import jakarta.enterprise.util.AnnotationLiteral; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.AbstractBean; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.Set; + + +class Neo4JRepositoryBean extends AbstractBean> { + + private final Class type; + + private final Set types; + + private final Set qualifiers = Collections.singleton(new AnnotationLiteral() { + }); + + Neo4JRepositoryBean(Class type) { + this.type = type; + this.types = Collections.singleton(type); + } + + @Override + public Class getBeanClass() { + return type; + } + + @SuppressWarnings("unchecked") + @Override + public Neo4JRepository create(CreationalContext> creationalContext) { + Neo4JTemplate template = getInstance(Neo4JTemplate.class); + Converters converters = getInstance(Converters.class); + EntitiesMetadata entitiesMetadata = getInstance(EntitiesMetadata.class); + Neo4JRepositoryProxy handler = new Neo4JRepositoryProxy<>(template, type, + converters, entitiesMetadata); + return (Neo4JRepository) 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() + "@neo4j"; + } + +} \ No newline at end of file From 0422ce1b605f302a08676a5c3d34a33d6a1c1be8 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 20:21:23 +0000 Subject: [PATCH 131/148] feat: create neo4j extesions Signed-off-by: Otavio Santana --- .../neo4j/mapping/Neo4JExtension.java | 48 +++++++++++++++++++ .../jakarta.enterprise.inject.spi.Extension | 1 + 2 files changed, 49 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JExtension.java create mode 100644 jnosql-neo4j/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JExtension.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JExtension.java new file mode 100644 index 000000000..48f2a6052 --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JExtension.java @@ -0,0 +1,48 @@ +/* + * 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.neo4j.mapping; + + +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.spi.AfterBeanDiscovery; +import jakarta.enterprise.inject.spi.Extension; +import org.eclipse.jnosql.mapping.metadata.ClassScanner; + +import java.util.Set; +import java.util.logging.Logger; + +/** + * CDI extension for Cassandra integration. + */ +public class Neo4JExtension implements Extension { + + private static final Logger LOGGER = Logger.getLogger(Neo4JExtension.class.getName()); + + /** + * Observes the AfterBeanDiscovery event to add Cassandra repository beans. + * + * @param afterBeanDiscovery the AfterBeanDiscovery event + */ + void onAfterBeanDiscovery(@Observes final AfterBeanDiscovery afterBeanDiscovery) { + ClassScanner scanner = ClassScanner.load(); + Set> crudTypes = scanner.repositories(Neo4JRepository.class); + + LOGGER.info("Starting the onAfterBeanDiscovery with elements number: " + crudTypes.size()); + + crudTypes.forEach(type -> afterBeanDiscovery.addBean(new Neo4JRepositoryBean<>(type))); + + LOGGER.info("Finished the onAfterBeanDiscovery"); + } +} \ No newline at end of file diff --git a/jnosql-neo4j/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension b/jnosql-neo4j/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension new file mode 100644 index 000000000..02761ccdc --- /dev/null +++ b/jnosql-neo4j/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension @@ -0,0 +1 @@ +org.eclipse.jnosql.databases.neo4j.mapping.Neo4JExtension \ No newline at end of file From 3a9244c02a5a42aa91386019e79b3466bce12012 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 20:22:33 +0000 Subject: [PATCH 132/148] test: define music repository Signed-off-by: Otavio Santana --- .../neo4j/mapping/MusicRepository.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/MusicRepository.java diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/MusicRepository.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/MusicRepository.java new file mode 100644 index 000000000..dc7884430 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/MusicRepository.java @@ -0,0 +1,22 @@ +/* + * 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.Repository; + +@Repository +public interface MusicRepository extends Neo4JRepository { +} From b1b4b1c4d9dd3333bec16ba92eb5a468dda2e260 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 20:24:06 +0000 Subject: [PATCH 133/148] test: neo4j extesnsion Signed-off-by: Otavio Santana --- .../neo4j/mapping/CassandraExtensionTest.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/CassandraExtensionTest.java diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/CassandraExtensionTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/CassandraExtensionTest.java new file mode 100644 index 000000000..6355a7769 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/CassandraExtensionTest.java @@ -0,0 +1,42 @@ +/* + * 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.inject.Inject; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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.Assertions; +import org.junit.jupiter.api.Test; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, Neo4JRepository.class, EntityConverter.class}) +@AddExtensions({EntityMetadataExtension.class, Neo4JExtension.class}) +@AddPackages(Reflections.class) +public class CassandraExtensionTest { + + + @Inject + private MusicRepository repository; + + @Test + public void shouldCreteNeo4j() { + Assertions.assertNotNull(repository); + } +} \ No newline at end of file From 8ee38da32a720ad6a4145e0d9598bbfcfce9b061 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 20:24:30 +0000 Subject: [PATCH 134/148] test: rename neoj4 extension Signed-off-by: Otavio Santana --- .../{CassandraExtensionTest.java => Neo4jExtensionTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/{CassandraExtensionTest.java => Neo4jExtensionTest.java} (97%) diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/CassandraExtensionTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4jExtensionTest.java similarity index 97% rename from jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/CassandraExtensionTest.java rename to jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4jExtensionTest.java index 6355a7769..b41249aaa 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/CassandraExtensionTest.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4jExtensionTest.java @@ -29,7 +29,7 @@ @AddPackages(value = {Converters.class, Neo4JRepository.class, EntityConverter.class}) @AddExtensions({EntityMetadataExtension.class, Neo4JExtension.class}) @AddPackages(Reflections.class) -public class CassandraExtensionTest { +public class Neo4jExtensionTest { @Inject From 66cb13577fa61b4052d1e2d1b9034aacb77789c3 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 20:35:16 +0000 Subject: [PATCH 135/148] test: generate new test on neo4j Signed-off-by: Otavio Santana --- .../databases/neo4j/mapping/Contact.java | 79 +++++++++ .../mapping/Neo4jRepositoryProxyTest.java | 156 ++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Contact.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4jRepositoryProxyTest.java diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Contact.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Contact.java new file mode 100644 index 000000000..13ce3d27c --- /dev/null +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Contact.java @@ -0,0 +1,79 @@ +/* + * 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.neo4j.mapping; + + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.Objects; + +@Entity +public class Contact { + + @Id("name") + private String name; + + @Column + private Integer age; + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public Contact(String name, Integer age) { + this.name = name; + this.age = age; + } + + public Contact() { + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Contact contact = (Contact) o; + return Objects.equals(name, contact.name) && + Objects.equals(age, contact.age); + } + + @Override + public int hashCode() { + return Objects.hash(name, age); + } + + @Override + public String toString() { + return "Person{" + + "name='" + name + '\'' + + ", age=" + age + + '}'; + } +} diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4jRepositoryProxyTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4jRepositoryProxyTest.java new file mode 100644 index 000000000..3a425b4f4 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4jRepositoryProxyTest.java @@ -0,0 +1,156 @@ +/* + * 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.neo4j.mapping; + +import jakarta.data.repository.Param; +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.semistructured.DeleteQuery; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +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.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.lang.reflect.Proxy; +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, Neo4JRepository.class, EntityConverter.class}) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, Neo4JExtension.class}) +public class Neo4jRepositoryProxyTest { + + private Neo4JTemplate template; + + @Inject + private Converters converters; + + @Inject + private EntitiesMetadata entitiesMetadata; + + private HumanRepository personRepository; + + @BeforeEach + public void setUp() { + this.template = Mockito.mock(Neo4JTemplate.class); + Neo4JRepositoryProxy handler = new Neo4JRepositoryProxy(template, + HumanRepository.class, converters, entitiesMetadata); + + when(template.insert(any(Contact.class))).thenReturn(new Contact()); + when(template.insert(any(Contact.class), any(Duration.class))).thenReturn(new Contact()); + when(template.update(any(Contact.class))).thenReturn(new Contact()); + this.personRepository = (HumanRepository) Proxy.newProxyInstance(HumanRepository.class.getClassLoader(), + new Class[]{HumanRepository.class}, + handler); + } + + + @Test + public void shouldFindByName() { + personRepository.findByName("Ada"); + verify(template).cypher("MATCH (p:Person) WHERE p.name = $1 RETURN p", Map.of("name", "Ada")); + } + + @Test + public void shouldDeleteByName() { + personRepository.deleteByName("Ada"); + verify(template).cypher("MATCH (p:Person {name: $name}) DELETE p", Collections.singletonMap("name", "Ada")); + } + + @Test + public void shouldFindAll() { + personRepository.findAllQuery(); + verify(template).cypher("MATCH (p:Person) RETURN p", Collections.emptyMap()); + } + + @Test + public void shouldFindByNameCQL() { + personRepository.findByName("Ada"); + verify(template).cypher("MATCH (p:Person) WHERE p.name = $1 RETURN p", Collections.singletonMap("name", "Ada")); + } + + @Test + public void shouldFindByName2CQL() { + ArgumentCaptor captor = ArgumentCaptor.forClass(Map.class); + + personRepository.findByName2("Ada"); + verify(template).cypher(Mockito.eq("MATCH (p:Person) WHERE p.name = $name RETURN p"), captor.capture()); + Map map = captor.getValue(); + assertEquals("Ada", map.get("name")); + } + + @Test + public void shouldSaveUsingInsert() { + Contact contact = new Contact("Ada", 10); + personRepository.save(contact); + verify(template).insert(eq(contact)); + } + + @Test + public void shouldSaveUsingUpdate() { + Contact contact = new Contact("Ada-2", 10); + when(template.find(Contact.class, "Ada-2")).thenReturn(Optional.of(contact)); + personRepository.save(contact); + verify(template).update(eq(contact)); + } + + @Test + public void shouldDelete(){ + personRepository.deleteById("id"); + verify(template).delete(Contact.class, "id"); + } + + + @Test + public void shouldDeleteEntity(){ + Contact contact = new Contact("Ada", 10); + personRepository.delete(contact); + verify(template).delete(Contact.class, contact.getName()); + } + + interface HumanRepository extends Neo4JRepository { + + @Cypher("MATCH (p:Person {name: $name}) DELETE p") + void deleteByName(@Param("name") String name); + + @Cypher("MATCH (p:Person) RETURN p") + List findAllQuery(); + + @Cypher("MATCH (p:Person) WHERE p.name = $1 RETURN p") + List findByName(@Param("name") String name); + + @Cypher("MATCH (p:Person) WHERE p.name = $name RETURN p") + List findByName2(@Param("name") String name); + } + + +} \ No newline at end of file From 3c04ad5ac4369d47b12b9217bc64d40645835fb2 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 20:42:21 +0000 Subject: [PATCH 136/148] feat: define integration test Signed-off-by: Otavio Santana --- .../neo4j/integration/MagazineRepository.java | 24 +++++++ .../RepositoryIntegrationTest.java | 66 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/MagazineRepository.java create mode 100644 jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/RepositoryIntegrationTest.java 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 new file mode 100644 index 000000000..1a87a4d71 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/MagazineRepository.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 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: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.neo4j.integration; + +import jakarta.data.repository.Repository; +import org.eclipse.jnosql.databases.neo4j.mapping.Neo4JRepository; +import org.eclipse.jnosql.mapping.NoSQLRepository; + +@Repository +public interface MagazineRepository extends Neo4JRepository { +} 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 new file mode 100644 index 000000000..ee941ed54 --- /dev/null +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/integration/RepositoryIntegrationTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023 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.integration; + + +import jakarta.inject.Inject; +import org.eclipse.jnosql.databases.neo4j.communication.DatabaseContainer; +import org.eclipse.jnosql.databases.neo4j.communication.Neo4JConfigurations; +import org.eclipse.jnosql.databases.neo4j.mapping.Neo4JExtension; +import org.eclipse.jnosql.databases.neo4j.mapping.Neo4JTemplate; +import org.eclipse.jnosql.mapping.Database; +import org.eclipse.jnosql.mapping.DatabaseType; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +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 org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import static java.util.UUID.randomUUID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; + +@EnableAutoWeld +@AddPackages(value = {Database.class, EntityConverter.class, Neo4JTemplate.class}) +@AddPackages(Magazine.class) +@AddPackages(Reflections.class) +@AddPackages(Converters.class) +@AddExtensions({EntityMetadataExtension.class, Neo4JExtension.class}) +@EnabledIfSystemProperty(named = NAMED, matches = MATCHES) +public class RepositoryIntegrationTest { + + static { + DatabaseContainer.INSTANCE.host(); + System.setProperty(Neo4JConfigurations.URI.get(), DatabaseContainer.INSTANCE.host()); + System.setProperty(Neo4JConfigurations.DATABASE.get(), "neo4j"); + } + + @Inject + private MagazineRepository repository; + + @Test + void shouldSave() { + Magazine magazine = new Magazine(null, "Effective Java", 1); + assertThat(repository.save(magazine)) + .isNotNull(); + + } + +} From 3035d45bdbf5943b2846f17fec4cf2d0aaed1827 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 20:43:44 +0000 Subject: [PATCH 137/148] feat: include header Signed-off-by: Otavio Santana --- .../integration/RepositoryIntegrationTest.java | 4 +--- .../integration/TemplateIntegrationTest.java | 15 ++++++++++++++- .../neo4j/mapping/Neo4JTemplateTest.java | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) 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 ee941ed54..51f0201c1 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Contributors to the Eclipse Foundation + * 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. @@ -21,7 +21,6 @@ import org.eclipse.jnosql.databases.neo4j.mapping.Neo4JExtension; import org.eclipse.jnosql.databases.neo4j.mapping.Neo4JTemplate; import org.eclipse.jnosql.mapping.Database; -import org.eclipse.jnosql.mapping.DatabaseType; import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; import org.eclipse.jnosql.mapping.reflection.Reflections; @@ -32,7 +31,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; -import static java.util.UUID.randomUUID; import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; 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 812d493bc..85cfe17af 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 @@ -1,6 +1,19 @@ +/* + * 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.integration; - import jakarta.inject.Inject; import org.eclipse.jnosql.databases.neo4j.communication.DatabaseContainer; import org.eclipse.jnosql.databases.neo4j.communication.Neo4JConfigurations; diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplateTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplateTest.java index 711423c5d..fff5f1b10 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplateTest.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Neo4JTemplateTest.java @@ -1,3 +1,17 @@ +/* + * 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.inject.Inject; From 814e0e1cd9080a5c58e26d722dfbcddcb17f8388 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 20:44:12 +0000 Subject: [PATCH 138/148] feat: enhance imports Signed-off-by: Otavio Santana --- .../neo4j/communication/DefaultNeo4JDatabaseManager.java | 1 - .../jnosql/databases/neo4j/communication/Neo4JConfiguration.java | 1 + .../neo4j/communication/Neo4JDatabaseManagerFactory.java | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) 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 c90fb8cc2..6f8eaaf04 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 @@ -32,7 +32,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.UUID; import java.util.logging.Logger; import java.util.stream.Stream; diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java index 6c5851277..ba66f4900 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java @@ -18,6 +18,7 @@ import org.eclipse.jnosql.communication.Settings; import org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration; + import java.util.logging.Logger; /** diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java index 6b25e25ac..87b3a94dd 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JDatabaseManagerFactory.java @@ -18,7 +18,6 @@ import org.eclipse.jnosql.communication.CommunicationException; import org.eclipse.jnosql.communication.semistructured.DatabaseManagerFactory; -import org.neo4j.driver.AuthToken; import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Driver; import org.neo4j.driver.GraphDatabase; From 205e27be050925842b646599d11303469b39c497 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 20:46:41 +0000 Subject: [PATCH 139/148] test: update builder neo4j Signed-off-by: Otavio Santana --- .../communication/Neo4JQueryBuilderTest.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilderTest.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilderTest.java index 12eb3bf14..cb9cf9ac1 100644 --- a/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilderTest.java +++ b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilderTest.java @@ -32,7 +32,6 @@ class Neo4JQueryBuilderTest { - @Test void shouldBuildDeleteQueryForNode() { DeleteQuery query = mock(DeleteQuery.class); @@ -63,6 +62,7 @@ void shouldBuildDeleteQueryForSpecificColumns() { void shouldBuildSelectQueryWithCondition() { SelectQuery query = mock(SelectQuery.class); when(query.name()).thenReturn("Person"); + CriteriaCondition condition = mock(CriteriaCondition.class); Element element = mock(Element.class); when(condition.element()).thenReturn(element); @@ -91,4 +91,26 @@ void shouldBuildSelectQueryWithoutCondition() { assertThat(cypher).isEqualTo("MATCH (e:Person) RETURN e.name, e.age"); } + + @Test + void shouldTranslateIdToElementId() { + SelectQuery query = mock(SelectQuery.class); + when(query.name()).thenReturn("Person"); + + CriteriaCondition condition = mock(CriteriaCondition.class); + Element element = mock(Element.class); + when(condition.element()).thenReturn(element); + when(element.name()).thenReturn("_id"); + when(element.get()).thenReturn("12345"); + when(condition.condition()).thenReturn(org.eclipse.jnosql.communication.Condition.EQUALS); + when(query.condition()).thenReturn(java.util.Optional.of(condition)); + when(query.columns()).thenReturn(List.of("name", "_id")); + + Map parameters = new HashMap<>(); + String cypher = Neo4JQueryBuilder.INSTANCE.buildQuery(query, parameters); + + assertThat(cypher).isEqualTo("MATCH (e:Person) WHERE elementId(e) = $id RETURN e.name, elementId(e)"); + assertThat(parameters).containsEntry("id", "12345"); + } + } \ No newline at end of file From e5d8f1d256bb1f9632bd0f494210ced6eccfd8b8 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 20:52:05 +0000 Subject: [PATCH 140/148] feat: fix neo4jquery builder Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JQueryBuilder.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java index 49ec8b5ea..81bca8b37 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java @@ -47,7 +47,8 @@ public String buildQuery(DeleteQuery query, Map parameters) { if (!columns.isEmpty()) { cypher.append(" SET "); cypher.append(columns.stream() - .map(col -> "e." + translateField(col) + " = NULL") + .map(this::translateField) + .map(col -> col + " = NULL") .collect(Collectors.joining(", "))); } else { cypher.append(" DELETE e"); @@ -95,7 +96,7 @@ public String buildQuery(SelectQuery query, Map parameters) { private void createWhereClause(StringBuilder cypher, CriteriaCondition condition, Map parameters) { Element element = condition.element(); String fieldName = element.name(); - String queryField = translateField(fieldName); // Correct field translation + String queryField = translateField(fieldName); switch (condition.condition()) { case EQUALS: @@ -134,9 +135,16 @@ private void createWhereClause(StringBuilder cypher, CriteriaCondition condition } private String translateField(String field) { - return INTERNAL_ID.equals(field) ? "elementId(e)" : "e." + field; + if (INTERNAL_ID.equals(field)) { + return "elementId(e)"; + } + if (field.startsWith("e.")) { + return field; + } + return "e." + field; } + private String getConditionOperator(Condition condition) { return switch (condition) { case EQUALS -> "="; From 7ae5386fc171c43d8a9557afb7aded904af973a2 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 20:57:19 +0000 Subject: [PATCH 141/148] docs: update documentation at javadoc Signed-off-by: Otavio Santana --- .../EdgeCommunicationException.java | 3 -- .../Neo4JCommunicationException.java | 3 -- .../communication/Neo4JDatabaseManager.java | 30 +++++++++---------- .../communication/Neo4JQueryBuilder.java | 2 +- .../databases/neo4j/mapping/Cypher.java | 6 ---- 5 files changed, 16 insertions(+), 28 deletions(-) diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java index 917408ea8..4f2297048 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java @@ -18,12 +18,9 @@ /** * Exception thrown when an issue occurs with edge (relationship) operations in Neo4j. - *

    * This exception is raised in cases where an edge cannot be created or removed * due to missing nodes, constraint violations, or other graph-related inconsistencies. - *

    * - *

    Common Scenarios:

    *
      *
    • Attempting to create an edge where either the source or target entity does not exist.
    • *
    • Removing an edge that is not found in the graph.
    • diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java index 527f3b500..a8b922d4a 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java @@ -20,12 +20,9 @@ /** * Exception representing general communication errors when interacting with a Neo4j database. - *

      * This exception is used as a base for more specific exceptions related to Neo4j operations, * such as Cypher syntax errors, transaction failures, or connectivity issues. - *

      * - *

      Common Causes:

      *
        *
      • Invalid Cypher query syntax.
      • *
      • Transaction failures during read or write operations.
      • 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 960b615d4..11131a292 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 @@ -29,12 +29,11 @@ * using the APOC library. Implementations of this interface should handle TTL-related methods * accordingly—either by integrating APOC's TTL features or throwing {@link UnsupportedOperationException} * if TTL is not supported.

        - * - * @apiNote All write operations, including {@code insert} and {@code update}, will be executed within a transaction. + *

        + * All write operations, including {@code insert} and {@code update}, will be executed within a transaction. * When performing batch inserts using an iterable, the entire operation will be executed as a single transaction * to ensure consistency. * - *

        Usage Example:

        *
          * Neo4JDatabaseManager manager = ...; // Obtain an instance
          * CommunicationEntity entity = CommunicationEntity.of("User");
        @@ -43,8 +42,8 @@
          *
          * manager.insert(entity); // Insert into Neo4j
          * 
        - * - * @apiNote Ensure proper transaction and session management when implementing this interface. + *

        + * Ensure proper transaction and session management when implementing this interface. * Unsupported TTL operations should result in an {@link UnsupportedOperationException}. * * @see DatabaseManager @@ -53,25 +52,26 @@ public interface Neo4JDatabaseManager extends DatabaseManager { /** * A specialized {@link DatabaseManager - - /** + *

        + * /** * Executes a custom Cypher query with parameters and returns a stream of {@link CommunicationEntity}. + * * @param cypher the Cypher query to execute. * @param parameters the parameters to bind to the query. * @return a stream of {@link CommunicationEntity} matching the query result. - * @throws NullPointerException if any of the arguments are {@code null}. - * @throws CypherException if there is an issue with the Cypher syntax. + * @throws NullPointerException if any of the arguments are {@code null}. + * @throws CypherException if there is an issue with the Cypher syntax. */ Stream executeQuery(String cypher, Map parameters); /** * Traverses the graph starting from a node and follows the specified relationship type up to a given depth. * - * @param startNodeId the ID of the starting node. - * @param relationship the type of relationship to traverse. - * @param depth the traversal depth limit. + * @param startNodeId the ID of the starting node. + * @param relationship the type of relationship to traverse. + * @param depth the traversal depth limit. * @return a stream of {@link CommunicationEntity} representing related nodes. - * @throws NullPointerException if any of the arguments are {@code null}. + * @throws NullPointerException if any of the arguments are {@code null}. */ Stream traverse(String startNodeId, String relationship, int depth); @@ -82,7 +82,7 @@ public interface Neo4JDatabaseManager extends DatabaseManager { * @param target the target entity. * @param relationshipType the type of relationship to create. * @throws EdgeCommunicationException if either the source or target entity does not exist in the database. - * @throws NullPointerException if any of the arguments are {@code null}. + * @throws NullPointerException if any of the arguments are {@code null}. */ void edge(CommunicationEntity source, String relationshipType, CommunicationEntity target); @@ -93,7 +93,7 @@ public interface Neo4JDatabaseManager extends DatabaseManager { * @param target the target entity, which must already exist in the database. * @param relationshipType the type of relationship to remove. * @throws EdgeCommunicationException if either the source or target entity does not exist in the database. - * @throws NullPointerException if any of the arguments are {@code null}. + * @throws NullPointerException if any of the arguments are {@code null}. */ void remove(CommunicationEntity source, String relationshipType, CommunicationEntity target); } diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java index 81bca8b37..ed8da1031 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java @@ -29,10 +29,10 @@ import java.util.Map; import java.util.stream.Collectors; public enum Neo4JQueryBuilder { + INSTANCE; private static final String INTERNAL_ID = "_id"; - private static final String NEO4J_ID = "elementId(e)"; public String buildQuery(DeleteQuery query, Map parameters) { StringBuilder cypher = new StringBuilder("MATCH (e:"); diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java index ec502b8e8..80f26c066 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Cypher.java @@ -22,12 +22,9 @@ /** * Annotation for defining Cypher queries in Neo4J repositories. - *

        * This annotation allows users to specify a Cypher query directly on repository methods, * enabling custom query execution within {@code Neo4JRepository}. - *

        * - *

        * Example usage: *

          * {@code
        @@ -35,13 +32,10 @@
          * List findByName(@Param("name") String name);
          * }
          * 
        - *

        * - *

        * The {@code value} attribute should contain a valid Cypher query. Query parameters * can be defined using the {@code $parameterName} syntax, which will be replaced by * method parameters annotated with {@code @Param}. - *

        * * @see Neo4JRepository */ From 1ba43a0418d767e27d956df9ed5e9206a4ab3fa0 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 21:03:19 +0000 Subject: [PATCH 142/148] docs: update javdaoc Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JDatabaseManager.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) 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 11131a292..31e0981a1 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 @@ -52,15 +52,11 @@ public interface Neo4JDatabaseManager extends DatabaseManager { /** * A specialized {@link DatabaseManager - *

        - * /** * Executes a custom Cypher query with parameters and returns a stream of {@link CommunicationEntity}. - * * @param cypher the Cypher query to execute. * @param parameters the parameters to bind to the query. * @return a stream of {@link CommunicationEntity} matching the query result. - * @throws NullPointerException if any of the arguments are {@code null}. - * @throws CypherException if there is an issue with the Cypher syntax. + * @throws NullPointerException if any of the arguments are null */ Stream executeQuery(String cypher, Map parameters); @@ -71,7 +67,7 @@ public interface Neo4JDatabaseManager extends DatabaseManager { * @param relationship the type of relationship to traverse. * @param depth the traversal depth limit. * @return a stream of {@link CommunicationEntity} representing related nodes. - * @throws NullPointerException if any of the arguments are {@code null}. + * @throws NullPointerException if any of the arguments are null. */ Stream traverse(String startNodeId, String relationship, int depth); @@ -82,7 +78,7 @@ public interface Neo4JDatabaseManager extends DatabaseManager { * @param target the target entity. * @param relationshipType the type of relationship to create. * @throws EdgeCommunicationException if either the source or target entity does not exist in the database. - * @throws NullPointerException if any of the arguments are {@code null}. + * @throws NullPointerException if any of the arguments are null. */ void edge(CommunicationEntity source, String relationshipType, CommunicationEntity target); @@ -93,7 +89,7 @@ public interface Neo4JDatabaseManager extends DatabaseManager { * @param target the target entity, which must already exist in the database. * @param relationshipType the type of relationship to remove. * @throws EdgeCommunicationException if either the source or target entity does not exist in the database. - * @throws NullPointerException if any of the arguments are {@code null}. + * @throws NullPointerException if any of the arguments are null. */ void remove(CommunicationEntity source, String relationshipType, CommunicationEntity target); } From ff0f883181a98b210c2e8d0e2e90f55cd3dd08fd Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 21:05:15 +0000 Subject: [PATCH 143/148] feat: move documentation Signed-off-by: Otavio Santana --- .../databases/neo4j/communication/Neo4JDatabaseManager.java | 1 - .../java/org/eclipse/jnosql/databases/neo4j/mapping/Contact.java | 0 2 files changed, 1 deletion(-) rename jnosql-neo4j/src/{main => test}/java/org/eclipse/jnosql/databases/neo4j/mapping/Contact.java (100%) 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 31e0981a1..8b6b52d9a 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 @@ -56,7 +56,6 @@ public interface Neo4JDatabaseManager extends DatabaseManager { * @param cypher the Cypher query to execute. * @param parameters the parameters to bind to the query. * @return a stream of {@link CommunicationEntity} matching the query result. - * @throws NullPointerException if any of the arguments are null */ Stream executeQuery(String cypher, Map parameters); diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Contact.java b/jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Contact.java similarity index 100% rename from jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/mapping/Contact.java rename to jnosql-neo4j/src/test/java/org/eclipse/jnosql/databases/neo4j/mapping/Contact.java From 4faf993caf1711182bafdc9191ed6d79887622b5 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 21:10:40 +0000 Subject: [PATCH 144/148] feat: enhance documentation Signed-off-by: Otavio Santana --- .../communication/DefaultNeo4JDatabaseManager.java | 3 +-- .../communication/EdgeCommunicationException.java | 11 +++++++++++ .../communication/Neo4JCommunicationException.java | 11 +++++++++++ .../neo4j/communication/Neo4JConfiguration.java | 6 ++++++ .../neo4j/communication/Neo4JQueryBuilder.java | 7 ++++--- .../databases/neo4j/mapping/DefaultNeo4JTemplate.java | 2 +- 6 files changed, 34 insertions(+), 6 deletions(-) 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 6f8eaaf04..1dc0eb51f 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 @@ -35,11 +35,10 @@ import java.util.logging.Logger; import java.util.stream.Stream; -public class DefaultNeo4JDatabaseManager implements Neo4JDatabaseManager { +class DefaultNeo4JDatabaseManager implements Neo4JDatabaseManager { private static final Logger LOGGER = Logger.getLogger(DefaultNeo4JDatabaseManager.class.getName()); public static final String ID = "_id"; - public static final String NEO4J_NATIVE_ID = "nodeId"; private final Session session; private final String database; diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java index 4f2297048..e298f116a 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/EdgeCommunicationException.java @@ -29,10 +29,21 @@ */ public class EdgeCommunicationException extends Neo4JCommunicationException{ + /** + * Constructs a new exception with the specified detail message. + * + * @param message the detail message + */ public EdgeCommunicationException(String message) { super(message); } + /** + * Constructs a new exception with the specified detail message and cause. + * + * @param message the detail message + * @param exception the cause + */ public EdgeCommunicationException(String message, Throwable exception) { super(message, exception); } diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java index a8b922d4a..a1eef8d2f 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JCommunicationException.java @@ -32,10 +32,21 @@ */ public class Neo4JCommunicationException extends CommunicationException { + /** + * Constructs a new exception with the specified detail message. + * + * @param message the detail message + */ public Neo4JCommunicationException(String message) { super(message); } + /** + * Constructs a new exception with the specified detail message and cause. + * + * @param message the detail message + * @param exception the cause + */ public Neo4JCommunicationException(String message, Throwable exception) { super(message, exception); } diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java index ba66f4900..7385820e0 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JConfiguration.java @@ -53,6 +53,12 @@ public final class Neo4JConfiguration implements DatabaseConfiguration { private static final String DEFAULT_BOLT = "bolt://localhost:7687"; + /** + * Applies the provided settings to the Neo4j database configuration. + * + * @param settings the settings to apply + * @return a new {@link Neo4JDatabaseManagerFactory} instance + */ @Override public Neo4JDatabaseManagerFactory apply(Settings settings) { var uri = settings.getOrDefault(Neo4JConfigurations.URI, DEFAULT_BOLT); diff --git a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java index ed8da1031..195d12c6d 100644 --- a/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java +++ b/jnosql-neo4j/src/main/java/org/eclipse/jnosql/databases/neo4j/communication/Neo4JQueryBuilder.java @@ -28,13 +28,14 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -public enum Neo4JQueryBuilder { + +enum Neo4JQueryBuilder { INSTANCE; private static final String INTERNAL_ID = "_id"; - public String buildQuery(DeleteQuery query, Map parameters) { + String buildQuery(DeleteQuery query, Map parameters) { StringBuilder cypher = new StringBuilder("MATCH (e:"); cypher.append(query.name()).append(")"); @@ -57,7 +58,7 @@ public String buildQuery(DeleteQuery query, Map parameters) { return cypher.toString(); } - public String buildQuery(SelectQuery query, Map parameters) { + String buildQuery(SelectQuery query, Map parameters) { StringBuilder cypher = new StringBuilder("MATCH (e:"); cypher.append(query.name()).append(")"); 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 9106d4182..4bb80a618 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 @@ -36,7 +36,7 @@ @ApplicationScoped @Typed(Neo4JTemplate.class) -public class DefaultNeo4JTemplate extends AbstractSemiStructuredTemplate implements Neo4JTemplate { +class DefaultNeo4JTemplate extends AbstractSemiStructuredTemplate implements Neo4JTemplate { private static final Logger LOGGER = Logger.getLogger(DefaultNeo4JTemplate.class.getName()); From de64a20c7d94b2b5afa687ae12e21b57edc4e2cc Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 21:12:19 +0000 Subject: [PATCH 145/148] feat: update javadoc Signed-off-by: Otavio Santana --- .../neo4j/communication/Neo4JDatabaseManager.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) 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 8b6b52d9a..493dc1bcd 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 @@ -51,11 +51,12 @@ public interface Neo4JDatabaseManager extends DatabaseManager { /** - * A specialized {@link DatabaseManager * Executes a custom Cypher query with parameters and returns a stream of {@link CommunicationEntity}. + * * @param cypher the Cypher query to execute. * @param parameters the parameters to bind to the query. - * @return a stream of {@link CommunicationEntity} matching the query result. + * @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); @@ -66,18 +67,18 @@ public interface Neo4JDatabaseManager extends DatabaseManager { * @param relationship the type of relationship to traverse. * @param depth the traversal depth limit. * @return a stream of {@link CommunicationEntity} representing related nodes. - * @throws NullPointerException if any of the arguments are null. + * @throws NullPointerException if {@code startNodeId}, {@code relationship}, or {@code depth} is null. */ Stream traverse(String startNodeId, String relationship, int depth); /** - * Creates a relationship between two {@link CommunicationEntity} nodes. + * Creates a relationship (edge) between two {@link CommunicationEntity} nodes. * * @param source the source entity. * @param target the target entity. * @param relationshipType the type of relationship to create. * @throws EdgeCommunicationException if either the source or target entity does not exist in the database. - * @throws NullPointerException if any of the arguments are null. + * @throws NullPointerException if {@code source}, {@code target}, or {@code relationshipType} is null. */ void edge(CommunicationEntity source, String relationshipType, CommunicationEntity target); @@ -88,7 +89,7 @@ public interface Neo4JDatabaseManager extends DatabaseManager { * @param target the target entity, which must already exist in the database. * @param relationshipType the type of relationship to remove. * @throws EdgeCommunicationException if either the source or target entity does not exist in the database. - * @throws NullPointerException if any of the arguments are null. + * @throws NullPointerException if {@code source}, {@code target}, or {@code relationshipType} is null. */ void remove(CommunicationEntity source, String relationshipType, CommunicationEntity target); } From a0ddf16d2338b0d628f4a46f8b910d42ad9c0e0d Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 21:14:46 +0000 Subject: [PATCH 146/148] docs: update documentation on readme Signed-off-by: Otavio Santana --- README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index cc3204997..a08aa08f5 100644 --- a/README.adoc +++ b/README.adoc @@ -1831,7 +1831,7 @@ List people = template.solr("age:@age AND type:@type AND _entity:@entity ---- -=== Graph (Apache Tinkerpop) +== Graph (Apache Tinkerpop) Currently, the Jakarta NoSQL doesn't define an API for Graph database types but Eclipse JNoSQL provides a Graph template to explore the specific behavior of this NoSQL type. From 7aea52b58729f72e811b5a3c02aa38da3e96eb27 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 21:25:14 +0000 Subject: [PATCH 147/148] docs: update readme file Signed-off-by: Otavio Santana --- README.adoc | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/README.adoc b/README.adoc index a08aa08f5..64e32b15a 100644 --- a/README.adoc +++ b/README.adoc @@ -1830,6 +1830,80 @@ SolrTemplate template; List people = template.solr("age:@age AND type:@type AND _entity:@entity", params); ---- +== Neo4J + +image::https://jnosql.github.io/img/logos/neo4j.png[Neo4J Project,align="center",width=25%,height=25%] +https://neo4j.com/[Neo4J] is a highly scalable, native graph database designed to manage complex relationships in data. It enables developers to build applications that leverage the power of graph traversal, pattern matching, and high-performance querying using the **Cypher** query language. + +This API provides support for **Graph** database operations, including entity persistence, query execution via Cypher, and relationship traversal. + +=== How To Install + +You can use either the Maven or Gradle dependencies: + +[source,xml] +---- + + org.eclipse.jnosql.databases + jnosql-neo4j + 1.1.4 + +---- + +=== Configuration + +This API provides the `Neo4JDatabaseConfigurations` class to programmatically establish the credentials. You can configure Neo4J properties using the https://microprofile.io/microprofile-config/[MicroProfile Config] specification. + +[cols="2,4"] +|=== +| Configuration Property | Description + +| `jnosql.neo4j.uri` | The connection URI for the Neo4J database. Example: `bolt://localhost:7687` +| `jnosql.neo4j.username` | The username for authentication. +| `jnosql.neo4j.password` | The password for authentication. +| `jnosql.neo4j.database` | The target database name. +|=== + +==== Example Using MicroProfile Config + +[source,properties] +---- +jnosql.neo4j.uri=bolt://localhost:7687 +jnosql.neo4j.username=neo4j +jnosql.neo4j.password=yourpassword +jnosql.neo4j.database=neo4j +---- + +=== Template API + +The `Neo4JTemplate` interface extends `GraphTemplate` and allows for dynamic Cypher execution. + +[source,java] +---- +@Inject +private Neo4JTemplate template; + +List people = template.cypherQuery("MATCH (p:Person) WHERE p.name = $name RETURN p", params); +var edge = template.edge(otavio, "FRIENDS_WITH", ada); +---- + +=== Repository Support + +The `Neo4JRepository` interface extends the `NoSQLRepository` interface and enables query execution using the `@Cypher` annotation. + +[source,java] +---- +@Repository +interface PersonRepository extends Neo4JRepository { + + @Cypher("MATCH (p:Person) RETURN p") + List findAll(); + + @Cypher("MATCH (p:Person) WHERE p.name = $name RETURN p") + List findByName(@Param("name") String name); +} +---- + == Graph (Apache Tinkerpop) From 55a082598d428ea12c493171159df0c7690a9741 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Sat, 1 Mar 2025 21:42:33 +0000 Subject: [PATCH 148/148] feat: remove tck reference Signed-off-by: Otavio Santana --- .../mapping/DefaultNeo4JTemplateTest.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) 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 023a6aa41..771360bb8 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 @@ -18,7 +18,6 @@ import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; -import jakarta.nosql.tck.entities.Person; import net.datafaker.Faker; import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; import org.eclipse.jnosql.communication.semistructured.Element; @@ -38,6 +37,7 @@ import java.util.Collections; import java.util.Map; +import java.util.UUID; import java.util.function.Supplier; import java.util.stream.Stream; @@ -84,13 +84,13 @@ void setUp() { @Test void shouldCypher() { - String cypher = "MATCH (n:Person) RETURN n"; + String cypher = "MATCH (n:Music) RETURN n"; Map parameters = Collections.emptyMap(); - CommunicationEntity entity = CommunicationEntity.of("Person"); + CommunicationEntity entity = CommunicationEntity.of("Music"); entity.add(Element.of("name", "Ada")); when(manager.executeQuery(cypher, parameters)).thenReturn(Stream.of(entity)); - Stream result = template.cypher(cypher, parameters); + Stream result = template.cypher(cypher, parameters); assertNotNull(result); assertTrue(result.findFirst().isPresent()); } @@ -104,15 +104,15 @@ void shouldThrowExceptionWhenQueryIsNull() { @Test void shouldCreateEdge() { var faker = new Faker(); - Person source = Person.of(faker); - Person target = Person.of(faker); + Music source = new Music(UUID.randomUUID().toString(), faker.funnyName().name(), 10); + Music target = new Music(UUID.randomUUID().toString(), faker.funnyName().name(), 15); String relationshipType = "FRIENDS"; - Edge edge = Edge.of(source, relationshipType, target); + Edge edge = Edge.of(source, relationshipType, target); Mockito.when(manager.insert(Mockito.any(CommunicationEntity.class))).thenAnswer(invocation -> invocation.getArgument(0)); - Edge result = template.edge(source, relationshipType, target); + Edge result = template.edge(source, relationshipType, target); assertNotNull(result); assertEquals(edge, result); @@ -122,8 +122,8 @@ void shouldCreateEdge() { @Test void shouldThrowExceptionWhenCreatingEdgeWithNullValues() { var faker = new Faker(); - Person source = Person.of(faker); - Person target = Person.of(faker); + Music source = new Music(UUID.randomUUID().toString(), faker.funnyName().name(), 10); + Music target = new Music(UUID.randomUUID().toString(), faker.funnyName().name(), 15); assertThrows(NullPointerException.class, () -> template.edge(null, "FRIENDS", target)); assertThrows(NullPointerException.class, () -> template.edge(source, (Supplier) null, target)); assertThrows(NullPointerException.class, () -> template.edge(source, "FRIENDS", null)); @@ -132,8 +132,8 @@ void shouldThrowExceptionWhenCreatingEdgeWithNullValues() { @Test void shouldRemoveEdge() { var faker = new Faker(); - Person source = Person.of(faker); - Person target = Person.of(faker); + Music source = new Music(UUID.randomUUID().toString(), faker.funnyName().name(), 10); + Music target = new Music(UUID.randomUUID().toString(), faker.funnyName().name(), 15); String relationshipType = "FRIENDS"; doNothing().when(manager).remove(any(), anyString(), any()); @@ -145,8 +145,8 @@ void shouldRemoveEdge() { @Test void shouldThrowExceptionWhenRemovingEdgeWithNullValues() { var faker = new Faker(); - Person source = Person.of(faker); - Person target = Person.of(faker); + Music source = new Music(UUID.randomUUID().toString(), faker.funnyName().name(), 10); + Music target = new Music(UUID.randomUUID().toString(), faker.funnyName().name(), 15); assertThrows(NullPointerException.class, () -> template.remove(null, "FRIENDS", target)); assertThrows(NullPointerException.class, () -> template.remove(source, (String) null, target)); assertThrows(NullPointerException.class, () -> template.remove(source, "FRIENDS", null));