Skip to content

Commit f20d96e

Browse files
authored
Migrate to using elementId (#84)
* Migrate to using elementId The cypher function `id` has been deprecated since Neo4j 5.0. This PR adds the capability of sniffing the DBMS version so that the DiffService can write an update query that matches the server version. Likewise has `entity.id()` in the driver been deprecated. The driver will set `elementId` to `id.toString()` if the server does not provide it (4.4 and before). Therefore, it's safe to always use `entity.elementId()` regardless of the server version. * Add integration test for fetching Neo4j version * Fix test assertion: expected & actual position
1 parent fece746 commit f20d96e

File tree

25 files changed

+272
-43
lines changed

25 files changed

+272
-43
lines changed

database/api/src/main/java/com/albertoventurini/graphdbplugin/database/api/GraphDatabaseApi.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
package com.albertoventurini.graphdbplugin.database.api;
88

9+
import com.albertoventurini.graphdbplugin.database.api.data.GraphDatabaseVersion;
910
import com.albertoventurini.graphdbplugin.database.api.data.GraphMetadata;
1011
import com.albertoventurini.graphdbplugin.database.api.query.GraphQueryResult;
1112

@@ -18,4 +19,6 @@ public interface GraphDatabaseApi {
1819
GraphQueryResult execute(String query, Map<String, Object> statementParameters);
1920

2021
GraphMetadata metadata();
22+
23+
GraphDatabaseVersion getVersion();
2124
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Copied and adapted from plugin
3+
* <a href="https://github.com/neueda/jetbrains-plugin-graph-database-support">Graph Database Support</a>
4+
* by Neueda Technologies, Ltd.
5+
* Modified by Alberto Venturini, 2022
6+
*/
7+
package com.albertoventurini.graphdbplugin.database.api.data;
8+
9+
public interface GraphDatabaseVersion {
10+
String toString();
11+
12+
String idFunction();
13+
14+
Object idToParameter(String id);
15+
}

database/neo4j/src/main/java/com/albertoventurini/graphdbplugin/database/neo4j/bolt/Neo4jBoltDatabase.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
package com.albertoventurini.graphdbplugin.database.neo4j.bolt;
88

99
import com.albertoventurini.graphdbplugin.database.api.GraphDatabaseApi;
10+
import com.albertoventurini.graphdbplugin.database.api.data.GraphDatabaseVersion;
1011
import com.albertoventurini.graphdbplugin.database.api.data.GraphMetadata;
1112
import com.albertoventurini.graphdbplugin.database.api.query.GraphQueryResult;
13+
import com.albertoventurini.graphdbplugin.database.neo4j.bolt.data.Neo4jGraphDatabaseVersion;
1214
import com.albertoventurini.graphdbplugin.database.neo4j.bolt.query.Neo4jBoltQueryResult;
15+
import com.albertoventurini.graphdbplugin.database.neo4j.bolt.query.Neo4jBoltQueryResultRow;
1316
import org.neo4j.driver.AuthToken;
1417
import org.neo4j.driver.AuthTokens;
1518
import org.neo4j.driver.Driver;
@@ -21,14 +24,21 @@
2124
import org.neo4j.driver.exceptions.ClientException;
2225

2326
import java.nio.channels.UnresolvedAddressException;
27+
import java.util.Arrays;
2428
import java.util.Collections;
2529
import java.util.Map;
30+
import java.util.stream.IntStream;
2631

2732
/**
2833
* Communicates with Neo4j 3.0+ database using Bolt driver.
2934
*/
3035
public class Neo4jBoltDatabase implements GraphDatabaseApi {
3136

37+
private static final String VERSION_QUERY = """
38+
CALL dbms.components() YIELD versions
39+
RETURN versions[0] AS version
40+
""";
41+
3242
private final String url;
3343
private final AuthToken auth;
3444
private final SessionConfig dbConfig;
@@ -110,4 +120,19 @@ public GraphQueryResult execute(String query, Map<String, Object> statementParam
110120
public GraphMetadata metadata() {
111121
throw new IllegalStateException("Not implemented");
112122
}
123+
124+
@Override
125+
public GraphDatabaseVersion getVersion() {
126+
var result = execute(VERSION_QUERY);
127+
var row = result.getRows().get(0);
128+
var neo4jRow = (Neo4jBoltQueryResultRow) row;
129+
var rawVersion = neo4jRow.getValue("version").asString();
130+
var parsedVersion = IntStream.concat(
131+
Arrays.stream(rawVersion.split("\\.", 4))
132+
.limit(3)
133+
.mapToInt(Integer::parseInt),
134+
IntStream.generate(() -> 0)
135+
).limit(3).toArray();
136+
return new Neo4jGraphDatabaseVersion(parsedVersion[0], parsedVersion[1], parsedVersion[2]);
137+
}
113138
}

database/neo4j/src/main/java/com/albertoventurini/graphdbplugin/database/neo4j/bolt/data/Neo4jBoltNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class Neo4jBoltNode implements GraphNode {
2121
private final List<String> types;
2222

2323
public Neo4jBoltNode(Node value) {
24-
this.id = String.valueOf(value.id());
24+
this.id = String.valueOf(value.elementId());
2525
this.types = Iterables.asList(value.labels());
2626
this.propertyContainer = new Neo4jBoltPropertyContainer(value.asMap());
2727
}

database/neo4j/src/main/java/com/albertoventurini/graphdbplugin/database/neo4j/bolt/data/Neo4jBoltRelationship.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ public class Neo4jBoltRelationship implements GraphRelationship {
2626
private GraphNode endNode;
2727

2828
public Neo4jBoltRelationship(Relationship rel) {
29-
this.id = String.valueOf(rel.id());
29+
this.id = String.valueOf(rel.elementId());
3030
this.types = Collections.singletonList(rel.type());
3131
this.propertyContainer = new Neo4jBoltPropertyContainer(rel.asMap());
3232

33-
this.startNodeId = String.valueOf(rel.startNodeId());
34-
this.endNodeId = String.valueOf(rel.endNodeId());
33+
this.startNodeId = String.valueOf(rel.startNodeElementId());
34+
this.endNodeId = String.valueOf(rel.endNodeElementId());
3535
}
3636

3737
public Neo4jBoltRelationship(String id, List<String> types, GraphPropertyContainer propertyContainer, String startNodeId, String endNodeId) {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Copied and adapted from plugin
3+
* <a href="https://github.com/neueda/jetbrains-plugin-graph-database-support">Graph Database Support</a>
4+
* by Neueda Technologies, Ltd.
5+
* Modified by Alberto Venturini, 2022
6+
*/
7+
package com.albertoventurini.graphdbplugin.database.neo4j.bolt.data;
8+
9+
import com.albertoventurini.graphdbplugin.database.api.data.GraphDatabaseVersion;
10+
11+
public record Neo4jGraphDatabaseVersion(int major, int minor, int patch) implements GraphDatabaseVersion {
12+
13+
@Override
14+
public String toString() {
15+
return "Neo4j/" + major + "." + minor + "." + patch;
16+
}
17+
18+
@Override
19+
public String idFunction() {
20+
if (major >= 5) {
21+
return "elementId";
22+
}
23+
return "id";
24+
}
25+
26+
@Override
27+
public Object idToParameter(String id) {
28+
if (major >= 5) {
29+
return id;
30+
}
31+
return Long.parseLong(id);
32+
}
33+
}

graph-database-plugin/src/main/resources/META-INF/plugin.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@
130130
serviceImplementation="com.albertoventurini.graphdbplugin.jetbrains.ui.datasource.DataSourcesView"/>
131131
<projectService
132132
serviceImplementation="com.albertoventurini.graphdbplugin.jetbrains.ui.console.params.ParametersService"/>
133+
<projectService
134+
serviceImplementation="com.albertoventurini.graphdbplugin.jetbrains.database.VersionService"/>
133135

134136
<scratch.rootType implementation="com.albertoventurini.graphdbplugin.jetbrains.ui.datasource.interactions.GraphDbEditorsConsoleRootType"/>
135137
<scratch.rootType implementation="com.albertoventurini.graphdbplugin.jetbrains.ui.console.params.ParameterRootType"/>

platform/src/main/java/icons/GraphIcons.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public static final class Window {
2727
}
2828

2929
public static final class Nodes {
30+
public static final Icon VERSION = AllIcons.Actions.InlayGear;
3031
public static final Icon INDEX = AllIcons.Nodes.ResourceBundle;
3132
public static final Icon CONSTRAINT = AllIcons.Nodes.C_protected;
3233
public static final Icon LABEL = AllIcons.Nodes.Class;

testing/integration-neo4j/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ dependencies {
1212
testImplementation project(':graph-database-plugin')
1313
testImplementation project(':ui:jetbrains')
1414
testImplementation project(':language:cypher')
15+
testImplementation project(':database:api')
1516
testImplementation project(':database:neo4j')
1617
testImplementation project(':testing:common')
1718
testImplementation project(':platform')

testing/integration-neo4j/src/test/java/com/albertoventurini/graphdbplugin/test/integration/neo4j/tests/database/common/AbstractDataSourceMetadataTest.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@
77
package com.albertoventurini.graphdbplugin.test.integration.neo4j.tests.database.common;
88

99
import com.albertoventurini.graphdbplugin.jetbrains.component.datasource.metadata.DataSourceMetadata;
10-
import com.albertoventurini.graphdbplugin.jetbrains.component.datasource.metadata.neo4j.Neo4jProcedureMetadata;
1110
import com.albertoventurini.graphdbplugin.jetbrains.component.datasource.state.DataSourceApi;
1211
import com.albertoventurini.graphdbplugin.test.integration.neo4j.util.base.BaseIntegrationTest;
1312

14-
import java.util.List;
1513
import java.util.Optional;
1614
import java.util.concurrent.CompletableFuture;
1715
import java.util.concurrent.ExecutionException;
@@ -32,13 +30,13 @@ public void setUp() throws Exception {
3230
public abstract DataSourceApi getDataSource();
3331

3432
public void testMetadataExists() throws ExecutionException, InterruptedException {
35-
Optional<DataSourceMetadata> metadata = component().dataSourcesMetadata().getMetadata(getDataSource()).get();
33+
Optional<DataSourceMetadata> metadata = component().dataSourcesMetadata().updateMetadata(getDataSource()).get();
3634
assertThat(metadata).isPresent();
3735
}
3836

3937
protected DataSourceMetadata getMetadata() {
4038
try {
41-
CompletableFuture<Optional<DataSourceMetadata>> futureMeta = component().dataSourcesMetadata().getMetadata(getDataSource());
39+
CompletableFuture<Optional<DataSourceMetadata>> futureMeta = component().dataSourcesMetadata().updateMetadata(getDataSource());
4240
return futureMeta.get(30, TimeUnit.SECONDS)
4341
.orElseThrow(() -> new IllegalStateException("Metadata should not be null"));
4442
} catch (InterruptedException | ExecutionException | TimeoutException e) {

0 commit comments

Comments
 (0)