Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

Commit

Permalink
#777 Unordered children in hierarchical display: replaced slow cypher…
Browse files Browse the repository at this point in the history
… queries with fast path traversal
  • Loading branch information
Luthien-in-edhil committed Jan 4, 2016
1 parent 167fb1a commit 4d52a42
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 120 deletions.
37 changes: 35 additions & 2 deletions neo4j-startup-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<properties>
<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<neo4j.version>2.1.8</neo4j.version>
</properties>

<dependencies>
Expand All @@ -29,13 +30,45 @@
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>server-api</artifactId>
<version>2.0.3</version>
<version>${neo4j.version}</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-cypher</artifactId>
<version>2.1.2</version>
<version>${neo4j.version}</version>
</dependency>
<!--<dependency>-->
<!--<groupId>org.neo4j</groupId>-->
<!--<artifactId>neo4j</artifactId>-->
<!--<version>${neo4j.version}</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.neo4j</groupId>-->
<!--<artifactId>neo4j-kernel</artifactId>-->
<!--<type>test-jar</type>-->
<!--<version>${neo4j.version}</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.neo4j.app</groupId>-->
<!--<artifactId>neo4j-server</artifactId>-->
<!--<version>${neo4j.version}</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.neo4j.app</groupId>-->
<!--<artifactId>neo4j-server</artifactId>-->
<!--<version>${neo4j.version}</version>-->
<!--<type>test-jar</type>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.neo4j</groupId>-->
<!--<artifactId>server-api</artifactId>-->
<!--<version>2.0.3</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.neo4j</groupId>-->
<!--<artifactId>neo4j-cypher</artifactId>-->
<!--<version>2.1.2</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,22 @@
package eu.europeana.neo4j.fetch;

import eu.europeana.neo4j.mapper.ObjectMapper;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.traversal.Evaluators;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.graphdb.traversal.Uniqueness;
import org.neo4j.tooling.GlobalGraphOperations;

import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.neo4j.cypher.javacompat.ExecutionEngine;
import org.neo4j.cypher.javacompat.ExecutionResult;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.neo4j.kernel.impl.util.StringLogger;
import java.io.IOException;
import java.util.*;

