Skip to content

Commit 7ae4da4

Browse files
Add support for new syntax for CREATE INDEX (#45) (#46)
* Add support for new syntax for CREATE INDEX (#45) * Classify whole CREATE INDEX commands as tokens This ensures that certain keywords, such as "range", are classified correctly depending on whether they are used as index type or function call.
1 parent 9144608 commit 7ae4da4

30 files changed

+656
-69
lines changed

language/cypher/src/main/java/com/albertoventurini/graphdbplugin/language/cypher/formatter/converter/KeywordCaseConverter.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,12 @@ public class KeywordCaseConverter extends AbstractCypherConverter {
3434
CypherTypes.K_SINGLE
3535
);
3636

37-
private static final Map<IElementType, String> SPECIAL_FUNCTIONS = Map.of(
37+
private static final Map<IElementType, String> SPECIAL_KEYWORDS = Map.of(
3838
CypherTypes.K_SHORTESTPATH, "shortestPath",
39-
CypherTypes.K_ALLSHORTESTPATHS, "allShortestPaths"
39+
CypherTypes.K_ALLSHORTESTPATHS, "allShortestPaths",
40+
CypherTypes.K_ON_TYPE, "ON type",
41+
CypherTypes.K_ON_EACH_TYPE, "ON EACH type",
42+
CypherTypes.K_ON_EACH_LABELS, "ON EACH labels"
4043
);
4144

4245
public KeywordCaseConverter(CypherPreFormatter.FormatterTask formatterTask, @NotNull Document document) {
@@ -59,8 +62,8 @@ protected String convert(PsiElement element) {
5962
return null;
6063
}
6164

62-
if (SPECIAL_FUNCTIONS.containsKey(type)) {
63-
return SPECIAL_FUNCTIONS.get(type);
65+
if (SPECIAL_KEYWORDS.containsKey(type)) {
66+
return SPECIAL_KEYWORDS.get(type);
6467
}
6568

6669
if (TO_LOWER_CASE_SPECIAL.contains(type)) {

language/cypher/src/main/java/com/albertoventurini/graphdbplugin/language/cypher/lexer/Cypher.bnf

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ private statement_recover::= !(';'
6666
| K_UNWIND
6767
| K_MERGE
6868
| K_CREATE
69+
| K_CREATE_INDEX
70+
| K_CREATE_RANGE_INDEX
71+
| K_CREATE_LOOKUP_INDEX
72+
| K_CREATE_POINT_INDEX
73+
| K_CREATE_TEXT_INDEX
6974
| K_DROP
7075
| K_SET
7176
| K_DELETE
@@ -110,10 +115,6 @@ Command ::= CreateIndex
110115
| CreateRelationshipPropertyExistenceConstraint
111116
| DropRelationshipPropertyExistenceConstraint
112117

113-
CreateIndex ::= K_CREATE IndexSyntax
114-
DropIndex ::= K_DROP IndexSyntax
115-
IndexSyntax ::= K_INDEX K_ON NodeLabel "(" PropertyKeyNames ")"
116-
117118
CreateUniqueConstraint ::= K_CREATE UniqueConstraintSyntax
118119
DropUniqueConstraint ::= K_DROP UniqueConstraintSyntax
119120
UniqueConstraintSyntax ::= K_CONSTRAINT K_ON "(" Variable NodeLabel ")" K_ASSERT PropertyExpression K_IS K_UNIQUE
@@ -129,6 +130,50 @@ RelationshipPatternSyntax ::= ("(" ")" "-" "[" Variable RelType "]" "-" "(" ")")
129130
| ("(" ")" "-" "[" Variable RelType "]" "-" ">" "(" ")")
130131
| ("(" ")" "<" "-" "[" Variable RelType "]" "-" "(" ")")
131132

133+
/*************************
134+
* Indexes
135+
*/
136+
137+
CreateIndex ::= CreateRangeIndex
138+
| CreateTextIndex
139+
| CreatePointIndex
140+
| CreateLookupIndex
141+
142+
CreateRangeIndex ::= CreateNamedRangeIndex | CreateUnnamedRangeIndex
143+
private CreateNamedRangeIndex ::= (K_CREATE_INDEX | K_CREATE_RANGE_INDEX) IndexName CreateIndexBody
144+
private CreateUnnamedRangeIndex ::= (K_CREATE_INDEX | K_CREATE_RANGE_INDEX) CreateIndexBody
145+
146+
CreateTextIndex ::= CreateNamedTextIndex | CreateUnnamedTextIndex
147+
private CreateNamedTextIndex ::= K_CREATE_TEXT_INDEX IndexName CreateIndexBody
148+
private CreateUnnamedTextIndex ::= K_CREATE_TEXT_INDEX CreateIndexBody
149+
150+
CreatePointIndex ::= CreateNamedPointIndex | CreateUnnamedPointIndex
151+
private CreateNamedPointIndex ::= K_CREATE_POINT_INDEX IndexName CreateIndexBody
152+
private CreateUnnamedPointIndex ::= K_CREATE_POINT_INDEX CreateIndexBody
153+
154+
IndexName ::= SymbolicNameString
155+
156+
private CreateIndexBody ::= K_IF_NOT_EXISTS? CreateIndexForPart CreateIndexOnPropertiesPart CreateIndexOptionsPart?
157+
158+
private CreateIndexForPart ::= K_FOR (CreateIndexForNodePart | CreateIndexForRelationshipPart)
159+
CreateIndexForNodePart ::= "(" Variable NodeLabel ")"
160+
CreateIndexForRelationshipPart ::= "(" ")" "-" "[" Variable RelType "]" "-" "(" ")"
161+
162+
CreateIndexOnPropertiesPart ::= K_ON "(" Variable "." PropertyKeyName ("," Variable "." PropertyKeyName)* ")"
163+
164+
// CREATE LOOKUP INDEX is a little different, so it is handled separately
165+
CreateLookupIndex ::= CreateNamedLookupIndex | CreateUnnamedLookupIndex
166+
private CreateNamedLookupIndex ::= K_CREATE_LOOKUP_INDEX IndexName CreateLookupIndexBody
167+
private CreateUnnamedLookupIndex ::= K_CREATE_LOOKUP_INDEX CreateLookupIndexBody
168+
169+
private CreateLookupIndexBody ::= K_IF_NOT_EXISTS? (CreateLookupIndexForNodePart | CreateLookupIndexForRelationshipPart) CreateIndexOptionsPart?
170+
CreateLookupIndexForNodePart ::= K_FOR "(" Variable ")" K_ON_EACH_LABELS "(" Variable ")"
171+
CreateLookupIndexForRelationshipPart ::= K_FOR "(" ")" "-" "[" Variable "]" "-" "(" ")" (K_ON_EACH_TYPE|K_ON_TYPE) "(" Variable ")"
172+
173+
CreateIndexOptionsPart ::= K_OPTIONS "{" LiteralEntry? ("," LiteralEntry)* "}"
174+
175+
DropIndex ::= K_DROP K_INDEX IndexName K_IF_EXISTS?
176+
132177
/*************************
133178
* Query
134179
*/

language/cypher/src/main/java/com/albertoventurini/graphdbplugin/language/cypher/lexer/CypherLexer.flex

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,28 @@ K_MANDATORY=[Mm][Aa][Nn][Dd][Aa][Tt][Oo][Rr][Yy]
112112
K_SCALAR=[Ss][Cc][Aa][Ll][Aa][Rr]
113113
K_OF=[Oo][Ff]
114114
K_ADD=[Aa][Dd][Dd]
115+
K_OPTIONS=[Oo][Pp][Tt][Ii][Oo][Nn][Ss]
116+
117+
_EACH=[Ee][Aa][Cc][Hh]
118+
_IF=[Ii][Ff]
119+
_RANGE=[Rr][Aa][Nn][Gg][Ee]
120+
_LOOKUP=[Ll][Oo][Oo][Kk][Uu][Pp]
121+
_TEXT=[Tt][Ee][Xx][Tt]
122+
_POINT=[Pp][Oo][Ii][Nn][Tt]
123+
_LABELS=[Ll][Aa][Bb][Ee][Ll][Ss]
124+
_TYPE=[Tt][Yy][Pp][Ee]
125+
126+
K_CREATE_INDEX={K_CREATE}{WHITE_SPACE}{K_INDEX}
127+
K_CREATE_RANGE_INDEX={K_CREATE}{WHITE_SPACE}{_RANGE}{WHITE_SPACE}{K_INDEX}
128+
K_CREATE_LOOKUP_INDEX={K_CREATE}{WHITE_SPACE}{_LOOKUP}{WHITE_SPACE}{K_INDEX}
129+
K_CREATE_TEXT_INDEX={K_CREATE}{WHITE_SPACE}{_TEXT}{WHITE_SPACE}{K_INDEX}
130+
K_CREATE_POINT_INDEX={K_CREATE}{WHITE_SPACE}{_POINT}{WHITE_SPACE}{K_INDEX}
131+
K_IF_EXISTS={_IF}{WHITE_SPACE}{K_EXISTS}
132+
K_IF_NOT_EXISTS={_IF}{WHITE_SPACE}{K_NOT}{WHITE_SPACE}{K_EXISTS}
133+
K_ON_EACH_LABELS={K_ON}{WHITE_SPACE}{_EACH}{WHITE_SPACE}{_LABELS}
134+
K_ON_EACH_TYPE={K_ON}{WHITE_SPACE}{_EACH}{WHITE_SPACE}{_TYPE}
135+
K_ON_TYPE={K_ON}{WHITE_SPACE}{_TYPE}
136+
115137
L_IDENTIFIER=[a-zA-Z_][a-zA-Z_$0-9]*
116138
L_IDENTIFIER_TEXT=\`[^`]+\`
117139
L_DECIMAL=(0|[1-9][0-9]*)\.[0-9]+
@@ -245,6 +267,19 @@ BLOCK_COMMENT = "/*" ( ([^"*"]|[\r\n])* ("*"+ [^"*""/"] )? )* ("*" | "*"+"/")?
245267
{K_OF} { return K_OF; }
246268
{K_ADD} { return K_ADD; }
247269

270+
{K_OPTIONS} { return K_OPTIONS; }
271+
272+
{K_CREATE_INDEX} { return K_CREATE_INDEX; }
273+
{K_CREATE_RANGE_INDEX} { return K_CREATE_RANGE_INDEX; }
274+
{K_CREATE_LOOKUP_INDEX} { return K_CREATE_LOOKUP_INDEX; }
275+
{K_CREATE_TEXT_INDEX} { return K_CREATE_TEXT_INDEX; }
276+
{K_CREATE_POINT_INDEX} { return K_CREATE_POINT_INDEX; }
277+
{K_IF_EXISTS} { return K_IF_EXISTS; }
278+
{K_IF_NOT_EXISTS} { return K_IF_NOT_EXISTS; }
279+
{K_ON_EACH_LABELS} { return K_ON_EACH_LABELS; }
280+
{K_ON_EACH_TYPE} { return K_ON_EACH_TYPE; }
281+
{K_ON_TYPE} { return K_ON_TYPE; }
282+
248283
{L_IDENTIFIER} { return L_IDENTIFIER; }
249284
{L_IDENTIFIER_TEXT} { return L_IDENTIFIER_TEXT; }
250285
{L_DECIMAL} { return L_DECIMAL; }

testing/integration-neo4j/src/test/java/com/albertoventurini/graphdbplugin/test/integration/neo4j/tests/cypher/formatting/CypherFormattingTest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ public void ignoreNestedWhereOnNewLineAndIndented() {
222222

223223
//negative line-breaking tests
224224
public void testNoBreakOnIndexOn() {
225-
doTest("create index on :Person(name)", "CREATE INDEX ON :Person(name)");
225+
doTest("create index for (n:Person) on (n.name)", "CREATE INDEX FOR (n:Person) ON (n.name)");
226226
}
227227

228228
public void testNoBreakOnConstraintOn() {
@@ -378,4 +378,9 @@ public void testAssertFormatting() {
378378
doTest("CALL apoc.schema.assert({}, {});",
379379
"CALL apoc.schema.assert({}, {});");
380380
}
381+
382+
public void testCreateLookupIndex() {
383+
doTest("create lookup index node_label_lookup_index for (n) on each LABELS(n)",
384+
"CREATE LOOKUP INDEX node_label_lookup_index FOR (n) ON EACH labels(n)");
385+
}
381386
}

testing/integration-neo4j/src/test/java/com/albertoventurini/graphdbplugin/test/integration/neo4j/tests/cypher/parsing/CommandParsingTest.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,4 @@ public void testConstraintPropertyUniqueCreate() {
3737
public void testConstraintPropertyUniqueDrop() {
3838
doTest(true);
3939
}
40-
41-
public void testIndexCreate() {
42-
doTest(true);
43-
}
44-
45-
public void testIndexDrop() {
46-
doTest(true);
47-
}
4840
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.albertoventurini.graphdbplugin.test.integration.neo4j.tests.cypher.parsing;
2+
3+
import com.albertoventurini.graphdbplugin.test.integration.neo4j.tests.cypher.util.BaseParsingTest;
4+
5+
public class IndexCommandParsingTest extends BaseParsingTest {
6+
7+
public IndexCommandParsingTest() {
8+
super("indexes");
9+
}
10+
11+
public void testCreateNamedIndex() {
12+
doTest(true);
13+
}
14+
15+
public void testCreateUnnamedIndex() {
16+
doTest(true);
17+
}
18+
19+
public void testCreateIndexOnRelationships() {
20+
doTest(true);
21+
}
22+
23+
public void testCreateIndexWithOptions() {
24+
doTest(true);
25+
}
26+
27+
public void testCreateIndexIfNotExists() {
28+
doTest(true);
29+
}
30+
31+
public void testCreateRangeIndex() {
32+
doTest(true);
33+
}
34+
35+
public void testCreateLookupIndex() {
36+
doTest(true);
37+
}
38+
39+
public void testCreatePointIndex() {
40+
doTest(true);
41+
}
42+
43+
public void testCreateTextIndex() {
44+
doTest(true);
45+
}
46+
47+
public void testDropIndex() {
48+
doTest(true);
49+
}
50+
}

testing/integration-neo4j/src/test/resources/parsing/command/IndexCreate.cyp

Lines changed: 0 additions & 1 deletion
This file was deleted.

testing/integration-neo4j/src/test/resources/parsing/command/IndexCreate.txt

Lines changed: 0 additions & 25 deletions
This file was deleted.

testing/integration-neo4j/src/test/resources/parsing/command/IndexDrop.cyp

Lines changed: 0 additions & 1 deletion
This file was deleted.

testing/integration-neo4j/src/test/resources/parsing/command/IndexDrop.txt

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)