Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

added better button for link generation, added crude auto-indexing su…

…pport for mutating cypher queries
  • Loading branch information...
commit 4625ab25206fb8667bc3f166fa7bd4054c8335cc 1 parent d73303e
@jexp jexp authored
View
7 src/main/java/org/neo4j/community/console/CypherExportService.java
@@ -21,9 +21,10 @@
public String export() {
StringBuilder sb = new StringBuilder();
init(sb);
- int nodes = appendNodes(sb);
- appendRelationships(sb, nodes);
- return sb.toString();
+ int count = appendNodes(sb);
+ count = appendRelationships(sb, count);
+ if (count > 0) return sb.toString();
+ return "";
}
private void init(StringBuilder sb) {
View
35 src/main/java/org/neo4j/community/console/CypherQueryExecutor.java
@@ -7,9 +7,9 @@
import scala.collection.JavaConversions;
import java.lang.reflect.Method;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* @author mh
@@ -17,12 +17,19 @@
*/
public class CypherQueryExecutor {
private static Method createTimedResults = getMethod(PipeExecutionResult.class, "createTimedResults");
+ private static final Pattern PROPERTY_PATTERN = Pattern.compile("((\\w+)\\s*:|\\w+\\.(\\w+)\\s*=)",Pattern.MULTILINE|Pattern.DOTALL);
private org.neo4j.cypher.ExecutionEngine executionEngine;
+ private final Index index;
- public CypherQueryExecutor(GraphDatabaseService gdb) {
+ public CypherQueryExecutor(GraphDatabaseService gdb, Index index) {
+ this.index = index;
executionEngine = new org.neo4j.cypher.ExecutionEngine(gdb);
}
+ public boolean isMutatingQuery(String query) {
+ return query.matches("(?is).*\\b(create|relate|delete|set)\\b.*");
+ }
+
public static class CypherResult implements Iterable<Map<String, Object>> {
private final List<String> columns;
private final String text;
@@ -59,11 +66,31 @@ public String toString() {
}
public CypherResult cypherQuery(String query) {
+ if (isMutatingQuery(query)) {
+ registerProperties(query);
+ }
org.neo4j.cypher.PipeExecutionResult result = (org.neo4j.cypher.PipeExecutionResult) executionEngine.execute(query);
Tuple2<scala.collection.immutable.List<scala.collection.immutable.Map<String, Object>>, String> timedResults = createTimedResults(result);
return new CypherResult(result.columns(), result.dumpToString(), timedResults._1());
}
+ private void registerProperties(String query) {
+ Set<String> properties = extractProperties(query);
+ index.registerProperty(properties);
+ }
+
+ // TODO should get metadata from the cypher query
+ // does not take care of quoted, non-identifier properties
+ Set<String> extractProperties(String query) {
+ final Matcher matcher = PROPERTY_PATTERN.matcher(query);
+ final Set<String> properties = new HashSet<String>();
+ while (matcher.find()) {
+ if (matcher.group(2)!=null) properties.add(matcher.group(2));
+ if (matcher.group(3)!=null) properties.add(matcher.group(3));
+ }
+ return properties;
+ }
+
@SuppressWarnings("unchecked")
private Tuple2<scala.collection.immutable.List<scala.collection.immutable.Map<String, Object>>, String> createTimedResults(PipeExecutionResult result) {
try {
View
10 ...neo4j/community/console/GeoffService.java → ...community/console/GeoffImportService.java
@@ -1,6 +1,5 @@
package org.neo4j.community.console;
-import com.google.gson.Gson;
import org.neo4j.geoff.Geoff;
import org.neo4j.geoff.Subgraph;
import org.neo4j.geoff.except.SubgraphError;
@@ -10,17 +9,16 @@
import java.util.HashMap;
import java.util.Map;
-import java.util.TreeMap;
/**
* @author mh
* @since 08.04.12
*/
-class GeoffService {
+class GeoffImportService {
private final GraphDatabaseService gdb;
private final Index index;
- GeoffService(GraphDatabaseService gdb, Index index) {
+ GeoffImportService(GraphDatabaseService gdb, Index index) {
this.gdb = gdb;
this.index = index;
}
@@ -41,8 +39,8 @@
private void registerProperties(Subgraph subgraph) {
for (Subgraph.Rule rule : subgraph) {
- if (rule==null) continue;
- index.registerProperty(rule.getData());
+ if (rule==null || rule.getData()==null) continue;
+ index.registerProperty(rule.getData().keySet());
}
}
}
View
7 src/main/java/org/neo4j/community/console/Index.java
@@ -6,6 +6,7 @@
import org.neo4j.graphdb.index.AutoIndexer;
import org.neo4j.graphdb.index.RelationshipAutoIndexer;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -30,9 +31,9 @@ private void enableAutoIndex(AutoIndexer<? extends PropertyContainer> autoIndexe
autoIndexer.getAutoIndex();
}
- public void registerProperty(Map<String, Object> data) {
- if (data == null) return;
- for (String prop : data.keySet()) {
+ public void registerProperty(final Collection<String> properties) {
+ if (properties == null) return;
+ for (String prop : properties) {
if (autoIndexedProperties.contains(prop)) continue;
System.out.println("Auto-Indexing "+prop);
autoIndexedProperties.add(prop);
View
16 src/main/java/org/neo4j/community/console/Neo4jService.java
@@ -17,13 +17,14 @@
class Neo4jService {
private GraphDatabaseService gdb = new ImpermanentGraphDatabase();
- private CypherQueryExecutor cypherQueryExecutor = new CypherQueryExecutor(gdb);
- private GeoffService geoffService = new GeoffService(gdb,new Index(gdb));
+ private Index index = new Index(gdb);
+ private CypherQueryExecutor cypherQueryExecutor = new CypherQueryExecutor(gdb,index);
+ private GeoffImportService geoffService = new GeoffImportService(gdb, index);
private GeoffExportService geoffExportService = new GeoffExportService(gdb);
private CypherExportService cypherExportService = new CypherExportService(gdb);
public Map cypherQueryViz(String query) {
- final boolean invalidQuery = query == null || query.trim().isEmpty() || isMutatingQuery(query);
+ final boolean invalidQuery = query == null || query.trim().isEmpty() || cypherQueryExecutor.isMutatingQuery(query);
return invalidQuery ? cypherQueryViz((CypherQueryExecutor.CypherResult) null) : cypherQueryViz(cypherQuery(query));
}
public Map cypherQueryViz(CypherQueryExecutor.CypherResult result) {
@@ -33,10 +34,6 @@ public Map cypherQueryViz(CypherQueryExecutor.CypherResult result) {
return map("nodes", nodes.values(), "links", relationships.values());
}
- public boolean isMutatingQuery(String query) {
- return query.matches("(?is).*\\b(create|relate|delete|set)\\b.*");
- }
-
private void markCypherResults(CypherQueryExecutor.CypherResult result, Map<Long, Map<String, Object>> nodes, Map<Long, Map<String, Object>> rels) {
if (result==null) return;
for (Map<String, Object> row : result) {
@@ -131,6 +128,7 @@ public void stop() {
if (gdb!=null) {
System.err.println("Shutting down service "+this);
gdb.shutdown();
+ index = null;
cypherQueryExecutor=null;
geoffExportService =null;
cypherExportService =null;
@@ -159,4 +157,8 @@ public boolean hasReferenceNode() {
return false;
}
}
+
+ public boolean isMutatingQuery(String query) {
+ return cypherQueryExecutor.isMutatingQuery(query);
+ }
}
View
3  src/main/webapp/index.html
@@ -58,7 +58,8 @@
<h3>Initial graph setup (<a href="#" onclick="export_graph('cypher');">Cypher</a> or <a href="#" onclick="export_graph('geoff');">Geoff</a>):</h3><textarea id="share_init" rows="5" cols="80" style="width:80%"></textarea>
<h3>Initial Query (Cypher):</h3><input type="text" id="share_query" style="width:80%"/><br/>
<h3 style="display: inline-block;">Remove Root Node: </h3><input type="checkbox" id="share_no_root" style="vertical-align: bottom;"/><br/>
- Adjust the above parameters to fit your intention, then <a href="#" onclick="generate_url();">click to generate</a> the final URLs to share.<br/>
+ Adjust the above parameters to fit your intention, then <button class="btn_pretty" href="#" onclick="generate_url();">click to generate</button> the final URLs to share.<br/>
+
Share as a link (email, web etc)
<input type="url" id="share_url" style="width:80%" onclick="this.select();"/><br/>
<!-- AddThis Button BEGIN -->
View
2  src/main/webapp/javascripts/console.js
@@ -109,7 +109,7 @@ function toggleGraph() {
}
function toggleShare() {
- $.ajax("console/to_cypher", {
+ $.ajax("console/to_geoff", {
type:"GET",
success:function (data) {
$('#share_init').val(data);
View
19 src/main/webapp/stylesheets/main.css
@@ -76,6 +76,25 @@ a:link, a:visited {
padding: 20px;
}
+.btn_pretty{
+ -moz-border-radius: 4px;-webkit-border-radius: 4px;border-radius: 4px;
+ -moz-box-shadow: 0 0 4px #000;-webkit-box-shadow: 0 0 4px #000;box-shadow: 0 0 4px #000;
+ border:none;
+ background-color:#606060;
+ padding:5px;
+ display:inline-block;
+ text-decoration:none;
+ color:#Fff;
+ text-shadow: 1px 1px 1px #2e2e2e;
+ filter: dropshadow(color=#2e2e2e, offx=1, offy=1);
+ font-size:100%;
+ vertical-align:middle;
+}
+.btn_pretty:hover{
+ -webkit-box-shadow: 0 0 0;
+ -moz-box-shadow: 0 0 0;
+ box-shadow: 0 0 0;
+}
.button {
z-index: 100;
position: absolute;
View
60 src/test/java/org/neo4j/community/console/CypherQueryExecutorTest.java
@@ -0,0 +1,60 @@
+package org.neo4j.community.console;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.neo4j.test.ImpermanentGraphDatabase;
+
+import java.util.Map;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.*;
+import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
+import static org.junit.internal.matchers.IsCollectionContaining.hasItems;
+
+/**
+ * @author mh
+ * @since 22.04.12
+ */
+public class CypherQueryExecutorTest {
+
+ private ImpermanentGraphDatabase gdb;
+ private CypherQueryExecutor cypherQueryExecutor;
+
+ @Before
+ public void setUp() throws Exception {
+ gdb = new ImpermanentGraphDatabase();
+ cypherQueryExecutor = new CypherQueryExecutor(gdb, new Index(gdb));
+ }
+
+ @Test
+ public void testIsMutatingQuery() throws Exception {
+ assertFalse(cypherQueryExecutor.isMutatingQuery(""));
+ assertFalse(cypherQueryExecutor.isMutatingQuery("start n = node(1) return n"));
+ assertTrue(cypherQueryExecutor.isMutatingQuery("start n = node(1) with n create node m={ name: 'Andres'}"));
+ assertTrue(cypherQueryExecutor.isMutatingQuery("start n = node(1) with n create rel n-[:KNOWS]->n"));
+ assertTrue(cypherQueryExecutor.isMutatingQuery("start n = node(1) with n delete n"));
+ assertTrue(cypherQueryExecutor.isMutatingQuery("start n = node(1) with n set n.name = 'Andres'"));
+ }
+
+ @Test
+ public void testExtractProperties() throws Exception {
+ assertTrue(cypherQueryExecutor.extractProperties("").isEmpty());
+ assertTrue(cypherQueryExecutor.extractProperties("start n = node(1) return n").isEmpty());
+ assertThat(cypherQueryExecutor.extractProperties("start n = node(1) with n create node m={ name: 'Andres'}"), hasItem("name"));
+ assertThat(cypherQueryExecutor.extractProperties("start n = node(1) with n create rel n-[:KNOWS {name:'Friends', since : 2000}]->n"), hasItems("name", "since"));
+ assertThat(cypherQueryExecutor.extractProperties("start n = node(1) with n create node m={ name: 'Andres'} set n.age = 19"), hasItems("name", "age"));
+ }
+
+ @Test
+ public void testCypherQuery() throws Exception {
+ final CypherQueryExecutor.CypherResult result = cypherQueryExecutor.cypherQuery("start n = node(0) return n");
+ assertEquals(asList("n"), result.getColumns());
+ assertTrue(result.getText().contains("Node[0]"));
+ for (Map<String, Object> row : result) {
+ assertEquals(1,row.size());
+ assertEquals(true,row.containsKey("n"));
+ assertEquals(gdb.getReferenceNode(),row.get("n"));
+ }
+ }
+
+}
View
9 ...rg/neo4j/community/console/IndexTest.java → ...rg/neo4j/community/console/IndexTest.java
@@ -8,6 +8,9 @@
import org.neo4j.helpers.collection.MapUtil;
import org.neo4j.test.ImpermanentGraphDatabase;
+import java.util.Arrays;
+import java.util.Collections;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -49,21 +52,21 @@ public void testRegisterNullProperty() throws Exception {
}
@Test
public void testRegisterEmptyProperty() throws Exception {
- index.registerProperty(MapUtil.map());
+ index.registerProperty(Collections.<String>emptySet());
assertEquals(true, index.getAutoIndexedProperties().isEmpty());
assertEquals(true,nodeAutoIndexer.getAutoIndexedProperties().isEmpty());
assertEquals(true,relationshipAutoIndexer.getAutoIndexedProperties().isEmpty());
}
@Test
public void testRegisterTwoProperties() throws Exception {
- index.registerProperty(MapUtil.map("foo",null,"bar",null));
+ index.registerProperty(Arrays.asList("foo", "bar"));
assertThat(index.getAutoIndexedProperties(),hasItems("foo","bar"));
assertThat(nodeAutoIndexer.getAutoIndexedProperties(),hasItems("foo","bar"));
assertThat(relationshipAutoIndexer.getAutoIndexedProperties(),hasItems("foo","bar"));
}
@Test
public void testRegisterOneProperty() throws Exception {
- index.registerProperty(MapUtil.map("foo",null));
+ index.registerProperty(Arrays.asList("foo"));
assertThat(index.getAutoIndexedProperties(), hasItems("foo"));
assertThat(nodeAutoIndexer.getAutoIndexedProperties(),hasItems("foo"));
assertThat(relationshipAutoIndexer.getAutoIndexedProperties(),hasItems("foo"));
Please sign in to comment.
Something went wrong with that request. Please try again.