/**
*
Expand All @@ -34,48 +30,131 @@
@javax.ws.rs.Path("/children")
public class Children {


private static final RelationshipType HAS_PART = DynamicRelationshipType.withName("dcterms:hasPart");
private static final RelationshipType IS_FAKE = DynamicRelationshipType.withName("isFakeOrder");
private static final RelationshipType IS_NEXT = DynamicRelationshipType.withName("edm:isNextInSequence");

private GraphDatabaseService db;
private ExecutionEngine engine;
// private GraphDatabaseService db;
// private ExecutionEngine engine;

// public Children(@Context GraphDatabaseService db) {
// this.db = db;
// this.engine = new ExecutionEngine(db, StringLogger.SYSTEM);
// }

public Children(@Context GraphDatabaseService db) {
this.db = db;
this.engine = new ExecutionEngine(db, StringLogger.SYSTEM);
}

@GET
@javax.ws.rs.Path("/nodeId/{nodeId}")
@Produces(MediaType.APPLICATION_JSON)

public Response getchildren(@PathParam("nodeId") String nodeId, @QueryParam("offset") @DefaultValue("0") int offset,
@QueryParam("limit") @DefaultValue("10") int limit) {
public Response getChildren(@PathParam("nodeId") String nodeId,
@QueryParam("offset") @DefaultValue("0") int offset,
@QueryParam("limit") @DefaultValue("10") int limit,
@Context GraphDatabaseService db) throws IOException {
List<Node> children = new ArrayList<>();
Transaction tx = db.beginTx();
try {
ExecutionResult result = engine.execute(
"start parent = node:edmsearch2(rdf_about=\"" + nodeId + "\") "
+ "MATCH (parent)-[:`dcterms:hasPart`]->(child) "
+ "WHERE NOT ()-[:isFakeOrder]->(child) "
+ "AND NOT ()-[:`edm:isNextInSequence`]->(child) "
+ "WITH child AS first "
+ "MATCH (first)-[:isFakeOrder|`edm:isNextInSequence`*]->(next) "
+ "WITH DISTINCT first + COLLECT(next) AS spool "
+ "UNWIND spool as children RETURN children "
+ "SKIP " + offset + " LIMIT " + limit);
Iterator<Node> childIterator = result.columnAs("children");
while (childIterator.hasNext()) {
children.add(childIterator.next());
try ( Transaction tx = db.beginTx() ) {
IndexManager index = db.index();
Index<Node> edmsearch2 = index.forNodes("edmsearch2");
IndexHits<Node> hits = edmsearch2.get("rdf_about", nodeId);
Node parent = hits.getSingle();
if (parent==null) {
throw new IllegalArgumentException("no node found in index for rdf_about = " + nodeId);
}
Node first = null;

// Get all children
for (Relationship r1 : parent.getRelationships(Direction.OUTGOING, HAS_PART)) {
Node child = r1.getEndNode();
if ((child.getDegree(IS_FAKE, Direction.INCOMING) == 0) &&
(child.getDegree(IS_NEXT, Direction.INCOMING) == 0)) {
first = child;
}
}
} catch (Exception e) {
Logger.getLogger(this.getClass().getCanonicalName()).log(Level.SEVERE, e.getMessage());
} finally {

if (first==null) {
throw new IllegalArgumentException("no first child for node " + parent);
}

// Go up to limit hops away
TraversalDescription td = db.traversalDescription()
.depthFirst()
.relationships(IS_FAKE, Direction.OUTGOING)
.relationships(IS_NEXT, Direction.OUTGOING)
.uniqueness(Uniqueness.RELATIONSHIP_GLOBAL)
.evaluator(Evaluators.toDepth(limit - 1));

// Add to the results
for (org.neo4j.graphdb.Path path : td.traverse(first)) {
Node child = path.endNode();
children.add(child);
}
String obj = new ObjectMapper().siblingsToJson(children, "siblings");
tx.success();
tx.finish();
return Response.ok().entity(obj).header(HttpHeaders.CONTENT_TYPE,
"application/json").build();
}
}

@GET
@javax.ws.rs.Path("degree")
public String getDegreeHistogram(@Context GraphDatabaseService gds) throws IOException {
SortedMap<Integer, Integer> histogram = new TreeMap<>();
try (Transaction tx = gds.beginTx()) {
for (Node n: GlobalGraphOperations.at(gds).getAllNodes()) {
int degree = n.getDegree();

Integer val = histogram.get(degree);
if (val==null) {
histogram.put(degree, 1);
} else {
histogram.put(degree, val+1);
}

}
tx.success();
}
StringBuilder sb = new StringBuilder();
for (Map.Entry<Integer, Integer> entry: histogram.entrySet()) {
sb.append(entry.getKey()).append(",").append(entry.getValue()).append("\n");
}
return sb.toString();
}




// @GET
// @javax.ws.rs.Path("/nodeId/{nodeId}")
// @Produces(MediaType.APPLICATION_JSON)
//
// public Response getchildren(@PathParam("nodeId") String nodeId,
// @QueryParam("offset") @DefaultValue("0") int offset,
// @QueryParam("limit") @DefaultValue("10") int limit) {
// List<Node> children = new ArrayList<>();
// Transaction tx = db.beginTx();
// try {
// ExecutionResult result = engine.execute(
// "start parent = node:edmsearch2(rdf_about=\"" + nodeId + "\") "
// + "MATCH (parent)-[:`dcterms:hasPart`]->(child) "
// + "WHERE NOT ()-[:isFakeOrder]->(child) "
// + "AND NOT ()-[:`edm:isNextInSequence`]->(child) "
// + "WITH child AS first "
// + "MATCH (first)-[:isFakeOrder|`edm:isNextInSequence`*]->(next) "
// + "WITH DISTINCT first + COLLECT(next) AS spool "
// + "UNWIND spool as children RETURN children "
// + "SKIP " + offset + " LIMIT " + limit);
// Iterator<Node> childIterator = result.columnAs("children");
// while (childIterator.hasNext()) {
// children.add(childIterator.next());
// }
// } catch (Exception e) {
// Logger.getLogger(this.getClass().getCanonicalName()).log(Level.SEVERE, e.getMessage());
// } finally {
//
// String obj = new ObjectMapper().siblingsToJson(children, "siblings");
// tx.success();
// tx.finish();
// return Response.ok().entity(obj).header(HttpHeaders.CONTENT_TYPE,
// "application/json").build();
// }
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,21 @@
package eu.europeana.neo4j.fetch;

import eu.europeana.neo4j.mapper.ObjectMapper;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.traversal.Evaluators;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.graphdb.traversal.Uniqueness;

import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.neo4j.cypher.javacompat.ExecutionEngine;
import org.neo4j.cypher.javacompat.ExecutionResult;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.neo4j.kernel.impl.util.StringLogger;
import java.util.ArrayList;
import java.util.List;

/**
*
Expand All @@ -34,14 +29,14 @@
@javax.ws.rs.Path("/following")
public class FollowingSiblings {



private static final RelationshipType IS_FAKE = DynamicRelationshipType.withName("isFakeOrder");
private static final RelationshipType IS_NEXT = DynamicRelationshipType.withName("edm:isNextInSequence");

private GraphDatabaseService db;
private ExecutionEngine engine;

public FollowingSiblings(@Context GraphDatabaseService db) {
this.db = db;
this.engine = new ExecutionEngine(db, StringLogger.SYSTEM);
}

@GET
Expand All @@ -50,26 +45,37 @@ public FollowingSiblings(@Context GraphDatabaseService db) {

public Response getfollowing(@PathParam("nodeId") String nodeId,
@QueryParam("limit") @DefaultValue("10") int limit) {

List<Node> followingSiblings = new ArrayList<>();
Transaction tx = db.beginTx();

try {
ExecutionResult result = engine.execute(
"start self = node:edmsearch2(rdf_about=\"" + nodeId + "\") "
+ " MATCH (self)-[:isFakeOrder|`edm:isNextInSequence`*]->(following) "
+ "RETURN following LIMIT " + limit);
Iterator<Node> followingIterator = result.columnAs("following");
int i = 0;
while (followingIterator.hasNext()) {
followingSiblings.add(followingIterator.next());
boolean first = false;
try ( Transaction tx = db.beginTx() ) {
IndexManager index = db.index();
Index<Node> edmsearch2 = index.forNodes("edmsearch2");
IndexHits<Node> hits = edmsearch2.get("rdf_about", nodeId);
Node sibling = hits.getSingle();
if (sibling==null) {
throw new IllegalArgumentException("no node found in index for rdf_about = " + nodeId);
}
} catch (Exception e) {
Logger.getLogger(this.getClass().getCanonicalName()).log(Level.SEVERE, e.getMessage());
} finally {

// Gather all ye following brothers and sisters but take heed! No more than in 'limit' number shall ye come!
TraversalDescription td = db.traversalDescription()
.breadthFirst()
.relationships(IS_FAKE, Direction.INCOMING)
.relationships(IS_NEXT, Direction.INCOMING)
.uniqueness(Uniqueness.RELATIONSHIP_GLOBAL)
.evaluator(Evaluators.toDepth(limit ));

// Add to the results
for (org.neo4j.graphdb.Path path : td.traverse(sibling)) {
Node child = path.endNode();
if (first) {
followingSiblings.add(child);
} else {
first = true;
}
}
String obj = new ObjectMapper().siblingsToJson(followingSiblings, "siblings");
tx.success();
tx.finish();
return Response.ok().entity(obj).header(HttpHeaders.CONTENT_TYPE,
"application/json").build();
}
Expand Down
Loading

0 comments on commit 4d52a42

Please sign in to comment.