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 @@ - +