Object differentiation - graph

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);

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 ;)

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

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()

Remove nodes which are single or have 2nd degree visjs

I've a network graph
Now I've some connected nodes and as you can see most of the nodes only have one connected node that is their degree is 1. Now I'd like to remove such nodes to clear the clutter. Unable to find how to since last 2 days. No such helper functions available in visjs documentation. Would appreciate help.
I believe the algorithm suggested by the 1st answer -by macramole- (before updates) would actually hide the non-connected nodes (degree 0), instead of the ones with degree 1.
I would probably just iterate over all the edges in the network while keeping 'degree' counters for each node that is an endpoint in the edge you are visiting (you can obtain these nodes by grabbing the edge.from and edge.to values, as shown above). You would increment the degree counter for a node, whenever the node is 'hit' in this search through the edges.
Eventually you'll end up with the degree value for each node in the network, at which point you can decide which ones to hide.
Updating this answer now to include my suggested code (note: nodes and edges are vis DataSet instances):
Example code:
var nodeToDegrees = {}; // keeps a map of node ids to degrees
var nodeFrom, nodeTo;
for (edge in edges) {
nodeFrom = edge.from;
nodeTo = edge.to;
nodeToDegrees[nodeFrom] = nodeToDegrees[nodeFrom] ? nodeToDegrees[nodeFrom] + 1 : 0;
nodeToDegrees[nodeTo] = nodeToDegrees[nodeTo] ? nodeToDegrees[nodeTo] + 1 : 0;
}
for (node in nodes) {
if (nodeToDegrees[node.id] = 1) nodes.update([{node.id, hidden: true}]);
}
This might work:
var DEGREES_HIDDEN = 1;
for ( var node of nodes ) {
node.cantLinks = 0;
for ( var link of links ) {
if ( link.from == node.id || link.to == node.id ) {
node.cantLinks++;
}
}
}
for ( var node of nodes ) {
if ( node.cantLinks <= DEGREES_HIDDEN ) {
node.hidden = true;
}
}
Nodes and links are arrays not vis.DataSet, I create the latter after doing that.
Doesn't look very nice perfomance wise but it does get the job done. Hope you find it useful.

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.

Why wrong with the breadth first search of graph

public static void BFS(TTNode node){
Set<TTNode> accessedNodes = new HashSet<TTNode>();
Queue<TTNode> queue = new LinkedList<TTNode>();
queue.offer(node);
while (!queue.isEmpty()){
TTNode accessingNode = queue.poll();
System.out.println(accessingNode.payload);
accessedNodes.add(accessingNode);
for (TTNode child : accessingNode.children){
if (!accessedNodes.contains(child)){
queue.offer(child);
}
}
}
}
why the result is : 1 2 3 4 5 5 5, the 5 shows up three times?
The problem with your code is that you should mark a node visited just before
pushing it in the queue. Say we have a graph with three nodes 1 to 3. And the edges
are (1-2) , (1-3) , (3-2). Now you push say 1 first, according to your code you pop 1 mark it as visited and push 2 and 3 ( say in this order ). Now you pop 2 and mark it visited. Now amongst neighbors of 2, 3 is still marked unvisited so you push it again. So node 3 ends up multiple times in the queue in bfs. I don't know what language you have written the code in but I have made the change hope syntax is correct
public static void BFS(TTNode node){
Set<TTNode> accessedNodes = new HashSet<TTNode>();
Queue<TTNode> queue = new LinkedList<TTNode>();
queue.offer(node);
accessedNodes.add(node);
while (!queue.isEmpty()){
TTNode accessingNode = queue.poll();
System.out.println(accessingNode.payload);
for (TTNode child : accessingNode.children){
if (!accessedNodes.contains(child)){
accessedNodes.add(child);
queue.offer(child);
}
}
}
}
Do comment if I made a mistake in code or if you still have a doubt.

Resources