Skip to content

Commit 61951e7

Browse files
committed
Documentation for from_snowflake
1 parent d91c1f3 commit 61951e7

File tree

7 files changed

+163
-28
lines changed

7 files changed

+163
-28
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Import from Snowflake Tables
2+
----------------------------
3+
4+
.. automodule:: neo4j_viz.snowflake
5+
:members:
6+
:exclude-members: Orientation, VizProjectConfig, VizRelationshipTableConfig

docs/source/api-reference/render_options.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,5 @@
1818
.. autoenum:: neo4j_viz.options.Packing
1919
:members:
2020

21-
2221
.. autoenum:: neo4j_viz.Renderer
2322
:members:

docs/source/index.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ The library allows you to visualize graph data interactively in Python using a s
1111

1212
The library wraps the `Neo4j Visualization JavaScript library (NVL) <https://neo4j.com/docs/nvl/current/>`_, and
1313
provides additional features for working with graph data in Python.
14-
Notably, there are convenience methods for importing data from `Pandas DataFrames <https://pandas.pydata.org/>`_,
15-
`Neo4j Graph Data Science <https://neo4j.com/docs/graph-data-science/current/>`_ and `Neo4j Database <https://neo4j.com/docs/python-manual/current/>`_.
14+
Notably, there are convenience methods for importing data from source such as `Pandas DataFrames <https://pandas.pydata.org/>`_,
15+
`Neo4j Graph Data Science <https://neo4j.com/docs/graph-data-science/current/>`_, `Neo4j Database <https://neo4j.com/docs/python-manual/current/>`_
16+
and `Snowflake tables <https://docs.snowflake.com/>`_.
1617

1718
The source code is available on `GitHub <https://github.com/neo4j/python-graph-visualization>`_.
1819
If you have a suggestion on how we can improve the library or want to report a problem, you can create a `new issue <https://github.com/neo4j/python-graph-visualization/issues/new>`_.

docs/source/installation.rst

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ To install the additional dependencies required for the :doc:`from_dfs importer
2424
2525
pip install neo4j-viz[pandas]
2626
27+
2728
Neo4j ``from_neo4j`` importer
28-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
29+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2930

3031
To install the additional dependencies required for the :doc:`from_neo4j importer <./api-reference/from_neo4j>` you can run:
3132

@@ -43,6 +44,17 @@ To install the additional dependencies required for the :doc:`from_gds importer
4344
4445
pip install neo4j-viz[gds]
4546
47+
48+
Snowflake tables ``from_snowflake`` importer
49+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
50+
51+
To install the additional dependencies required for the :doc:`from_snowflake importer <./api-reference/from_snowflake>` you can run:
52+
53+
.. code-block:: bash
54+
55+
pip install neo4j-viz[snowflake]
56+
57+
4658
Notebook tutorials
4759
~~~~~~~~~~~~~~~~~~
4860

docs/source/integration.rst

Lines changed: 126 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ In addition to creating graphs from scratch, with ``neo4j-viz`` as is shown in t
55
:doc:`Getting started section <./getting-started>`, you can also import data directly from external sources.
66
In this section we will cover how to import data from `Pandas DataFrames <https://pandas.pydata.org/>`_,
77
`Neo4j Graph Data Science <https://neo4j.com/docs/graph-data-science/current/>`_,
8-
`Neo4j Database <https://neo4j.com/docs/python-manual/current/>`_ and
9-
`GQL CREATE queries <https://neo4j.com/docs/cypher-manual/current/clauses/create/>`_.
8+
`Neo4j Database <https://neo4j.com/docs/python-manual/current/>`_,
9+
`GQL CREATE queries <https://neo4j.com/docs/cypher-manual/current/clauses/create/>`_,
10+
and `Snowflake tables <https://docs.snowflake.com/>`_.
1011

1112

1213
.. contents:: On this page:
@@ -19,14 +20,14 @@ Pandas DataFrames
1920
-----------------
2021

