Closed xKHUNx closed 3 weeks ago
Thanks for the feedback @xKHUNx!
I plan to add callback functionality to update Streamlit component return values. Initially, this feature will be disabled by default to avoid unnecessary communication unless explicitly enabled through a parameter passed to component initialization. I’ll prioritize working on this.
Regarding expanding and deleting nodes on the frontend, could you provide an example use case? For a purely frontend solution, the full set of elements would need to be passed to the component. Some questions that comes to mind is what would the initial state look like, and which nodes should be displayed initially? What if you hide all nodes?
Thanks for the prompt response!
I'm basically trying to create a Streamlit version of Neo4j browser. In Neo4J browser, when user select a node, a menu will show up and users are allowed to either:
Expand
: Shows all the adjacent nodes of the selected node.Hide
: Hide/delete the node from the visualization, it will still exist in the database.Lock
: Honestly not sure what it does.I figure we need some kind of callback function to perform the expanding/hiding operation.
I could achieve something like it with that with Dash Cytoscape with something like this:
import dash
from dash import dcc, html, Input, Output, ctx
import dash_cytoscape as cyto
# Dash app setup
app = dash.Dash(__name__)
@app.callback(
Output('cytoscape-graph', 'elements'),
Input('run-query-button', 'n_clicks'),
Input('nl-query', 'value'),
Input('cytoscape-graph', 'tapNode'),
Input('cytoscape-graph', 'elements')
)
def update_graph(n_clicks, nl_query, tap_node, current_elements):
elements = current_elements or []
if ctx.triggered_id == 'run-query-button' and nl_query:
cypher_query = nl_query.strip()
data = conn.query(cypher_query)
elements = create_cytoscape_elements(data, [], [])
elif ctx.triggered_id == 'cytoscape-graph' and tap_node:
node_id = tap_node['data']['id']
expand_outgoing_query = f"MATCH (n)-[r]->(m) WHERE elementId(n) = '{node_id}' RETURN n, r, m LIMIT 20"
expand_incoming_query = f"MATCH (n)<-[r]-(m) WHERE elementId(n) = '{node_id}' RETURN n, r, m LIMIT 20"
outgoing_data = conn.query(expand_outgoing_query)
incoming_data = conn.query(expand_incoming_query)
elements = create_cytoscape_elements(outgoing_data, incoming_data, elements)
return elements
...
Problem is it is limited to single click (tapNode) interaction only, while Cytoscape actually supports more types of interaction.
The idea is that we can update the dictionary of elements
in Python side with some callback functions, while there's some input from the frontend.
Got it. Yes, we will need to add actionable elements in the UI to trigger expand/hide events and send them back to Python. There’s a Cytoscape.js context menu extension that offers a similar UI to what you shared. I’ll explore how customizable it is within a reasonable timeframe. If it proves too complex, might opt to integrate these actions into the existing toolbar (e.g., as attached).
Side note: the lock functionality restricts the node's position when running the layout algorithm. Both expand, and lock functionalities don't play very well (animation wise) except with layouts that run in realtime (e.g. cola with infinite
option set to true
)
Thank you for the valuable insights! It is a critical feature, especially when working with large graphs.
Also see PR: #21
I really love how customizable the appearance of the graph can be, and also the information panel showing the details of the node when selected. But I would like to request for the ability to expand or delete nodes from the graph. I noticed there's no support for callback functions when I select a node. It would be great to have these features as it would be almost perfect replication of a Neo4j browser