Skip to content

Commit

Permalink
added fkr.createRelationships procedure
Browse files Browse the repository at this point in the history
  • Loading branch information
kvegter committed Jun 12, 2017
1 parent 0f1ef01 commit fb683ac
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 10 deletions.
48 changes: 46 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ The tool is inspired by GraphGen and the faker libraries (java faker), However t

The complete help with detailed instructions and examples on how to create the property files with the data definitions for the data loader can be found in the doc directory (doc/index.html).

## Cypher Faker Functions
## Cypher Faker Functions and Procedures

You can call dbms.function() in the browser to see the available fkr. functions.
You can call dbms.function() in the browser to see the available fkr. functions. There is one procedure to create relationships: fkr.createRelations.

examples:

```$xslt
// generating 1000 Person nodes
foreach (i in range(0,1000) |
Expand All @@ -50,6 +51,7 @@ foreach (i in range(0,1000) |
)
```


```
// generating 500 CreditCards
foreach (a in range(0,500) |
Expand All @@ -59,6 +61,48 @@ foreach (a in range(0,500) |
}
```

```$xslt
//
// generating a Person City structute with relationships
//
create index on :City(name);
foreach (i in range(0,100000) |
create (p:Person:Proc { uid : i })
set p += fkr.person('1960-01-01','2000-01-01')
)
;
foreach (ci in range(0,40) |
merge (cit:City:Proc { name : fkr.stringFromFile("cities.txt") })
)
;
match (c:City:Proc) remove c:Proc with collect(c) as cities
match (p:Person:Proc) remove p:Proc with cities, collect(p) as persons
with cities
, persons
call fkr.createRelations(persons, "LIVES_IN" , cities, "n-1") yield relationships as livesRelations
call fkr.createRelations(persons, "IS_MARE" , cities , "1-n") yield relationships as mareRelations
call fkr.createRelations(cities, "HAS_POLICE_CHIEF" , persons, "1-1") yield relationships as chiefRelations
foreach ( rel in livesRelations |
set rel.likes = fkr.long(0,100)
)
return size(cities), size(persons), size(livesRelations), size(mareRelations), size(chiefRelations)
;
```
In this example we use a temporary extra Label 'Proc' to match only the nodes just created for creating relationships.

### Procedures

| name | signature | description |
| ---- | --------- | ----------- |
| fkr.createRelations | fkr.createRelations(startNodes :: LIST? OF ANY?, relationshipType :: STRING?, endNodes :: LIST? OF ANY?, cardinality :: STRING?) :: (relationships :: LIST? OF RELATIONSHIP?) | Create Relationships between a list of start nodes and a list of end nodes. cardinality can be '1-n' (The end node may have max 1 relation from the start node), 'n-1' (The start node may have max one relation to the end node) or '1-1' (The start and end node may have max one relationship of this type). Note that the procure returns one row with a list of relationships in it|


### Functions

#### Special Functions (generating multiple property values)

Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@

<groupId>org.neo4j</groupId>
<artifactId>neo4j-faker</artifactId>
<version>0.1-beta</version>
<version>0.9.0</version>

<properties>
<neo4j.version>3.2.0</neo4j.version>
<artifactId>neo4jFaker</artifactId>
<version>0.9-beta</version>
<version>0.9.0</version>
</properties>
<dependencies>
<dependency>
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/org/neo4j/faker/core/DynRel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.neo4j.faker.core;

import org.neo4j.graphdb.RelationshipType;

/**
* Created by keesv on 12/06/2017.
*/
public class DynRel {
public static RelationshipType get(final String type) {
return new RelationshipType() {
@Override
public String name() {
return type;
}
};
}
}
2 changes: 1 addition & 1 deletion src/main/java/org/neo4j/faker/core/TDGConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ public class TDGConstants {
public static final String PROP_PRE_STATEMENTS_PREFIX="tdg.pre.processing.cypher.";
public static final String PROP_START_COMMENT="tdg.comment.start";
public static final String PROP_END_COMMENT="tdg.comment.end";
public static final String VERSION = "0.8.2 Beta";
public static final String VERSION = "0.9.0";
}
132 changes: 127 additions & 5 deletions src/main/java/org/neo4j/faker/proc/DemoDataGen.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package org.neo4j.faker.proc;

import org.neo4j.faker.core.DynRel;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.kernel.internal.GraphDatabaseAPI;
import org.neo4j.logging.Log;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.UserFunction;
import org.neo4j.procedure.*;

import java.util.*;
import java.util.stream.Stream;