2122
The ``neo4j-viz`` library provides a convenience method for importing data from Pandas DataFrames.
22-
These DataFrames can be created from many sources, such as CSV files or :doc:`Snowflake tables<./tutorials/snowflake-example>`.
23+
These DataFrames can be created from many sources, such as CSV files.
2324
It requires and additional dependency to be installed, which you can do by running:
2425

2526
.. code-block:: bash
2627
2728
pip install neo4j-viz[pandas]
2829
29-
Once you have installed the additional dependency, you can use the :doc:`from_gds <./api-reference/from_pandas>` method
30+
Once you have installed the additional dependency, you can use the :doc:`from_pandas <./api-reference/from_pandas>` method
3031
to import pandas DataFrames.
3132

3233
The ``from_dfs`` method takes two mandatory positional parameters:
@@ -82,9 +83,6 @@ and :doc:`Relationships <./api-reference/relationship>`.
8283
8384
VG = from_dfs(nodes, relationships)
8485
85-
For another example of the ``from_dfs`` importer in action, see the
86-
:doc:`Visualizing Snowflake Tables tutorial <./tutorials/snowflake-example>`.
87-
8886
8987
Neo4j Graph Data Science (GDS) library
9088
--------------------------------------
@@ -118,9 +116,9 @@ and will be used to determine the sizes of the nodes in the visualization.
118116

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

