Skip to content

Include apache tinkerpop as graph #282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version
=== Added

- Include between query support at MongoDB
- Include Graph as Apache TinkerPop

=== Changed

Expand Down
112 changes: 112 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1701,6 +1701,118 @@ SolrTemplate template;
List<Person> people = template.solr("age:@age AND type:@type AND _entity:@entity", params);
----


=== 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.

Eclipse JNoSQL offers a mapping implementation for Graph NoSQL types:

[source,xml]
----
<dependency>
<groupId>org.eclipse.jnosql.mapping</groupId>
<artifactId>jnosql-mapping-graph</artifactId>
<version>1.1.1</version>
</dependency>
----

Despite the other three NoSQL types, Eclipse JNoSQL API does not offer a communication layer for Graph NoSQL types. Instead, it integrates with https://tinkerpop.apache.org/[Apache Tinkerpop 3.x].

[source,java]
----
@Inject
GraphTemplate template;
...

Category java = Category.of("Java");
Book effectiveJava = Book.of("Effective Java");

template.insert(java);
template.insert(effectiveJava);
EdgeEntity edge = template.edge(java, "is", software);

Stream<Book> books = template.getTraversalVertex()
.hasLabel("Category")
.has("name", "Java")
.in("is")
.hasLabel("Book")
.getResult();
----

Apache TinkerPop is database agnostic. Thus, you can change the database in your application with no or minimal impact on source code.

You can define the database settings using the https://microprofile.io/microprofile-config/[MicroProfile Config] specification, so you can add properties and overwrite it in the environment following the https://12factor.net/config[Twelve-Factor App].

[source,properties]
----
jnosql.graph.provider=<CLASS-DRIVER>
jnosql.provider.host=<HOST>
jnosql.provider.user=<USER>
jnosql.provider.password=<PASSWORD>
----

TIP: The ```jnosql.graph.provider``` property is necessary when you have more than one driver in the classpath. Otherwise, it will take the first one.

These configuration settings are the default behavior. Nevertheless, there is an option to programmatically configure these settings. Create a class that implements the ```Supplier<Graph>```, then define it using the ```@Alternative``` and ```@Priority``` annotations.

[source,java]
----
@Alternative
@Priority(Interceptor.Priority.APPLICATION)
public class ManagerSupplier implements Supplier<Graph> {

@Produces
public Graph get() {
Graph graph = ...; // from a provider
return graph;
}
}
----

You can work with several document database instances through CDI qualifier. To identify each database instance, make a `Graph` visible for CDI by putting the ```@Produces``` and the ```@Database``` annotations in the method.

[source,java]
----
@Inject
@Database(value = DatabaseType.GRAPH, provider = "databaseA")
private GraphTemplate templateA;

@Inject
@Database(value = DatabaseType.GRAPH, provider = "databaseB")
private GraphTemplate templateB;

// producers methods
@Produces
@Database(value = DatabaseType.GRAPH, provider = "databaseA")
public Graph getManagerA() {
return manager;
}

@Produces
@Database(value = DatabaseType.GRAPH, provider = "databaseB")
public Graph getManagerB() {
return manager;
}
----


Eclipse JNoSQL does not provide https://mvnrepository.com/artifact/org.apache.tinkerpop/gremlin-core[Apache Tinkerpop 3 dependency]; check if the provider does. Otherwise, do it manually.

[source,xml]
----
<dependency>
<groupId>org.apache.tinkerpop</groupId>
<artifactId>jnosql-gremlin-core</artifactId>
<version>${tinkerpop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tinkerpop</groupId>
<artifactId>jnosql-gremlin-groovy</artifactId>
<version>${tinkerpop.version}</version>
</dependency>
----

== Getting Help

Having trouble with Eclipse JNoSQL databases? We’d love to help!
Expand Down
160 changes: 160 additions & 0 deletions jnosql-tinkerpop/antlr4/org/eclipse/jnosql/query/grammar/data/JDQL.g4
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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] ;
Loading