public class DemoDataGen {
public static final String ONE_TO_MANY = "1-n";
public static final String MANY_TO_ONE = "n-1";
public static final String ONE_TO_ONE = "1-1";


@Context public GraphDatabaseAPI db;

Expand Down Expand Up @@ -68,7 +73,6 @@ public Map<String,Object> generateDateAndTimeNowR(final @Name ("minutesRange") L
Map<String,Object> m = new HashMap<>();

long cur = System.currentTimeMillis();
//System.out.println("def " + def);
// the amount of minutes
Random r = new Random();
int cor = r.nextInt(minutes.intValue() * 60000);
Expand Down Expand Up @@ -543,4 +547,122 @@ public List<String> txtParagraphs(final @Name("paragraphCount") Long parCount) t
}



@Procedure(name = "fkr.createRelations", mode = Mode.WRITE)
@Description("Create Relationships between a list of start nodes and a list of end nodes. cardinality can be '1-n' (The end node may have max 1 relation from the start node), 'n-1' (The start node may have max one relation to the end node) or '1-1' (The start and end node may have max one relationship of this type).")
public Stream<RelationshipResult> createRelations(@Name("startNodes") final List<Object> startNodes
, @Name("relationshipType") final String relType
, @Name("endNodes") final List<Object> endNodes
, @Name("cardinality") final String cardinality) throws Exception {

List<RelationshipResult> res = new ArrayList<>();
res.add(new RelationshipResult(privateCreateRelations(startNodes,relType,endNodes,cardinality)));
return res.stream();
}

private List<Relationship> privateCreateRelations(final List<Object> startNodes
, final String relType
, final List<Object> endNodes
, final String cardinality) throws Exception {

try {
if (relType == null || relType.isEmpty()) {
throw new Exception("RelationshipType is missing!");
}
if (cardinality == null || cardinality.isEmpty()) {
throw new Exception("cardinality is missing!");
}
List<Relationship> res = new LinkedList<>();

if (startNodes == null || startNodes.size() == 0 || endNodes == null || endNodes.size() == 0) {
return res;
}
// check on startNodes
if (!validNodes(startNodes.get(0), endNodes.get(0))) {
throw new Exception("The lists of nodes must contain a node id (Long) or a Node object.");
}
if (cardinality.equals(ONE_TO_MANY)) {
// The "end node" may have max one relation from the start node
// we loop here through the end nodes and every end node may get a relation to the start node
// for determining the start node we use a randomizer.
Random random = new Random();
for (Object endNode : endNodes) {
Object startNode = startNodes.get(random.nextInt(startNodes.size()));
res.add( createRelation(startNode, relType, endNode));
}
return res;

} else if (cardinality.equals(MANY_TO_ONE)) {

// The "start node" may have max one relation to the end node
// we loop here through the start nodes and pick a random end node to connect to
Random random = new Random();
for (Object startNode: startNodes) {
Object endNode = endNodes.get(random.nextInt(endNodes.size()));
res.add( createRelation(startNode, relType, endNode));
}
return res;
} else if (cardinality.equals(ONE_TO_ONE)) {

if (startNodes.equals(endNodes)) {
// self reference we have to create now an extra list
List<Node> tmpList = new ArrayList<>();
Collections.copy(endNodes, tmpList);
Collections.shuffle(tmpList);
int index = 0;
for (Object startNode : startNodes) {
Object endNode = endNodes.get(index);
index++;
res.add(createRelation(startNode, relType, endNode));
}
return res;
}
if (startNodes.size() <= endNodes.size()) {

int index = 0;
for (Object startNode : startNodes) {
Object endNode = endNodes.get(index);
index++;
res.add(createRelation(startNode, relType, endNode));
}
return res;
} else {

int index = 0;
for (Object endNode : endNodes) {
Object startNode = startNodes.get(index);
index++;
res.add(createRelation(startNode, relType, endNode));
}
return res;
}
} else {
// invalid cardinality
throw new Exception("Invalid cardinality, Only '1-n','n-1' and '1-1' are allowed");
}
} catch (Throwable t) {
t.printStackTrace();
throw t;
}

}


private Relationship createRelation(Object start, String type, Object end) {
Node nStart = getNode(start);
Node nEnd = getNode(end);
return nStart.createRelationshipTo(nEnd, DynRel.get(type));
}

private Node getNode(Object nob) {
if (nob instanceof Number) {
return db.getNodeById(((Number) nob).longValue());
} else {
return (Node) nob;
}
}
private boolean validNodes(Object startObject, Object endObject) {
return (startObject instanceof Number || startObject instanceof Node) && (endObject instanceof Number || endObject instanceof Node);
}

}
17 changes: 17 additions & 0 deletions src/main/java/org/neo4j/faker/proc/RelationshipResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.neo4j.faker.proc;

import org.neo4j.graphdb.Relationship;

import java.util.List;

/**
* Created by keesv on 12/06/2017.
*/
public class RelationshipResult {
public final List<Relationship> relationships;

public RelationshipResult(List<Relationship> rels) {
this.relationships = rels;
}

}

0 comments on commit fb683ac

Please sign in to comment.