diff --git a/modules/Neo4jPlugin/Apache v2.txt b/modules/Neo4jPlugin/Apache v2.txt
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/modules/Neo4jPlugin/Apache v2.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/modules/Neo4jPlugin/README.md b/modules/Neo4jPlugin/README.md
new file mode 100644
index 0000000000..a7c4a13151
--- /dev/null
+++ b/modules/Neo4jPlugin/README.md
@@ -0,0 +1,57 @@
+## Neo4j plugin
+
+This plugin allows you to import a network from a Neo4j database 4.X.
+
+### Import
+
+#### Step 1: Neo4j connection
+
+To import data from Neo4J, you must define how to connect to the Neo4j server.
+The plugin uses the official Neo4j driver with neo4j/bolt protocol.
+
+So to create a connection to the database you must fill :
+
+- Neo4j URL (ex: `neo4j://localhost` )
+- Database : If empty we will use the default database
+- Authentication : Select the authentication mechanism. We support "username/password" and "No authentication" modes.
+- Username : Neo4j user with whom you want to connect to the database (ex: `neo4j`)
+- Password: Neo4j user's password
+
+You can click on the button *Verify* to test the connection to the database with the provided values.
+If an error occurred, its message will be displayed.
+
+#### Step 2: Import configuration
+
+There are two import modes available :
+
+- By selecting labels and relationship types
+- By specifying two Cypher queries, one for nodes and the other for edges.
+
+##### By labels and relationship types
+
+On this screen you have two list one for nodes, and the other for edges.
+If you select nothing, it's the same as selecting everything.
+
+So for example, if it's the first time you see this screen, you can click on *Finish*, and the plugin will import the whole Neo4j otherwise.
+
+If you want to import a subset of your Neo4j database, you must provide a selection.
+In order to do a multi-selection, just use *ctrl + click*.
+
+NOTE: We only import relationships where node extremities are part from the labels selections.
+
+##### By queries
+
+On this screen you can define two Cypher queries :
+
+- One for retrieving nodes
+- The other for edges
+
+With this configuration, you can define precisely what you want to import.
+It's really useful when you want to project your graph (ex: create the colleagues graph when in your database you have `(:Person)-[:WORK_AT]->(:Company)` )
+
+The node query must return a field named *id* that is the unique identifier of your node.
+Ex: `MATCH (n) RETURN id(n) AS id, labels(n) AS labels`
+
+The edge query must return a field named *id* that is the unique identifier of your edge, but also a *sourceId* & *targetId* that must match an id from the node query
+Ex: `MATCH (n)-[r]->(m) RETURN id(r) AS id, type(r) AS type, id(n) AS sourceId, id(m) AS targetId`
+
diff --git a/modules/Neo4jPlugin/pom.xml b/modules/Neo4jPlugin/pom.xml
new file mode 100644
index 0000000000..efcc6e6e5c
--- /dev/null
+++ b/modules/Neo4jPlugin/pom.xml
@@ -0,0 +1,136 @@
+
+
+ 4.0.0
+
+
+ gephi-plugin-parent
+ org.gephi
+ 0.11.3
+
+
+ ouestware
+ neo4j-plugin
+ Neo4j Plugin
+ 5.0.1
+ nbm
+
+
+ 5.28.9
+ 1.21.3
+ 2.2.21
+
+
+
+
+
+ org.netbeans.api
+ org-openide-dialogs
+
+
+ org.netbeans.api
+ org-openide-awt
+
+
+ org.gephi
+ core-library-wrapper
+
+
+ org.netbeans.api
+ org-openide-util-lookup
+
+
+ org.gephi
+ graph-api
+
+
+ org.netbeans.api
+ org-openide-io
+
+
+ org.netbeans.api
+ org-openide-util
+
+
+ org.netbeans.api
+ org-openide-util-ui
+
+
+ org.gephi
+ io-importer-api
+
+
+ org.gephi
+ db-drivers
+
+
+
+
+ org.gephi
+ project-api
+
+
+ org.gephi
+ utils-longtask
+
+
+ org.netbeans.api
+ org-netbeans-api-annotations-common
+
+
+
+ org.neo4j.driver
+ neo4j-java-driver
+ ${neo4j.driver.version}
+
+
+ io.reactivex.rxjava2
+ rxjava
+ ${rxjava.version}
+
+
+
+ org.testcontainers
+ testcontainers
+ ${testcontainers.version}
+ test
+
+
+ org.testcontainers
+ neo4j
+ ${testcontainers.version}
+ test
+
+
+
+
+
+
+ org.apache.netbeans.utilities
+ nbm-maven-plugin
+
+ skip
+ Apache 2.0
+ Apache v2.txt
+ Benoit Simard
+
+
+
+
+
+
+
+
+
+
+
+ oss-sonatype
+ oss-sonatype
+ https://oss.sonatype.org/content/repositories/snapshots/
+
+ true
+
+
+
+
+
+
diff --git a/modules/Neo4jPlugin/src/img/neo4j-logo.png b/modules/Neo4jPlugin/src/img/neo4j-logo.png
new file mode 100644
index 0000000000..fabf6ce360
Binary files /dev/null and b/modules/Neo4jPlugin/src/img/neo4j-logo.png differ
diff --git a/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/Neo4jDatabaseImporter.java b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/Neo4jDatabaseImporter.java
new file mode 100644
index 0000000000..5f2fc7060a
--- /dev/null
+++ b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/Neo4jDatabaseImporter.java
@@ -0,0 +1,520 @@
+package org.gephi.plugins.neo4j.importer;
+
+import io.reactivex.Flowable;
+import io.reactivex.Observable;
+import org.gephi.io.importer.api.*;
+import org.gephi.io.importer.spi.WizardImporter;
+import org.gephi.utils.longtask.spi.LongTask;
+import org.gephi.utils.progress.ProgressTicket;
+import org.neo4j.driver.*;
+import org.neo4j.driver.reactive.RxResult;
+import org.neo4j.driver.types.Node;
+import org.neo4j.driver.types.Relationship;
+import org.neo4j.driver.util.Pair;
+import org.openide.util.NotImplementedException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import static org.neo4j.driver.Values.parameters;
+
+public class Neo4jDatabaseImporter implements WizardImporter, LongTask {
+
+ private final static String QUERY_BY_LABELS = "" +
+ "MATCH (n) " +
+ "WHERE any(label IN labels(n) WHERE label IN $labels) " +
+ "RETURN n";
+
+ private final static String QUERY_BY_RELS = "" +
+ "MATCH (n)-[r]->(m) " +
+ "WHERE type(r) IN $relationshipTypes AND " +
+ " any(label IN labels(n) WHERE label IN $labels) AND " +
+ " any(label IN labels(m) WHERE label IN $labels) " +
+ "RETURN r";
+
+ public ContainerLoader container;
+ private Report report;
+ private boolean cancel = false;
+ private ProgressTicket progressTicket;
+
+
+ /**
+ * Url of Neo4j database.
+ * Default is `neo4j://localhost`
+ */
+ private String url = "neo4j://localhost";
+
+ /**
+ * Username for the Neo4j database.
+ * Default is `neo4j`
+ */
+ private String username = "neo4j";
+
+ /**
+ * Password for the Neo4j database.
+ * If not specified, we do not do auth
+ */
+ private String passwd;
+
+ /**
+ * Database name
+ * If not specified, we use the default neo4j database
+ */
+ private String DBName;
+
+ /**
+ * List of labels that we want to import
+ */
+ private List labels;
+
+ /**
+ * List of relationship that we want to import
+ */
+ private List relationshipTypes;
+
+ /**
+ * Cypher query to retrieve nodes
+ */
+ private String nodeQuery;
+
+ /**
+ * Cypher query to retrieve edges
+ */
+ private String edgeQuery;
+
+ /**
+ * Neo4j driver instance.
+ */
+ private Driver driver;
+
+ @Override
+ public boolean execute(ContainerLoader containerLoader) {
+ this.container = containerLoader;
+ this.report = new Report();
+ if (this.progressTicket != null) {
+ this.progressTicket.start();
+ this.progressTicket.setDisplayName("Neo4j import");
+ this.progressTicket.switchToIndeterminate();
+ }
+
+ try {
+ // Create the neo4j driver
+ if (this.progressTicket != null) this.progressTicket.progress("Connecting to neo4j...");
+ if (this.driver != null) this.driver.close();
+ this.driver = GraphDatabase.driver(
+ url != null ? url : "neo4j://localhost",
+ this.passwd != null ? AuthTokens.basic(this.username, this.passwd) : AuthTokens.none()
+ );
+ this.driver.verifyConnectivity();
+
+ // Creating default columns for nodes/edges
+ this.getContainer().addNodeColumn("labels", String[].class);
+
+ // Do the related import
+ if (this.nodeQuery != null && this.edgeQuery != null) {
+ this.doImportByNodeAndEdgeQueries();
+ } else {
+ this.doImportByNodeAndEdgeTypes(
+ this.labels != null ? this.labels : this.getDbLabels(),
+ this.relationshipTypes != null ? this.relationshipTypes : this.getDbRelationshipTypes()
+ );
+ }
+ } catch (Exception e) {
+ this.getReport().logIssue(new Issue(e, Issue.Level.CRITICAL));
+ } finally {
+ if (this.driver != null) this.driver.close();
+ if (this.progressTicket != null) this.progressTicket.finish();
+ }
+
+ return !cancel;
+ }
+
+ /**
+ * Do import by specifying node & edge types.
+ */
+ private void doImportByNodeAndEdgeTypes(List labels, List relationshipTypes) {
+ Value parameters = parameters("labels", labels, "relationshipTypes", relationshipTypes);
+
+ // Import nodes
+ if (this.progressTicket != null) this.progressTicket.progress("Importing nodes...");
+ long nbNodesImported = Flowable.using(
+ () -> this.driver.rxSession(this.DBName != null ? SessionConfig.forDatabase(this.DBName) : SessionConfig.defaultConfig()),
+ session -> session.readTransaction(tx -> {
+ RxResult result = tx.run(QUERY_BY_LABELS, parameters);
+ return Flowable.fromPublisher(result.records())
+ .map(record -> record.get("n").asNode())
+ .map(this::mergeNodeInGephi)
+ .filter(Boolean::booleanValue)
+ .count()
+ .toFlowable();
+ }),
+ session -> Observable.fromPublisher(session.close()).subscribe()
+ ).blockingSingle();
+ this.getReport().log(String.format("%s nodes imported", nbNodesImported));
+
+ // Import edges
+ if (this.progressTicket != null) this.progressTicket.progress("Importing edges...");
+ long nbEdgesImported = Flowable.using(
+ () -> this.driver.rxSession(this.DBName != null ? SessionConfig.forDatabase(this.DBName) : SessionConfig.defaultConfig()),
+ session -> session.readTransaction(tx -> {
+ RxResult result = tx.run(QUERY_BY_RELS, parameters);
+ return Flowable.fromPublisher(result.records())
+ .map(record -> record.get("r").asRelationship())
+ .map(this::mergeEdgeInGephi)
+ .filter(Boolean::booleanValue)
+ .count()
+ .toFlowable();
+ }),
+ session -> Observable.fromPublisher(session.close()).subscribe()
+ ).blockingSingle();
+ this.getReport().log(String.format("%s edges imported", nbEdgesImported));
+
+ }
+
+ /**
+ * Do import by specifying node & edge query.
+ */
+ private void doImportByNodeAndEdgeQueries() {
+ // Import nodes
+ if (this.progressTicket != null) this.progressTicket.progress("Importing nodes...");
+ long nbNodesImported = Flowable.using(
+ () -> this.driver.rxSession(this.DBName != null ? SessionConfig.forDatabase(this.DBName) : SessionConfig.defaultConfig()),
+ session -> session.readTransaction(tx -> {
+ RxResult result = tx.run(this.nodeQuery);
+ return Flowable.fromPublisher(result.records())
+ .map(record -> {
+ if (record.get("id").isNull()) {
+ this.getReport().logIssue(new Issue(new Exception(String.format("Node result %s has no ID", record.toString())), Issue.Level.SEVERE));
+ return false;
+ }
+ return this.mergeNodeInGephi(
+ record.get("id").toString(),
+ record.containsKey("labels") ? record.get("labels").asList(Value::toString).toArray(new String[0]) : null,
+ record.fields().stream().filter(t -> !Arrays.asList("id", "labels").contains(t.key())).collect(Collectors.toMap(Pair::key, Pair::value))
+ );
+ })
+ .filter(Boolean::booleanValue)
+ .count()
+ .toFlowable();
+ }),
+ session -> Observable.fromPublisher(session.close()).subscribe()
+ ).blockingSingle();
+ this.getReport().log(String.format("%s nodes imported", nbNodesImported));
+
+ // Import edges
+ if (this.progressTicket != null) this.progressTicket.progress("Importing edges...");
+ long nbEdgesImported = Flowable.using(
+ () -> this.driver.rxSession(this.DBName != null ? SessionConfig.forDatabase(this.DBName) : SessionConfig.defaultConfig()),
+ session -> session.readTransaction(tx -> {
+ RxResult result = tx.run(this.edgeQuery);
+ return Flowable.fromPublisher(result.records())
+ .map(record -> {
+ if (record.get("id").isNull()) {
+ this.getReport().logIssue(new Issue(new Exception(String.format("Edge result %s has no ID", record.toString())), Issue.Level.SEVERE));
+ return false;
+ }
+ if (record.get("sourceId").isNull()) {
+ this.getReport().logIssue(new Issue(new Exception(String.format("Edge %s has no sourceId", record.get("id").asString())), Issue.Level.SEVERE));
+ return false;
+ }
+ if (record.get("targetId").isNull()) {
+ this.getReport().logIssue(new Issue(new Exception(String.format("Edge %s has no targetId", record.get("id").asString())), Issue.Level.SEVERE));
+ return false;
+ }
+ return this.mergeEdgeInGephi(
+ record.get("id").toString(),
+ record.containsKey("type") ? record.get("type").asString() : null,
+ record.get("sourceId").toString(),
+ record.get("targetId").toString(),
+ record.fields().stream().filter(t -> !Arrays.asList("id", "type", "sourceId", "targetId").contains(t.key())).collect(Collectors.toMap(Pair::key, Pair::value))
+ );
+ })
+ .filter(Boolean::booleanValue)
+ .count()
+ .toFlowable();
+ }),
+ session -> Observable.fromPublisher(session.close()).subscribe()
+ ).blockingSingle();
+ this.getReport().log(String.format("%s edges imported", nbEdgesImported));
+ }
+
+ /**
+ * Get the list of labels from Neo4j.
+ */
+ private List getDbLabels() {
+ try (Session session = this.driver.session(this.DBName != null ? SessionConfig.forDatabase(this.DBName) : SessionConfig.defaultConfig())) {
+ return session.readTransaction(tx -> {
+ List labels = new ArrayList<>();
+ Result rs = tx.run("CALL db.labels()");
+ while (rs.hasNext()) {
+ labels.add(rs.next().get(0).asString());
+ }
+ return labels;
+ });
+ }
+ }
+
+ /**
+ * Get the list of relationship types from Neo4j.
+ */
+ private List getDbRelationshipTypes() {
+ try (Session session = this.driver.session(this.DBName != null ? SessionConfig.forDatabase(this.DBName) : SessionConfig.defaultConfig())) {
+ return session.readTransaction(tx -> {
+ List labels = new ArrayList<>();
+ Result rs = tx.run("CALL db.relationshipTypes()");
+ while (rs.hasNext()) {
+ labels.add(rs.next().get(0).asString());
+ }
+ return labels;
+ });
+ }
+ }
+
+
+ /**
+ * Merge a Neo4j node in Gephi container.
+ */
+ private Boolean mergeNodeInGephi(Node neo4jNode) {
+ return this.mergeNodeInGephi(
+ String.valueOf(neo4jNode.id()),
+ StreamSupport.stream(neo4jNode.labels().spliterator(), false).toArray(String[]::new),
+ neo4jNode.asMap(value -> value));
+ }
+
+ /**
+ * Merge a Node in Gephi container.
+ *
+ * @param id Neo4j node's ID
+ * @param labels Neo4j node's labels
+ * @param attributes Neo4j node's attributes
+ */
+ private Boolean mergeNodeInGephi(String id, String[] labels, Map attributes) {
+ if (this.getContainer().nodeExists(id)) return false;
+
+ NodeDraft draft = this.getContainer().factory().newNodeDraft(id);
+ String mainLabel = (labels != null && labels.length > 0) ? labels[0] : "";
+
+ // Setting gephi label
+ if (attributes.containsKey("name")) {
+ draft.setLabel(attributes.get("name").toString());
+ } else if (attributes.containsKey("id")) {
+ draft.setLabel(attributes.get("id").toString());
+ } else if (attributes.containsKey("title")) {
+ draft.setLabel(attributes.get("title").toString());
+ } else {
+ draft.setLabel(id);
+ }
+ // Setting node label
+ if (labels != null) draft.setValue("labels", labels);
+ // Setting attributes
+ this.addNeo4jAttributes(draft, attributes, mainLabel);
+
+ // Add node to gephi
+ this.getContainer().addNode(draft);
+ return true;
+ }
+
+ /**
+ * Merge a Neo4j relationship in Gephi container.
+ */
+ private Boolean mergeEdgeInGephi(Relationship neo4jRel) {
+ return this.mergeEdgeInGephi(
+ String.valueOf(neo4jRel.id()),
+ neo4jRel.type(),
+ String.valueOf(neo4jRel.startNodeId()),
+ String.valueOf(neo4jRel.endNodeId()),
+ neo4jRel.asMap(value -> value));
+ }
+
+ /**
+ * Merge a relationship in Gephi container.
+ *
+ * @param id Neo4j rel's ID
+ * @param type Neo4j rel's type
+ * @param attributes Neo4j rel's attributes
+ */
+ private Boolean mergeEdgeInGephi(String id, String type, String sourceId, String
+ targetId, Map attributes) {
+
+ // Do some check to validate that the edge can be imported
+ if (this.getContainer().edgeExists(id)) return false;
+ if (!this.getContainer().nodeExists(sourceId) || !this.getContainer().nodeExists(targetId)) {
+ this.report.log(String.format("Edge %s has been skipped due to missing extremity", id));
+ return false;
+ }
+
+ EdgeDraft draft = this.getContainer().factory().newEdgeDraft(id);
+ draft.setLabel(type);
+ draft.setType(type);
+ draft.setSource(this.getContainer().getNode(sourceId));
+ draft.setTarget(this.getContainer().getNode(targetId));
+
+ // Setting gephi Weight if possible
+ try {
+ if (attributes.get("Weight") != null) {
+ draft.setWeight(attributes.get("Weight").asDouble());
+ } else if (attributes.get("weight") != null) {
+ draft.setWeight(attributes.get("weight").asDouble());
+ } else if (attributes.get("score") != null) {
+ draft.setWeight(attributes.get("score").asDouble());
+ }
+ } catch (Exception e) {
+ // nothing to do
+ }
+ this.addNeo4jAttributes(draft, attributes, type);
+
+ // Add edge to Gephi
+ this.getContainer().addEdge(draft);
+ return true;
+ }
+
+ private void addNeo4jAttributes(ElementDraft draft, Map attributes, String attributePrefix) {
+ attributes.keySet().forEach(key -> {
+ Value value = attributes.get(key);
+ String gephiColKey = attributePrefix != null ? attributePrefix + "_" + key : key;
+ try {
+ Object gephiValue = this.neo4jValueToGephi(value);
+ if (gephiValue != null) {
+ draft.setValue(gephiColKey, gephiValue);
+ }
+ } catch (Exception e) {
+ this.getReport().log(String.format("Property %s on %s %s has been skipped: %s", key, draft instanceof NodeDraft ? "node" : "edge", draft.getId(), e.getMessage()));
+ }
+ });
+ }
+
+ private Object neo4jValueToGephi(Value value) {
+ Object result = null;
+ switch (value.type().name()) {
+ case "ANY":
+ throw new NotImplementedException("Any value is not implemented");
+ case "BOOLEAN":
+ result = value.asBoolean();
+ break;
+ case "BYTES":
+ throw new NotImplementedException("Bytes value is not implemented");
+ case "DATE_TIME":
+ throw new NotImplementedException("DateTime value is not implemented");
+ case "DATE":
+ throw new NotImplementedException("Date value is not implemented");
+ case "DURATION":
+ throw new NotImplementedException("Duration value is not implemented");
+ case "FLOAT":
+ result = value.asFloat();
+ break;
+ case "INTEGER":
+ result = value.asInt();
+ break;
+ case "LIST":
+ case "LIST OF ANY?":
+ if (value.asList().size() > 0) {
+ result = toArray(value.asList(this::neo4jValueToGephi));
+ }
+
+ break;
+ case "LOCAL_DATE_TIME":
+ throw new NotImplementedException("LocalDateTime value is not implemented");
+ case "LOCAL_TIME":
+ throw new NotImplementedException("LocalTime value is not implemented");
+ case "MAP":
+ throw new NotImplementedException("Map value is not implemented");
+ case "NODE":
+ throw new NotImplementedException("Node value is not implemented");
+ case "NULL":
+ result = null;
+ break;
+ case "NUMBER":
+ result = value.asNumber();
+ break;
+ case "ObjectValueAdapter":
+ throw new NotImplementedException("Object value is not implemented");
+ case "PATH":
+ throw new NotImplementedException("Path value is not implemented");
+ case "POINT":
+ throw new NotImplementedException("Point value is not implemented");
+ case "RELATIONSHIP":
+ throw new NotImplementedException("Relationship value is not implemented");
+ case "STRING":
+ result = value.asString();
+ break;
+ case "TIME":
+ throw new NotImplementedException("Time value is not implemented");
+ }
+ return result;
+ }
+
+ /**
+ * Create an
+ *
+ * @param list
+ * @param
+ * @return
+ */
+ private T[] toArray(List list) {
+ Class clazz = list.get(0).getClass(); // check for size and null before
+ T[] array = (T[]) java.lang.reflect.Array.newInstance(clazz, list.size());
+ return list.toArray(array);
+ }
+
+ @Override
+ public void setProgressTicket(ProgressTicket progressTicket) {
+ this.progressTicket = progressTicket;
+ }
+
+ @Override
+ public ContainerLoader getContainer() {
+ return container;
+ }
+
+ @Override
+ public Report getReport() {
+ return report;
+ }
+
+ @Override
+ public boolean cancel() {
+ cancel = true;
+ return cancel;
+ }
+
+ //
+ // Generated setter for import parameters
+ //
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public void setPasswd(String passwd) {
+ this.passwd = passwd;
+ }
+
+ public void setDBName(String DBName) {
+ this.DBName = DBName;
+ }
+
+ public void setLabels(List labels) {
+ this.labels = labels;
+ }
+
+ public void setRelationshipTypes(List relationshipTypes) {
+ this.relationshipTypes = relationshipTypes;
+ }
+
+ public void setNodeQuery(String nodeQuery) {
+ this.nodeQuery = nodeQuery;
+ }
+
+ public void setEdgeQuery(String edgeQuery) {
+ this.edgeQuery = edgeQuery;
+ }
+
+}
diff --git a/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/Neo4jDatabaseImpoterBuilder.java b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/Neo4jDatabaseImpoterBuilder.java
new file mode 100644
index 0000000000..8d2f0bae9c
--- /dev/null
+++ b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/Neo4jDatabaseImpoterBuilder.java
@@ -0,0 +1,19 @@
+package org.gephi.plugins.neo4j.importer;
+
+import org.gephi.io.importer.spi.WizardImporter;
+import org.gephi.io.importer.spi.WizardImporterBuilder;
+import org.openide.util.lookup.ServiceProvider;
+
+@ServiceProvider(service = WizardImporterBuilder.class)
+public class Neo4jDatabaseImpoterBuilder implements WizardImporterBuilder {
+
+ @Override
+ public WizardImporter buildImporter() {
+ return new Neo4jDatabaseImporter();
+ }
+
+ @Override
+ public String getName() {
+ return "Neo4j";
+ }
+}
diff --git a/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterConnectionPanel.form b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterConnectionPanel.form
new file mode 100644
index 0000000000..b013b4b6dd
--- /dev/null
+++ b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterConnectionPanel.form
@@ -0,0 +1,1106 @@
+
+
+
diff --git a/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterConnectionPanel.java b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterConnectionPanel.java
new file mode 100644
index 0000000000..54c2a444c7
--- /dev/null
+++ b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterConnectionPanel.java
@@ -0,0 +1,246 @@
+package org.gephi.plugins.neo4j.importer.wizard;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class Neo4jDatabaseImporterConnectionPanel extends javax.swing.JPanel {
+
+ /**
+ * Creates new form Neo4jDatabaseImporterConnectionPanel
+ */
+ public Neo4jDatabaseImporterConnectionPanel() {
+ initComponents();
+ this.actionText.setVisible(false);
+ }
+
+ @Override
+ public String getName() {
+ return "Neo4j connection"; //this will be the title of the panel in the wizard
+ }
+
+ /**
+ * Check that the form is valid.
+ */
+ public void checkValidity() throws Exception {
+ // Check inputs
+ if (Utils.isEmptyOrNull(dbUrl.getText())) throw new Exception("Url field is mandatory");
+ if (this.dbAuthType.getSelectedIndex() == 0 && (Utils.isEmptyOrNull(dbUsername.getText()) || Utils.isEmptyOrNull(new String(dbPassword.getPassword()))))
+ throw new Exception("Username and password are mandatory");
+
+ // Check connection
+ if (this.dbAuthType.getSelectedIndex() == 0)
+ Utils.neo4jCheckConnection(dbUrl.getText(), dbUsername.getText(), new String(dbPassword.getPassword()), dbName.getText());
+ else
+ Utils.neo4jCheckConnection(dbUrl.getText(), null, null, dbName.getText());
+
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+ java.awt.GridBagConstraints gridBagConstraints;
+
+ dbUrlLabel = new javax.swing.JLabel();
+ dbUrl = new javax.swing.JTextField();
+ dbUsernameLabel = new javax.swing.JLabel();
+ dbUsername = new javax.swing.JTextField();
+ dbPasswordLabel = new javax.swing.JLabel();
+ dbPassword = new javax.swing.JPasswordField();
+ checkBtn = new javax.swing.JButton();
+ dbNameLabel = new javax.swing.JLabel();
+ dbName = new javax.swing.JTextField();
+ dbAuthTypeLabel = new javax.swing.JLabel();
+ dbAuthType = new javax.swing.JComboBox<>();
+ actionTextScroll = new javax.swing.JScrollPane();
+ actionText = new javax.swing.JTextArea();
+
+ java.awt.GridBagLayout layout = new java.awt.GridBagLayout();
+ layout.columnWidths = new int[] {0, 5, 0, 5, 0, 5, 0};
+ layout.rowHeights = new int[] {0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0};
+ setLayout(layout);
+
+ dbUrlLabel.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterConnectionPanel.class, "Neo4jDatabaseImporterConnectionPanel.dbUrlLabel.text_1_1")); // NOI18N
+ dbUrlLabel.setName("dbUrlLabel"); // NOI18N
+ dbUrlLabel.setPreferredSize(new java.awt.Dimension(100, 25));
+ dbUrlLabel.setRequestFocusEnabled(false);
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 4;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ add(dbUrlLabel, gridBagConstraints);
+
+ dbUrl.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterConnectionPanel.class, "Neo4jDatabaseImporterConnectionPanel.dbUrl.text_1_1")); // NOI18N
+ dbUrl.setName("dbUrl"); // NOI18N
+ dbUrl.setPreferredSize(new java.awt.Dimension(300, 25));
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 2;
+ gridBagConstraints.gridy = 4;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ add(dbUrl, gridBagConstraints);
+
+ dbUsernameLabel.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterConnectionPanel.class, "Neo4jDatabaseImporterConnectionPanel.dbUsernameLabel.text_1_1")); // NOI18N
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 10;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ add(dbUsernameLabel, gridBagConstraints);
+
+ dbUsername.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterConnectionPanel.class, "Neo4jDatabaseImporterConnectionPanel.dbUsername.text_1_1")); // NOI18N
+ dbUsername.setName("dbUsername"); // NOI18N
+ dbUsername.setPreferredSize(new java.awt.Dimension(10, 25));
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 2;
+ gridBagConstraints.gridy = 10;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ add(dbUsername, gridBagConstraints);
+
+ dbPasswordLabel.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterConnectionPanel.class, "Neo4jDatabaseImporterConnectionPanel.dbPasswordLabel.text_1_1")); // NOI18N
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 12;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ add(dbPasswordLabel, gridBagConstraints);
+
+ dbPassword.setName("dbPassword"); // NOI18N
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 2;
+ gridBagConstraints.gridy = 12;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ add(dbPassword, gridBagConstraints);
+
+ checkBtn.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterConnectionPanel.class, "Neo4jDatabaseImporterConnectionPanel.checkBtn.text_1")); // NOI18N
+ checkBtn.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ checkBtnActionPerformed(evt);
+ }
+ });
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 2;
+ gridBagConstraints.gridy = 16;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_END;
+ add(checkBtn, gridBagConstraints);
+
+ dbNameLabel.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterConnectionPanel.class, "Neo4jDatabaseImporterConnectionPanel.dbNameLabel.text_1_1")); // NOI18N
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 6;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
+ add(dbNameLabel, gridBagConstraints);
+
+ dbName.setToolTipText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterConnectionPanel.class, "Neo4jDatabaseImporterConnectionPanel.dbName.toolTipText_1")); // NOI18N
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 2;
+ gridBagConstraints.gridy = 6;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ add(dbName, gridBagConstraints);
+
+ dbAuthTypeLabel.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterConnectionPanel.class, "Neo4jDatabaseImporterConnectionPanel.dbAuthTypeLabel.text_1_1")); // NOI18N
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 8;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
+ add(dbAuthTypeLabel, gridBagConstraints);
+
+ dbAuthType.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Username / Password", "No authentication" }));
+ dbAuthType.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ dbAuthTypeActionPerformed(evt);
+ }
+ });
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 2;
+ gridBagConstraints.gridy = 8;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ add(dbAuthType, gridBagConstraints);
+
+ actionTextScroll.setBorder(null);
+
+ actionText.setEditable(false);
+ actionText.setColumns(20);
+ actionText.setLineWrap(true);
+ actionText.setRows(2);
+ actionText.setBorder(null);
+ actionText.setFocusable(false);
+ actionTextScroll.setViewportView(actionText);
+
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 0;
+ gridBagConstraints.gridwidth = 3;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ add(actionTextScroll, gridBagConstraints);
+ }// //GEN-END:initComponents
+
+ private void checkBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkBtnActionPerformed
+ this.checkBtn.setEnabled(false);
+ this.dbUrl.setEditable(false);
+ this.dbUsername.setEditable(false);
+ this.dbPassword.setEnabled(false);
+ this.dbName.setEditable(false);
+ this.actionText.setVisible(false);
+ try {
+ this.checkValidity();
+ this.actionText.setText("Connection successful");
+ this.actionText.setForeground(Color.GREEN);
+ } catch (Exception e) {
+ this.actionText.setText(e.getMessage());
+ this.actionText.setForeground(Color.RED);
+ } finally {
+ this.checkBtn.setEnabled(true);
+ this.dbUrl.setEditable(true);
+ this.dbUsername.setEditable(true);
+ this.dbPassword.setEnabled(true);
+ this.dbName.setEditable(true);
+ this.actionText.setVisible(true);
+ }
+ }//GEN-LAST:event_checkBtnActionPerformed
+
+ private void dbAuthTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dbAuthTypeActionPerformed
+ this.dbUsername.setVisible(this.dbAuthType.getSelectedIndex() == 0);
+ this.dbUsernameLabel.setVisible(this.dbAuthType.getSelectedIndex() == 0);
+ this.dbPassword.setVisible(this.dbAuthType.getSelectedIndex() == 0);
+ this.dbPasswordLabel.setVisible(this.dbAuthType.getSelectedIndex() == 0);
+ }//GEN-LAST:event_dbAuthTypeActionPerformed
+
+
+ public JComboBox getDbAuthType() {
+ return dbAuthType;
+ }
+
+ public JTextField getDbName() {
+ return dbName;
+ }
+
+ public JPasswordField getDbPassword() {
+ return dbPassword;
+ }
+
+ public JTextField getDbUrl() {
+ return dbUrl;
+ }
+
+ public JTextField getDbUsername() {
+ return dbUsername;
+ }
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JTextArea actionText;
+ private javax.swing.JScrollPane actionTextScroll;
+ private javax.swing.JButton checkBtn;
+ private javax.swing.JComboBox dbAuthType;
+ private javax.swing.JLabel dbAuthTypeLabel;
+ private javax.swing.JTextField dbName;
+ private javax.swing.JLabel dbNameLabel;
+ private javax.swing.JPasswordField dbPassword;
+ private javax.swing.JLabel dbPasswordLabel;
+ private javax.swing.JTextField dbUrl;
+ private javax.swing.JLabel dbUrlLabel;
+ private javax.swing.JTextField dbUsername;
+ private javax.swing.JLabel dbUsernameLabel;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterConnectionWizard.java b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterConnectionWizard.java
new file mode 100644
index 0000000000..c8043b2730
--- /dev/null
+++ b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterConnectionWizard.java
@@ -0,0 +1,64 @@
+package org.gephi.plugins.neo4j.importer.wizard;
+
+import org.openide.WizardDescriptor;
+import org.openide.WizardValidationException;
+import org.openide.util.HelpCtx;
+
+import javax.swing.event.ChangeListener;
+import java.awt.*;
+
+public class Neo4jDatabaseImporterConnectionWizard implements WizardDescriptor.ValidatingPanel {
+
+ private final Neo4jDatabaseImporterConnectionPanel component = new Neo4jDatabaseImporterConnectionPanel();
+
+ @Override
+ public Component getComponent() {
+ return this.component;
+ }
+
+ @Override
+ public HelpCtx getHelp() {
+ return HelpCtx.DEFAULT_HELP;
+ }
+
+ @Override
+ public void readSettings(Object settings) {
+ this.component.getDbUrl().setText(Utils.isEmptyOrNull(Neo4jImporterWizardData.dbUrl) ? "neo4j://localhost" : Neo4jImporterWizardData.dbUrl);
+ this.component.getDbName().setText(Neo4jImporterWizardData.dbName != null ? Neo4jImporterWizardData.dbName : "");
+ this.component.getDbAuthType().setSelectedIndex(Neo4jImporterWizardData.dbAuthType != null ? Neo4jImporterWizardData.dbAuthType : 0);
+ this.component.getDbUsername().setText(Utils.isEmptyOrNull(Neo4jImporterWizardData.dbUsername) ? "neo4j" : Neo4jImporterWizardData.dbUsername);
+ this.component.getDbPassword().setText(Neo4jImporterWizardData.dbPassword != null ? Neo4jImporterWizardData.dbPassword : "");
+ }
+
+ @Override
+ public void storeSettings(Object settings) {
+ Neo4jImporterWizardData.dbUrl = this.component.getDbUrl().getText();
+ Neo4jImporterWizardData.dbName = Utils.isEmptyOrNull(this.component.getDbName().getText()) ? null : this.component.getDbName().getText();
+ Neo4jImporterWizardData.dbAuthType = this.component.getDbAuthType().getSelectedIndex();
+ Neo4jImporterWizardData.dbUsername = Utils.isEmptyOrNull(this.component.getDbUsername().getText()) ? "neo4j" : this.component.getDbUsername().getText();
+ Neo4jImporterWizardData.dbPassword = this.component.getDbPassword().getText();
+ }
+
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public void validate() throws WizardValidationException {
+ try {
+ this.component.checkValidity();
+ } catch (Exception e) {
+ throw new WizardValidationException(this.component, e.getMessage(), e.getLocalizedMessage());
+ }
+ }
+
+ @Override
+ public void addChangeListener(ChangeListener l) {
+ }
+
+ @Override
+ public void removeChangeListener(ChangeListener l) {
+ }
+}
diff --git a/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterImportPanel.form b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterImportPanel.form
new file mode 100644
index 0000000000..cd032526a3
--- /dev/null
+++ b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterImportPanel.form
@@ -0,0 +1,936 @@
+
+
+
diff --git a/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterImportPanel.java b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterImportPanel.java
new file mode 100644
index 0000000000..6bd0718c29
--- /dev/null
+++ b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterImportPanel.java
@@ -0,0 +1,395 @@
+package org.gephi.plugins.neo4j.importer.wizard;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Arrays;
+
+/**
+ * @author bsimard
+ */
+public class Neo4jDatabaseImporterImportPanel extends javax.swing.JPanel {
+
+ /**
+ * Creates new form Neo4jDatabaseImporterImportPanel
+ */
+ public Neo4jDatabaseImporterImportPanel() {
+ initComponents();
+ this.nodeActionText.setVisible(false);
+ this.edgeActionText.setVisible(false);
+ }
+
+ @Override
+ public String getName() {
+ return "Import configuration"; //this will be the title of the panel in the wizard
+ }
+
+ /**
+ * Check that the form is valid.
+ */
+ public void checkValidity() throws Exception {
+ if (this.menu.getSelectedIndex() == 1) {
+ // Check the node query
+ if (Utils.isEmptyOrNull(this.nodeQuery.getText())) throw new Exception("Node query is mandatory");
+ Utils.neo4jWizardCheckQuery(this.nodeQuery.getText(), Arrays.asList("id"));
+
+ // Check the edge query
+ if (Utils.isEmptyOrNull(this.edgeQuery.getText())) throw new Exception("Edge query is mandatory");
+ Utils.neo4jWizardCheckQuery(this.edgeQuery.getText(), Arrays.asList("id", "sourceId", "targetId"));
+ }
+ }
+
+ private void checkNodeQueryActionPerformed(java.awt.event.ActionEvent evt) {
+ this.nodeActionText.setVisible(false);
+ this.checkNodeQuery.setEnabled(false);
+ try{
+ if (Utils.isEmptyOrNull(this.nodeQuery.getText())) throw new Exception("Node query is empty");
+ Utils.neo4jWizardCheckQuery(this.nodeQuery.getText(), Arrays.asList("id"));
+ this.nodeActionText.setText("Query is valid");
+ this.nodeActionText.setForeground(Color.GREEN);
+ }
+ catch(Exception e) {
+ this.nodeActionText.setText(e.getMessage());
+ this.nodeActionText.setForeground(Color.RED);
+ }
+ finally {
+ this.checkNodeQuery.setEnabled(true);
+ this.nodeActionText.setVisible(true);
+ }
+ }
+
+ private void checkEdgeQueryActionPerformed(java.awt.event.ActionEvent evt) {
+ this.edgeActionText.setVisible(false);
+ this.checkEdgeQuery.setEnabled(false);
+ try{
+ if (Utils.isEmptyOrNull(this.edgeQuery.getText())) throw new Exception("Node query is empty");
+ Utils.neo4jWizardCheckQuery(this.edgeQuery.getText(), Arrays.asList("id", "sourceId", "targetId"));
+ this.edgeActionText.setText("Query is valid");
+ this.edgeActionText.setForeground(Color.GREEN);
+ }
+ catch(Exception e) {
+ this.edgeActionText.setText(e.getMessage());
+ this.edgeActionText.setForeground(Color.RED);
+ }
+ finally {
+ this.checkEdgeQuery.setEnabled(true);
+ this.edgeActionText.setVisible(true);
+ }
+ }
+
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+ java.awt.GridBagConstraints gridBagConstraints;
+
+ menu = new javax.swing.JTabbedPane();
+ typesPanel = new javax.swing.JPanel();
+ labelsScroll = new javax.swing.JScrollPane();
+ labels = new javax.swing.JList<>();
+ typesScroll = new javax.swing.JScrollPane();
+ types = new javax.swing.JList<>();
+ labelsLabel = new javax.swing.JLabel();
+ typesLabels = new javax.swing.JLabel();
+ help = new javax.swing.JLabel();
+ queriesPanel = new javax.swing.JPanel();
+ nodeQueryLabel = new javax.swing.JLabel();
+ nodeQueryScroll = new javax.swing.JScrollPane();
+ nodeQuery = new javax.swing.JTextArea();
+ edgeQueryLabel = new javax.swing.JLabel();
+ edgeQueryScroll = new javax.swing.JScrollPane();
+ edgeQuery = new javax.swing.JTextArea();
+ nodeActionTextScroll = new javax.swing.JScrollPane();
+ nodeActionText = new javax.swing.JTextArea();
+ checkNodeQuery = new javax.swing.JButton();
+ checkEdgeQuery = new javax.swing.JButton();
+ edgeActionTextScroll = new javax.swing.JScrollPane();
+ edgeActionText = new javax.swing.JTextArea();
+ nodeQueryHelp = new javax.swing.JLabel();
+ edgeQueryHelp = new javax.swing.JLabel();
+
+ setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 0, 0));
+
+ menu.setTabLayoutPolicy(javax.swing.JTabbedPane.SCROLL_TAB_LAYOUT);
+ menu.setMaximumSize(null);
+ menu.setMinimumSize(new java.awt.Dimension(500, 500));
+ menu.setPreferredSize(new java.awt.Dimension(500, 500));
+
+ typesPanel.setName("Labels & Relationship types"); // NOI18N
+ typesPanel.setOpaque(false);
+ typesPanel.setPreferredSize(new java.awt.Dimension(500, 500));
+ java.awt.GridBagLayout typesPanelLayout = new java.awt.GridBagLayout();
+ typesPanelLayout.columnWidths = new int[] {0, 5, 0, 5, 0};
+ typesPanelLayout.rowHeights = new int[] {0, 5, 0, 5, 0};
+ typesPanel.setLayout(typesPanelLayout);
+
+ labelsScroll.setMaximumSize(null);
+ labelsScroll.setMinimumSize(null);
+ labelsScroll.setPreferredSize(new java.awt.Dimension(200, 380));
+
+ labels.setModel(new javax.swing.AbstractListModel() {
+ String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
+ public int getSize() { return strings.length; }
+ public String getElementAt(int i) { return strings[i]; }
+ });
+ labels.setPreferredSize(new java.awt.Dimension(200, 450));
+ labelsScroll.setViewportView(labels);
+
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 4;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.LAST_LINE_START;
+ typesPanel.add(labelsScroll, gridBagConstraints);
+
+ typesScroll.setMaximumSize(null);
+ typesScroll.setMinimumSize(null);
+ typesScroll.setPreferredSize(new java.awt.Dimension(200, 380));
+
+ types.setModel(new javax.swing.AbstractListModel() {
+ String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
+ public int getSize() { return strings.length; }
+ public String getElementAt(int i) { return strings[i]; }
+ });
+ types.setPreferredSize(new java.awt.Dimension(200, 500));
+ typesScroll.setViewportView(types);
+
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 4;
+ gridBagConstraints.gridy = 4;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.LAST_LINE_END;
+ typesPanel.add(typesScroll, gridBagConstraints);
+
+ labelsLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
+ labelsLabel.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterImportPanel.class, "Neo4jDatabaseImporterImportPanel.labelsLabel.text_1")); // NOI18N
+ labelsLabel.setMaximumSize(null);
+ labelsLabel.setMinimumSize(new java.awt.Dimension(200, 24));
+ labelsLabel.setPreferredSize(new java.awt.Dimension(245, 24));
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 2;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
+ typesPanel.add(labelsLabel, gridBagConstraints);
+
+ typesLabels.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
+ typesLabels.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterImportPanel.class, "Neo4jDatabaseImporterImportPanel.typesLabels.text_1")); // NOI18N
+ typesLabels.setMinimumSize(new java.awt.Dimension(200, 24));
+ typesLabels.setPreferredSize(new java.awt.Dimension(245, 24));
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 4;
+ gridBagConstraints.gridy = 2;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_END;
+ typesPanel.add(typesLabels, gridBagConstraints);
+
+ help.setFont(new java.awt.Font("sansserif", 2, 13)); // NOI18N
+ help.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
+ help.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterImportPanel.class, "Neo4jDatabaseImporterImportPanel.help.text_1")); // NOI18N
+ help.setPreferredSize(new java.awt.Dimension(500, 48));
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 0;
+ gridBagConstraints.gridwidth = 5;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+ typesPanel.add(help, gridBagConstraints);
+
+ menu.addTab(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterImportPanel.class, "Neo4jDatabaseImporterImportPanel.Labels & Relationship types.TabConstraints.tabTitle"), typesPanel); // NOI18N
+
+ queriesPanel.setName("Nodes & Edges queries"); // NOI18N
+ queriesPanel.setPreferredSize(new java.awt.Dimension(500, 500));
+ queriesPanel.setLayout(new java.awt.GridBagLayout());
+
+ nodeQueryLabel.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterImportPanel.class, "Neo4jDatabaseImporterImportPanel.nodesQueryLabel.text")); // NOI18N
+ nodeQueryLabel.setToolTipText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterImportPanel.class, "Neo4jDatabaseImporterImportPanel.nodeQueryLabel.toolTipText_1")); // NOI18N
+ nodeQueryLabel.setName("nodesQueryLabel"); // NOI18N
+ nodeQueryLabel.setPreferredSize(new java.awt.Dimension(500, 24));
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 0;
+ gridBagConstraints.gridwidth = 2;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ queriesPanel.add(nodeQueryLabel, gridBagConstraints);
+
+ nodeQueryScroll.setPreferredSize(new java.awt.Dimension(400, 120));
+
+ nodeQuery.setColumns(20);
+ nodeQuery.setRows(5);
+ nodeQuery.setPreferredSize(null);
+ nodeQueryScroll.setViewportView(nodeQuery);
+
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 2;
+ gridBagConstraints.gridwidth = 2;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+ queriesPanel.add(nodeQueryScroll, gridBagConstraints);
+
+ edgeQueryLabel.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterImportPanel.class, "Neo4jDatabaseImporterImportPanel.edgeQueryLabel.text_1")); // NOI18N
+ edgeQueryLabel.setPreferredSize(new java.awt.Dimension(500, 24));
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 5;
+ gridBagConstraints.gridwidth = 2;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_END;
+ queriesPanel.add(edgeQueryLabel, gridBagConstraints);
+
+ edgeQueryScroll.setPreferredSize(new java.awt.Dimension(400, 120));
+
+ edgeQuery.setColumns(20);
+ edgeQuery.setRows(5);
+ edgeQueryScroll.setViewportView(edgeQuery);
+
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 7;
+ gridBagConstraints.gridwidth = 2;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+ queriesPanel.add(edgeQueryScroll, gridBagConstraints);
+
+ nodeActionTextScroll.setBorder(null);
+ nodeActionTextScroll.setPreferredSize(new java.awt.Dimension(390, 60));
+
+ nodeActionText.setEditable(false);
+ nodeActionText.setColumns(20);
+ nodeActionText.setLineWrap(true);
+ nodeActionText.setRows(2);
+ nodeActionText.setBorder(null);
+ nodeActionText.setMinimumSize(new java.awt.Dimension(200, 48));
+ nodeActionText.setPreferredSize(new java.awt.Dimension(400, 60));
+ nodeActionTextScroll.setViewportView(nodeActionText);
+
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 3;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ queriesPanel.add(nodeActionTextScroll, gridBagConstraints);
+
+ checkNodeQuery.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterImportPanel.class, "Neo4jDatabaseImporterImportPanel.checkNodeQuery.text_1")); // NOI18N
+ checkNodeQuery.setActionCommand(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterImportPanel.class, "Neo4jDatabaseImporterImportPanel.checkNodeQuery.actionCommand_1")); // NOI18N
+ checkNodeQuery.setPreferredSize(new java.awt.Dimension(110, 25));
+ checkNodeQuery.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ checkNodeQueryActionPerformed(evt);
+ }
+ });
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.gridy = 3;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
+ queriesPanel.add(checkNodeQuery, gridBagConstraints);
+
+ checkEdgeQuery.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterImportPanel.class, "Neo4jDatabaseImporterImportPanel.checkEdgeQuery.text_1")); // NOI18N
+ checkEdgeQuery.setPreferredSize(new java.awt.Dimension(110, 25));
+ checkEdgeQuery.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ checkEdgeQueryActionPerformed(evt);
+ }
+ });
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.gridy = 8;
+ gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
+ queriesPanel.add(checkEdgeQuery, gridBagConstraints);
+
+ edgeActionTextScroll.setBorder(null);
+ edgeActionTextScroll.setPreferredSize(new java.awt.Dimension(390, 60));
+
+ edgeActionText.setEditable(false);
+ edgeActionText.setColumns(20);
+ edgeActionText.setLineWrap(true);
+ edgeActionText.setRows(2);
+ edgeActionText.setBorder(null);
+ edgeActionText.setPreferredSize(null);
+ edgeActionTextScroll.setViewportView(edgeActionText);
+
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 8;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+ queriesPanel.add(edgeActionTextScroll, gridBagConstraints);
+
+ nodeQueryHelp.setFont(new java.awt.Font("sansserif", 2, 13)); // NOI18N
+ nodeQueryHelp.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterImportPanel.class, "Neo4jDatabaseImporterImportPanel.nodeQueryHelp.text_1")); // NOI18N
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 1;
+ gridBagConstraints.gridwidth = 2;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+ queriesPanel.add(nodeQueryHelp, gridBagConstraints);
+
+ edgeQueryHelp.setFont(new java.awt.Font("sansserif", 2, 13)); // NOI18N
+ edgeQueryHelp.setText(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterImportPanel.class, "Neo4jDatabaseImporterImportPanel.edgeQueryHelp.text_1")); // NOI18N
+ gridBagConstraints = new java.awt.GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = 6;
+ gridBagConstraints.gridwidth = 2;
+ gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+ queriesPanel.add(edgeQueryHelp, gridBagConstraints);
+
+ menu.addTab(org.openide.util.NbBundle.getMessage(Neo4jDatabaseImporterImportPanel.class, "Neo4jDatabaseImporterImportPanel.Nodes & Edges queries.TabConstraints.tabTitle"), queriesPanel); // NOI18N
+
+ menu.setSelectedComponent(typesPanel);
+
+ add(menu);
+ }// //GEN-END:initComponents
+
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ javax.swing.JButton checkEdgeQuery;
+ javax.swing.JButton checkNodeQuery;
+ javax.swing.JTextArea edgeActionText;
+ javax.swing.JScrollPane edgeActionTextScroll;
+ javax.swing.JTextArea edgeQuery;
+ javax.swing.JLabel edgeQueryHelp;
+ javax.swing.JLabel edgeQueryLabel;
+ javax.swing.JScrollPane edgeQueryScroll;
+ javax.swing.JLabel help;
+ private javax.swing.JList labels;
+ javax.swing.JLabel labelsLabel;
+ javax.swing.JScrollPane labelsScroll;
+ javax.swing.JTabbedPane menu;
+ javax.swing.JTextArea nodeActionText;
+ javax.swing.JScrollPane nodeActionTextScroll;
+ javax.swing.JTextArea nodeQuery;
+ javax.swing.JLabel nodeQueryHelp;
+ javax.swing.JLabel nodeQueryLabel;
+ javax.swing.JScrollPane nodeQueryScroll;
+ javax.swing.JPanel queriesPanel;
+ private javax.swing.JList types;
+ javax.swing.JLabel typesLabels;
+ javax.swing.JPanel typesPanel;
+ javax.swing.JScrollPane typesScroll;
+ // End of variables declaration//GEN-END:variables
+
+
+ public JButton getCheckEdgeQuery() {
+ return checkEdgeQuery;
+ }
+
+ public JTextArea getEdgeQuery() {
+ return edgeQuery;
+ }
+
+ public JList getLabels() {
+ return labels;
+ }
+
+ public JTabbedPane getMenu() {
+ return menu;
+ }
+
+ public JTextArea getNodeQuery() {
+ return nodeQuery;
+ }
+
+ public JList getTypes() {
+ return types;
+ }
+}
diff --git a/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterImportWizard.java b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterImportWizard.java
new file mode 100644
index 0000000000..cfd0def388
--- /dev/null
+++ b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterImportWizard.java
@@ -0,0 +1,66 @@
+package org.gephi.plugins.neo4j.importer.wizard;
+
+import org.openide.WizardDescriptor;
+import org.openide.WizardValidationException;
+import org.openide.util.HelpCtx;
+
+import javax.swing.event.ChangeListener;
+import java.awt.*;
+
+public class Neo4jDatabaseImporterImportWizard implements WizardDescriptor.ValidatingPanel {
+
+ private final Neo4jDatabaseImporterImportPanel component = new Neo4jDatabaseImporterImportPanel();
+
+
+ @Override
+ public Component getComponent() {
+ return this.component;
+ }
+
+ @Override
+ public HelpCtx getHelp() {
+ return HelpCtx.DEFAULT_HELP;
+ }
+
+ @Override
+ public void readSettings(Object settings) {
+ this.component.getLabels().setListData(Utils.neo4jWizardGetLabels().toArray(new String[0]));
+ this.component.getTypes().setListData(Utils.neo4jWizardGetRelationshipTypes().toArray(new String[0]));
+ this.component.getMenu().setSelectedIndex(Neo4jImporterWizardData.importMode != null ? Neo4jImporterWizardData.importMode : 0);
+ this.component.getNodeQuery().setText(Utils.isEmptyOrNull(Neo4jImporterWizardData.nodeQuery) ? "MATCH (n) RETURN id(n) AS id, labels(n) AS labels" : Neo4jImporterWizardData.nodeQuery);
+ this.component.getEdgeQuery().setText(Utils.isEmptyOrNull(Neo4jImporterWizardData.edgeQuery) ? "MATCH (n)-[r]->(m) RETURN id(r) AS id, type(r) AS type, id(n) AS sourceId, id(m) AS targetId" : Neo4jImporterWizardData.edgeQuery);
+ }
+
+ @Override
+ public void storeSettings(Object settings) {
+ Neo4jImporterWizardData.importMode = this.component.getMenu().getSelectedIndex();
+ Neo4jImporterWizardData.labels = this.component.getLabels().getSelectedValuesList();
+ Neo4jImporterWizardData.relationshipTypes = this.component.getTypes().getSelectedValuesList();
+ Neo4jImporterWizardData.nodeQuery = this.component.getNodeQuery().getText();
+ Neo4jImporterWizardData.edgeQuery = this.component.getEdgeQuery().getText();
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public void validate() throws WizardValidationException {
+ try {
+ this.component.checkValidity();
+ } catch (Exception e) {
+ throw new WizardValidationException(this.component, e.getMessage(), e.getLocalizedMessage());
+ }
+ }
+
+ @Override
+ public void addChangeListener(ChangeListener l) {
+
+ }
+
+ @Override
+ public void removeChangeListener(ChangeListener l) {
+
+ }
+}
diff --git a/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterWizard.java b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterWizard.java
new file mode 100644
index 0000000000..04f5e08652
--- /dev/null
+++ b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jDatabaseImporterWizard.java
@@ -0,0 +1,82 @@
+package org.gephi.plugins.neo4j.importer.wizard;
+
+import org.gephi.io.importer.spi.Importer;
+import org.gephi.io.importer.spi.ImporterWizardUI;
+import org.gephi.io.importer.spi.WizardImporter;
+import org.gephi.plugins.neo4j.importer.Neo4jDatabaseImporter;
+import org.openide.WizardDescriptor.Panel;
+import org.openide.util.lookup.ServiceProvider;
+
+@ServiceProvider(service = ImporterWizardUI.class)
+public class Neo4jDatabaseImporterWizard implements ImporterWizardUI {
+
+ private Panel[] panels = null;
+
+ @Override
+ public String getDisplayName() {
+ return "Neo4j - Import networks from a Neo4j database";
+ }
+
+ @Override
+ public String getCategory() {
+ return "import";
+ }
+
+ @Override
+ public String getDescription() {
+ return "";
+ }
+
+ @Override
+ public Panel[] getPanels() {
+ if (panels == null) {
+ panels = new Panel[2];
+ panels[0] = new Neo4jDatabaseImporterConnectionWizard();
+ panels[1] = new Neo4jDatabaseImporterImportWizard();
+ }
+ return panels;
+ }
+
+ @Override
+ public void setup(Panel panel) {
+ System.out.println(panel);
+
+ /*this.importer = new Neo4jDatabaseImporter();
+ i
+ */
+ }
+
+ @Override
+ public void unsetup(WizardImporter importer, Panel panel) {
+ this.panels = null;
+ if (this.isUIForImporter(importer)) {
+ Neo4jDatabaseImporter neoImporter = (Neo4jDatabaseImporter) importer;
+ neoImporter.setUrl(Neo4jImporterWizardData.dbUrl);
+ neoImporter.setDBName(Utils.isEmptyOrNull(Neo4jImporterWizardData.dbName) ? null : Neo4jImporterWizardData.dbName);
+ if (Neo4jImporterWizardData.dbAuthType == 0) {
+ neoImporter.setUsername(Neo4jImporterWizardData.dbUsername);
+ neoImporter.setPasswd(Neo4jImporterWizardData.dbPassword);
+ } else {
+ neoImporter.setUsername(null);
+ neoImporter.setPasswd(null);
+ }
+
+ if (Neo4jImporterWizardData.importMode == 0) {
+ neoImporter.setLabels(Neo4jImporterWizardData.labels.size() == 0 ? null : Neo4jImporterWizardData.labels);
+ neoImporter.setRelationshipTypes(Neo4jImporterWizardData.relationshipTypes.size() == 0 ? null : Neo4jImporterWizardData.relationshipTypes);
+ neoImporter.setNodeQuery(null);
+ neoImporter.setEdgeQuery(null);
+ } else {
+ neoImporter.setLabels(null);
+ neoImporter.setRelationshipTypes(null);
+ neoImporter.setNodeQuery(Neo4jImporterWizardData.nodeQuery);
+ neoImporter.setEdgeQuery(Neo4jImporterWizardData.edgeQuery);
+ }
+ }
+ }
+
+ @Override
+ public boolean isUIForImporter(Importer importer) {
+ return importer instanceof Neo4jDatabaseImporter;
+ }
+}
diff --git a/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jImporterWizardData.java b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jImporterWizardData.java
new file mode 100644
index 0000000000..420376280d
--- /dev/null
+++ b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Neo4jImporterWizardData.java
@@ -0,0 +1,62 @@
+package org.gephi.plugins.neo4j.importer.wizard;
+
+import java.util.List;
+
+public class Neo4jImporterWizardData {
+ /**
+ * Neo4j DB Url (ex: neo4j://localhost)
+ */
+ public static String dbUrl = null;
+
+ /**
+ * Neo4j database name.
+ * If empty, we use the default one.
+ */
+ public static String dbName = null;
+
+ /**
+ * Neo4j authentication mechanism
+ * - 0 -> login/password
+ * - 1 -> no auth
+ */
+ public static Integer dbAuthType = null;
+
+ /**
+ * Neo4j username
+ */
+ public static String dbUsername = null;
+
+ /**
+ * Neo4j password
+ */
+ public static String dbPassword = null;
+
+ /**
+ * Mode for the import
+ * - 0 -> by types
+ * - 1 -> by queries
+ */
+ public static Integer importMode = null;
+
+ /**
+ * Neo4j labels to import
+ * If null we import every labels
+ */
+ public static List labels = null;
+
+ /**
+ * Neo4j types to import
+ * If null we import every types
+ */
+ public static List relationshipTypes = null;
+
+ /**
+ * Cypher query for nodes to import
+ */
+ public static String nodeQuery = null;
+
+ /**
+ * Cypher query for edges to import
+ */
+ public static String edgeQuery = null;
+}
diff --git a/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Utils.java b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Utils.java
new file mode 100644
index 0000000000..e6c4639eaa
--- /dev/null
+++ b/modules/Neo4jPlugin/src/main/java/org/gephi/plugins/neo4j/importer/wizard/Utils.java
@@ -0,0 +1,108 @@
+package org.gephi.plugins.neo4j.importer.wizard;
+
+import org.neo4j.driver.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class Utils {
+
+ /**
+ * Returns true if the value is null or empty, false otherwise.
+ */
+ public static Boolean isEmptyOrNull(String value) {
+ return value == null || value.trim().isEmpty();
+ }
+
+ /**
+ * Create a neo4j driver.
+ */
+ public static Driver neo4jDriver(String url, String username, String password, Integer fetchSize) {
+ if (fetchSize != null && fetchSize > 0)
+ return GraphDatabase.driver(url, isEmptyOrNull(password) ? AuthTokens.none() : AuthTokens.basic(username, password), Config.builder().withFetchSize(fetchSize).build());
+ else
+ return GraphDatabase.driver(url, isEmptyOrNull(password) ? AuthTokens.none() : AuthTokens.basic(username, password));
+ }
+
+ /**
+ * Check parameters by performing a call to the db.
+ */
+ public static void neo4jCheckConnection(String url, String username, String password, String dbName) throws Exception {
+ // Do checks
+ if (isEmptyOrNull(url)) throw new Exception("Url is mandatory");
+ if ((!isEmptyOrNull(password) && isEmptyOrNull(username)) || (isEmptyOrNull(password) && !isEmptyOrNull(username)))
+ throw new Exception("Username and password are mandatory");
+
+ // Create driver and check connectivity
+ try (Driver driver = GraphDatabase.driver(url, isEmptyOrNull(password) ? AuthTokens.none() : AuthTokens.basic(username, password))) {
+ driver.verifyConnectivity();
+ driver.session(isEmptyOrNull(dbName) ? SessionConfig.defaultConfig() : SessionConfig.forDatabase(dbName)).run("RETURN 1");
+ } catch (Exception e) {
+ throw e;
+ }
+ }
+
+ /**
+ * Create a neo4j driver from the Neo4jImporterWizardData.
+ */
+ public static Driver neo4jWizardDriver() {
+ return neo4jWizardDriver(null);
+ }
+
+ public static Driver neo4jWizardDriver(Integer fetchSize) {
+ if (Neo4jImporterWizardData.dbAuthType == 0)
+ return neo4jDriver(Neo4jImporterWizardData.dbUrl, Neo4jImporterWizardData.dbUsername, Neo4jImporterWizardData.dbPassword, fetchSize);
+ else
+ return neo4jDriver(Neo4jImporterWizardData.dbUrl, null, null, fetchSize);
+ }
+
+ /**
+ * Check a query against server defined in Neo4jImporterWizardData
+ */
+ public static void neo4jWizardCheckQuery(String query, List mandatoryFields) throws Exception {
+ try (Driver driver = neo4jWizardDriver(10)) {
+ Result result = driver.session(Neo4jImporterWizardData.dbName != null ? SessionConfig.forDatabase(Neo4jImporterWizardData.dbName) : SessionConfig.defaultConfig()).run(query);
+ if (!result.keys().containsAll(mandatoryFields))
+ throw new Exception("Query MUST returns the following field(s) " + mandatoryFields.stream().collect(Collectors.joining(", ")));
+ if (result.list().size() == 0) throw new Exception("Query returns no data");
+ } catch (Exception e) {
+ throw e;
+ }
+ }
+
+ /**
+ * Get server labels from server defined in Neo4jImporterWizardData
+ */
+ public static List neo4jWizardGetLabels() {
+ try (Driver driver = neo4jWizardDriver()) {
+ Session session = driver.session(Neo4jImporterWizardData.dbName != null ? SessionConfig.forDatabase(Neo4jImporterWizardData.dbName) : SessionConfig.defaultConfig());
+ return session.readTransaction(tx -> {
+ List labels = new ArrayList<>();
+ Result rs = tx.run("CALL db.labels()");
+ while (rs.hasNext()) {
+ labels.add(rs.next().get(0).asString());
+ }
+ return labels;
+ });
+ }
+ }
+
+ /**
+ * Get server relationship types from server defined in Neo4jImporterWizardData
+ */
+ public static List neo4jWizardGetRelationshipTypes() {
+ try (Driver driver = neo4jWizardDriver()) {
+ Session session = driver.session(Neo4jImporterWizardData.dbName != null ? SessionConfig.forDatabase(Neo4jImporterWizardData.dbName) : SessionConfig.defaultConfig());
+ return session.readTransaction(tx -> {
+ List rels = new ArrayList<>();
+ Result rs = tx.run("CALL db.relationshipTypes()");
+ while (rs.hasNext()) {
+ rels.add(rs.next().get(0).asString());
+ }
+ return rels;
+ });
+ }
+ }
+
+}
diff --git a/modules/Neo4jPlugin/src/main/nbm/manifest.mf b/modules/Neo4jPlugin/src/main/nbm/manifest.mf
new file mode 100644
index 0000000000..a145ae7ab1
--- /dev/null
+++ b/modules/Neo4jPlugin/src/main/nbm/manifest.mf
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+OpenIDE-Module-Localizing-Bundle: org/gephi/plugins/neo4j/Bundle.properties
diff --git a/modules/Neo4jPlugin/src/main/resources/org/gephi/plugins/neo4j/Bundle.properties b/modules/Neo4jPlugin/src/main/resources/org/gephi/plugins/neo4j/Bundle.properties
new file mode 100644
index 0000000000..22b41021a5
--- /dev/null
+++ b/modules/Neo4jPlugin/src/main/resources/org/gephi/plugins/neo4j/Bundle.properties
@@ -0,0 +1,4 @@
+OpenIDE-Module-Name: Neo4j plugin
+OpenIDE-Module-Display-Category: Import
+OpenIDE-Module-Short-Description: Import Neo4j data in gephi
+OpenIDE-Module-Long-Description: Helps you to import Neo4j data in Gephi by selecting nodes & relationships types, or by defining two cypher queries, one for nodes and the other for edges
diff --git a/modules/Neo4jPlugin/src/main/resources/org/gephi/plugins/neo4j/importer/wizard/Bundle.properties b/modules/Neo4jPlugin/src/main/resources/org/gephi/plugins/neo4j/importer/wizard/Bundle.properties
new file mode 100644
index 0000000000..74df1bc5d4
--- /dev/null
+++ b/modules/Neo4jPlugin/src/main/resources/org/gephi/plugins/neo4j/importer/wizard/Bundle.properties
@@ -0,0 +1,22 @@
+Neo4jDatabaseImporterImportPanel.Labels\ &\ Relationship\ types.TabConstraints.tabTitle=Labels & Types
+Neo4jDatabaseImporterImportPanel.Nodes\ &\ Edges\ queries.TabConstraints.tabTitle=Queries
+Neo4jDatabaseImporterImportPanel.nodesQueryLabel.text=Query for nodes
+Neo4jDatabaseImporterConnectionPanel.dbUrl.text_1_1=neo4j://localhost
+Neo4jDatabaseImporterConnectionPanel.dbUrlLabel.text_1_1=URL
+Neo4jDatabaseImporterConnectionPanel.dbAuthTypeLabel.text_1_1=Authentication
+Neo4jDatabaseImporterConnectionPanel.dbName.toolTipText_1=leave empty for default
+Neo4jDatabaseImporterConnectionPanel.dbNameLabel.text_1_1=Database
+Neo4jDatabaseImporterConnectionPanel.checkBtn.text_1=Verify
+Neo4jDatabaseImporterConnectionPanel.dbPasswordLabel.text_1_1=Password
+Neo4jDatabaseImporterConnectionPanel.dbUsername.text_1_1=neo4j
+Neo4jDatabaseImporterConnectionPanel.dbUsernameLabel.text_1_1=Username
+Neo4jDatabaseImporterImportPanel.edgeQueryLabel.text_1=Query for edges
+Neo4jDatabaseImporterImportPanel.nodeQueryLabel.toolTipText_1=The query must returns and "id" field and may returns a "labels" field\nExample: MATCH (n) RETURN id(n) AS id, labels(n) AS labels
+Neo4jDatabaseImporterImportPanel.help.text_1=Select multiple labels and relationship types with ctrl+click
+Neo4jDatabaseImporterImportPanel.typesLabels.text_1=Relationships
+Neo4jDatabaseImporterImportPanel.labelsLabel.text_1=Labels
+Neo4jDatabaseImporterImportPanel.edgeQueryHelp.text_1=Cypher query that returns edges (result must contains 'id', 'sourceId' & 'targetId' fields)
+Neo4jDatabaseImporterImportPanel.nodeQueryHelp.text_1=Cypher query that returns nodes (result must contains an 'id' field)
+Neo4jDatabaseImporterImportPanel.checkEdgeQuery.text_1=Check query
+Neo4jDatabaseImporterImportPanel.checkNodeQuery.actionCommand_1=
+Neo4jDatabaseImporterImportPanel.checkNodeQuery.text_1=Check query
diff --git a/modules/Neo4jPlugin/src/test/java/org/gephi/plugins/neo4j/AbstractNeo4jIntegrationTest.java b/modules/Neo4jPlugin/src/test/java/org/gephi/plugins/neo4j/AbstractNeo4jIntegrationTest.java
new file mode 100644
index 0000000000..67ee8f82a8
--- /dev/null
+++ b/modules/Neo4jPlugin/src/test/java/org/gephi/plugins/neo4j/AbstractNeo4jIntegrationTest.java
@@ -0,0 +1,54 @@
+package org.gephi.plugins.neo4j;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.neo4j.driver.AuthTokens;
+import org.neo4j.driver.Driver;
+import org.neo4j.driver.GraphDatabase;
+import org.neo4j.driver.Session;
+import org.testcontainers.containers.Neo4jContainer;
+
+import java.util.Scanner;
+
+public abstract class AbstractNeo4jIntegrationTest {
+
+ @ClassRule
+ public static Neo4jContainer> neo4j = new Neo4jContainer("neo4j:4.4");
+
+ @BeforeClass
+ public static void before() {
+ AbstractNeo4jIntegrationTest.neo4j.start();
+ }
+
+ @AfterClass
+ public static void after() {
+ AbstractNeo4jIntegrationTest.neo4j.stop();
+ }
+
+ /**
+ * Retrieve the Neo4j driver instance for the running container.
+ */
+ public static Driver getNeo4jDriver() {
+ return GraphDatabase.driver(AbstractNeo4jIntegrationTest.neo4j.getBoltUrl(), AuthTokens.basic("neo4j", AbstractNeo4jIntegrationTest.neo4j.getAdminPassword()));
+ }
+
+ /**
+ * Load the given cypher script file in the database.
+ */
+ public static void loadFile(String file) {
+ // init db if needed
+ if (file != null) {
+ String query = "";
+ try (Scanner s = new Scanner(AbstractNeo4jIntegrationTest.class.getResourceAsStream(file)).useDelimiter("\\n")) {
+ while (s.hasNext()) {
+ query += s.next() + "\n";
+ }
+ }
+ Driver driver = AbstractNeo4jIntegrationTest.getNeo4jDriver();
+ try (Session session = driver.session()) {
+ session.run(query);
+ }
+ }
+ }
+}
diff --git a/modules/Neo4jPlugin/src/test/java/org/gephi/plugins/neo4j/importer/Neo4jDatabaseImporterTest.java b/modules/Neo4jPlugin/src/test/java/org/gephi/plugins/neo4j/importer/Neo4jDatabaseImporterTest.java
new file mode 100644
index 0000000000..806b2fff5a
--- /dev/null
+++ b/modules/Neo4jPlugin/src/test/java/org/gephi/plugins/neo4j/importer/Neo4jDatabaseImporterTest.java
@@ -0,0 +1,164 @@
+package org.gephi.plugins.neo4j.importer;
+
+import org.gephi.io.importer.api.Report;
+import org.gephi.io.importer.impl.ImportContainerImpl;
+import org.gephi.plugins.neo4j.AbstractNeo4jIntegrationTest;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.neo4j.driver.Result;
+import org.neo4j.driver.Session;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+
+public class Neo4jDatabaseImporterTest extends AbstractNeo4jIntegrationTest {
+
+ @BeforeClass
+ public static void before() {
+ Neo4jDatabaseImporterTest.loadFile("/cypher/movie.cyp");
+ }
+
+ @Test
+ public void import_full_database_should_work() {
+ ImportContainerImpl container = new ImportContainerImpl();
+
+ Neo4jDatabaseImporter importer = new Neo4jDatabaseImporter();
+ importer.setUrl(Neo4jDatabaseImporterTest.neo4j.getBoltUrl());
+ importer.setPasswd(Neo4jDatabaseImporterTest.neo4j.getAdminPassword());
+ importer.execute(container.getLoader());
+
+ try (Session session = Neo4jDatabaseImporterTest.getNeo4jDriver().session()) {
+ long nbNodes = session.readTransaction(tx -> {
+ Result rs = tx.run("MATCH (n) RETURN count(n)");
+ return rs.next().get(0).asLong();
+ });
+ long nbEdges = session.readTransaction(tx -> {
+ Result rs = tx.run("MATCH ()-[r]->() RETURN count(r)");
+ return rs.next().get(0).asLong();
+ });
+
+ assertEquals("Bad node size", nbNodes, container.getNodeCount());
+ assertEquals("Bad edge size", nbEdges, container.getEdgeCount());
+
+ this.logReport(importer.getReport());
+ }
+ }
+
+ @Test
+ public void import_by_node_and_edge_types_should_work() {
+ ImportContainerImpl container = new ImportContainerImpl();
+
+ Neo4jDatabaseImporter importer = new Neo4jDatabaseImporter();
+ importer.setUrl(Neo4jDatabaseImporterTest.neo4j.getBoltUrl());
+ importer.setPasswd(Neo4jDatabaseImporterTest.neo4j.getAdminPassword());
+ importer.setLabels(Arrays.asList("Person"));
+ importer.setRelationshipTypes(Arrays.asList("FOLLOWS"));
+ importer.execute(container.getLoader());
+
+ try (Session session = Neo4jDatabaseImporterTest.getNeo4jDriver().session()) {
+ long nbNodes = session.readTransaction(tx -> {
+ Result rs = tx.run("MATCH (n:Person) RETURN count(n)");
+ return rs.next().get(0).asLong();
+ });
+ long nbEdges = session.readTransaction(tx -> {
+ Result rs = tx.run("MATCH (:Person)-[r:FOLLOWS]->(:Person) RETURN count(r)");
+ return rs.next().get(0).asLong();
+ });
+
+ assertEquals("Bad node size", nbNodes, container.getNodeCount());
+ assertEquals("Bad edge size", nbEdges, container.getEdgeCount());
+
+ this.logReport(importer.getReport());
+ }
+ }
+
+ @Test
+ public void import_by_node_types_should_work() {
+ ImportContainerImpl container = new ImportContainerImpl();
+
+ Neo4jDatabaseImporter importer = new Neo4jDatabaseImporter();
+ importer.setUrl(Neo4jDatabaseImporterTest.neo4j.getBoltUrl());
+ importer.setPasswd(Neo4jDatabaseImporterTest.neo4j.getAdminPassword());
+ importer.setLabels(Arrays.asList("Person"));
+ importer.execute(container.getLoader());
+
+ try (Session session = Neo4jDatabaseImporterTest.getNeo4jDriver().session()) {
+ long nbNodes = session.readTransaction(tx -> {
+ Result rs = tx.run("MATCH (n:Person) RETURN count(n)");
+ return rs.next().get(0).asLong();
+ });
+ long nbEdges = session.readTransaction(tx -> {
+ Result rs = tx.run("MATCH (:Person)-[r]->(:Person) RETURN count(r)");
+ return rs.next().get(0).asLong();
+ });
+
+ assertEquals("Bad node size", nbNodes, container.getNodeCount());
+ assertEquals("Bad edge size", nbEdges, container.getEdgeCount());
+
+ this.logReport(importer.getReport());
+ }
+ }
+
+ @Test
+ public void import_by_edge_types_should_work() {
+ ImportContainerImpl container = new ImportContainerImpl();
+
+ Neo4jDatabaseImporter importer = new Neo4jDatabaseImporter();
+ importer.setUrl(Neo4jDatabaseImporterTest.neo4j.getBoltUrl());
+ importer.setPasswd(Neo4jDatabaseImporterTest.neo4j.getAdminPassword());
+ importer.setRelationshipTypes(Arrays.asList("FOLLOWS"));
+ importer.execute(container.getLoader());
+
+ try (Session session = Neo4jDatabaseImporterTest.getNeo4jDriver().session()) {
+ long nbNodes = session.readTransaction(tx -> {
+ Result rs = tx.run("MATCH (n) RETURN count(n)");
+ return rs.next().get(0).asLong();
+ });
+ long nbEdges = session.readTransaction(tx -> {
+ Result rs = tx.run("MATCH ()-[r:FOLLOWS]->() RETURN count(r)");
+ return rs.next().get(0).asLong();
+ });
+
+ assertEquals("Bad node size", nbNodes, container.getNodeCount());
+ assertEquals("Bad edge size", nbEdges, container.getEdgeCount());
+
+ this.logReport(importer.getReport());
+ }
+ }
+
+ @Test
+ public void import_by_nodes_and_edge_queries_should_work() {
+ ImportContainerImpl container = new ImportContainerImpl();
+ System.out.println(container.getNodeCount());
+
+ Neo4jDatabaseImporter importer = new Neo4jDatabaseImporter();
+ importer.setUrl(Neo4jDatabaseImporterTest.neo4j.getBoltUrl());
+ importer.setPasswd(Neo4jDatabaseImporterTest.neo4j.getAdminPassword());
+ importer.setNodeQuery("MATCH (n:Person) RETURN id(n) AS id, n.name AS label");
+ importer.setEdgeQuery("MATCH (p1:Person)-[:ACTED_IN]->(m:Movie)<-[:ACTED_IN]-(p2:Person) WHERE id(p1) < id(p2) RETURN id(p1) + '-' + id(p2) AS id, id(p1) AS sourceId, id(p2) AS targetId, count(m) AS weight");
+ importer.execute(container.getLoader());
+
+ try (Session session = Neo4jDatabaseImporterTest.getNeo4jDriver().session()) {
+ long nbNodes = session.readTransaction(tx -> {
+ Result rs = tx.run("MATCH (n:Person) RETURN count(n)");
+ return rs.next().get(0).asLong();
+ });
+ long nbEdges = session.readTransaction(tx -> {
+ Result rs = tx.run("MATCH (p1:Person)-[:ACTED_IN]->(m:Movie)<-[:ACTED_IN]-(p2:Person) WHERE id(p1) < id(p2) WITH DISTINCT p1, p2 RETURN count(*)");
+ return rs.next().get(0).asLong();
+ });
+
+ assertEquals("Bad node size", nbNodes, container.getNodeCount());
+ assertEquals("Bad edge size", nbEdges, container.getEdgeCount());
+
+ this.logReport(importer.getReport());
+ }
+ }
+
+ private void logReport(Report report) {
+ System.out.println(report.getText());
+ report.getIssues(100).forEachRemaining(i -> System.out.println(i.getMessage()));
+ }
+
+}
diff --git a/modules/Neo4jPlugin/src/test/resources/cypher/movie.cyp b/modules/Neo4jPlugin/src/test/resources/cypher/movie.cyp
new file mode 100644
index 0000000000..e6105174ef
--- /dev/null
+++ b/modules/Neo4jPlugin/src/test/resources/cypher/movie.cyp
@@ -0,0 +1,508 @@
+CREATE (TheMatrix:Movie {title:'The Matrix', released:1999, tagline:'Welcome to the Real World'})
+CREATE (Keanu:Person {name:'Keanu Reeves', born:1964})
+CREATE (Carrie:Person {name:'Carrie-Anne Moss', born:1967})
+CREATE (Laurence:Person {name:'Laurence Fishburne', born:1961})
+CREATE (Hugo:Person {name:'Hugo Weaving', born:1960})
+CREATE (LillyW:Person {name:'Lilly Wachowski', born:1967})
+CREATE (LanaW:Person {name:'Lana Wachowski', born:1965})
+CREATE (JoelS:Person {name:'Joel Silver', born:1952})
+CREATE
+ (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrix),
+ (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrix),
+ (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrix),
+ (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrix),
+ (LillyW)-[:DIRECTED]->(TheMatrix),
+ (LanaW)-[:DIRECTED]->(TheMatrix),
+ (JoelS)-[:PRODUCED]->(TheMatrix)
+
+CREATE (Emil:Person {name:"Emil Eifrem", born:1978})
+CREATE (Emil)-[:ACTED_IN {roles:["Emil"]}]->(TheMatrix)
+
+CREATE (TheMatrixReloaded:Movie {title:'The Matrix Reloaded', released:2003, tagline:'Free your mind'})
+CREATE
+ (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixReloaded),
+ (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixReloaded),
+ (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixReloaded),
+ (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixReloaded),
+ (LillyW)-[:DIRECTED]->(TheMatrixReloaded),
+ (LanaW)-[:DIRECTED]->(TheMatrixReloaded),
+ (JoelS)-[:PRODUCED]->(TheMatrixReloaded)
+
+CREATE (TheMatrixRevolutions:Movie {title:'The Matrix Revolutions', released:2003, tagline:'Everything that has a beginning has an end'})
+CREATE
+ (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixRevolutions),
+ (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixRevolutions),
+ (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixRevolutions),
+ (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixRevolutions),
+ (LillyW)-[:DIRECTED]->(TheMatrixRevolutions),
+ (LanaW)-[:DIRECTED]->(TheMatrixRevolutions),
+ (JoelS)-[:PRODUCED]->(TheMatrixRevolutions)
+
+CREATE (TheDevilsAdvocate:Movie {title:"The Devil's Advocate", released:1997, tagline:'Evil has its winning ways'})
+CREATE (Charlize:Person {name:'Charlize Theron', born:1975})
+CREATE (Al:Person {name:'Al Pacino', born:1940})
+CREATE (Taylor:Person {name:'Taylor Hackford', born:1944})
+CREATE
+ (Keanu)-[:ACTED_IN {roles:['Kevin Lomax']}]->(TheDevilsAdvocate),
+ (Charlize)-[:ACTED_IN {roles:['Mary Ann Lomax']}]->(TheDevilsAdvocate),
+ (Al)-[:ACTED_IN {roles:['John Milton']}]->(TheDevilsAdvocate),
+ (Taylor)-[:DIRECTED]->(TheDevilsAdvocate)
+
+CREATE (AFewGoodMen:Movie {title:"A Few Good Men", released:1992, tagline:"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth."})
+CREATE (TomC:Person {name:'Tom Cruise', born:1962})
+CREATE (JackN:Person {name:'Jack Nicholson', born:1937})
+CREATE (DemiM:Person {name:'Demi Moore', born:1962})
+CREATE (KevinB:Person {name:'Kevin Bacon', born:1958})
+CREATE (KieferS:Person {name:'Kiefer Sutherland', born:1966})
+CREATE (NoahW:Person {name:'Noah Wyle', born:1971})
+CREATE (CubaG:Person {name:'Cuba Gooding Jr.', born:1968})
+CREATE (KevinP:Person {name:'Kevin Pollak', born:1957})
+CREATE (JTW:Person {name:'J.T. Walsh', born:1943})
+CREATE (JamesM:Person {name:'James Marshall', born:1967})
+CREATE (ChristopherG:Person {name:'Christopher Guest', born:1948})
+CREATE (RobR:Person {name:'Rob Reiner', born:1947})
+CREATE (AaronS:Person {name:'Aaron Sorkin', born:1961})
+CREATE
+ (TomC)-[:ACTED_IN {roles:['Lt. Daniel Kaffee']}]->(AFewGoodMen),
+ (JackN)-[:ACTED_IN {roles:['Col. Nathan R. Jessup']}]->(AFewGoodMen),
+ (DemiM)-[:ACTED_IN {roles:['Lt. Cdr. JoAnne Galloway']}]->(AFewGoodMen),
+ (KevinB)-[:ACTED_IN {roles:['Capt. Jack Ross']}]->(AFewGoodMen),
+ (KieferS)-[:ACTED_IN {roles:['Lt. Jonathan Kendrick']}]->(AFewGoodMen),
+ (NoahW)-[:ACTED_IN {roles:['Cpl. Jeffrey Barnes']}]->(AFewGoodMen),
+ (CubaG)-[:ACTED_IN {roles:['Cpl. Carl Hammaker']}]->(AFewGoodMen),
+ (KevinP)-[:ACTED_IN {roles:['Lt. Sam Weinberg']}]->(AFewGoodMen),
+ (JTW)-[:ACTED_IN {roles:['Lt. Col. Matthew Andrew Markinson']}]->(AFewGoodMen),
+ (JamesM)-[:ACTED_IN {roles:['Pfc. Louden Downey']}]->(AFewGoodMen),
+ (ChristopherG)-[:ACTED_IN {roles:['Dr. Stone']}]->(AFewGoodMen),
+ (AaronS)-[:ACTED_IN {roles:['Man in Bar']}]->(AFewGoodMen),
+ (RobR)-[:DIRECTED]->(AFewGoodMen),
+ (AaronS)-[:WROTE]->(AFewGoodMen)
+
+CREATE (TopGun:Movie {title:"Top Gun", released:1986, tagline:'I feel the need, the need for speed.'})
+CREATE (KellyM:Person {name:'Kelly McGillis', born:1957})
+CREATE (ValK:Person {name:'Val Kilmer', born:1959})
+CREATE (AnthonyE:Person {name:'Anthony Edwards', born:1962})
+CREATE (TomS:Person {name:'Tom Skerritt', born:1933})
+CREATE (MegR:Person {name:'Meg Ryan', born:1961})
+CREATE (TonyS:Person {name:'Tony Scott', born:1944})
+CREATE (JimC:Person {name:'Jim Cash', born:1941})
+CREATE
+ (TomC)-[:ACTED_IN {roles:['Maverick']}]->(TopGun),
+ (KellyM)-[:ACTED_IN {roles:['Charlie']}]->(TopGun),
+ (ValK)-[:ACTED_IN {roles:['Iceman']}]->(TopGun),
+ (AnthonyE)-[:ACTED_IN {roles:['Goose']}]->(TopGun),
+ (TomS)-[:ACTED_IN {roles:['Viper']}]->(TopGun),
+ (MegR)-[:ACTED_IN {roles:['Carole']}]->(TopGun),
+ (TonyS)-[:DIRECTED]->(TopGun),
+ (JimC)-[:WROTE]->(TopGun)
+
+CREATE (JerryMaguire:Movie {title:'Jerry Maguire', released:2000, tagline:'The rest of his life begins now.'})
+CREATE (ReneeZ:Person {name:'Renee Zellweger', born:1969})
+CREATE (KellyP:Person {name:'Kelly Preston', born:1962})
+CREATE (JerryO:Person {name:"Jerry O'Connell", born:1974})
+CREATE (JayM:Person {name:'Jay Mohr', born:1970})
+CREATE (BonnieH:Person {name:'Bonnie Hunt', born:1961})
+CREATE (ReginaK:Person {name:'Regina King', born:1971})
+CREATE (JonathanL:Person {name:'Jonathan Lipnicki', born:1996})
+CREATE (CameronC:Person {name:'Cameron Crowe', born:1957})
+CREATE
+ (TomC)-[:ACTED_IN {roles:['Jerry Maguire']}]->(JerryMaguire),
+ (CubaG)-[:ACTED_IN {roles:['Rod Tidwell']}]->(JerryMaguire),
+ (ReneeZ)-[:ACTED_IN {roles:['Dorothy Boyd']}]->(JerryMaguire),
+ (KellyP)-[:ACTED_IN {roles:['Avery Bishop']}]->(JerryMaguire),
+ (JerryO)-[:ACTED_IN {roles:['Frank Cushman']}]->(JerryMaguire),
+ (JayM)-[:ACTED_IN {roles:['Bob Sugar']}]->(JerryMaguire),
+ (BonnieH)-[:ACTED_IN {roles:['Laurel Boyd']}]->(JerryMaguire),
+ (ReginaK)-[:ACTED_IN {roles:['Marcee Tidwell']}]->(JerryMaguire),
+ (JonathanL)-[:ACTED_IN {roles:['Ray Boyd']}]->(JerryMaguire),
+ (CameronC)-[:DIRECTED]->(JerryMaguire),
+ (CameronC)-[:PRODUCED]->(JerryMaguire),
+ (CameronC)-[:WROTE]->(JerryMaguire)
+
+CREATE (StandByMe:Movie {title:"Stand By Me", released:1986, tagline:"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of."})
+CREATE (RiverP:Person {name:'River Phoenix', born:1970})
+CREATE (CoreyF:Person {name:'Corey Feldman', born:1971})
+CREATE (WilW:Person {name:'Wil Wheaton', born:1972})
+CREATE (JohnC:Person {name:'John Cusack', born:1966})
+CREATE (MarshallB:Person {name:'Marshall Bell', born:1942})
+CREATE
+ (WilW)-[:ACTED_IN {roles:['Gordie Lachance']}]->(StandByMe),
+ (RiverP)-[:ACTED_IN {roles:['Chris Chambers']}]->(StandByMe),
+ (JerryO)-[:ACTED_IN {roles:['Vern Tessio']}]->(StandByMe),
+ (CoreyF)-[:ACTED_IN {roles:['Teddy Duchamp']}]->(StandByMe),
+ (JohnC)-[:ACTED_IN {roles:['Denny Lachance']}]->(StandByMe),
+ (KieferS)-[:ACTED_IN {roles:['Ace Merrill']}]->(StandByMe),
+ (MarshallB)-[:ACTED_IN {roles:['Mr. Lachance']}]->(StandByMe),
+ (RobR)-[:DIRECTED]->(StandByMe)
+
+CREATE (AsGoodAsItGets:Movie {title:'As Good as It Gets', released:1997, tagline:'A comedy from the heart that goes for the throat.'})
+CREATE (HelenH:Person {name:'Helen Hunt', born:1963})
+CREATE (GregK:Person {name:'Greg Kinnear', born:1963})
+CREATE (JamesB:Person {name:'James L. Brooks', born:1940})
+CREATE
+ (JackN)-[:ACTED_IN {roles:['Melvin Udall']}]->(AsGoodAsItGets),
+ (HelenH)-[:ACTED_IN {roles:['Carol Connelly']}]->(AsGoodAsItGets),
+ (GregK)-[:ACTED_IN {roles:['Simon Bishop']}]->(AsGoodAsItGets),
+ (CubaG)-[:ACTED_IN {roles:['Frank Sachs']}]->(AsGoodAsItGets),
+ (JamesB)-[:DIRECTED]->(AsGoodAsItGets)
+
+CREATE (WhatDreamsMayCome:Movie {title:'What Dreams May Come', released:1998, tagline:'After life there is more. The end is just the beginning.'})
+CREATE (AnnabellaS:Person {name:'Annabella Sciorra', born:1960})
+CREATE (MaxS:Person {name:'Max von Sydow', born:1929})
+CREATE (WernerH:Person {name:'Werner Herzog', born:1942})
+CREATE (Robin:Person {name:'Robin Williams', born:1951})
+CREATE (VincentW:Person {name:'Vincent Ward', born:1956})
+CREATE
+ (Robin)-[:ACTED_IN {roles:['Chris Nielsen']}]->(WhatDreamsMayCome),
+ (CubaG)-[:ACTED_IN {roles:['Albert Lewis']}]->(WhatDreamsMayCome),
+ (AnnabellaS)-[:ACTED_IN {roles:['Annie Collins-Nielsen']}]->(WhatDreamsMayCome),
+ (MaxS)-[:ACTED_IN {roles:['The Tracker']}]->(WhatDreamsMayCome),
+ (WernerH)-[:ACTED_IN {roles:['The Face']}]->(WhatDreamsMayCome),
+ (VincentW)-[:DIRECTED]->(WhatDreamsMayCome)
+
+CREATE (SnowFallingonCedars:Movie {title:'Snow Falling on Cedars', released:1999, tagline:'First loves last. Forever.'})
+CREATE (EthanH:Person {name:'Ethan Hawke', born:1970})
+CREATE (RickY:Person {name:'Rick Yune', born:1971})
+CREATE (JamesC:Person {name:'James Cromwell', born:1940})
+CREATE (ScottH:Person {name:'Scott Hicks', born:1953})
+CREATE
+ (EthanH)-[:ACTED_IN {roles:['Ishmael Chambers']}]->(SnowFallingonCedars),
+ (RickY)-[:ACTED_IN {roles:['Kazuo Miyamoto']}]->(SnowFallingonCedars),
+ (MaxS)-[:ACTED_IN {roles:['Nels Gudmundsson']}]->(SnowFallingonCedars),
+ (JamesC)-[:ACTED_IN {roles:['Judge Fielding']}]->(SnowFallingonCedars),
+ (ScottH)-[:DIRECTED]->(SnowFallingonCedars)
+
+CREATE (YouveGotMail:Movie {title:"You've Got Mail", released:1998, tagline:'At odds in life... in love on-line.'})
+CREATE (ParkerP:Person {name:'Parker Posey', born:1968})
+CREATE (DaveC:Person {name:'Dave Chappelle', born:1973})
+CREATE (SteveZ:Person {name:'Steve Zahn', born:1967})
+CREATE (TomH:Person {name:'Tom Hanks', born:1956})
+CREATE (NoraE:Person {name:'Nora Ephron', born:1941})
+CREATE
+ (TomH)-[:ACTED_IN {roles:['Joe Fox']}]->(YouveGotMail),
+ (MegR)-[:ACTED_IN {roles:['Kathleen Kelly']}]->(YouveGotMail),
+ (GregK)-[:ACTED_IN {roles:['Frank Navasky']}]->(YouveGotMail),
+ (ParkerP)-[:ACTED_IN {roles:['Patricia Eden']}]->(YouveGotMail),
+ (DaveC)-[:ACTED_IN {roles:['Kevin Jackson']}]->(YouveGotMail),
+ (SteveZ)-[:ACTED_IN {roles:['George Pappas']}]->(YouveGotMail),
+ (NoraE)-[:DIRECTED]->(YouveGotMail)
+
+CREATE (SleeplessInSeattle:Movie {title:'Sleepless in Seattle', released:1993, tagline:'What if someone you never met, someone you never saw, someone you never knew was the only someone for you?'})
+CREATE (RitaW:Person {name:'Rita Wilson', born:1956})
+CREATE (BillPull:Person {name:'Bill Pullman', born:1953})
+CREATE (VictorG:Person {name:'Victor Garber', born:1949})
+CREATE (RosieO:Person {name:"Rosie O'Donnell", born:1962})
+CREATE
+ (TomH)-[:ACTED_IN {roles:['Sam Baldwin']}]->(SleeplessInSeattle),
+ (MegR)-[:ACTED_IN {roles:['Annie Reed']}]->(SleeplessInSeattle),
+ (RitaW)-[:ACTED_IN {roles:['Suzy']}]->(SleeplessInSeattle),
+ (BillPull)-[:ACTED_IN {roles:['Walter']}]->(SleeplessInSeattle),
+ (VictorG)-[:ACTED_IN {roles:['Greg']}]->(SleeplessInSeattle),
+ (RosieO)-[:ACTED_IN {roles:['Becky']}]->(SleeplessInSeattle),
+ (NoraE)-[:DIRECTED]->(SleeplessInSeattle)
+
+CREATE (JoeVersustheVolcano:Movie {title:'Joe Versus the Volcano', released:1990, tagline:'A story of love, lava and burning desire.'})
+CREATE (JohnS:Person {name:'John Patrick Stanley', born:1950})
+CREATE (Nathan:Person {name:'Nathan Lane', born:1956})
+CREATE
+ (TomH)-[:ACTED_IN {roles:['Joe Banks']}]->(JoeVersustheVolcano),
+ (MegR)-[:ACTED_IN {roles:['DeDe', 'Angelica Graynamore', 'Patricia Graynamore']}]->(JoeVersustheVolcano),
+ (Nathan)-[:ACTED_IN {roles:['Baw']}]->(JoeVersustheVolcano),
+ (JohnS)-[:DIRECTED]->(JoeVersustheVolcano)
+
+CREATE (WhenHarryMetSally:Movie {title:'When Harry Met Sally', released:1998, tagline:'Can two friends sleep together and still love each other in the morning?'})
+CREATE (BillyC:Person {name:'Billy Crystal', born:1948})
+CREATE (CarrieF:Person {name:'Carrie Fisher', born:1956})
+CREATE (BrunoK:Person {name:'Bruno Kirby', born:1949})
+CREATE
+ (BillyC)-[:ACTED_IN {roles:['Harry Burns']}]->(WhenHarryMetSally),
+ (MegR)-[:ACTED_IN {roles:['Sally Albright']}]->(WhenHarryMetSally),
+ (CarrieF)-[:ACTED_IN {roles:['Marie']}]->(WhenHarryMetSally),
+ (BrunoK)-[:ACTED_IN {roles:['Jess']}]->(WhenHarryMetSally),
+ (RobR)-[:DIRECTED]->(WhenHarryMetSally),
+ (RobR)-[:PRODUCED]->(WhenHarryMetSally),
+ (NoraE)-[:PRODUCED]->(WhenHarryMetSally),
+ (NoraE)-[:WROTE]->(WhenHarryMetSally)
+
+CREATE (ThatThingYouDo:Movie {title:'That Thing You Do', released:1996, tagline:'In every life there comes a time when that thing you dream becomes that thing you do'})
+CREATE (LivT:Person {name:'Liv Tyler', born:1977})
+CREATE
+ (TomH)-[:ACTED_IN {roles:['Mr. White']}]->(ThatThingYouDo),
+ (LivT)-[:ACTED_IN {roles:['Faye Dolan']}]->(ThatThingYouDo),
+ (Charlize)-[:ACTED_IN {roles:['Tina']}]->(ThatThingYouDo),
+ (TomH)-[:DIRECTED]->(ThatThingYouDo)
+
+CREATE (TheReplacements:Movie {title:'The Replacements', released:2000, tagline:'Pain heals, Chicks dig scars... Glory lasts forever'})
+CREATE (Brooke:Person {name:'Brooke Langton', born:1970})
+CREATE (Gene:Person {name:'Gene Hackman', born:1930})
+CREATE (Orlando:Person {name:'Orlando Jones', born:1968})
+CREATE (Howard:Person {name:'Howard Deutch', born:1950})
+CREATE
+ (Keanu)-[:ACTED_IN {roles:['Shane Falco']}]->(TheReplacements),
+ (Brooke)-[:ACTED_IN {roles:['Annabelle Farrell']}]->(TheReplacements),
+ (Gene)-[:ACTED_IN {roles:['Jimmy McGinty']}]->(TheReplacements),
+ (Orlando)-[:ACTED_IN {roles:['Clifford Franklin']}]->(TheReplacements),
+ (Howard)-[:DIRECTED]->(TheReplacements)
+
+CREATE (RescueDawn:Movie {title:'RescueDawn', released:2006, tagline:"Based on the extraordinary true story of one man's fight for freedom"})
+CREATE (ChristianB:Person {name:'Christian Bale', born:1974})
+CREATE (ZachG:Person {name:'Zach Grenier', born:1954})
+CREATE
+ (MarshallB)-[:ACTED_IN {roles:['Admiral']}]->(RescueDawn),
+ (ChristianB)-[:ACTED_IN {roles:['Dieter Dengler']}]->(RescueDawn),
+ (ZachG)-[:ACTED_IN {roles:['Squad Leader']}]->(RescueDawn),
+ (SteveZ)-[:ACTED_IN {roles:['Duane']}]->(RescueDawn),
+ (WernerH)-[:DIRECTED]->(RescueDawn)
+
+CREATE (TheBirdcage:Movie {title:'The Birdcage', released:1996, tagline:'Come as you are'})
+CREATE (MikeN:Person {name:'Mike Nichols', born:1931})
+CREATE
+ (Robin)-[:ACTED_IN {roles:['Armand Goldman']}]->(TheBirdcage),
+ (Nathan)-[:ACTED_IN {roles:['Albert Goldman']}]->(TheBirdcage),
+ (Gene)-[:ACTED_IN {roles:['Sen. Kevin Keeley']}]->(TheBirdcage),
+ (MikeN)-[:DIRECTED]->(TheBirdcage)
+
+CREATE (Unforgiven:Movie {title:'Unforgiven', released:1992, tagline:"It's a hell of a thing, killing a man"})
+CREATE (RichardH:Person {name:'Richard Harris', born:1930})
+CREATE (ClintE:Person {name:'Clint Eastwood', born:1930})
+CREATE
+ (RichardH)-[:ACTED_IN {roles:['English Bob']}]->(Unforgiven),
+ (ClintE)-[:ACTED_IN {roles:['Bill Munny']}]->(Unforgiven),
+ (Gene)-[:ACTED_IN {roles:['Little Bill Daggett']}]->(Unforgiven),
+ (ClintE)-[:DIRECTED]->(Unforgiven)
+
+CREATE (JohnnyMnemonic:Movie {title:'Johnny Mnemonic', released:1995, tagline:'The hottest data on earth. In the coolest head in town'})
+CREATE (Takeshi:Person {name:'Takeshi Kitano', born:1947})
+CREATE (Dina:Person {name:'Dina Meyer', born:1968})
+CREATE (IceT:Person {name:'Ice-T', born:1958})
+CREATE (RobertL:Person {name:'Robert Longo', born:1953})
+CREATE
+ (Keanu)-[:ACTED_IN {roles:['Johnny Mnemonic']}]->(JohnnyMnemonic),
+ (Takeshi)-[:ACTED_IN {roles:['Takahashi']}]->(JohnnyMnemonic),
+ (Dina)-[:ACTED_IN {roles:['Jane']}]->(JohnnyMnemonic),
+ (IceT)-[:ACTED_IN {roles:['J-Bone']}]->(JohnnyMnemonic),
+ (RobertL)-[:DIRECTED]->(JohnnyMnemonic)
+
+CREATE (CloudAtlas:Movie {title:'Cloud Atlas', released:2012, tagline:'Everything is connected'})
+CREATE (HalleB:Person {name:'Halle Berry', born:1966})
+CREATE (JimB:Person {name:'Jim Broadbent', born:1949})
+CREATE (TomT:Person {name:'Tom Tykwer', born:1965})
+CREATE (DavidMitchell:Person {name:'David Mitchell', born:1969})
+CREATE (StefanArndt:Person {name:'Stefan Arndt', born:1961})
+CREATE
+ (TomH)-[:ACTED_IN {roles:['Zachry', 'Dr. Henry Goose', 'Isaac Sachs', 'Dermot Hoggins']}]->(CloudAtlas),
+ (Hugo)-[:ACTED_IN {roles:['Bill Smoke', 'Haskell Moore', 'Tadeusz Kesselring', 'Nurse Noakes', 'Boardman Mephi', 'Old Georgie']}]->(CloudAtlas),
+ (HalleB)-[:ACTED_IN {roles:['Luisa Rey', 'Jocasta Ayrs', 'Ovid', 'Meronym']}]->(CloudAtlas),
+ (JimB)-[:ACTED_IN {roles:['Vyvyan Ayrs', 'Captain Molyneux', 'Timothy Cavendish']}]->(CloudAtlas),
+ (TomT)-[:DIRECTED]->(CloudAtlas),
+ (LillyW)-[:DIRECTED]->(CloudAtlas),
+ (LanaW)-[:DIRECTED]->(CloudAtlas),
+ (DavidMitchell)-[:WROTE]->(CloudAtlas),
+ (StefanArndt)-[:PRODUCED]->(CloudAtlas)
+
+CREATE (TheDaVinciCode:Movie {title:'The Da Vinci Code', released:2006, tagline:'Break The Codes'})
+CREATE (IanM:Person {name:'Ian McKellen', born:1939})
+CREATE (AudreyT:Person {name:'Audrey Tautou', born:1976})
+CREATE (PaulB:Person {name:'Paul Bettany', born:1971})
+CREATE (RonH:Person {name:'Ron Howard', born:1954})
+CREATE
+ (TomH)-[:ACTED_IN {roles:['Dr. Robert Langdon']}]->(TheDaVinciCode),
+ (IanM)-[:ACTED_IN {roles:['Sir Leight Teabing']}]->(TheDaVinciCode),
+ (AudreyT)-[:ACTED_IN {roles:['Sophie Neveu']}]->(TheDaVinciCode),
+ (PaulB)-[:ACTED_IN {roles:['Silas']}]->(TheDaVinciCode),
+ (RonH)-[:DIRECTED]->(TheDaVinciCode)
+
+CREATE (VforVendetta:Movie {title:'V for Vendetta', released:2006, tagline:'Freedom! Forever!'})
+CREATE (NatalieP:Person {name:'Natalie Portman', born:1981})
+CREATE (StephenR:Person {name:'Stephen Rea', born:1946})
+CREATE (JohnH:Person {name:'John Hurt', born:1940})
+CREATE (BenM:Person {name: 'Ben Miles', born:1967})
+CREATE
+ (Hugo)-[:ACTED_IN {roles:['V']}]->(VforVendetta),
+ (NatalieP)-[:ACTED_IN {roles:['Evey Hammond']}]->(VforVendetta),
+ (StephenR)-[:ACTED_IN {roles:['Eric Finch']}]->(VforVendetta),
+ (JohnH)-[:ACTED_IN {roles:['High Chancellor Adam Sutler']}]->(VforVendetta),
+ (BenM)-[:ACTED_IN {roles:['Dascomb']}]->(VforVendetta),
+ (JamesM)-[:DIRECTED]->(VforVendetta),
+ (LillyW)-[:PRODUCED]->(VforVendetta),
+ (LanaW)-[:PRODUCED]->(VforVendetta),
+ (JoelS)-[:PRODUCED]->(VforVendetta),
+ (LillyW)-[:WROTE]->(VforVendetta),
+ (LanaW)-[:WROTE]->(VforVendetta)
+
+CREATE (SpeedRacer:Movie {title:'Speed Racer', released:2008, tagline:'Speed has no limits'})
+CREATE (EmileH:Person {name:'Emile Hirsch', born:1985})
+CREATE (JohnG:Person {name:'John Goodman', born:1960})
+CREATE (SusanS:Person {name:'Susan Sarandon', born:1946})
+CREATE (MatthewF:Person {name:'Matthew Fox', born:1966})
+CREATE (ChristinaR:Person {name:'Christina Ricci', born:1980})
+CREATE (Rain:Person {name:'Rain', born:1982})
+CREATE
+ (EmileH)-[:ACTED_IN {roles:['Speed Racer']}]->(SpeedRacer),
+ (JohnG)-[:ACTED_IN {roles:['Pops']}]->(SpeedRacer),
+ (SusanS)-[:ACTED_IN {roles:['Mom']}]->(SpeedRacer),
+ (MatthewF)-[:ACTED_IN {roles:['Racer X']}]->(SpeedRacer),
+ (ChristinaR)-[:ACTED_IN {roles:['Trixie']}]->(SpeedRacer),
+ (Rain)-[:ACTED_IN {roles:['Taejo Togokahn']}]->(SpeedRacer),
+ (BenM)-[:ACTED_IN {roles:['Cass Jones']}]->(SpeedRacer),
+ (LillyW)-[:DIRECTED]->(SpeedRacer),
+ (LanaW)-[:DIRECTED]->(SpeedRacer),
+ (LillyW)-[:WROTE]->(SpeedRacer),
+ (LanaW)-[:WROTE]->(SpeedRacer),
+ (JoelS)-[:PRODUCED]->(SpeedRacer)
+
+CREATE (NinjaAssassin:Movie {title:'Ninja Assassin', released:2009, tagline:'Prepare to enter a secret world of assassins'})
+CREATE (NaomieH:Person {name:'Naomie Harris'})
+CREATE
+ (Rain)-[:ACTED_IN {roles:['Raizo']}]->(NinjaAssassin),
+ (NaomieH)-[:ACTED_IN {roles:['Mika Coretti']}]->(NinjaAssassin),
+ (RickY)-[:ACTED_IN {roles:['Takeshi']}]->(NinjaAssassin),
+ (BenM)-[:ACTED_IN {roles:['Ryan Maslow']}]->(NinjaAssassin),
+ (JamesM)-[:DIRECTED]->(NinjaAssassin),
+ (LillyW)-[:PRODUCED]->(NinjaAssassin),
+ (LanaW)-[:PRODUCED]->(NinjaAssassin),
+ (JoelS)-[:PRODUCED]->(NinjaAssassin)
+
+CREATE (TheGreenMile:Movie {title:'The Green Mile', released:1999, tagline:"Walk a mile you'll never forget."})
+CREATE (MichaelD:Person {name:'Michael Clarke Duncan', born:1957})
+CREATE (DavidM:Person {name:'David Morse', born:1953})
+CREATE (SamR:Person {name:'Sam Rockwell', born:1968})
+CREATE (GaryS:Person {name:'Gary Sinise', born:1955})
+CREATE (PatriciaC:Person {name:'Patricia Clarkson', born:1959})
+CREATE (FrankD:Person {name:'Frank Darabont', born:1959})
+CREATE
+ (TomH)-[:ACTED_IN {roles:['Paul Edgecomb']}]->(TheGreenMile),
+ (MichaelD)-[:ACTED_IN {roles:['John Coffey']}]->(TheGreenMile),
+ (DavidM)-[:ACTED_IN {roles:['Brutus "Brutal" Howell']}]->(TheGreenMile),
+ (BonnieH)-[:ACTED_IN {roles:['Jan Edgecomb']}]->(TheGreenMile),
+ (JamesC)-[:ACTED_IN {roles:['Warden Hal Moores']}]->(TheGreenMile),
+ (SamR)-[:ACTED_IN {roles:['"Wild Bill" Wharton']}]->(TheGreenMile),
+ (GaryS)-[:ACTED_IN {roles:['Burt Hammersmith']}]->(TheGreenMile),
+ (PatriciaC)-[:ACTED_IN {roles:['Melinda Moores']}]->(TheGreenMile),
+ (FrankD)-[:DIRECTED]->(TheGreenMile)
+
+CREATE (FrostNixon:Movie {title:'Frost/Nixon', released:2008, tagline:'400 million people were waiting for the truth.'})
+CREATE (FrankL:Person {name:'Frank Langella', born:1938})
+CREATE (MichaelS:Person {name:'Michael Sheen', born:1969})
+CREATE (OliverP:Person {name:'Oliver Platt', born:1960})
+CREATE
+ (FrankL)-[:ACTED_IN {roles:['Richard Nixon']}]->(FrostNixon),
+ (MichaelS)-[:ACTED_IN {roles:['David Frost']}]->(FrostNixon),
+ (KevinB)-[:ACTED_IN {roles:['Jack Brennan']}]->(FrostNixon),
+ (OliverP)-[:ACTED_IN {roles:['Bob Zelnick']}]->(FrostNixon),
+ (SamR)-[:ACTED_IN {roles:['James Reston, Jr.']}]->(FrostNixon),
+ (RonH)-[:DIRECTED]->(FrostNixon)
+
+CREATE (Hoffa:Movie {title:'Hoffa', released:1992, tagline:"He didn't want law. He wanted justice."})
+CREATE (DannyD:Person {name:'Danny DeVito', born:1944})
+CREATE (JohnR:Person {name:'John C. Reilly', born:1965})
+CREATE
+ (JackN)-[:ACTED_IN {roles:['Hoffa']}]->(Hoffa),
+ (DannyD)-[:ACTED_IN {roles:['Robert "Bobby" Ciaro']}]->(Hoffa),
+ (JTW)-[:ACTED_IN {roles:['Frank Fitzsimmons']}]->(Hoffa),
+ (JohnR)-[:ACTED_IN {roles:['Peter "Pete" Connelly']}]->(Hoffa),
+ (DannyD)-[:DIRECTED]->(Hoffa)
+
+CREATE (Apollo13:Movie {title:'Apollo 13', released:1995, tagline:'Houston, we have a problem.'})
+CREATE (EdH:Person {name:'Ed Harris', born:1950})
+CREATE (BillPax:Person {name:'Bill Paxton', born:1955})
+CREATE
+ (TomH)-[:ACTED_IN {roles:['Jim Lovell']}]->(Apollo13),
+ (KevinB)-[:ACTED_IN {roles:['Jack Swigert']}]->(Apollo13),
+ (EdH)-[:ACTED_IN {roles:['Gene Kranz']}]->(Apollo13),
+ (BillPax)-[:ACTED_IN {roles:['Fred Haise']}]->(Apollo13),
+ (GaryS)-[:ACTED_IN {roles:['Ken Mattingly']}]->(Apollo13),
+ (RonH)-[:DIRECTED]->(Apollo13)
+
+CREATE (Twister:Movie {title:'Twister', released:1996, tagline:"Don't Breathe. Don't Look Back."})
+CREATE (PhilipH:Person {name:'Philip Seymour Hoffman', born:1967})
+CREATE (JanB:Person {name:'Jan de Bont', born:1943})
+CREATE
+ (BillPax)-[:ACTED_IN {roles:['Bill Harding']}]->(Twister),
+ (HelenH)-[:ACTED_IN {roles:['Dr. Jo Harding']}]->(Twister),
+ (ZachG)-[:ACTED_IN {roles:['Eddie']}]->(Twister),
+ (PhilipH)-[:ACTED_IN {roles:['Dustin "Dusty" Davis']}]->(Twister),
+ (JanB)-[:DIRECTED]->(Twister)
+
+CREATE (CastAway:Movie {title:'Cast Away', released:2000, tagline:'At the edge of the world, his journey begins.'})
+CREATE (RobertZ:Person {name:'Robert Zemeckis', born:1951})
+CREATE
+ (TomH)-[:ACTED_IN {roles:['Chuck Noland']}]->(CastAway),
+ (HelenH)-[:ACTED_IN {roles:['Kelly Frears']}]->(CastAway),
+ (RobertZ)-[:DIRECTED]->(CastAway)
+
+CREATE (OneFlewOvertheCuckoosNest:Movie {title:"One Flew Over the Cuckoo's Nest", released:1975, tagline:"If he's crazy, what does that make you?"})
+CREATE (MilosF:Person {name:'Milos Forman', born:1932})
+CREATE
+ (JackN)-[:ACTED_IN {roles:['Randle McMurphy']}]->(OneFlewOvertheCuckoosNest),
+ (DannyD)-[:ACTED_IN {roles:['Martini']}]->(OneFlewOvertheCuckoosNest),
+ (MilosF)-[:DIRECTED]->(OneFlewOvertheCuckoosNest)
+
+CREATE (SomethingsGottaGive:Movie {title:"Something's Gotta Give", released:2003})
+CREATE (DianeK:Person {name:'Diane Keaton', born:1946})
+CREATE (NancyM:Person {name:'Nancy Meyers', born:1949})
+CREATE
+ (JackN)-[:ACTED_IN {roles:['Harry Sanborn']}]->(SomethingsGottaGive),
+ (DianeK)-[:ACTED_IN {roles:['Erica Barry']}]->(SomethingsGottaGive),
+ (Keanu)-[:ACTED_IN {roles:['Julian Mercer']}]->(SomethingsGottaGive),
+ (NancyM)-[:DIRECTED]->(SomethingsGottaGive),
+ (NancyM)-[:PRODUCED]->(SomethingsGottaGive),
+ (NancyM)-[:WROTE]->(SomethingsGottaGive)
+
+CREATE (BicentennialMan:Movie {title:'Bicentennial Man', released:1999, tagline:"One robot's 200 year journey to become an ordinary man."})
+CREATE (ChrisC:Person {name:'Chris Columbus', born:1958})
+CREATE
+ (Robin)-[:ACTED_IN {roles:['Andrew Marin']}]->(BicentennialMan),
+ (OliverP)-[:ACTED_IN {roles:['Rupert Burns']}]->(BicentennialMan),
+ (ChrisC)-[:DIRECTED]->(BicentennialMan)
+
+CREATE (CharlieWilsonsWar:Movie {title:"Charlie Wilson's War", released:2007, tagline:"A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire."})
+CREATE (JuliaR:Person {name:'Julia Roberts', born:1967})
+CREATE
+ (TomH)-[:ACTED_IN {roles:['Rep. Charlie Wilson']}]->(CharlieWilsonsWar),
+ (JuliaR)-[:ACTED_IN {roles:['Joanne Herring']}]->(CharlieWilsonsWar),
+ (PhilipH)-[:ACTED_IN {roles:['Gust Avrakotos']}]->(CharlieWilsonsWar),
+ (MikeN)-[:DIRECTED]->(CharlieWilsonsWar)
+
+CREATE (ThePolarExpress:Movie {title:'The Polar Express', released:2004, tagline:'This Holiday Season... Believe'})
+CREATE
+ (TomH)-[:ACTED_IN {roles:['Hero Boy', 'Father', 'Conductor', 'Hobo', 'Scrooge', 'Santa Claus']}]->(ThePolarExpress),
+ (RobertZ)-[:DIRECTED]->(ThePolarExpress)
+
+CREATE (ALeagueofTheirOwn:Movie {title:'A League of Their Own', released:1992, tagline:'Once in a lifetime you get a chance to do something different.'})
+CREATE (Madonna:Person {name:'Madonna', born:1954})
+CREATE (GeenaD:Person {name:'Geena Davis', born:1956})
+CREATE (LoriP:Person {name:'Lori Petty', born:1963})
+CREATE (PennyM:Person {name:'Penny Marshall', born:1943})
+CREATE
+ (TomH)-[:ACTED_IN {roles:['Jimmy Dugan']}]->(ALeagueofTheirOwn),
+ (GeenaD)-[:ACTED_IN {roles:['Dottie Hinson']}]->(ALeagueofTheirOwn),
+ (LoriP)-[:ACTED_IN {roles:['Kit Keller']}]->(ALeagueofTheirOwn),
+ (RosieO)-[:ACTED_IN {roles:['Doris Murphy']}]->(ALeagueofTheirOwn),
+ (Madonna)-[:ACTED_IN {roles:['"All the Way" Mae Mordabito']}]->(ALeagueofTheirOwn),
+ (BillPax)-[:ACTED_IN {roles:['Bob Hinson']}]->(ALeagueofTheirOwn),
+ (PennyM)-[:DIRECTED]->(ALeagueofTheirOwn)
+
+CREATE (PaulBlythe:Person {name:'Paul Blythe'})
+CREATE (AngelaScope:Person {name:'Angela Scope'})
+CREATE (JessicaThompson:Person {name:'Jessica Thompson'})
+CREATE (JamesThompson:Person {name:'James Thompson'})
+
+CREATE
+ (JamesThompson)-[:FOLLOWS]->(JessicaThompson),
+ (AngelaScope)-[:FOLLOWS]->(JessicaThompson),
+ (PaulBlythe)-[:FOLLOWS]->(AngelaScope)
+
+CREATE
+ (JessicaThompson)-[:REVIEWED {summary:'An amazing journey', rating:95}]->(CloudAtlas),
+ (JessicaThompson)-[:REVIEWED {summary:'Silly, but fun', rating:65}]->(TheReplacements),
+ (JamesThompson)-[:REVIEWED {summary:'The coolest football movie ever', rating:100}]->(TheReplacements),
+ (AngelaScope)-[:REVIEWED {summary:'Pretty funny at times', rating:62}]->(TheReplacements),
+ (JessicaThompson)-[:REVIEWED {summary:'Dark, but compelling', rating:85}]->(Unforgiven),
+ (JessicaThompson)-[:REVIEWED {summary:"Slapstick redeemed only by the Robin Williams and Gene Hackman's stellar performances", rating:45}]->(TheBirdcage),
+ (JessicaThompson)-[:REVIEWED {summary:'A solid romp', rating:68}]->(TheDaVinciCode),
+ (JamesThompson)-[:REVIEWED {summary:'Fun, but a little far fetched', rating:65}]->(TheDaVinciCode),
+ (JessicaThompson)-[:REVIEWED {summary:'You had me at Jerry', rating:92}]->(JerryMaguire)
+
+WITH TomH as a
+MATCH (a)-[:ACTED_IN]->(m)<-[:DIRECTED]-(d) RETURN a,m,d LIMIT 10;
diff --git a/pom.xml b/pom.xml
index dd87e68dd6..db325c0b95 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,12 +8,13 @@
pom
gephi-plugins
-
+
+ modules/Neo4jPlugin
-
+
@@ -21,7 +22,7 @@
${project.build.directory}/plugins_clusters
github
-
+
scm:git:git://github.com/gephi/gephi-plugins.git
@@ -63,7 +64,7 @@
tar.gz
-
+
@@ -97,7 +98,7 @@
-
+
@@ -123,7 +124,7 @@
-
+
org.apache.maven.plugins
@@ -147,7 +148,7 @@
-
+
org.apache.netbeans.utilities
@@ -171,7 +172,7 @@
-
+