Gremlin Scala Neo4j: Search for node, add new node and edge between - gremlin

I am unable to find a node via a key and then add a new node and an edge between them. with the movie nodes already in graph, i use:
case class GraphBuilder(movieId: Int, personId: Int)
//
val Ident = Key[String]("personId")
val ItemId = Key[String]("movieId")
//
def applyToGraph(it: GraphBuilder): Unit = {
val thisPerson = graph + ("Person", Ident -> it.personId.asInstanceOf[String])
val movies = graph.V.hasLabel("Movie").has(ItemId, it.movieId)
movies.headOption match {
case Some(v) =>
v --- "likedBy" --> thisPerson // tested with println("yay" + v)
case None => println("youre a failure")
}
graph.tx.commit()
}
But each time I run this programmatically, it correctly adds the person to the graph via thisPerson val, correctly finds the movie vertex based on the movieId, but does not create the "likedBy" edge. I have also tried without pattern matching on the option but that does not work either.
What method is best to find node, add node, add edge between added and found?

I'm a bit confused by the syntax in your snippet, but since you have to have the identifiers for both vertices, the following query should do the trick:
g.V().has("Movie", "movieId", movieId).as("m").
V().has("Person", "personId", personId).
addE("likedBy").to("m").iterate()
If the person vertex doesn't exist yet:
g.V().has("Movie", "movieId", movieId).as("m").
addV("Person").property("personId", personId).
addE("likedBy").to("m").iterate()
And if you don't know whether the person vertex already exists or not:
g.V().has("Movie", "movieId", movieId).as("m").
coalesce(
V().has("Person", "personId", personId)
addV("Person").property("personId", personId)).
addE("likedBy").to("m").iterate()

Related

Neo4j : create a recursive query/function

☼ Hello !
I want to get the critical path of an activity list, but through Neo4j.
For this, I need the Earliest Times (Start and Finish). The Earliest Start of an activity equals the greatest Earliest Finish of its predecessors, and so on.
I already had something "working". But my problem is that I just need to "recall the function". I can go down by hand, but I can't do it indefinitely...
The Activity List
Here is my code :
// LEVEL 1
/****** collect (start.successors) as startSucessors *****/
MATCH (project:Project)-[:CONTAINS]->(:Activity{tag:'Start'})-[s:ENABLES]->(:Activity)
WHERE ID(project)=toInteger(322)
WITH collect(endNode(s)) AS startSuccessors
/**** foreach node in startSucessors ****/
UNWIND startSuccessors AS node
/**** collect (node.predecessors) as nodePredecessors ****/
MATCH (activity:Activity)-[p:ENABLES]->(node)
WITH collect(startNode(p)) AS nodePredecessors, node, startSuccessors
/**** foreach activity in nodePredecessors ****/
UNWIND nodePredecessors AS activity
/**** IF (node.ES is null OR node.ES < activity.EF) ****/
WITH node, activity, startSuccessors,(node.ES = 0) AS cond1, (node.ES < activity.EF) AS cond2
MERGE (activity)-[:ENABLES]->(node)
ON MATCH SET node.ES =
CASE
/**if**/ WHEN cond1 OR cond2
/**node.ES = activity.EF**/ THEN activity.EF
END
ON MATCH SET node.EF = node.ES + node.ET
// LEVEL 2
/**T.O.D.O. : loop for each node in startSuccessors and their nodes **/
WITH startSuccessors
UNWIND startSuccessors AS node
MERGE (node)-[s2:ENABLES]->(successor:Activity)
WITH collect(successor) AS nodeSuccessors,node
UNWIND nodeSuccessors AS successor
CREATE UNIQUE (act:Activity)-[p2:ENABLES]->(successor)
WITH successor, node,act, (successor.ES = 0) AS cond3, (successor.ES < act.EF) AS cond4
MERGE (act)-[p2:ENABLES]->(successor)
ON MATCH SET successor.ES =
CASE
/**if**/ WHEN cond3 OR cond4
/**node.ES = activity.EF**/ THEN act.EF
END
ON MATCH SET successor.EF = successor.ES + successor.ET
Here is the result
Earliest Times Query Result
The second problem is that if I rerun the query, the ES and EF properties disappear ... (prove below)
Problem when rerunning the query
To repair this, I have to run this query :
MATCH (p:Project) WHERE ID(p)=322
MATCH (p)-[:CONTAINS]->(one:Activity{tag:'one'}),(p)-[:CONTAINS]->(zrht:Activity{tag:'zrht'}),(p)-[:CONTAINS]->(ore:Activity{tag:'ore'}),(p)-[:CONTAINS]->(bam:Activity{tag:'bam'}),(p)-[:CONTAINS]->(two:Activity{tag:'two'})
SET one.EF = 0,one.ES = 0,one.LF=0,one.LS=0,zrht.EF = 0,zrht.ES = 0,zrht.LF=0,zrht.LS=0,ore.EF = 0,ore.ES = 0,ore.LF=0,ore.LS=0,bam.EF = 0,bam.ES = 0,bam.LF=0,bam.LS=0,two.EF = 0,two.ES = 0,two.LF=0,two.LS=0
This javascript code reaches what I want to do.
Thank you very much for your help.
☼ I finally found what I was looking for : Project Management with Neo4j
In hopes it will help other to find in a quicker way ;)

