Skip to content

Commit 6cc7241

Browse files
authored
Merge pull request #180 from neo4j/all-gds-props
Fetch all node properties by default with `from_gds`
2 parents 3cb68b5 + 9598389 commit 6cc7241

File tree

4 files changed

+63
-4
lines changed

4 files changed

+63
-4
lines changed

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
## Breaking changes
55

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

79
## New features
810

docs/source/integration.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,10 @@ and will be used to determine the sizes of the nodes in the visualization.
110110

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

116118
The last optional property, ``node_radius_min_max``, can be used (and is used by default) to scale the node sizes for
117119
the visualization.

python-wrapper/src/neo4j_viz/gds.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ def from_gds(
6363
size_property : str, optional
6464
Property to use for node size, by default None.
6565
additional_node_properties : list[str], optional
66-
Additional properties to include in the visualization node, by default None. They can be used later for modifying the node appearance.
66+
Additional properties to include in the visualization node, by default None which means that all node
67+
properties will be fetched.
6768
node_radius_min_max : tuple[float, float], optional
6869
Minimum and maximum node radius, by default (3, 60).
6970
To avoid tiny or huge nodes in the visualization, the node sizes are scaled to fit in the given range.
@@ -75,7 +76,9 @@ def from_gds(
7576
if size_property is not None and size_property not in actual_node_properties:
7677
raise ValueError(f"There is no node property '{size_property}' in graph '{G.name()}'")
7778

78-
if additional_node_properties is not None:
79+
if additional_node_properties is None:
80+
additional_node_properties = actual_node_properties
81+
else:
7982
for prop in additional_node_properties:
8083
if prop not in actual_node_properties:
8184
raise ValueError(f"There is no node property '{prop}' in graph '{G.name()}'")

python-wrapper/tests/test_gds.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,58 @@ def test_from_gds_integration(gds: Any) -> None:
6161
]
6262

6363

64+
@pytest.mark.requires_neo4j_and_gds
65+
def test_from_gds_integration_all_properties(gds: Any) -> None:
66+
from neo4j_viz.gds import from_gds
67+
68+
nodes = pd.DataFrame(
69+
{
70+
"nodeId": [0, 1, 2],
71+
"labels": [["A"], ["C"], ["A", "B"]],
72+
"score": [1337, 42, 3.14],
73+
"component": [1, 4, 2],
74+
"size": [0.1, 0.2, 0.3],
75+
}
76+
)
77+
rels = pd.DataFrame(
78+
{
79+
"sourceNodeId": [0, 1, 2],
80+
"targetNodeId": [1, 2, 0],
81+
"cost": [1.0, 2.0, 3.0],
82+
"weight": [0.5, 1.5, 2.5],
83+
"relationshipType": ["REL", "REL2", "REL"],
84+
}
85+
)
86+
87+
with gds.graph.construct("flo", nodes, rels) as G:
88+
VG = from_gds(
89+
gds,
90+
G,
91+
node_radius_min_max=None,
92+
)
93+
94+
assert len(VG.nodes) == 3
95+
assert sorted(VG.nodes, key=lambda x: x.id) == [
96+
Node(id=0, size=0.1, properties=dict(labels=["A"], component=float(1), score=1337.0)),
97+
Node(id=1, size=0.2, properties=dict(labels=["C"], component=float(4), score=42.0)),
98+
Node(id=2, size=0.3, properties=dict(labels=["A", "B"], component=float(2), score=3.14)),
99+
]
100+
101+
assert len(VG.relationships) == 3
102+
vg_rels = sorted(
103+
[
104+
(e.source, e.target, e.properties["relationshipType"], e.properties["cost"], e.properties["weight"])
105+
for e in VG.relationships
106+
],
107+
key=lambda x: x[0],
108+
)
109+
assert vg_rels == [
110+
(0, 1, "REL", 1.0, 0.5),
111+
(1, 2, "REL2", 2.0, 1.5),
112+
(2, 0, "REL", 3.0, 2.5),
113+
]
114+
115+
64116
def test_from_gds_mocked(mocker: MockerFixture) -> None:
65117
from graphdatascience import Graph, GraphDataScience
66118

0 commit comments

Comments
 (0)