126124
The last optional property, ``node_radius_min_max``, can be used (and is used by default) to scale the node sizes for
@@ -285,3 +283,122 @@ In this small example, we create a visualization graph from a GQL ``CREATE`` que
285283
"""
286284
287285
VG = from_gql_create(query)
286+
287+
288+
Snowflake Tables
289+
----------------
290+
291+
The ``neo4j-viz`` library provides a convenience method for importing data from Snowflake tables.
292+
It requires and additional dependency to be installed, which you can do by running:
293+
294+
.. code-block:: bash
295+
296+
pip install neo4j-viz[snowflake]
297+
298+
Once you have installed the additional dependency, you can use the :doc:`from_snowflake <./api-reference/from_snowflake>` method
299+
to import Snowflake tables into a ``VisualizationGraph``.
300+
301+
The ``from_snowflake`` method takes two mandatory positional parameters:
302+
303+
* A ``snowflake.snowpark.Session`` object for the connection to Snowflake, and
304+
* A `project configuration <https://neo4j.com/docs/snowflake-graph-analytics/current/jobs/#jobs-project>`_ as a dictionary, that specifies how you want your tables to be projected as a graph.
305+
This configuration is the same as the project configuration of the `Neo4j Snowflake Graph Analytics application <https://neo4j.com/docs/snowflake-graph-analytics/current/>`_.
306+
307+
``from_snowflake`` also takes an optional property, ``node_radius_min_max``, that can be used (and is used by default) to
308+
scale the node sizes for the visualization.
309+
It is a tuple of two numbers, representing the radii (sizes) in pixels of the smallest and largest nodes respectively in
310+
the visualization.
311+
The node sizes will be scaled such that the smallest node will have the size of the first value, and the largest node
312+
will have the size of the second value.
313+
The other nodes will be scaled linearly between these two values according to their relative size.
314+
This can be useful if node sizes vary a lot, or are all very small or very big.
315+
316+
317+
Special columns
318+
~~~~~~~~~~~~~~~
319+
320+
It is possible to modify the visualization directly by including columns of certain specific names in the node and relationship tables.
321+
322+
All such special columns can be found :doc:`here <./api-reference/node>` for nodes and :doc:`here <./api-reference/relationship>` for relationships.
323+
Though listed in ``snake_case`` here, ``SCREAMING_SNAKE_CASE`` and ``camelCase`` are also supported.
324+
Some of the most commonly used special columns are:
325+
326+
* **Node sizes**: The sizes of nodes can be controlled by including a column named "SIZE" in node tables.
327+
The values in these columns should be of a numeric type. This can be useful for visualizing the relative importance or size of nodes in the graph, for example using a computed centrality score.
328+
329+
* **Captions**: The caption text of nodes and relationships can be controlled by including a column named "CAPTION" in the tables.
330+
The values in these columns should be of a string type. This can be useful for displaying additional information about the nodes, such as their names or labels. If no "CAPTION" column is provided, the default captions in the visualization will be the names of the corresponding node and relationship tables.
331+
332+
Please also note that you can further customize the visualization after the `VisualizationGraph` has been created, by using the methods described in the :doc:`Customizing the visualization <./customizing>` section.
333+
334+
335+
Default behavior
336+
~~~~~~~~~~~~~~~~
337+
338+
Unless there are "CAPTION" columns in the tables, the node and relationship captions will be set to the names of the corresponding tables.
339+
Similarly, if there are are no "COLOR" node table columns, the nodes will be colored be colored so that nodes from the same table have the same color, and different tables have different colors.
340+
341+
342+
Example
343+
~~~~~~~
344+
345+
In this small example, we import a toy graph representing a social network from two tables in Snowflake.
346+
347+
.. code-block:: python
348+
349+
from snowflake.snowpark import Session
350+
from neo4j_viz.snowflake import from_dfs
351+
352+
# Configure according to your own setup
353+
connection_parameters = {
354+
"account": os.environ.get("SNOWFLAKE_ACCOUNT"),
355+
"user": os.environ.get("SNOWFLAKE_USER"),
356+
"password": os.environ.get("SNOWFLAKE_PASSWORD"),
357+
"role": os.environ.get("SNOWFLAKE_ROLE"),
358+
"warehouse": os.environ.get("SNOWFLAKE_WAREHOUSE"),
359+
}
360+
361+
session.sql(
362+
"CREATE OR REPLACE TABLE EXAMPLE_DB.DATA_SCHEMA.PERSONS (NODEID VARCHAR);"
363+
).collect()
364+
365+
session.sql("""
366+
INSERT INTO EXAMPLE_DB.DATA_SCHEMA.PERSONS VALUES
367+
('Alice'),
368+
('Bob'),
369+
('Carol'),
370+
('Dave'),
371+
('Eve');
372+
""").collect()
373+
374+
session.sql(
375+
"CREATE OR REPLACE TABLE EXAMPLE_DB.DATA_SCHEMA.KNOWS (SOURCENODEID VARCHAR, TARGETNODEID VARCHAR);"
376+
).collect()
377+
378+
session.sql("""
379+
INSERT INTO EXAMPLE_DB.DATA_SCHEMA.KNOWS VALUES
380+
('Alice', 'Dave'),
381+
('Alice', 'Carol'),
382+
('Bob', 'Carol'),
383+
('Dave', 'Eve'),
384+
""").collect()
385+
386+
VG = from_snowflake(
387+
session,
388+
{
389+
"nodeTables": [
390+
"EXAMPLE_DB.DATA_SCHEMA.PERSONS",
391+
],
392+
"relationshipTables": {
393+
"EXAMPLE_DB.DATA_SCHEMA.KNOWS": {
394+
"sourceTable": "EXAMPLE_DB.DATA_SCHEMA.PERSONS",
395+
"targetTable": "EXAMPLE_DB.DATA_SCHEMA.PERSONS",
396+
"orientation": "UNDIRECTED",
397+
}
398+
},
399+
},
400+
)
401+
402+
For a full example of the ``from_snowflake`` importer in action, please see the
403+
:doc:`Visualizing Snowflake Tables tutorial <./tutorials/snowflake-example>`.
404+

python-wrapper/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ notebook = [
6969
"ipywidgets>=8.0.0",
7070
"palettable>=3.3.3",
7171
"matplotlib>=3.9.4",
72-
"snowflake-snowpark-python==1.26.0",
72+
"snowflake-snowpark-python==1.37.0",
7373
]
7474

7575
[project.urls]

python-wrapper/src/neo4j_viz/snowflake.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
from neo4j_viz.pandas import from_dfs
4242

4343

44-
def data_type_name(type: DataType) -> str:
44+
def _data_type_name(type: DataType) -> str:
4545
if isinstance(type, StringType):
4646
return "VARCHAR"
4747
elif isinstance(type, LongType):
@@ -83,7 +83,7 @@ def data_type_name(type: DataType) -> str:
8383
return type.simple_string().upper()
8484

8585

86-
SUPPORTED_ID_TYPES = [data_type_name(data_type) for data_type in [StringType(), LongType(), IntegerType()]]
86+
SUPPORTED_ID_TYPES = [_data_type_name(data_type) for data_type in [StringType(), LongType(), IntegerType()]]
8787

8888

8989
def _validate_id_column(schema: StructType, column_name: str, index: int, supported_types: list[str]) -> None:
@@ -95,13 +95,13 @@ def _validate_id_column(schema: StructType, column_name: str, index: int, suppor
9595
if field.name.lower() != column_name.lower():
9696
raise ValueError(f"Column `{column_name}` must have column index {index}")
9797

98-
if data_type_name(field.datatype) not in supported_types:
98+
if _data_type_name(field.datatype) not in supported_types:
9999
raise ValueError(
100-
f"Column `{column_name}` has invalid type `{data_type_name(field.datatype)}`. Expected one of [{', '.join(supported_types)}]"
100+
f"Column `{column_name}` has invalid type `{_data_type_name(field.datatype)}`. Expected one of [{', '.join(supported_types)}]"
101101
)
102102

103103

104-
def validate_viz_node_table(table: str, info: ValidationInfo) -> str:
104+
def _validate_viz_node_table(table: str, info: ValidationInfo) -> str:
105105
context = info.context
106106
if context and context["session"] is not None:
107107
session = context["session"]
@@ -113,7 +113,7 @@ def validate_viz_node_table(table: str, info: ValidationInfo) -> str:
113113
return table
114114

115115

116-
def validate_viz_relationship_table(
116+
def _validate_viz_relationship_table(
117117
table: str,
118118
info: ValidationInfo,
119119
) -> str:
@@ -129,7 +129,7 @@ def validate_viz_relationship_table(
129129
return table
130130

131131

132-
def parse_identifier_groups(identifier: str) -> list[str]:
132+
def _parse_identifier_groups(identifier: str) -> list[str]:
133133
"""
134134
Parses a table identifier into a list of individual identifier groups.
135135
@@ -208,12 +208,12 @@ def parse_identifier_groups(identifier: str) -> list[str]:
208208
return words
209209

