Skip to content

Commit

Permalink
Merge pull request #60 from wey-gu/dynamic_render_filename
Browse files Browse the repository at this point in the history
optmize graph rendering
  • Loading branch information
wey-gu authored May 23, 2024
2 parents f8968a2 + 1d766a9 commit d24c8e0
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 31 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ examples/*.html

# python
.venv
.ipynb_checkpoints/
Untitled.ipynb
40 changes: 32 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,42 @@ https://github.com/wey-gu/jupyter_nebulagraph/assets/1651790/10135264-77b5-4d3c-

## Getting Started

Explore the capabilities of `jupyter_nebulagraph` by trying it on [Google Colab](https://colab.research.google.com/github/wey-gu/jupyter_nebulagraph/blob/main/docs/get_started.ipynb), and the equivalent Jupyter Notebook is available in Docs [here](https://jupyter-nebulagraph.readthedocs.io/en/latest/get_started_docs/).
```bash
pip install jupyter_nebulagraph
```

Load the extension in Jupyter Notebook or iPython:

```python
%load_ext ngql
%ngql --address 127.0.0.1 --port 9669 --user root --password nebula
```

Make queries:

```python
%ngql USE basketballplayer;
%ngql MATCH p=(v:player)-->(v2:player) WHERE id(v) == "player100" RETURN p;
```

Draw the graph:

```python
%ng_draw
```

Discover the features of `jupyter_nebulagraph` by experimenting with it on [Google Colab](https://colab.research.google.com/github/wey-gu/jupyter_nebulagraph/blob/main/docs/get_started.ipynb). You can also access a similar Jupyter Notebook in the documentation [here](https://jupyter-nebulagraph.readthedocs.io/en/stable/get_started_docs/).

For a comprehensive guide, visit the [official documentation](https://jupyter-nebulagraph.readthedocs.io/).
For a detailed guide, refer to the [official documentation](https://jupyter-nebulagraph.readthedocs.io/en/stable).

| Feature | Cheat Sheet | Example | Command Documentation |
| ------- | ----------- | --------- | ---------------------- |
| Connect | `%ngql --address 127.0.0.1 --port 9669 --user user --password password` | [Connect](https://jupyter-nebulagraph.readthedocs.io/en/latest/get_started_docs/#connect-to-nebulagraph) | [`%ngql`](https://jupyter-nebulagraph.readthedocs.io/en/latest/magic_words/ngql/#connect-to-nebulagraph) |
| Load Data from CSV | `%ng_load --source actor.csv --tag player --vid 0 --props 1:name,2:age --space basketballplayer` | [Load Data](https://jupyter-nebulagraph.readthedocs.io/en/latest/get_started_docs/#load-data-from-csv) | [`%ng_load`](https://jupyter-nebulagraph.readthedocs.io/en/latest/magic_words/ng_load/) |
| Query Execution | `%ngql MATCH p=(v:player{name:"Tim Duncan"})-->(v2:player) RETURN p;`| [Query Execution](https://jupyter-nebulagraph.readthedocs.io/en/latest/get_started_docs/#query) | [`%ngql` or `%%ngql`(multi-line)](https://jupyter-nebulagraph.readthedocs.io/en/latest/magic_words/ngql/#make-queries) |
| Result Visualization | `%ng_draw` | [Draw Graph](https://jupyter-nebulagraph.readthedocs.io/en/latest/magic_words/ng_draw/) | [`%ng_draw`](https://jupyter-nebulagraph.readthedocs.io/en/latest/magic_words/ng_draw/) |
| Draw Schema | `%ng_draw_schema` | [Draw Schema](https://jupyter-nebulagraph.readthedocs.io/en/latest/magic_words/ng_draw_schema/) | [`%ng_draw_schema`](https://jupyter-nebulagraph.readthedocs.io/en/latest/magic_words/ng_draw_schema/) |
| Tweak Query Result | `df = _` to get last query result as `pd.dataframe` or [`ResultSet`](https://github.com/vesoft-inc/nebula-python/blob/master/nebula3/data/ResultSet.py) | [Tweak Result](https://jupyter-nebulagraph.readthedocs.io/en/latest/get_started_docs/#result-handling) | [Configure `ngql_result_style`](https://jupyter-nebulagraph.readthedocs.io/en/latest/configurations/#configure-ngql_result_style) |
| Connect | `%ngql --address 127.0.0.1 --port 9669 --user user --password password` | [Connect](https://jupyter-nebulagraph.readthedocs.io/en/stable/get_started_docs/#connect-to-nebulagraph) | [`%ngql`](https://jupyter-nebulagraph.readthedocs.io/en/stable/magic_words/ngql/#connect-to-nebulagraph) |
| Load Data from CSV | `%ng_load --source actor.csv --tag player --vid 0 --props 1:name,2:age --space basketballplayer` | [Load Data](https://jupyter-nebulagraph.readthedocs.io/en/stable/get_started_docs/#load-data-from-csv) | [`%ng_load`](https://jupyter-nebulagraph.readthedocs.io/en/stable/magic_words/ng_load/) |
| Query Execution | `%ngql MATCH p=(v:player{name:"Tim Duncan"})-->(v2:player) RETURN p;`| [Query Execution](https://jupyter-nebulagraph.readthedocs.io/en/stable/get_started_docs/#query) | [`%ngql` or `%%ngql`(multi-line)](https://jupyter-nebulagraph.readthedocs.io/en/stable/magic_words/ngql/#make-queries) |
| Result Visualization | `%ng_draw` | [Draw Graph](https://jupyter-nebulagraph.readthedocs.io/en/stable/magic_words/ng_draw/) | [`%ng_draw`](https://jupyter-nebulagraph.readthedocs.io/en/stable/magic_words/ng_draw/) |
| Draw Schema | `%ng_draw_schema` | [Draw Schema](https://jupyter-nebulagraph.readthedocs.io/en/stable/magic_words/ng_draw_schema/) | [`%ng_draw_schema`](https://jupyter-nebulagraph.readthedocs.io/en/stable/magic_words/ng_draw_schema/) |
| Tweak Query Result | `df = _` to get last query result as `pd.dataframe` or [`ResultSet`](https://github.com/vesoft-inc/nebula-python/blob/master/nebula3/data/ResultSet.py) | [Tweak Result](https://jupyter-nebulagraph.readthedocs.io/en/stable/get_started_docs/#result-handling) | [Configure `ngql_result_style`](https://jupyter-nebulagraph.readthedocs.io/en/stable/configurations/#configure-ngql_result_style) |


<details>
Expand Down
107 changes: 86 additions & 21 deletions ngql/magic.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ def get_color(input_str):
return COLORS[hash_val % len(COLORS)]


def is_human_readable(field):
return any(c.isalpha() for c in field) and len(field) < 20


@magics_class
class IPythonNGQL(Magics, Configurable):
ngql_verbose = Bool(False, config=True, help="Set verbose mode")
Expand Down Expand Up @@ -404,6 +408,18 @@ def ng_draw(self, line, cell=None, local_ns={}):
for item in row:
self.render_pd_item(g, g_nx, item)

try:
# Calculate PageRank
pagerank_scores = nx.pagerank(g_nx)

# Update node sizes based on PageRank scores
for node_id, score in pagerank_scores.items():
g.get_node(node_id)["size"] = (
10 + score * 130
) # Normalized size for visibility
except Exception as e:
print(f"[WARN]: failed to calculate PageRank\n { e }")

g.repulsion(
node_distance=90,
central_gravity=0.2,
Expand All @@ -412,20 +428,22 @@ def ng_draw(self, line, cell=None, local_ns={}):
damping=0.09,
)
# g.show_buttons(filter_='physics')
# return g.show("nebulagraph_draw.html", notebook=True)
g_html_string = g.generate_html("nebulagraph.html")
with open("nebulagraph.html", "w", encoding="utf-8") as f:
# return g.show("nebulagraph.html", notebook=True)
cell_num = get_ipython().execution_count
graph_render_filename = f"nebulagraph_cell_{cell_num}.html"
g_html_string = g.generate_html(graph_render_filename)
with open(graph_render_filename, "w", encoding="utf-8") as f:
f.write(g_html_string)
# detect if we are in colab or not
try:
if "google.colab" in str(get_ipython()):
display(HTML(g_html_string))
else:
display(IFrame(src="nebulagraph.html", width="100%", height="500px"))
display(IFrame(src=graph_render_filename, width="100%", height="500px"))
except Exception as e:
print(f"[WARN]: failed to display the graph\n { e }")
try:
display(IFrame(src="nebulagraph.html", width="100%", height="500px"))
display(IFrame(src=graph_render_filename, width="100%", height="500px"))
except Exception as e:
print(f"[WARN]: failed to display the graph\n { e }")

Expand Down Expand Up @@ -549,9 +567,26 @@ def ng_draw_schema(self, line, cell=None, local_ns={}):
edge_schema["dst_tag"],
edge_schema["edge_type"],
)
g.add_edge(src_tag, dst_tag, label=edge_type, title=str(edge_schema))
title = (
"{\n "
+ "\n ".join([f"{k}: {v}" for k, v in edge_schema.items()])
+ "\n}"
)
g.add_edge(src_tag, dst_tag, label=edge_type, title=title)
g_nx.add_edge(src_tag, dst_tag, **edge_schema)

try:
# Calculate PageRank
pagerank_scores = nx.pagerank(g_nx)

# Update node sizes based on PageRank scores
for node_id, score in pagerank_scores.items():
g.get_node(node_id)["size"] = (
10 + score * 130
) # Normalized size for visibility
except Exception as e:
print(f"[WARN]: failed to calculate PageRank\n { e }")

g.repulsion(
node_distance=90,
central_gravity=0.2,
Expand All @@ -561,23 +596,21 @@ def ng_draw_schema(self, line, cell=None, local_ns={}):
)
# g.show_buttons(filter_='physics')
# return g.show("nebulagraph_draw.html", notebook=True)
g_html_string = g.generate_html("nebulagraph_schema.html")
with open("nebulagraph_schema.html", "w", encoding="utf-8") as f:
cell_num = get_ipython().execution_count
schema_html_filename = f"nebulagraph_schema_cell_{cell_num}_{space}.html"
g_html_string = g.generate_html(schema_html_filename)
with open(schema_html_filename, "w", encoding="utf-8") as f:
f.write(g_html_string)
# detect if we are in colab or not
try:
if "google.colab" in str(get_ipython()):
display(HTML(g_html_string))
else:
display(
IFrame(src="nebulagraph_schema.html", width="100%", height="500px")
)
display(IFrame(src=schema_html_filename, width="100%", height="500px"))
except Exception as e:
print(f"[WARN]: failed to display the graph\n { e }")
try:
display(
IFrame(src="nebulagraph_schema.html", width="100%", height="500px")
)
display(IFrame(src=schema_html_filename, width="100%", height="500px"))
except Exception as e:
print(f"[WARN]: failed to display the graph\n { e }")

Expand All @@ -590,26 +623,36 @@ def render_pd_item(self, g, g_nx, item):
if isinstance(item, Node):
node_id = str(item.get_id().cast())
tags = item.tags() # list of strings
tags_str = tags[0] if len(tags) == 1 else ",".join(tags)
props_raw = dict()
for tag in tags:
props_raw.update(item.properties(tag))
props = {
k: str(v.cast()) if hasattr(v, "cast") else str(v)
for k, v in props_raw.items()
}
# populating empty and null properties
props = {
k: v for k, v in props.items() if v not in ["__NULL__", "__EMPTY__"]
}

if "name" in props:
label = props["name"]
else:
label = f"tag: {tags}, id: {node_id}"
if is_human_readable(node_id):
label = f"tag: {tags_str},\nid: {node_id}"
else:
label = f"tag: {tags_str},\nid: {node_id[:3]}..{node_id[-3:]}"
for k in props:
if "name" in str(k).lower():
label = props[k]
break

if "id" not in props:
props["id"] = node_id
title = "\n".join([f"{k}: {v}" for k, v in props.items()])

g.add_node(node_id, label=label, title=str(props), color=get_color(node_id))
g.add_node(node_id, label=label, title=title, color=get_color(node_id))

# networkx
if len(tags) > 1:
Expand All @@ -627,19 +670,33 @@ def render_pd_item(self, g, g_nx, item):
}
if rank != 0:
props.update({"rank": rank})
# populating empty and null properties
props = {
k: v for k, v in props.items() if v not in ["__NULL__", "__EMPTY__"]
}
# ensure start and end vertex exist in graph
if not src_id in g.node_ids:
label = (
f"tag: {src_id[:3]}..{src_id[-3:]}"
if not is_human_readable(src_id)
else src_id
)
g.add_node(
src_id,
label=str(src_id),
title=str(src_id),
label=label,
title=src_id,
color=get_color(src_id),
)
if not dst_id in g.node_ids:
label = (
f"tag: {dst_id[:3]}..{dst_id[-3:]}"
if not is_human_readable(dst_id)
else dst_id
)
g.add_node(
dst_id,
label=str(dst_id),
title=str(dst_id),
label=label,
title=dst_id,
color=get_color(dst_id),
)
props_str_list: List[str] = []
Expand All @@ -650,11 +707,19 @@ def render_pd_item(self, g, g_nx, item):
props_str = "\n".join(props_str_list)

label = f"{props_str}\n{edge_name}" if props else edge_name
if props:
title = (
"{\n "
+ "\n ".join([f"{k}: {v}" for k, v in props.items()])
+ "\n}"
)
else:
title = edge_name
g.add_edge(
src_id,
dst_id,
label=label,
title=str(props),
title=title,
weight=props.get("rank", 0),
)
# networkx
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="jupyter_nebulagraph",
version="0.12.3",
version="0.12.4",
author="Wey Gu",
author_email="[email protected]",
description="Jupyter extension for NebulaGraph",
Expand Down
2 changes: 1 addition & 1 deletion setup_ipython.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="ipython-ngql",
version="0.12.3",
version="0.12.4",
author="Wey Gu",
author_email="[email protected]",
description="Jupyter extension for NebulaGraph",
Expand Down

0 comments on commit d24c8e0

Please sign in to comment.