After
my first
experience in installing Neo4j
graph database I decided to continue my experiments by writing a
little Java demo program. The scope of my program just to learn how
to connect to a Neo4j embedded graph, to generate ,connect and query
some hundreds of nodes. Neo4j site and the downloaded manual
provide plenty of documentation about interfacing with Java, and the
other supported languages.
Project
set-up
Setting
up a Java project is quit simple: just matter of including all Neo4j
libraries jars, available in the 'lib' folder, in the project
class-path. To make easier future projects set-up I prepared, in
Netbeans, a custom library configuration.
In
order to work with a Neo4J database it must be initialized by
providing the path where datawill be saved:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
protected final static String DBPATH = "./localdb"; | |
protected GraphDatabaseService db = null; | |
… | |
protected void startup() | |
{ | |
// instantiate the database | |
db = new GraphDatabaseFactory().newEmbeddedDatabase(DBPATH); | |
… |
since
database startup is a relatively heavy operation documentation warmly
suggests to execute this operation only at startup.
As
all database operations are concluded, before closing the
application, database resources must be released by calling the
shutdown method:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
… | |
db.shutdown(); | |
… |
Documentation
suggest attaching a shutdown hook to application, just after database
startup, in order to ensure proper database shutdown in every case
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// register shutdown hook | |
Runtime.getRuntime().addShutdownHook(new Thread() | |
{ | |
@Override | |
public void run() | |
{ | |
db.shutdown(); | |
} | |
}); |
Last
but not least recommendation: all nodes operation must be carried
inside a transaction
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
try (final Transaction t = db.beginTx()) | |
{ | |
…. | |
t.success(); | |
} |
Hello
→ World
The
most basic example coming from documentation creates two nodes and
links them together
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
… | |
Node n1 = db.createNode(); | |
n1.setProperty("text", "Hello"); | |
Node n2 = db.createNode(); | |
n2.setProperty("text", "World"); | |
Relationship r = n1.createRelationshipTo(n2, DemoRelationship.linked_to); | |
r.setProperty("text", "to all the"); | |
… |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
… | |
public enum DemoRelationship implements RelationshipType | |
{ | |
linked_to | |
} | |
… |
To go a little further than the usual
“hello-world” I decided to write a small demo that filled a graph
with a group of random generated “people”. First I made a
function that adds to the graph a node representing a person with
some random properties:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
… | |
private Node randomPerson(String family, int generation) | |
{ | |
Node person = db.createNode(personLabel); | |
String sex = Math.random()>.5?"M":"F"; | |
String name; | |
if("F".equals(sex)) | |
{ | |
name = fNames[(int)(Math.random()*fNames.length)]; | |
} | |
else | |
{ | |
name = mNames[(int)(Math.random()*mNames.length)]; | |
} | |
person.setProperty("sex", sex); | |
person.setProperty("name", name); | |
person.setProperty("family", family); | |
person.setProperty("generation", generation); | |
return person; | |
} | |
… |
then I defined a first population pass where I
filled the graph with an initial group (generation “0”) of random
people. I used a “root” node as a marker to avoid repeating this
step more than once.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
… | |
ResourceIterable<Node> gotRoot = db.findNodesByLabelAndProperty(rootLabel, "type", "root"); | |
if(gotRoot==null || !gotRoot.iterator().hasNext()) | |
{ | |
Node root = db.createNode(rootLabel); | |
root.setProperty("type", "root"); | |
// initial database fill | |
for(int j=0;j<INITIAL_FILL;j++) | |
{ | |
Node person = randomPerson(); | |
person.createRelationshipTo(root, PersonRelationships.initial_setup); | |
} | |
t.success(); | |
result = root; | |
} | |
else | |
{ | |
result = gotRoot.iterator().next(); | |
} | |
… |
In each “generation” iteration I selected two
random group of “nodes”, filtered by sex, from the previous
generation using the following Cypher
query.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
… | |
private static final String GENERATION_QUERY = "match (n:person) " | |
+ "where not (n-[:married_with]->(:person)) " | |
+ "and n.sex={sex} " | |
+ "and rand()>{pick} " | |
+ "and n.generation={generation} " | |
+ "return n;"; | |
… |
the nodes from the two sets are then linked
together and some “child” nodes are then created
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
… | |
// mate some of same level nodes | |
Node male = (Node) males.columnAs("n").next(); | |
Node female = (Node) females.columnAs("n").next(); | |
male.createRelationshipTo(female, PersonRelationships.married_with); | |
// add random children to some random mated nodes | |
if(Math.random()>BIRTH_RATIO) | |
{ | |
final double children = Math.random()*MAX_CHLDREN; | |
for(int j=1;j<children;j++) | |
{ | |
Node c = randomPerson((String) male.getProperty("family"), generation + 1); | |
c.createRelationshipTo(male, PersonRelationships.child_of); | |
c.createRelationshipTo(female, PersonRelationships.child_of); | |
} | |
} | |
… |
Conclusions
After playing a little with birth ratio and other
parameters I managed to fill the graph with some hundreds of widely
connected nodes. The graph is not a realistic representation of a
real population ,since I should have considered many more
constraints, but is still an interesting starting point. The full
code can be downloaded from here.
No comments :
Post a Comment