I am still struggling with adding nodes to graphs dynamically so I hope to get some hints.
So far I add new nodes to a graph using the addNode() method of the Graph class, with setting the fields of the node with setString() etc, something like
Node n = g.addNode();
n.setString("label","jump out of the window");
Slowly I come to the conclusion that this "high level" approach can't really work proper in the case of adding nodes to an existing graph, because once a new node is added to the graph, some Listener react on it and before the "label" field in the above case is set, the value of the field is already asked for. (for example search index updating).
I saw that some of you working on the underlying Table level to add nodes, edges, so I wonder if somebody can give me a small example how to do this. In the case of adding rows to Tables, are the column fields already set before the row is added or might the same problem occur as with my approach?
I also add them dynamically, but I found the way around the bugs of updating to soon and not enough listeners witin Prefusion. Because as you have found out, the updating happens to soon and it doesn't catch updates later on.
What I do first is the addition of the node:
Node vNewNode = tree.addChild(vNode);
vNewNode.setString(TREE_DISPLAYFIELD,"some variable label from the database");
vNewNode.set(TREE_ITEMFIELD, item); // the link to the database
vNewNode.setString(TREE_IMAGEFIELD, getIconName(item));
item.addObserver(this);
vSearchSet =
(PrefixSearchTupleSet) getVisualizationSelection().getGroup(Visualization.SEARCH_ITEMS);
if (vSearchSet != null) {
VisualItem vVisualSelectionItem =
getVisualizationSelection().getVisualItem(TREE_NODES,aNode);
if (vVisualSelectionItem != null) {
// yes it is on the selection display
vSearchSet.index(vVisualSelectionItem, TREE_DISPLAYFIELD);
I know the solution is a bit stupid, but it works.
The only problem I have now is that if I add a 1.000 nodes at ones, I often get the error of index out of bounds (see other topics). I will change that to not running all the filters and afterwards run the filters only once (it is a bit against the observer pattern, but necessary).
Hope this helps Martin,
Regards, Martien (the names only differ an "e")
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
> I know the solution is a bit stupid, but it works.
Oh, I can really relate to this kind of solutions, most likely the only ones I can really relate to :-)
And I wouldn't call it stupid anyway, I think we have here kind of a weakness of the prefuse core so great that you found a solution for it.
My remaining problem is that the "search index thing" is just one of the problems I have with my node adding obsession, so I still hope we get a general solution for this whole issue. Maybe I can write a small node/edges adding utility class which solves the issue once I understand how to add nodes/edges on the table level, so if somebody can help me to understand that - wonderful.
Thanks once more
martin
(MartiEn? dutch?)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
But I think the problem is that if it wants to react on value changes on nodes, it also has to implement listeners to it, which cost again memory and some slight performance. And that is not suitable for laAAAArge graphs. But it is more suitable for small graphs.
I think that something has to change to the code to implement observers on demand. Or maybe more easy an "SignalUpdate" method, which may do all the hardwork on request (and saves listeners).
However I see some listeners, but not implemented (I have not fully digged into it).
This is why I have implemented the change of the label of a node myself and add a listener to the node at creation and that works. But not for the search list. That doesn't get updated unfortunately.
Regards, Martien
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I looked more into the code now and understand now a bit more why no listeners are being used.
The search uses an internal tree for each word of a node, to optimize the searching. That makes it more complicated if a node value changes. You would have to check the tree all over again. And then just reissuing a search looks as effective as the first. However Jeff provided a listener for adding and removing nodes and that is used (see the init in JSearchPanel):
public void tupleSetChanged(TupleSet tset,
Tuple[] add, Tuple[] rem)
{
if ( add != null ) {
for ( int i=0; i<add.length; ++i ) {
for ( int j=0; j<m_fields.length; j++ )
m_searcher.index(add[i], m_fields[j]);
}
}
if ( rem != null && m_searcher.isUnindexSupported() ) {
for ( int i=0; i<rem.length; ++i ) {
for ( int j=0; j<m_fields.length; j++ )
m_searcher.unindex(rem[i], m_fields[j]);
}
}
}
And the TupleSetListener only provides an interface for the ADD and Remove operations....
And you could act a change like a "remove action" and a "add action" for the search. So that should not be the problem.
The real cause/problem is to change the TupleSetListener to include change events as well. And that is a real change to Prefuse I guess.
We could also add a tupleChanged method to the listener, or add a new listener type and as action in the search change do the remove and add.
Looking at that code, I now fully understand that when you add a node, the listener gets fired and that one updates the searchpanel correctly, but there is no listener for changes of values, so it will NEVER get updated.
I would solve this to get some notification of updates, providing a workaround for the massive update actions, because then just reissuing a new search will be better?
All in all this really means a change to prefuse. Or am I getting it wrong?
And then the next question arises: Is Jeff maintaining it now or is it left to the community (us)?
Martien
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
thank you very much for your further investigation and clarifications.
I think the last question you raised is properly the most important one. I actually wrote Jeff an email a while go (concerning this dynamic stuff) but haven't heard from him since (not sure if I used the proper email so).
I really hope he is fine and just very busy.
I am offline soon for hopefully :-) quite a while so it wouldn't make sense trying to build up a community/development platform from my side at the moment. But obviously this forum alone doesn't do.
Anyway, the current prefuse release has birthday soon so let's hope Jeff jumps out of the birthday cake.
cheers
martin
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Nothing heared of Jeff, too. Just knowing that he is doing a doctor's degree...
@addNode:
I believe, graph.addNode is just forwarding to table.addNode which is itself forwarding to something even deeper. If you find the deepest point where prefuse really adds this data, maybe synchronizing on this may help?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Good morning prefusians,
hope everbody is happy out there.
I am still struggling with adding nodes to graphs dynamically so I hope to get some hints.
So far I add new nodes to a graph using the addNode() method of the Graph class, with setting the fields of the node with setString() etc, something like
Node n = g.addNode();
n.setString("label","jump out of the window");
Slowly I come to the conclusion that this "high level" approach can't really work proper in the case of adding nodes to an existing graph, because once a new node is added to the graph, some Listener react on it and before the "label" field in the above case is set, the value of the field is already asked for. (for example search index updating).
I saw that some of you working on the underlying Table level to add nodes, edges, so I wonder if somebody can give me a small example how to do this. In the case of adding rows to Tables, are the column fields already set before the row is added or might the same problem occur as with my approach?
I also tried to synchronize the above lines (for example using the active visualization object as recommend by Jeff
http://sourceforge.net/forum/message.php?msg_id=3752452 but I was not successful)
Thanks
martin
Hi Martin,
I also add them dynamically, but I found the way around the bugs of updating to soon and not enough listeners witin Prefusion. Because as you have found out, the updating happens to soon and it doesn't catch updates later on.
What I do first is the addition of the node:
Node vNewNode = tree.addChild(vNode);
vNewNode.setString(TREE_DISPLAYFIELD,"some variable label from the database");
vNewNode.set(TREE_ITEMFIELD, item); // the link to the database
vNewNode.setString(TREE_IMAGEFIELD, getIconName(item));
item.addObserver(this);
getVisualizationExplorer().run("filter");
getVisualizationSelection().run("filter");
Then I have the node loaded, but need to update the searchbox, which is after the lines above completely out of sync (is not aware of the change).
The solution I use now for e.g. the search box is like this:
if (doUpdateVisualizations) {
PrefixSearchTupleSet vSearchSet =
(PrefixSearchTupleSet) getVisualizationExplorer().getGroup(Visualization.SEARCH_ITEMS);
vSearchSet.index(
getVisualizationExplorer().getVisualItem(TREE_NODES,aNode),
TREE_DISPLAYFIELD);
if (!searchPanelExplorer.getQuery().equals("")) {
String vQuery = searchPanelExplorer.getQuery();
searchPanelExplorer.setQuery("");
searchPanelExplorer.setQuery(vQuery);
}
vSearchSet =
(PrefixSearchTupleSet) getVisualizationSelection().getGroup(Visualization.SEARCH_ITEMS);
if (vSearchSet != null) {
VisualItem vVisualSelectionItem =
getVisualizationSelection().getVisualItem(TREE_NODES,aNode);
if (vVisualSelectionItem != null) {
// yes it is on the selection display
vSearchSet.index(vVisualSelectionItem, TREE_DISPLAYFIELD);
if ((searchPanelSelection != null) &&
(!searchPanelSelection.getQuery().equals(""))) {
String vQuery = searchPanelSelection.getQuery();
searchPanelSelection.setQuery("");
searchPanelSelection.setQuery(vQuery);
}
}
}
}
I know the solution is a bit stupid, but it works.
The only problem I have now is that if I add a 1.000 nodes at ones, I often get the error of index out of bounds (see other topics). I will change that to not running all the filters and afterwards run the filters only once (it is a bit against the observer pattern, but necessary).
Hope this helps Martin,
Regards, Martien (the names only differ an "e")
Hello MartiEn,
thanks you so much for this one.
> I know the solution is a bit stupid, but it works.
Oh, I can really relate to this kind of solutions, most likely the only ones I can really relate to :-)
And I wouldn't call it stupid anyway, I think we have here kind of a weakness of the prefuse core so great that you found a solution for it.
My remaining problem is that the "search index thing" is just one of the problems I have with my node adding obsession, so I still hope we get a general solution for this whole issue. Maybe I can write a small node/edges adding utility class which solves the issue once I understand how to add nodes/edges on the table level, so if somebody can help me to understand that - wonderful.
Thanks once more
martin
(MartiEn? dutch?)
Yes, typical dutch :))
But I think the problem is that if it wants to react on value changes on nodes, it also has to implement listeners to it, which cost again memory and some slight performance. And that is not suitable for laAAAArge graphs. But it is more suitable for small graphs.
I think that something has to change to the code to implement observers on demand. Or maybe more easy an "SignalUpdate" method, which may do all the hardwork on request (and saves listeners).
However I see some listeners, but not implemented (I have not fully digged into it).
This is why I have implemented the change of the label of a node myself and add a listener to the node at creation and that works. But not for the search list. That doesn't get updated unfortunately.
Regards, Martien
I looked more into the code now and understand now a bit more why no listeners are being used.
The search uses an internal tree for each word of a node, to optimize the searching. That makes it more complicated if a node value changes. You would have to check the tree all over again. And then just reissuing a search looks as effective as the first. However Jeff provided a listener for adding and removing nodes and that is used (see the init in JSearchPanel):
public void tupleSetChanged(TupleSet tset,
Tuple[] add, Tuple[] rem)
{
if ( add != null ) {
for ( int i=0; i<add.length; ++i ) {
for ( int j=0; j<m_fields.length; j++ )
m_searcher.index(add[i], m_fields[j]);
}
}
if ( rem != null && m_searcher.isUnindexSupported() ) {
for ( int i=0; i<rem.length; ++i ) {
for ( int j=0; j<m_fields.length; j++ )
m_searcher.unindex(rem[i], m_fields[j]);
}
}
}
And the TupleSetListener only provides an interface for the ADD and Remove operations....
And you could act a change like a "remove action" and a "add action" for the search. So that should not be the problem.
The real cause/problem is to change the TupleSetListener to include change events as well. And that is a real change to Prefuse I guess.
We could also add a tupleChanged method to the listener, or add a new listener type and as action in the search change do the remove and add.
Looking at that code, I now fully understand that when you add a node, the listener gets fired and that one updates the searchpanel correctly, but there is no listener for changes of values, so it will NEVER get updated.
I would solve this to get some notification of updates, providing a workaround for the massive update actions, because then just reissuing a new search will be better?
All in all this really means a change to prefuse. Or am I getting it wrong?
And then the next question arises: Is Jeff maintaining it now or is it left to the community (us)?
Martien
Hi Martien,
thank you very much for your further investigation and clarifications.
I think the last question you raised is properly the most important one. I actually wrote Jeff an email a while go (concerning this dynamic stuff) but haven't heard from him since (not sure if I used the proper email so).
I really hope he is fine and just very busy.
I am offline soon for hopefully :-) quite a while so it wouldn't make sense trying to build up a community/development platform from my side at the moment. But obviously this forum alone doesn't do.
Anyway, the current prefuse release has birthday soon so let's hope Jeff jumps out of the birthday cake.
cheers
martin
Nothing heared of Jeff, too. Just knowing that he is doing a doctor's degree...
@addNode:
I believe, graph.addNode is just forwarding to table.addNode which is itself forwarding to something even deeper. If you find the deepest point where prefuse really adds this data, maybe synchronizing on this may help?