210210

211-
def validate_table_name(table: str) -> str:
211+
def _validate_table_name(table: str) -> str:
212212
if not isinstance(table, str):
213213
raise TypeError(f"Table name must be a string, got {type(table).__name__}")
214214

215215
try:
216-
words = parse_identifier_groups(table)
216+
words = _parse_identifier_groups(table)
217217
except ValueError as e:
218218
raise ValueError(f"Invalid table name '{table}'. {str(e)}") from e
219219

@@ -225,10 +225,10 @@ def validate_table_name(table: str) -> str:
225225
return table
226226

227227

228-
Table = Annotated[str, BeforeValidator(validate_table_name)]
228+
Table = Annotated[str, BeforeValidator(_validate_table_name)]
229229

230-
VizNodeTable = Annotated[Table, AfterValidator(validate_viz_node_table)]
231-
VizRelationshipTable = Annotated[Table, AfterValidator(validate_viz_relationship_table)]
230+
VizNodeTable = Annotated[Table, AfterValidator(_validate_viz_node_table)]
231+
VizRelationshipTable = Annotated[Table, AfterValidator(_validate_viz_relationship_table)]
232232

233233

234234
class Orientation(Enum):
@@ -237,11 +237,11 @@ class Orientation(Enum):
237237
REVERSE = "reverse"
238238

239239

240-
def to_lower(value: str) -> str:
240+
def _to_lower(value: str) -> str:
241241
return value.lower() if value and isinstance(value, str) else value
242242

243243

244-
LowercaseOrientation = Annotated[Orientation, BeforeValidator(to_lower)]
244+
LowercaseOrientation = Annotated[Orientation, BeforeValidator(_to_lower)]
245245

246246

247247
class VizRelationshipTableConfig(BaseModel, extra="forbid"):

0 commit comments

Comments
 (0)