Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

## Breaking changes

* The `from_gds` methods now fetches all node properties of a given GDS projection by default, instead of none.


## New features

Expand Down
6 changes: 4 additions & 2 deletions docs/source/integration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,10 @@ and will be used to determine the sizes of the nodes in the visualization.

The ``additional_node_properties`` parameter is also optional, and should be a list of additional node properties of the
projection that you want to include in the visualization.
For example, these properties could be used to color the nodes, or give captions to them in the visualization, or simply
included in the nodes' `Node.properties` maps without directly impacting the visualization.
The default is `None`, which means that all properties of the nodes in the projection will be included.
Apart from being visible through on-hover tooltips, these properties could be used to color the nodes, or give captions
to them in the visualization, or simply included in the nodes' `Node.properties` maps without directly impacting the
visualization.

The last optional property, ``node_radius_min_max``, can be used (and is used by default) to scale the node sizes for
the visualization.
Expand Down
7 changes: 5 additions & 2 deletions python-wrapper/src/neo4j_viz/gds.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ def from_gds(
size_property : str, optional
Property to use for node size, by default None.
additional_node_properties : list[str], optional
Additional properties to include in the visualization node, by default None. They can be used later for modifying the node appearance.
Additional properties to include in the visualization node, by default None which means that all node
properties will be fetched.
node_radius_min_max : tuple[float, float], optional
Minimum and maximum node radius, by default (3, 60).
To avoid tiny or huge nodes in the visualization, the node sizes are scaled to fit in the given range.
Expand All @@ -75,7 +76,9 @@ def from_gds(
if size_property is not None and size_property not in actual_node_properties:
raise ValueError(f"There is no node property '{size_property}' in graph '{G.name()}'")

if additional_node_properties is not None:
if additional_node_properties is None:
additional_node_properties = actual_node_properties
else:
for prop in additional_node_properties:
if prop not in actual_node_properties:
raise ValueError(f"There is no node property '{prop}' in graph '{G.name()}'")
Expand Down
52 changes: 52 additions & 0 deletions python-wrapper/tests/test_gds.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,58 @@ def test_from_gds_integration(gds: Any) -> None:
]


@pytest.mark.requires_neo4j_and_gds
def test_from_gds_integration_all_properties(gds: Any) -> None:
from neo4j_viz.gds import from_gds

nodes = pd.DataFrame(
{
"nodeId": [0, 1, 2],
"labels": [["A"], ["C"], ["A", "B"]],
"score": [1337, 42, 3.14],
"component": [1, 4, 2],
"size": [0.1, 0.2, 0.3],
}
)
rels = pd.DataFrame(
{
"sourceNodeId": [0, 1, 2],
"targetNodeId": [1, 2, 0],
"cost": [1.0, 2.0, 3.0],
"weight": [0.5, 1.5, 2.5],
"relationshipType": ["REL", "REL2", "REL"],
}
)

with gds.graph.construct("flo", nodes, rels) as G:
VG = from_gds(
gds,
G,
node_radius_min_max=None,
)

assert len(VG.nodes) == 3
assert sorted(VG.nodes, key=lambda x: x.id) == [
Node(id=0, size=0.1, properties=dict(labels=["A"], component=float(1), score=1337.0)),
Node(id=1, size=0.2, properties=dict(labels=["C"], component=float(4), score=42.0)),
Node(id=2, size=0.3, properties=dict(labels=["A", "B"], component=float(2), score=3.14)),
]

assert len(VG.relationships) == 3
vg_rels = sorted(
[
(e.source, e.target, e.properties["relationshipType"], e.properties["cost"], e.properties["weight"])
for e in VG.relationships
],
key=lambda x: x[0],
)
assert vg_rels == [
(0, 1, "REL", 1.0, 0.5),
(1, 2, "REL2", 2.0, 1.5),
(2, 0, "REL", 3.0, 2.5),
]


def test_from_gds_mocked(mocker: MockerFixture) -> None:
from graphdatascience import Graph, GraphDataScience

Expand Down