Traversing based on multiple vertexes

I've a graph in OrientDB with vertexes Area & Place with edges visited. Your average path goes Area > visited > Place > visited > Place > visited > Place > visited > Place and so on. It tracks which places user visited after the previous one. visited contains YYYYmmDD datestamp.
I'm trying to find out all Area vertexes based on arbitrary Place vertexes for certain day - i.e. I want to know from which areas users came to a certain place after visiting certain place first.
Traversing from any single Place in the path would be easy but I need to to follow the path for only for a specific datestamp. What I did was that I created index for datestamp to get day's visited edges quickly and then finds the one that has in to the first Place. However now I can't figure out how to create a fast query that finds all Area vertexes based on the first Place while also making sure that the path contains second Place as well. I can get path between first and second Place via shortestPath() but I still have the same problem with extending the path to include Area vertexes.
I found some theory on the subject but if somebody could point me to the right direction how to use OrientDB to do this instead of pure graph theory I would really appreciate it - I've been working on this for the past week now. Originally this was done via bruteforce by traversing everything and then selecting but as the database grows it's not obviously sustainable.
I created the three Vertices 'Area', 'Place' and 'User' and the two Edges 'visited' and 'placed' where datestamp is a property on the edge 'visited.
In this way you don't have to insert everytime the User as a property on the edge.
Edit
Try this JavaScript function that has three parameters(places,date,propertyPlace)
var g=orient.getGraph();
var myPlaces=places.substring(1,places.length-1).split(",");
var b=g.command("sql","select from Area");
var result=[];
if(checkPlaces){
for(i=0;i<b.length;i++){
var listPlaces=[];
for(ind=0;ind<myPlaces.length;ind++){
listPlaces.push(myPlaces[ind]);
}
if(search(b[i],listPlaces)){
result.push(b[i]);
}
}
}
return result;
function checkPlaces() {
for(index=0;index<myPlaces.length;index++){
var place=g.command("sql","select from Place where "+ propertyPlace + "='"+myPlaces[index]+"'");
if(place.length==0){
return false;
}
}
return true;
}
function checkDate(edge){
var datestamp=edge.getRecord().field("datestamp");
var year=datestamp.getYear()+1900;
var month=datestamp.getMonth()+1;
var day=datestamp.getDate();
var app=date.split("-");
var myYear=parseInt(app[0]);
var myMonth=parseInt(app[1]);
var myDay=parseInt(app[2]);
if(year==myYear && month==myMonth && day==myDay){
return true;
}
return false;
}
function search(v,places){
var edge=v.getRecord().field("out_visited");
if(edge!=null){
var edgeIterator=edge.iterator();
while(edgeIterator.hasNext()){
var edge = edgeIterator.next();
if (checkDate(edge)) {
var v1 = edge.field("in");
if(v1!=null){
var name = v1.field(propertyPlace);
for(j=0;j<places.length;j++){
if(name==(places[j])) {
places.splice(j, 1);
break;
}
}
if(places.length==0){
return true;
}
else if(search(v1,places)){
return true;
}
}
}
}
}
return false;
}
Using the following command
select expand(result) from (select myFunction("[place1,place2]","2015-12-03","name") as result)
Let me know if it works
This is not a solution to this exact problem but instead a workaround I came up with. Inspired by Orientdb get last vertex from each path when Traversing by edge property
I changed to structure so that Area is now created per visitation instead of being static and it includes yyyymmdd timestamp also. Now I can use Area to start the query and use visited edges to get Place vertices only for a certain date.
Here's the query:
SELECT $path, $depth FROM (
TRAVERSE * FROM (
SELECT outE('visited') FROM (
SELECT EXPAND(rid) FROM INDEX:area.dt WHERE key = 20151205
)
) WHILE (#class = 'visited' AND dt = 20151205) OR #class = 'place')
WHERE #class = 'place' AND NOT (outE() contains (dt=20151205))
This returns correct paths with vertices and edges so you can verify it's only for a certain day. However note that Area is not contained in the path and I still need to figure out how to do that but if you want, you can just traverse the first visited edge backwards in the path and get it that way.

OrientDB: How to traverse a graph and get the full path of a given type

I have a programming language graph in which packages are structured as Eclipse represents, so every package is a vertex. I want to, starting from a class, traverse the edge belongsTo where the vertex's class is code_Package. In the end, I want to print the full package name. Is that possible?
The structure is similar to this one:
If I understood correctly, your situation is something like this:
create class code_Package extends V
create property code_Package.name string
create class code_ClassUnit extends V
create property code_ClassUnit.name string
create class belongsTo extends E
# class sdc.spd.ssi.Main
create vertex code_Package set name = 'sdc' #12:0
create vertex code_Package set name = 'spd' #12:1
create vertex code_Package set name = 'ssi' #12:2
create vertex code_ClassUnit set name = 'Main' #13:0
create edge belongsTo from #13:0 to #12:2
create edge belongsTo from #12:2 to #12:1
create edge belongsTo from #12:1 to #12:0
What you're asking can be achieved by defining a function (e.g. join):
var result = "";
for(var i = input.length - 1; i >= 0; i--){
result += input[i] + ".";
}
return result.substring(0, result.length - 1);
like this:
and then:
select join($all.fullPath[0])
let $all = (select unionall(name) as fullPath from (traverse out('belongsTo') from #13:0))
output:

Object differentiation

I'm creating a graph with nodes (with integer value) and edges (source, destination and weight) by reading a file with the format
1 51 1
1 72 2
1 77 1
etc.
Set<Node> nodes = new HashSet<Node>(); //a set of the nodes of a graph
ArrayList<Node> nodeList = new ArrayList<Node>();
ArrayList<Edge> edgeList = new ArrayList<Edge>();
...
Node node1=new Node(Integer.parseInt(temprelation[0]));
Node node2=new Node(Integer.parseInt(temprelation[1]));
nodes.add(node1);
nodes.add(node2);
Edge edge = new Edge(node1, node2, Integer.parseInt(temprelation[2]));
edgeList.add(edge);
}
The class Node has also a field "number of neighbors", and I wanted to go through all the edges and increment the number of neighbors whenever either source or destinatio appears.
for (int edge=0; edge<graph.getEdges().size(); edge++){
graph.getEdges().get(edge).getSource().neighborUp();
graph.getEdges().get(edge).getDestination().neighborUp();
}
Strangely enough, although the objects seem to be the same (I checked it with equals), the counter does not go up. E.g., for 1, it goes up once with the first edge, but does not go up when I try to increment it when the second edge is concerned. When considering the second edge before incrementing, it somehow shows the number of neighbors is 0, although I incremented the number of neighbors of the first node already in the first edge. So if I did printouts of counters before and after incrementation I always get 0 1 0 1 as if some other objects were concerned.
I assume that you use Java.
The problem is with creation of the graph, every time when you create an edge you create new objects for nodes:
Node node1=new Node(Integer.parseInt(temprelation[0]));
Node node2=new Node(Integer.parseInt(temprelation[1]));
The set contains only one copy for every Integer, but your edges contain different instancies.
To solve it you can create a map of all already parsed nodes and at every iteration instead of creating object from Integer, check if you have already created the object from Integer:
//one global object
Map<Integer,Node> map = new HashMap<Integer,Node> ();
...
Integer val = Integer.parseInt(temprelation[0]);
if (map.get(val)==null) {
map.put(val, new Node(val));
}
Node node1 = map.get(val);
val = Integer.parseInt(temprelation[1]);
if (map.get(val)==null) {
map.put(val, new Node(val));
}
Node node2 = map.get(val);

Reflection on a Scala case class

I'm trying to write a trait (in Scala 2.8) that can be mixed in to a case class, allowing its fields to be inspected at runtime, for a particular debugging purpose. I want to get them back in the order that they were declared in the source file, and I'd like to omit any other fields inside the case class. For example:
trait CaseClassReflector extends Product {
def getFields: List[(String, Any)] = {
var fieldValueToName: Map[Any, String] = Map()
for (field <- getClass.getDeclaredFields) {
field.setAccessible(true)
fieldValueToName += (field.get(this) -> field.getName)
}
productIterator.toList map { value => fieldValueToName(value) -> value }
}
}
case class Colour(red: Int, green: Int, blue: Int) extends CaseClassReflector {
val other: Int = 42
}
scala> val c = Colour(234, 123, 23)
c: Colour = Colour(234,123,23)
scala> val fields = c.getFields
fields: List[(String, Any)] = List((red,234), (green,123), (blue,23))
The above implementation is clearly flawed because it guesses the relationship between a field's position in the Product and its name by equality of the value on those field, so that the following, say, will not work:
Colour(0, 0, 0).getFields
Is there any way this can be implemented?
Look in trunk and you'll find this. Listen to the comment, this is not supported: but since I also needed those names...
/** private[scala] so nobody gets the idea this is a supported interface.
*/
private[scala] def caseParamNames(path: String): Option[List[String]] = {
val (outer, inner) = (path indexOf '$') match {
case -1 => (path, "")
case x => (path take x, path drop (x + 1))
}
for {
clazz <- getSystemLoader.tryToLoadClass[AnyRef](outer)
ssig <- ScalaSigParser.parse(clazz)
}
yield {
val f: PartialFunction[Symbol, List[String]] =
if (inner.isEmpty) {
case x: MethodSymbol if x.isCaseAccessor && (x.name endsWith " ") => List(x.name dropRight 1)
}
else {
case x: ClassSymbol if x.name == inner =>
val xs = x.children filter (child => child.isCaseAccessor && (child.name endsWith " "))
xs.toList map (_.name dropRight 1)
}
(ssig.symbols partialMap f).flatten toList
}
}
Here's a short and working version, based on the example above
trait CaseClassReflector extends Product {
def getFields = getClass.getDeclaredFields.map(field => {
field setAccessible true
field.getName -> field.get(this)
})
}
In every example I've seen the fields are in reverse order: the last item in the getFields array is the first one listed in the case class. If you use case classes "nicely", then you should just be able to map productElement(n) onto getDeclaredFields()( getDeclaredFields.length-n-1).
But this is rather dangerous, as I don't know of anything in the spec that insists that it must be that way, and if you override a val in the case class, it won't even appear in getDeclaredFields (it'll appear in the fields of that superclass).
You might change your code to assume things are this way, but check that the getter method with that name and the productIterator return the same value and throw an exception if they don't (which means that you don't actually know what corresponds to what).
You can also use the ProductCompletion from the interpreter package to get to attribute names and values of case classes:
import tools.nsc.interpreter.ProductCompletion
// get attribute names
new ProductCompletion(Colour(1, 2, 3)).caseNames
// returns: List(red, green, blue)
// get attribute values
new ProductCompletion(Colour(1, 2, 3)).caseFields
Edit: hints by roland and virtualeyes
It is necessary to include the scalap library which is part of the scala-lang collection.
Thanks for your hints, roland and virtualeyes.

Resources