├── .github
└── workflows
│ ├── build-package.yml
│ └── publish.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE.md
├── README.md
├── README_DEV.md
├── environment.build.yml
├── environment.dev.yml
├── examples
├── basic_example.ipynb
├── configurations_example.ipynb
├── feature_example.ipynb
├── grouping.ipynb
└── selection_example.ipynb
├── images
├── example.png
└── features
│ ├── grouping_feature.png
│ ├── heat_feature.png
│ ├── map_feature.png
│ └── size_feature.png
├── pyproject.toml
└── src
└── yfiles_jupyter_graphs_for_neo4j
├── Yfiles_Neo4j_Graphs.py
└── __init__.py
/.github/workflows/build-package.yml:
--------------------------------------------------------------------------------
1 | name: Build package
2 |
3 | on: [workflow_dispatch]
4 |
5 | permissions:
6 | contents: read
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v3
13 | - name: Set up Python 3.10
14 | uses: actions/setup-python@v3
15 | with:
16 | python-version: "3.10"
17 | - name: Install dependencies
18 | run: |
19 | python -m pip install --upgrade pip
20 | pip install build
21 | - name: Build package
22 | run: |
23 | python -m build
24 | - name: Upload artifact
25 | uses: actions/upload-artifact@v4
26 | with:
27 | path: dist/
28 | overwrite: true
29 |
30 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Build and Publish to PyPi
2 |
3 | on: [workflow_dispatch]
4 |
5 | permissions:
6 | contents: read
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v3
13 | - name: Set up Python 3.10
14 | uses: actions/setup-python@v3
15 | with:
16 | python-version: "3.10"
17 | - name: Install dependencies
18 | run: |
19 | python -m pip install --upgrade pip
20 | pip install build
21 | - name: Build package
22 | run: |
23 | python -m build
24 | - name: Upload artifact
25 | uses: actions/upload-artifact@v4
26 | with:
27 | path: dist/
28 | overwrite: true
29 | publish:
30 | runs-on: ubuntu-latest
31 | needs: build
32 | environment:
33 | name: pypi
34 | url: https://pypi.org/p/yfiles-jupyter-graphs-for-neo4j/
35 | permissions:
36 | id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
37 | steps:
38 | - uses: actions/checkout@v3
39 | - name: Download artifact
40 | uses: actions/download-artifact@v4
41 | with:
42 | path: dist/
43 | - name: Display structure of downloaded files
44 | run: ls -R dist/
45 | - name: Publish package distributions to PyPI
46 | uses: pypa/gh-action-pypi-publish@release/v1
47 | with:
48 | packages-dir: dist/artifact
49 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | .ipynb_checkpoints
4 | .idea
5 | /src/yfiles_jupyter_graphs_for_neo4j.egg-info/
6 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to make participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | - Using welcoming and inclusive language
18 | - Being respectful of differing viewpoints and experiences
19 | - Gracefully accepting constructive criticism
20 | - Focusing on what is best for the community
21 | - Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | - The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | - Trolling, insulting/derogatory comments, and personal or political attacks
28 | - Public or private harassment
29 | - Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | - Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies within all project spaces, and it also applies when
49 | an individual is representing the project or its community in public spaces.
50 | Examples of representing a project or community include using an official
51 | project e-mail address, posting via an official social media account, or acting
52 | as an appointed representative at an online or offline event. Representation of
53 | a project may be further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at [contact@yworks.com](mailto:contact@yworks.com). All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 yWorks GmbH
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # yFiles Jupyter Graphs for Neo4j
2 | 
3 |
4 | [](https://badge.fury.io/py/yfiles-jupyter-graphs-for-neo4j)
5 |
6 | Easily visualize a [Neo4j](https://neo4j.com/) Cypher query as a graph in a Jupyter Notebook.
7 |
8 | This packages provides an easy-to-use interface to
9 | the [yFiles Graphs for Jupyter](https://github.com/yWorks/yfiles-jupyter-graphs) widget to directly visualize Cypher
10 | queries.
11 |
12 | ## Installation
13 | Just install it from the [Python Package Index](https://pypi.org/project/yfiles-jupyter-graphs-for-neo4j/)
14 | ```bash
15 | pip install yfiles_jupyter_graphs_for_neo4j
16 | ```
17 | or see [README_DEV.md](https://github.com/yWorks/yfiles-jupyter-graphs-for-neo4j/blob/main/README_DEV.md) to build it yourself.
18 |
19 | ## Usage
20 |
21 | ```python
22 | from yfiles_jupyter_graphs_for_neo4j import Neo4jGraphWidget
23 | from neo4j import GraphDatabase
24 |
25 | NEO4J_URI = "neo4j+ssc://demo.neo4jlabs.com"
26 | NEO4J_USERNAME = "movies"
27 | NEO4J_PASSWORD = "movies"
28 | driver = GraphDatabase.driver(uri = NEO4J_URI, auth = (NEO4J_USERNAME, NEO4J_PASSWORD), database = 'movies')
29 |
30 | g = Neo4jGraphWidget(driver)
31 |
32 | g.show_cypher("MATCH (s)-[r]->(t) RETURN s,r,t LIMIT 20")
33 | ```
34 |
35 | See
36 | the [basic example notebook](https://github.com/yWorks/yfiles-jupyter-graphs-for-neo4j/blob/main/examples/basic_example.ipynb)
37 | for a running example.
38 |
39 | ## Supported Environments
40 |
41 | The widget uses yFiles Graphs for Jupyter at its core, and therefore runs in any environment that is supported by it,
42 | see [supported environments](https://github.com/yWorks/yfiles-jupyter-graphs/tree/main?tab=readme-ov-file#supported-environments).
43 |
44 | ## Documentation
45 |
46 | The main class `Neo4jGraphWidget` provides the following API:
47 |
48 | ### Constructor
49 |
50 | - `Neo4jGraphWidget`: Creates a new class instance with the following arguments
51 |
52 | | Argument | Description | Default |
53 | |--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|
54 | | `driver` | The neo4j `driver` that is used to execute Cypher queries. | `None` |
55 | | `widget_layout` | Can be used to specify general widget appearance through css attributes. See ipywidget's [`layout`](https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Layout.html#the-layout-attribute) for more information. | `None` |
56 | | `overview_enabled` | Enable graph overview component. Default behaviour depends on cell width. | `None` |
57 | | `context_start_with` | Start with a specific side-panel opened in the interactive widget. Starts with closed side-panel by default. | `None` |
58 | | `layout` | Can be used to specify a general default node and edge layout. Available algorithms are: "circular", "hierarchic", "organic", "interactive_organic", "orthogonal", "radial", "tree", "map", "orthogonal_edge_router", "organic_edge_router" | `organic` |
59 |
60 | ### Methods
61 |
62 | - `show_cypher(cypher: str, layout: Optional[str] = None, **kwargs: Dict[str, Any]) -> None`
63 | - `cypher (str)`: The [Cypher query](https://neo4j.com/docs/cypher-manual/current/introduction/) that should be
64 | visualized.
65 | - `layout (Optional[str])`: The graph layout that is used. This overwrites the general layout in this specific graph instance. The following arguments are supported:
66 | - `hierarchic`
67 | - `organic`
68 | - `interactive_organic`
69 | - `circular`
70 | - `circular_straight_line`
71 | - `orthogonal`
72 | - `tree`
73 | - `radial`
74 | - `map`
75 | - `orthogonal_edge_router`
76 | - `organic_edge_router`
77 | - `**kwargs (Dict[str, Any])`: Additional parameters that should be passed to the Cypher query (e.g., see
78 | the [selection example](https://github.com/yWorks/yfiles-jupyter-graphs-for-neo4j/blob/main/examples/selection_example.ipynb)).
79 |
80 | The default behavior is to only show the nodes and relationships returned by the Cypher query.
81 | This can be changed to autocomplete relationships like in neo4j browser:
82 | - `set_autocomplete_relationships(autocomplete_relationships: Union[bool, str, list[str]]) -> None`: Sets whether to autocomplete relationships in the graph or not.
83 |
84 | The Cypher queries are executed by the provided Neo4j driver. If you have not specified a driver when instantiating the
85 | class, you can set
86 | a driver afterward:
87 |
88 | - `set_driver(driver)`: Sets the Neo4j driver that is used to resolve the Cypher queries.
89 | - `get_driver()`: Returns the current Neo4j driver.
90 |
91 | The graph visualization can be adjusted by adding configurations to each node label or edge type with the following
92 | functions:
93 |
94 | - `add_node_configuration(label: Union[str, list[str]], **kwargs: Dict[str, Any]) -> None`
95 | - `label (Union[str, list[str]])`: The node label(s) for which this configuration should be used. Supports `*` to address all labels.
96 | - `**kwargs (Dict[str, Any])`: Visualization configuration for the given node label. The following arguments are supported:
97 | - `text`: The text that displayed at the node. By default, the node's label is used.
98 | - `color`: A convenience color binding for the node (see also `styles` argument).
99 | - `size`: The size of the node.
100 | - `styles`: A dictionary that may contain the following attributes `color`, `shape` (one of 'ellipse', '
101 | hexagon', 'hexagon2', 'octagon', 'pill', 'rectangle', 'round-rectangle' or 'triangle'), `image`.
102 | - `property`: Allows to specify additional properties on the node, which may be bound by other bindings.
103 | - `type`: Defines a specific "type" for the node as described
104 | in [yFiles Graphs for Jupyter](https://yworks.github.io/yfiles-jupyter-graphs/02_graph_widget/#def-default_node_type_mappingindex-node)
105 | which affects the automatic positioning of nodes (same "type"s are preferred to be placed next to each other).
106 | - `parent_configuration`: Configure grouping for this node label. See [grouping.ipynb](https://github.com/yWorks/yfiles-jupyter-graphs-for-neo4j/blob/main/examples/grouping.ipynb)
107 | for examples.
108 |
109 | - `add_relationship_configuration(type: Union[str, list[str]], **kwargs: Dict[str, Any]) -> None`
110 | - `type (Union[str, list[str]])`: The relationship type for which this configuration should be used. Supports `*` to address all types.
111 | - `**kwargs`: Visualization configuration for the given relationship type. The following arguments are supported:
112 | - `text`: The text that displayed at the relationship. By default, the relationship's type is used.
113 | - `color`: The relationship's color.
114 | - `thickness_factor`: The relationship's stroke thickness factor. By default, `1`.
115 | - `styles`: The style of the edge.
116 | - `property`: Allows to specify additional properties on the relationship, which may be bound by other bindings.
117 |
118 | - `add_parent_relationship_configuration(type: Union[str, list[str]], reverse: Optional[bool] = False) -> None`
119 | - `type`: The relationship type that should be visualized as node grouping hierarchy instead of the actual relationship.
120 | - `reverse`: By default the target node is considered as parent. This can be reverted with this argument.
121 |
122 | To remove a configuration use the following functions:
123 |
124 | - `del_node_configuration(label: Union[str, list[str]]) -> None`: Deletes configuration for the given node label(s). Supports `*` to address all types.
125 | - `del_relationship_configurations(type: Union[str, list[str]]) -> None`: Deletes configuration for the given relationship type(s). Supports `*` to address all labels.
126 | - `del_parent_relationship_configuration(type: Union[str, list[str]]) -> None`: Deletes configuration for the given parent relationship type(s).
127 |
128 | You can select nodes and relationships to retrieve their ids. For example, you can use these ids in new Cypher queries
129 | by providing them as parameter to `show_cypher` as shown in
130 | the [selection example](https://github.com/yWorks/yfiles-jupyter-graphs-for-neo4j/blob/main/examples/selection_example.ipynb).
131 |
132 | - `get_selected_node_ids(widget: Optional[Type["Neo4jGraphWidget"]] = None) -> List[str]`: Returns an Array of node ids
133 | - `widget`: The widget that is used to select nodes from. If `None` is specified, the most recently shown widget is
134 | used.
135 |
136 | - `get_selected_relationship_ids(widget: Optional[Type["Neo4jGraphWidget"]] = None) -> List[str]`: Returns an Array of relationship ids
137 | - `widget`: The widget that is used to select edges from. If `None` is specified, the most recently shown widget is
138 | used.
139 |
140 | ## How configuration bindings are resolved
141 |
142 | The configuration bindings (see `add_node_configuration` or `add_relationship_configuration`) are resolved as follows:
143 |
144 | If the configuration binding is a string, the package first tries to resolve it against the item's properties
145 | and uses the property value if available. If there is no property with the given key, the string value itself is used as
146 | a constant binding.
147 |
148 | In case you want to create a constant string value as binding, which also happens to be a property key, use a binding
149 | function with a constant string as return value instead.
150 |
151 | If the configuration binding is a function, the return value of the function is used as value for the respective
152 | configuration.
153 |
154 | ## yFiles Graphs for Jupyter
155 |
156 | The graph visualization is provided by [yFiles Graphs for Jupyter](https://github.com/yWorks/yfiles-jupyter-graphs), a
157 | versatile graph visualization widget for Jupyter Notebooks.
158 |
159 | It can import and visualize graphs from various popular Python packages
160 | (e.g. [NetworkX](https://github.com/yWorks/yfiles-jupyter-graphs/blob/main/examples/13_networkx_import.ipynb),
161 | [PyGraphviz](https://github.com/yWorks/yfiles-jupyter-graphs/blob/main/examples/15_graphviz_import.ipynb),
162 | [igraph](https://github.com/yWorks/yfiles-jupyter-graphs/blob/main/examples/17_igraph_import.ipynb)) or just structured
163 | [node and edge lists](https://github.com/yWorks/yfiles-jupyter-graphs/blob/main/examples/01_introduction.ipynb).
164 |
165 | And provides a rich set of visualization options to bring your data to life (see
166 | the [example notebooks](https://github.com/yWorks/yfiles-jupyter-graphs/blob/main/examples/00_toc.ipynb)).
167 |
168 | ### Feature Highlights
169 |
170 |
184 |
185 | For a detailed feature guide, check out the main widget [example notebooks](https://colab.research.google.com/github/yWorks/yfiles-jupyter-graphs/blob/main/examples/00_toc.ipynb)
186 |
187 | ## Code of Conduct
188 |
189 | This project and everyone participating in it is governed by
190 | the [Code of Conduct](https://github.com/yWorks/yfiles-jupyter-graphs-for-neo4j/blob/main/CODE_OF_CONDUCT.md).
191 | By participating, you are expected to uphold this code.
192 | Please report unacceptable behavior to [contact@yworks.com](mailto:contact@yworks.com).
193 |
194 | ## Feedback
195 |
196 | This widget is by no means perfect.
197 | If you find something is not working as expected
198 | we are glad to receive an issue report from you.
199 | Please make sure
200 | to [search for existing issues](https://github.com/yWorks/yfiles-jupyter-graphs-for-neo4j/search?q=is%3Aissue) first
201 | and check if the issue is not an unsupported feature or known issue.
202 | If you did not find anything related, report a new issue with necessary information.
203 | Please also provide a clear and descriptive title and stick to the issue templates.
204 | See [issues](https://github.com/yWorks/yfiles-jupyter-graphs-for-neo4j/issues).
205 |
206 | ## Dependencies
207 |
208 | * [yFiles Graphs for Jupyter](https://github.com/yWorks/yfiles-jupyter-graphs)
209 |
210 | ## License
211 | See [LICENSE](https://github.com/yWorks/yfiles-jupyter-graphs-for-neo4j/blob/main/LICENSE.md) file.
--------------------------------------------------------------------------------
/README_DEV.md:
--------------------------------------------------------------------------------
1 | # How to build this project
2 |
3 | Open a terminal in the yfiles-jupyter-graphs-for-neo4j directory and execute the following command:
4 |
5 | ```bash
6 | python -m build
7 | ```
8 |
9 | This creates two files,
10 | - yfiles_jupyter_graphs_for_neo4j-1.0.0b0.tar.gz and
11 | - yfiles_jupyter_graphs_for_neo4j-1.0.0b0-py3-none-any.whl
12 |
13 | To use the build package install either one of them:
14 |
15 | ```bash
16 | pip install dist/yfiles_jupyter_graphs_for_neo4j-1.0.0b0.whl
17 | ```
18 |
19 | Now you're ready to use this in jupyter lab or jupyter notebook.
--------------------------------------------------------------------------------
/environment.build.yml:
--------------------------------------------------------------------------------
1 | name: yfiles_jupyter_graphs_for_neo4j_build
2 | channels:
3 | - conda-forge
4 | dependencies:
5 | - python
6 | - build
7 |
--------------------------------------------------------------------------------
/environment.dev.yml:
--------------------------------------------------------------------------------
1 | name: yfiles_jupyter_graphs_for_neo4j_dev
2 | channels:
3 | - conda-forge
4 | dependencies:
5 | - python
6 | - jupyterlab
7 | - build
8 |
--------------------------------------------------------------------------------
/examples/basic_example.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "58495ba6-70f8-4a34-b849-70f378cf7a58",
6 | "metadata": {},
7 | "source": [
8 | "# Basic Usage
"
9 | ]
10 | },
11 | {
12 | "cell_type": "code",
13 | "execution_count": null,
14 | "id": "879409d1-37c2-4ab2-80ae-deb4d7dc8412",
15 | "metadata": {},
16 | "outputs": [],
17 | "source": [
18 | "%pip install yfiles_jupyter_graphs_for_neo4j --quiet\n",
19 | "%pip install neo4j --quiet\n",
20 | "from yfiles_jupyter_graphs_for_neo4j import Neo4jGraphWidget\n",
21 | "from neo4j import GraphDatabase"
22 | ]
23 | },
24 | {
25 | "cell_type": "markdown",
26 | "id": "b8a1fc4a-e8ef-4346-abcd-9fb99aa62429",
27 | "metadata": {},
28 | "source": [
29 | "You can also open this notebook in Google Colab when Google Colab's custom widget manager is enabled:"
30 | ]
31 | },
32 | {
33 | "cell_type": "code",
34 | "execution_count": null,
35 | "id": "08c37cdc-b2f4-498f-8e24-d87cc9547048",
36 | "metadata": {},
37 | "outputs": [],
38 | "source": [
39 | "try:\n",
40 | " import google.colab\n",
41 | " from google.colab import output\n",
42 | " output.enable_custom_widget_manager()\n",
43 | "except:\n",
44 | " pass"
45 | ]
46 | },
47 | {
48 | "cell_type": "markdown",
49 | "id": "15fdc273-8bf8-4746-82c1-4fea2fc422d5",
50 | "metadata": {},
51 | "source": [
52 | "## Use the Neo4j driver"
53 | ]
54 | },
55 | {
56 | "cell_type": "code",
57 | "execution_count": null,
58 | "id": "53b5f2b5-0279-40d9-9434-c888f4e85924",
59 | "metadata": {},
60 | "outputs": [],
61 | "source": [
62 | "NEO4J_URI = \"neo4j+ssc://demo.neo4jlabs.com\" \n",
63 | "NEO4J_USERNAME = \"movies\"\n",
64 | "NEO4J_PASSWORD = \"movies\"\n",
65 | "driver = GraphDatabase.driver(uri = NEO4J_URI, auth = (NEO4J_USERNAME, NEO4J_PASSWORD), database = 'movies')\n",
66 | "\n",
67 | "g = Neo4jGraphWidget(driver)"
68 | ]
69 | },
70 | {
71 | "cell_type": "markdown",
72 | "id": "8f350852-71ce-49e7-828b-a4285c221d29",
73 | "metadata": {},
74 | "source": [
75 | "## Query the database with Cypher"
76 | ]
77 | },
78 | {
79 | "cell_type": "code",
80 | "execution_count": null,
81 | "id": "e7bc9c8a-d32d-4af7-935f-08154753210e",
82 | "metadata": {},
83 | "outputs": [],
84 | "source": [
85 | "g.show_cypher(\"MATCH (s)-[r]->(t) RETURN s,r,t LIMIT 10\")"
86 | ]
87 | }
88 | ],
89 | "metadata": {
90 | "kernelspec": {
91 | "display_name": "Python 3 (ipykernel)",
92 | "language": "python",
93 | "name": "python3"
94 | },
95 | "language_info": {
96 | "codemirror_mode": {
97 | "name": "ipython",
98 | "version": 3
99 | },
100 | "file_extension": ".py",
101 | "mimetype": "text/x-python",
102 | "name": "python",
103 | "nbconvert_exporter": "python",
104 | "pygments_lexer": "ipython3",
105 | "version": "3.9.19"
106 | }
107 | },
108 | "nbformat": 4,
109 | "nbformat_minor": 5
110 | }
111 |
--------------------------------------------------------------------------------
/examples/configurations_example.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "5dbe0b6a-35ce-49d1-acbf-d0590d1619c2",
6 | "metadata": {},
7 | "source": [
8 | "# Custom Data Configurations
"
9 | ]
10 | },
11 | {
12 | "cell_type": "code",
13 | "execution_count": null,
14 | "id": "8891bd52-e89c-46ba-a236-7a8ffee9b3da",
15 | "metadata": {},
16 | "outputs": [],
17 | "source": [
18 | "%pip install yfiles_jupyter_graphs_for_neo4j --quiet\n",
19 | "%pip install neo4j --quiet\n",
20 | "from yfiles_jupyter_graphs_for_neo4j import Neo4jGraphWidget\n",
21 | "from neo4j import GraphDatabase\n",
22 | "from datetime import datetime"
23 | ]
24 | },
25 | {
26 | "cell_type": "markdown",
27 | "id": "ebbc75dd-35db-4aff-bde2-2be4a9b11309",
28 | "metadata": {},
29 | "source": [
30 | "You can also open this notebook in Google Colab when Google Colab's custom widget manager is enabled:"
31 | ]
32 | },
33 | {
34 | "cell_type": "code",
35 | "execution_count": null,
36 | "id": "db54c371-ccb6-4f5c-9d96-917692af08bb",
37 | "metadata": {},
38 | "outputs": [],
39 | "source": [
40 | "try:\n",
41 | " import google.colab\n",
42 | " from google.colab import output\n",
43 | " output.enable_custom_widget_manager()\n",
44 | "except:\n",
45 | " pass"
46 | ]
47 | },
48 | {
49 | "cell_type": "markdown",
50 | "id": "e85cde2a-5dba-4c06-9c9e-af199246dd53",
51 | "metadata": {},
52 | "source": [
53 | "## Use the Neo4j driver"
54 | ]
55 | },
56 | {
57 | "cell_type": "code",
58 | "execution_count": null,
59 | "id": "0a50afba-06d6-47a4-9e38-106aa2823a83",
60 | "metadata": {},
61 | "outputs": [],
62 | "source": [
63 | "NEO4J_URI = \"neo4j+ssc://demo.neo4jlabs.com\" \n",
64 | "NEO4J_USERNAME = \"movies\"\n",
65 | "NEO4J_PASSWORD = \"movies\"\n",
66 | "driver = GraphDatabase.driver(uri = NEO4J_URI, auth = (NEO4J_USERNAME, NEO4J_PASSWORD), database = 'movies')\n",
67 | "\n",
68 | "g = Neo4jGraphWidget(driver)"
69 | ]
70 | },
71 | {
72 | "cell_type": "markdown",
73 | "id": "1ac1f749-f180-4870-955b-ea397879e6ce",
74 | "metadata": {},
75 | "source": [
76 | "## Configure data mappings\n",
77 | "\n",
78 | "Configurable values for the node visualization: `text`, `color`, `size`, `styles`, `property`, `coordinate`, `heat` and `type`.\n",
79 | "\n",
80 | "Configurable values for the relationship visualization: `text`, `color`, `thickness_factor`, `styles`, `heat` and `property`.\n",
81 | "\n",
82 | "When adding a configuration, you always need to specify the type of node or edge you want to adjust. (Here 'Person' or 'DIRECTED'),\n",
83 | "additionally you can define the new values for the above mentioned properties. The values may either be:\n",
84 | "- A lambda the resolves the node or relationship to a specific value.\n",
85 | "- A constant value which is directly used.\n",
86 | "- A property-key that points to the value that should be used for the configuration.\n",
87 | "\n",
88 | "For nodes, there is an additional `parent_configuration` that defines parent nodes for the node label. This configuration may either be:\n",
89 | "- A constant string value that is used as text for the created group node.\n",
90 | "- A property-key pointing to a string value that is used as text for the created group node.\n",
91 | "- A dict that with a 'text' property as label and additional styling properties (similar to the above mentioned visualization properties)."
92 | ]
93 | },
94 | {
95 | "cell_type": "code",
96 | "execution_count": null,
97 | "id": "af00a09e-9842-4dcd-b296-fe0f01ba09e4",
98 | "metadata": {},
99 | "outputs": [],
100 | "source": [
101 | "g.add_node_configuration('Person', text='name', size=(100,100), styles=lambda node : {'shape': 'rectangle'}, parent_configuration= {'text': 'people', 'color': '#cca9ff'})\n",
102 | "g.add_node_configuration('Movie', text= lambda node : {\n",
103 | " 'text': 'Title:\\n' + node['properties']['title'], \n",
104 | " 'backgroundColor': 'rgba(0,0,0,0.7)', \n",
105 | " 'fontSize': 20, \n",
106 | " 'color': '#FFFFFF', \n",
107 | " 'position': 'north', \n",
108 | " 'fontWeight': 'lighter',\n",
109 | " 'maximumWidth': 130, \n",
110 | " 'wrapping': 'word', \n",
111 | " 'textAlignment': 'center'\n",
112 | " }, parent_configuration='movies') \n",
113 | "g.add_relationship_configuration('DIRECTED', color=\"blue\", styles={'dashStyle': 'dot'})\n",
114 | "g.add_relationship_configuration('PRODUCED', text = lambda edge: {'text': 'produced', 'fontWeight': 'bolder', 'fontSize': 16}, color='#AC94F4')\n",
115 | "g.add_relationship_configuration('ACTED_IN', thickness_factor=2, color='#AC94F4') "
116 | ]
117 | },
118 | {
119 | "cell_type": "markdown",
120 | "id": "7b88d8d4-dbb8-4d87-b9bf-ee6316d96dd5",
121 | "metadata": {},
122 | "source": [
123 | "## Query the database with Cypher"
124 | ]
125 | },
126 | {
127 | "cell_type": "code",
128 | "execution_count": null,
129 | "id": "514823d3-b3a4-4d5e-a48f-97058110ea4e",
130 | "metadata": {},
131 | "outputs": [],
132 | "source": [
133 | "g.show_cypher(\"MATCH (s)-[r]->(t) RETURN s,r,t LIMIT 20\")"
134 | ]
135 | },
136 | {
137 | "cell_type": "markdown",
138 | "id": "a5d61081-2075-4d85-91a9-0ec49d720918",
139 | "metadata": {},
140 | "source": [
141 | "### Visualize \"ACTED_IN\" as nested hierarchy\n",
142 | "\n",
143 | "Relationship types may be replaced by a nesting hierarchy. This restructures the graph by removing these relationships and instead representing them as nested parent-child structure. The relationship structure may als be reversed with the `reverse` keyword argument to the `add_parent_relationship_configuration` method."
144 | ]
145 | },
146 | {
147 | "cell_type": "code",
148 | "execution_count": null,
149 | "id": "69211d43-39c5-4fb3-8fcb-5b6f1d4c0652",
150 | "metadata": {},
151 | "outputs": [],
152 | "source": [
153 | "g.add_parent_relationship_configuration('ACTED_IN')\n",
154 | "g.show_cypher(\"MATCH (s)-[r]->(t:Movie {title: 'The Matrix'}) RETURN s,r,t LIMIT 20\")"
155 | ]
156 | },
157 | {
158 | "cell_type": "markdown",
159 | "id": "69570f85-9fb9-4cf3-b7bf-4125ff677a81",
160 | "metadata": {},
161 | "source": [
162 | "## Wildcard configurations\n",
163 | "\n",
164 | "Using `'*'` as the node or relationship type, all nodes or relationships are affected by the given configuration.\n",
165 | "\n",
166 | "Using an explicit type configuration overwrites the wildcard configuration."
167 | ]
168 | },
169 | {
170 | "cell_type": "code",
171 | "execution_count": null,
172 | "id": "158bf75e-3382-4748-a920-dc0f756b1bbe",
173 | "metadata": {},
174 | "outputs": [],
175 | "source": [
176 | "g2 = Neo4jGraphWidget(driver, layout='circular')\n",
177 | "g2.add_relationship_configuration('*', color='gray') # all relationship types gray\n",
178 | "g2.add_relationship_configuration('PRODUCED', color='red') # highlight the \"produced\" type\n",
179 | "g2.show_cypher(\"MATCH (s)-[r]->(t) RETURN s,r,t LIMIT 10\")"
180 | ]
181 | },
182 | {
183 | "cell_type": "code",
184 | "execution_count": null,
185 | "id": "255cfdb6-7cd9-44b5-beee-834208bc0535",
186 | "metadata": {},
187 | "outputs": [],
188 | "source": []
189 | }
190 | ],
191 | "metadata": {
192 | "kernelspec": {
193 | "display_name": "Python 3 (ipykernel)",
194 | "language": "python",
195 | "name": "python3"
196 | },
197 | "language_info": {
198 | "codemirror_mode": {
199 | "name": "ipython",
200 | "version": 3
201 | },
202 | "file_extension": ".py",
203 | "mimetype": "text/x-python",
204 | "name": "python",
205 | "nbconvert_exporter": "python",
206 | "pygments_lexer": "ipython3",
207 | "version": "3.12.3"
208 | },
209 | "widgets": {
210 | "application/vnd.jupyter.widget-state+json": {
211 | "state": {
212 | "02ec6d2ada004284ae819698a43f3a94": {
213 | "model_module": "yfiles-jupyter-graphs",
214 | "model_module_version": "^1.10.2",
215 | "model_name": "GraphModel",
216 | "state": {
217 | "_context_pane_mapping": [
218 | {
219 | "id": "Neighborhood",
220 | "title": "Neighborhood"
221 | },
222 | {
223 | "id": "Data",
224 | "title": "Data"
225 | },
226 | {
227 | "id": "Search",
228 | "title": "Search"
229 | },
230 | {
231 | "id": "About",
232 | "title": "About"
233 | }
234 | ],
235 | "_data_importer": "neo4j",
236 | "_directed": true,
237 | "_edges": [
238 | {
239 | "color": "gray",
240 | "directed": true,
241 | "end": 0,
242 | "id": 0,
243 | "label": "ACTED_IN",
244 | "properties": {
245 | "label": "ACTED_IN",
246 | "roles": [
247 | "Neo"
248 | ]
249 | },
250 | "start": 1,
251 | "styles": {},
252 | "thickness_factor": 1
253 | },
254 | {
255 | "color": "gray",
256 | "directed": true,
257 | "end": 0,
258 | "id": 1,
259 | "label": "ACTED_IN",
260 | "properties": {
261 | "label": "ACTED_IN",
262 | "roles": [
263 | "Trinity"
264 | ]
265 | },
266 | "start": 2,
267 | "styles": {},
268 | "thickness_factor": 1
269 | },
270 | {
271 | "color": "gray",
272 | "directed": true,
273 | "end": 0,
274 | "id": 2,
275 | "label": "ACTED_IN",
276 | "properties": {
277 | "label": "ACTED_IN",
278 | "roles": [
279 | "Morpheus"
280 | ]
281 | },
282 | "start": 3,
283 | "styles": {},
284 | "thickness_factor": 1
285 | },
286 | {
287 | "color": "gray",
288 | "directed": true,
289 | "end": 0,
290 | "id": 3,
291 | "label": "ACTED_IN",
292 | "properties": {
293 | "label": "ACTED_IN",
294 | "roles": [
295 | "Agent Smith"
296 | ]
297 | },
298 | "start": 4,
299 | "styles": {},
300 | "thickness_factor": 1
301 | },
302 | {
303 | "color": "gray",
304 | "directed": true,
305 | "end": 0,
306 | "id": 4,
307 | "label": "DIRECTED",
308 | "properties": {
309 | "label": "DIRECTED"
310 | },
311 | "start": 5,
312 | "styles": {},
313 | "thickness_factor": 1
314 | },
315 | {
316 | "color": "gray",
317 | "directed": true,
318 | "end": 0,
319 | "id": 5,
320 | "label": "DIRECTED",
321 | "properties": {
322 | "label": "DIRECTED"
323 | },
324 | "start": 6,
325 | "styles": {},
326 | "thickness_factor": 1
327 | },
328 | {
329 | "color": "red",
330 | "directed": true,
331 | "end": 0,
332 | "id": 6,
333 | "label": "PRODUCED",
334 | "properties": {
335 | "label": "PRODUCED"
336 | },
337 | "start": 7,
338 | "styles": {},
339 | "thickness_factor": 1
340 | },
341 | {
342 | "color": "gray",
343 | "directed": true,
344 | "end": 0,
345 | "id": 7,
346 | "label": "ACTED_IN",
347 | "properties": {
348 | "label": "ACTED_IN",
349 | "roles": [
350 | "Emil"
351 | ]
352 | },
353 | "start": 8,
354 | "styles": {},
355 | "thickness_factor": 1
356 | },
357 | {
358 | "color": "gray",
359 | "directed": true,
360 | "end": 9,
361 | "id": 8,
362 | "label": "ACTED_IN",
363 | "properties": {
364 | "label": "ACTED_IN",
365 | "roles": [
366 | "Neo"
367 | ]
368 | },
369 | "start": 1,
370 | "styles": {},
371 | "thickness_factor": 1
372 | },
373 | {
374 | "color": "gray",
375 | "directed": true,
376 | "end": 9,
377 | "id": 9,
378 | "label": "ACTED_IN",
379 | "properties": {
380 | "label": "ACTED_IN",
381 | "roles": [
382 | "Trinity"
383 | ]
384 | },
385 | "start": 2,
386 | "styles": {},
387 | "thickness_factor": 1
388 | }
389 | ],
390 | "_graph_layout": {
391 | "algorithm": "circular",
392 | "options": {}
393 | },
394 | "_model_module_version": "^1.10.2",
395 | "_nodes": [
396 | {
397 | "color": "#2196F3",
398 | "id": 1,
399 | "label": "Keanu Reeves",
400 | "position": [
401 | 0,
402 | 0
403 | ],
404 | "properties": {
405 | "born": 1964,
406 | "label": "Person",
407 | "name": "Keanu Reeves"
408 | },
409 | "scale_factor": 1,
410 | "size": [
411 | 55,
412 | 55
413 | ],
414 | "styles": {},
415 | "type": "#2196F3"
416 | },
417 | {
418 | "color": "#4CAF50",
419 | "id": 0,
420 | "label": "The Matrix",
421 | "position": [
422 | 0,
423 | 0
424 | ],
425 | "properties": {
426 | "label": "Movie",
427 | "released": 1999,
428 | "tagline": "Welcome to the Real World",
429 | "title": "The Matrix",
430 | "votes": 6345
431 | },
432 | "scale_factor": 1,
433 | "size": [
434 | 55,
435 | 55
436 | ],
437 | "styles": {},
438 | "type": "#4CAF50"
439 | },
440 | {
441 | "color": "#2196F3",
442 | "id": 2,
443 | "label": "Carrie-Anne Moss",
444 | "position": [
445 | 0,
446 | 0
447 | ],
448 | "properties": {
449 | "born": 1967,
450 | "label": "Person",
451 | "name": "Carrie-Anne Moss"
452 | },
453 | "scale_factor": 1,
454 | "size": [
455 | 55,
456 | 55
457 | ],
458 | "styles": {},
459 | "type": "#2196F3"
460 | },
461 | {
462 | "color": "#2196F3",
463 | "id": 3,
464 | "label": "Laurence Fishburne",
465 | "position": [
466 | 0,
467 | 0
468 | ],
469 | "properties": {
470 | "born": 1961,
471 | "label": "Person",
472 | "name": "Laurence Fishburne"
473 | },
474 | "scale_factor": 1,
475 | "size": [
476 | 55,
477 | 55
478 | ],
479 | "styles": {},
480 | "type": "#2196F3"
481 | },
482 | {
483 | "color": "#2196F3",
484 | "id": 4,
485 | "label": "Hugo Weaving",
486 | "position": [
487 | 0,
488 | 0
489 | ],
490 | "properties": {
491 | "born": 1960,
492 | "label": "Person",
493 | "name": "Hugo Weaving"
494 | },
495 | "scale_factor": 1,
496 | "size": [
497 | 55,
498 | 55
499 | ],
500 | "styles": {},
501 | "type": "#2196F3"
502 | },
503 | {
504 | "color": "#2196F3",
505 | "id": 5,
506 | "label": "Lilly Wachowski",
507 | "position": [
508 | 0,
509 | 0
510 | ],
511 | "properties": {
512 | "born": 1967,
513 | "label": "Person",
514 | "name": "Lilly Wachowski"
515 | },
516 | "scale_factor": 1,
517 | "size": [
518 | 55,
519 | 55
520 | ],
521 | "styles": {},
522 | "type": "#2196F3"
523 | },
524 | {
525 | "color": "#2196F3",
526 | "id": 6,
527 | "label": "Lana Wachowski",
528 | "position": [
529 | 0,
530 | 0
531 | ],
532 | "properties": {
533 | "born": 1965,
534 | "label": "Person",
535 | "name": "Lana Wachowski"
536 | },
537 | "scale_factor": 1,
538 | "size": [
539 | 55,
540 | 55
541 | ],
542 | "styles": {},
543 | "type": "#2196F3"
544 | },
545 | {
546 | "color": "#2196F3",
547 | "id": 7,
548 | "label": "Joel Silver",
549 | "position": [
550 | 0,
551 | 0
552 | ],
553 | "properties": {
554 | "born": 1952,
555 | "label": "Person",
556 | "name": "Joel Silver"
557 | },
558 | "scale_factor": 1,
559 | "size": [
560 | 55,
561 | 55
562 | ],
563 | "styles": {},
564 | "type": "#2196F3"
565 | },
566 | {
567 | "color": "#2196F3",
568 | "id": 8,
569 | "label": "Emil Eifrem",
570 | "position": [
571 | 0,
572 | 0
573 | ],
574 | "properties": {
575 | "born": 1978,
576 | "label": "Person",
577 | "name": "Emil Eifrem"
578 | },
579 | "scale_factor": 1,
580 | "size": [
581 | 55,
582 | 55
583 | ],
584 | "styles": {},
585 | "type": "#2196F3"
586 | },
587 | {
588 | "color": "#4CAF50",
589 | "id": 9,
590 | "label": "The Matrix Reloaded",
591 | "position": [
592 | 0,
593 | 0
594 | ],
595 | "properties": {
596 | "label": "Movie",
597 | "released": 2003,
598 | "tagline": "Free your mind",
599 | "title": "The Matrix Reloaded",
600 | "votes": 1841
601 | },
602 | "scale_factor": 1,
603 | "size": [
604 | 55,
605 | 55
606 | ],
607 | "styles": {},
608 | "type": "#4CAF50"
609 | }
610 | ],
611 | "_overview": {
612 | "enabled": null,
613 | "overview_set": false
614 | },
615 | "_selected_graph": [
616 | [],
617 | []
618 | ],
619 | "_sidebar": {
620 | "enabled": false,
621 | "start_with": "About"
622 | },
623 | "_view_module_version": "^1.10.2",
624 | "layout": "IPY_MODEL_0462e2af62b4401f92fb547db77996ee"
625 | }
626 | },
627 | "0462e2af62b4401f92fb547db77996ee": {
628 | "model_module": "@jupyter-widgets/base",
629 | "model_module_version": "2.0.0",
630 | "model_name": "LayoutModel",
631 | "state": {
632 | "height": "500px",
633 | "width": "100%"
634 | }
635 | },
636 | "21ebaf0ab0b841aca22867906841cceb": {
637 | "model_module": "yfiles-jupyter-graphs",
638 | "model_module_version": "^1.10.2",
639 | "model_name": "GraphModel",
640 | "state": {
641 | "_context_pane_mapping": [
642 | {
643 | "id": "Neighborhood",
644 | "title": "Neighborhood"
645 | },
646 | {
647 | "id": "Data",
648 | "title": "Data"
649 | },
650 | {
651 | "id": "Search",
652 | "title": "Search"
653 | },
654 | {
655 | "id": "About",
656 | "title": "About"
657 | }
658 | ],
659 | "_data_importer": "neo4j",
660 | "_directed": true,
661 | "_edges": [
662 | {
663 | "color": "blue",
664 | "directed": true,
665 | "end": 0,
666 | "id": 4,
667 | "label": "DIRECTED",
668 | "properties": {
669 | "label": "DIRECTED"
670 | },
671 | "start": 5,
672 | "styles": {
673 | "dashStyle": "dot"
674 | },
675 | "thickness_factor": 1
676 | },
677 | {
678 | "color": "blue",
679 | "directed": true,
680 | "end": 0,
681 | "id": 5,
682 | "label": "DIRECTED",
683 | "properties": {
684 | "label": "DIRECTED"
685 | },
686 | "start": 6,
687 | "styles": {
688 | "dashStyle": "dot"
689 | },
690 | "thickness_factor": 1
691 | },
692 | {
693 | "color": "#AC94F4",
694 | "directed": true,
695 | "end": 0,
696 | "id": 6,
697 | "label": "produced",
698 | "properties": {
699 | "label": "PRODUCED"
700 | },
701 | "start": 7,
702 | "styles": {
703 | "label_styles": {}
704 | },
705 | "thickness_factor": 1
706 | },
707 | {
708 | "color": "blue",
709 | "directed": true,
710 | "end": 9,
711 | "id": 12,
712 | "label": "DIRECTED",
713 | "properties": {
714 | "label": "DIRECTED"
715 | },
716 | "start": 5,
717 | "styles": {
718 | "dashStyle": "dot"
719 | },
720 | "thickness_factor": 1
721 | },
722 | {
723 | "color": "blue",
724 | "directed": true,
725 | "end": 9,
726 | "id": 13,
727 | "label": "DIRECTED",
728 | "properties": {
729 | "label": "DIRECTED"
730 | },
731 | "start": 6,
732 | "styles": {
733 | "dashStyle": "dot"
734 | },
735 | "thickness_factor": 1
736 | },
737 | {
738 | "color": "#AC94F4",
739 | "directed": true,
740 | "end": 9,
741 | "id": 14,
742 | "label": "produced",
743 | "properties": {
744 | "label": "PRODUCED"
745 | },
746 | "start": 7,
747 | "styles": {
748 | "label_styles": {}
749 | },
750 | "thickness_factor": 1
751 | },
752 | {
753 | "color": "blue",
754 | "directed": true,
755 | "end": 10,
756 | "id": 19,
757 | "label": "DIRECTED",
758 | "properties": {
759 | "label": "DIRECTED"
760 | },
761 | "start": 5,
762 | "styles": {
763 | "dashStyle": "dot"
764 | },
765 | "thickness_factor": 1
766 | }
767 | ],
768 | "_graph_layout": {
769 | "algorithm": "organic",
770 | "options": {}
771 | },
772 | "_model_module_version": "^1.10.2",
773 | "_nodes": [
774 | {
775 | "color": "#2196F3",
776 | "id": 1,
777 | "label": "Keanu Reeves",
778 | "parentId": 10,
779 | "position": [
780 | 0,
781 | 0
782 | ],
783 | "properties": {
784 | "born": 1964,
785 | "label": "Person",
786 | "name": "Keanu Reeves"
787 | },
788 | "scale_factor": 1,
789 | "size": [
790 | 100,
791 | 100
792 | ],
793 | "styles": {
794 | "shape": "rectangle"
795 | },
796 | "type": "#2196F3"
797 | },
798 | {
799 | "color": "#4CAF50",
800 | "id": 0,
801 | "label": "Title:\nThe Matrix",
802 | "parentId": "GroupNodemovies",
803 | "position": [
804 | 0,
805 | 0
806 | ],
807 | "properties": {
808 | "label": "Movie",
809 | "released": 1999,
810 | "tagline": "Welcome to the Real World",
811 | "title": "The Matrix",
812 | "votes": 6345
813 | },
814 | "scale_factor": 1,
815 | "size": [
816 | 55,
817 | 55
818 | ],
819 | "styles": {
820 | "label_styles": {
821 | "backgroundColor": "rgba(0,0,0,0.7)",
822 | "color": "#FFFFFF",
823 | "fontSize": 20,
824 | "fontWeight": "lighter",
825 | "maximumWidth": 130,
826 | "position": "north",
827 | "textAlignment": "center",
828 | "wrapping": "word"
829 | }
830 | },
831 | "type": "#4CAF50"
832 | },
833 | {
834 | "color": "#2196F3",
835 | "id": 2,
836 | "label": "Carrie-Anne Moss",
837 | "parentId": 10,
838 | "position": [
839 | 0,
840 | 0
841 | ],
842 | "properties": {
843 | "born": 1967,
844 | "label": "Person",
845 | "name": "Carrie-Anne Moss"
846 | },
847 | "scale_factor": 1,
848 | "size": [
849 | 100,
850 | 100
851 | ],
852 | "styles": {
853 | "shape": "rectangle"
854 | },
855 | "type": "#2196F3"
856 | },
857 | {
858 | "color": "#2196F3",
859 | "id": 3,
860 | "label": "Laurence Fishburne",
861 | "parentId": 10,
862 | "position": [
863 | 0,
864 | 0
865 | ],
866 | "properties": {
867 | "born": 1961,
868 | "label": "Person",
869 | "name": "Laurence Fishburne"
870 | },
871 | "scale_factor": 1,
872 | "size": [
873 | 100,
874 | 100
875 | ],
876 | "styles": {
877 | "shape": "rectangle"
878 | },
879 | "type": "#2196F3"
880 | },
881 | {
882 | "color": "#2196F3",
883 | "id": 4,
884 | "label": "Hugo Weaving",
885 | "parentId": 10,
886 | "position": [
887 | 0,
888 | 0
889 | ],
890 | "properties": {
891 | "born": 1960,
892 | "label": "Person",
893 | "name": "Hugo Weaving"
894 | },
895 | "scale_factor": 1,
896 | "size": [
897 | 100,
898 | 100
899 | ],
900 | "styles": {
901 | "shape": "rectangle"
902 | },
903 | "type": "#2196F3"
904 | },
905 | {
906 | "color": "#2196F3",
907 | "id": 5,
908 | "label": "Lilly Wachowski",
909 | "parentId": "GroupNodepeople",
910 | "position": [
911 | 0,
912 | 0
913 | ],
914 | "properties": {
915 | "born": 1967,
916 | "label": "Person",
917 | "name": "Lilly Wachowski"
918 | },
919 | "scale_factor": 1,
920 | "size": [
921 | 100,
922 | 100
923 | ],
924 | "styles": {
925 | "shape": "rectangle"
926 | },
927 | "type": "#2196F3"
928 | },
929 | {
930 | "color": "#2196F3",
931 | "id": 6,
932 | "label": "Lana Wachowski",
933 | "parentId": "GroupNodepeople",
934 | "position": [
935 | 0,
936 | 0
937 | ],
938 | "properties": {
939 | "born": 1965,
940 | "label": "Person",
941 | "name": "Lana Wachowski"
942 | },
943 | "scale_factor": 1,
944 | "size": [
945 | 100,
946 | 100
947 | ],
948 | "styles": {
949 | "shape": "rectangle"
950 | },
951 | "type": "#2196F3"
952 | },
953 | {
954 | "color": "#2196F3",
955 | "id": 7,
956 | "label": "Joel Silver",
957 | "parentId": "GroupNodepeople",
958 | "position": [
959 | 0,
960 | 0
961 | ],
962 | "properties": {
963 | "born": 1952,
964 | "label": "Person",
965 | "name": "Joel Silver"
966 | },
967 | "scale_factor": 1,
968 | "size": [
969 | 100,
970 | 100
971 | ],
972 | "styles": {
973 | "shape": "rectangle"
974 | },
975 | "type": "#2196F3"
976 | },
977 | {
978 | "color": "#2196F3",
979 | "id": 8,
980 | "label": "Emil Eifrem",
981 | "parentId": 0,
982 | "position": [
983 | 0,
984 | 0
985 | ],
986 | "properties": {
987 | "born": 1978,
988 | "label": "Person",
989 | "name": "Emil Eifrem"
990 | },
991 | "scale_factor": 1,
992 | "size": [
993 | 100,
994 | 100
995 | ],
996 | "styles": {
997 | "shape": "rectangle"
998 | },
999 | "type": "#2196F3"
1000 | },
1001 | {
1002 | "color": "#4CAF50",
1003 | "id": 9,
1004 | "label": "Title:\nThe Matrix Reloaded",
1005 | "parentId": "GroupNodemovies",
1006 | "position": [
1007 | 0,
1008 | 0
1009 | ],
1010 | "properties": {
1011 | "label": "Movie",
1012 | "released": 2003,
1013 | "tagline": "Free your mind",
1014 | "title": "The Matrix Reloaded",
1015 | "votes": 1841
1016 | },
1017 | "scale_factor": 1,
1018 | "size": [
1019 | 55,
1020 | 55
1021 | ],
1022 | "styles": {
1023 | "label_styles": {
1024 | "backgroundColor": "rgba(0,0,0,0.7)",
1025 | "color": "#FFFFFF",
1026 | "fontSize": 20,
1027 | "fontWeight": "lighter",
1028 | "maximumWidth": 130,
1029 | "position": "north",
1030 | "textAlignment": "center",
1031 | "wrapping": "word"
1032 | }
1033 | },
1034 | "type": "#4CAF50"
1035 | },
1036 | {
1037 | "color": "#4CAF50",
1038 | "id": 10,
1039 | "label": "Title:\nThe Matrix Revolutions",
1040 | "parentId": "GroupNodemovies",
1041 | "position": [
1042 | 0,
1043 | 0
1044 | ],
1045 | "properties": {
1046 | "label": "Movie",
1047 | "released": 2003,
1048 | "tagline": "Everything that has a beginning has an end",
1049 | "title": "The Matrix Revolutions",
1050 | "votes": 1534
1051 | },
1052 | "scale_factor": 1,
1053 | "size": [
1054 | 55,
1055 | 55
1056 | ],
1057 | "styles": {
1058 | "label_styles": {
1059 | "backgroundColor": "rgba(0,0,0,0.7)",
1060 | "color": "#FFFFFF",
1061 | "fontSize": 20,
1062 | "fontWeight": "lighter",
1063 | "maximumWidth": 130,
1064 | "position": "north",
1065 | "textAlignment": "center",
1066 | "wrapping": "word"
1067 | }
1068 | },
1069 | "type": "#4CAF50"
1070 | },
1071 | {
1072 | "color": "#cca9ff",
1073 | "id": "GroupNodepeople",
1074 | "label": "people",
1075 | "position": [
1076 | 0,
1077 | 0
1078 | ],
1079 | "properties": {
1080 | "label": "people"
1081 | },
1082 | "scale_factor": 1,
1083 | "size": [
1084 | 55,
1085 | 55
1086 | ],
1087 | "styles": {},
1088 | "type": "#cca9ff"
1089 | },
1090 | {
1091 | "color": "#F44336",
1092 | "id": "GroupNodemovies",
1093 | "label": "movies",
1094 | "position": [
1095 | 0,
1096 | 0
1097 | ],
1098 | "properties": {
1099 | "label": "movies"
1100 | },
1101 | "scale_factor": 1,
1102 | "size": [
1103 | 55,
1104 | 55
1105 | ],
1106 | "styles": {},
1107 | "type": "#F44336"
1108 | }
1109 | ],
1110 | "_overview": {
1111 | "enabled": null,
1112 | "overview_set": false
1113 | },
1114 | "_selected_graph": [
1115 | [],
1116 | []
1117 | ],
1118 | "_sidebar": {
1119 | "enabled": false,
1120 | "start_with": "About"
1121 | },
1122 | "_view_module_version": "^1.10.2",
1123 | "layout": "IPY_MODEL_6bdf055e98594e2599fa5156c3f65956"
1124 | }
1125 | },
1126 | "34bbd98495ca4f32aae4dbb34758f654": {
1127 | "model_module": "@jupyter-widgets/base",
1128 | "model_module_version": "2.0.0",
1129 | "model_name": "LayoutModel",
1130 | "state": {
1131 | "height": "500px",
1132 | "width": "100%"
1133 | }
1134 | },
1135 | "577983e1cec84593b62c1b70fb1637ae": {
1136 | "model_module": "@jupyter-widgets/base",
1137 | "model_module_version": "2.0.0",
1138 | "model_name": "LayoutModel",
1139 | "state": {
1140 | "height": "500px",
1141 | "width": "100%"
1142 | }
1143 | },
1144 | "5fd3993005a24254b3a3d6b0ee922944": {
1145 | "model_module": "@jupyter-widgets/base",
1146 | "model_module_version": "2.0.0",
1147 | "model_name": "LayoutModel",
1148 | "state": {
1149 | "height": "630px",
1150 | "width": "100%"
1151 | }
1152 | },
1153 | "61b4005ae6884205994ae640442962eb": {
1154 | "model_module": "@jupyter-widgets/base",
1155 | "model_module_version": "2.0.0",
1156 | "model_name": "LayoutModel",
1157 | "state": {
1158 | "height": "500px",
1159 | "width": "100%"
1160 | }
1161 | },
1162 | "63e098dd1ddd46d08940956fca858f87": {
1163 | "model_module": "yfiles-jupyter-graphs",
1164 | "model_module_version": "^1.10.2",
1165 | "model_name": "GraphModel",
1166 | "state": {
1167 | "_context_pane_mapping": [
1168 | {
1169 | "id": "Neighborhood",
1170 | "title": "Neighborhood"
1171 | },
1172 | {
1173 | "id": "Data",
1174 | "title": "Data"
1175 | },
1176 | {
1177 | "id": "Search",
1178 | "title": "Search"
1179 | },
1180 | {
1181 | "id": "About",
1182 | "title": "About"
1183 | }
1184 | ],
1185 | "_data_importer": "neo4j",
1186 | "_directed": true,
1187 | "_edges": [
1188 | {
1189 | "color": "blue",
1190 | "directed": true,
1191 | "end": 0,
1192 | "id": 4,
1193 | "label": "DIRECTED",
1194 | "properties": {
1195 | "label": "DIRECTED"
1196 | },
1197 | "start": 5,
1198 | "styles": {
1199 | "dashStyle": "dot"
1200 | },
1201 | "thickness_factor": 1
1202 | },
1203 | {
1204 | "color": "blue",
1205 | "directed": true,
1206 | "end": 0,
1207 | "id": 5,
1208 | "label": "DIRECTED",
1209 | "properties": {
1210 | "label": "DIRECTED"
1211 | },
1212 | "start": 6,
1213 | "styles": {
1214 | "dashStyle": "dot"
1215 | },
1216 | "thickness_factor": 1
1217 | },
1218 | {
1219 | "color": "#AC94F4",
1220 | "directed": true,
1221 | "end": 0,
1222 | "id": 6,
1223 | "label": "produced",
1224 | "properties": {
1225 | "label": "PRODUCED"
1226 | },
1227 | "start": 7,
1228 | "styles": {
1229 | "label_styles": {
1230 | "fontWeight": "bolder"
1231 | }
1232 | },
1233 | "thickness_factor": 1
1234 | },
1235 | {
1236 | "color": "blue",
1237 | "directed": true,
1238 | "end": 9,
1239 | "id": 12,
1240 | "label": "DIRECTED",
1241 | "properties": {
1242 | "label": "DIRECTED"
1243 | },
1244 | "start": 5,
1245 | "styles": {
1246 | "dashStyle": "dot"
1247 | },
1248 | "thickness_factor": 1
1249 | },
1250 | {
1251 | "color": "blue",
1252 | "directed": true,
1253 | "end": 9,
1254 | "id": 13,
1255 | "label": "DIRECTED",
1256 | "properties": {
1257 | "label": "DIRECTED"
1258 | },
1259 | "start": 6,
1260 | "styles": {
1261 | "dashStyle": "dot"
1262 | },
1263 | "thickness_factor": 1
1264 | },
1265 | {
1266 | "color": "#AC94F4",
1267 | "directed": true,
1268 | "end": 9,
1269 | "id": 14,
1270 | "label": "produced",
1271 | "properties": {
1272 | "label": "PRODUCED"
1273 | },
1274 | "start": 7,
1275 | "styles": {
1276 | "label_styles": {
1277 | "fontWeight": "bolder"
1278 | }
1279 | },
1280 | "thickness_factor": 1
1281 | },
1282 | {
1283 | "color": "blue",
1284 | "directed": true,
1285 | "end": 10,
1286 | "id": 19,
1287 | "label": "DIRECTED",
1288 | "properties": {
1289 | "label": "DIRECTED"
1290 | },
1291 | "start": 5,
1292 | "styles": {
1293 | "dashStyle": "dot"
1294 | },
1295 | "thickness_factor": 1
1296 | }
1297 | ],
1298 | "_graph_layout": {
1299 | "algorithm": "organic",
1300 | "options": {}
1301 | },
1302 | "_model_module_version": "^1.10.2",
1303 | "_nodes": [
1304 | {
1305 | "color": "#2196F3",
1306 | "id": 1,
1307 | "label": "Keanu Reeves",
1308 | "parentId": 10,
1309 | "position": [
1310 | 0,
1311 | 0
1312 | ],
1313 | "properties": {
1314 | "born": 1964,
1315 | "label": "Person",
1316 | "name": "Keanu Reeves"
1317 | },
1318 | "scale_factor": 1,
1319 | "size": [
1320 | 100,
1321 | 100
1322 | ],
1323 | "styles": {
1324 | "shape": "rectangle"
1325 | },
1326 | "type": "#2196F3"
1327 | },
1328 | {
1329 | "color": "#4CAF50",
1330 | "id": 0,
1331 | "label": "Title:\nThe Matrix",
1332 | "parentId": "GroupNodemovies",
1333 | "position": [
1334 | 0,
1335 | 0
1336 | ],
1337 | "properties": {
1338 | "label": "Movie",
1339 | "released": 1999,
1340 | "tagline": "Welcome to the Real World",
1341 | "title": "The Matrix",
1342 | "votes": 6345
1343 | },
1344 | "scale_factor": 1,
1345 | "size": [
1346 | 55,
1347 | 55
1348 | ],
1349 | "styles": {
1350 | "label_styles": {
1351 | "backgroundColor": "rgba(0,0,0,0.7)",
1352 | "color": "#FFFFFF",
1353 | "fontSize": 20,
1354 | "fontWeight": "lighter",
1355 | "maximumWidth": 130,
1356 | "position": "north",
1357 | "textAlignment": "center",
1358 | "wrapping": "word"
1359 | }
1360 | },
1361 | "type": "#4CAF50"
1362 | },
1363 | {
1364 | "color": "#2196F3",
1365 | "id": 2,
1366 | "label": "Carrie-Anne Moss",
1367 | "parentId": 10,
1368 | "position": [
1369 | 0,
1370 | 0
1371 | ],
1372 | "properties": {
1373 | "born": 1967,
1374 | "label": "Person",
1375 | "name": "Carrie-Anne Moss"
1376 | },
1377 | "scale_factor": 1,
1378 | "size": [
1379 | 100,
1380 | 100
1381 | ],
1382 | "styles": {
1383 | "shape": "rectangle"
1384 | },
1385 | "type": "#2196F3"
1386 | },
1387 | {
1388 | "color": "#2196F3",
1389 | "id": 3,
1390 | "label": "Laurence Fishburne",
1391 | "parentId": 10,
1392 | "position": [
1393 | 0,
1394 | 0
1395 | ],
1396 | "properties": {
1397 | "born": 1961,
1398 | "label": "Person",
1399 | "name": "Laurence Fishburne"
1400 | },
1401 | "scale_factor": 1,
1402 | "size": [
1403 | 100,
1404 | 100
1405 | ],
1406 | "styles": {
1407 | "shape": "rectangle"
1408 | },
1409 | "type": "#2196F3"
1410 | },
1411 | {
1412 | "color": "#2196F3",
1413 | "id": 4,
1414 | "label": "Hugo Weaving",
1415 | "parentId": 10,
1416 | "position": [
1417 | 0,
1418 | 0
1419 | ],
1420 | "properties": {
1421 | "born": 1960,
1422 | "label": "Person",
1423 | "name": "Hugo Weaving"
1424 | },
1425 | "scale_factor": 1,
1426 | "size": [
1427 | 100,
1428 | 100
1429 | ],
1430 | "styles": {
1431 | "shape": "rectangle"
1432 | },
1433 | "type": "#2196F3"
1434 | },
1435 | {
1436 | "color": "#2196F3",
1437 | "id": 5,
1438 | "label": "Lilly Wachowski",
1439 | "parentId": "GroupNodepeople",
1440 | "position": [
1441 | 0,
1442 | 0
1443 | ],
1444 | "properties": {
1445 | "born": 1967,
1446 | "label": "Person",
1447 | "name": "Lilly Wachowski"
1448 | },
1449 | "scale_factor": 1,
1450 | "size": [
1451 | 100,
1452 | 100
1453 | ],
1454 | "styles": {
1455 | "shape": "rectangle"
1456 | },
1457 | "type": "#2196F3"
1458 | },
1459 | {
1460 | "color": "#2196F3",
1461 | "id": 6,
1462 | "label": "Lana Wachowski",
1463 | "parentId": "GroupNodepeople",
1464 | "position": [
1465 | 0,
1466 | 0
1467 | ],
1468 | "properties": {
1469 | "born": 1965,
1470 | "label": "Person",
1471 | "name": "Lana Wachowski"
1472 | },
1473 | "scale_factor": 1,
1474 | "size": [
1475 | 100,
1476 | 100
1477 | ],
1478 | "styles": {
1479 | "shape": "rectangle"
1480 | },
1481 | "type": "#2196F3"
1482 | },
1483 | {
1484 | "color": "#2196F3",
1485 | "id": 7,
1486 | "label": "Joel Silver",
1487 | "parentId": "GroupNodepeople",
1488 | "position": [
1489 | 0,
1490 | 0
1491 | ],
1492 | "properties": {
1493 | "born": 1952,
1494 | "label": "Person",
1495 | "name": "Joel Silver"
1496 | },
1497 | "scale_factor": 1,
1498 | "size": [
1499 | 100,
1500 | 100
1501 | ],
1502 | "styles": {
1503 | "shape": "rectangle"
1504 | },
1505 | "type": "#2196F3"
1506 | },
1507 | {
1508 | "color": "#2196F3",
1509 | "id": 8,
1510 | "label": "Emil Eifrem",
1511 | "parentId": 0,
1512 | "position": [
1513 | 0,
1514 | 0
1515 | ],
1516 | "properties": {
1517 | "born": 1978,
1518 | "label": "Person",
1519 | "name": "Emil Eifrem"
1520 | },
1521 | "scale_factor": 1,
1522 | "size": [
1523 | 100,
1524 | 100
1525 | ],
1526 | "styles": {
1527 | "shape": "rectangle"
1528 | },
1529 | "type": "#2196F3"
1530 | },
1531 | {
1532 | "color": "#4CAF50",
1533 | "id": 9,
1534 | "label": "Title:\nThe Matrix Reloaded",
1535 | "parentId": "GroupNodemovies",
1536 | "position": [
1537 | 0,
1538 | 0
1539 | ],
1540 | "properties": {
1541 | "label": "Movie",
1542 | "released": 2003,
1543 | "tagline": "Free your mind",
1544 | "title": "The Matrix Reloaded",
1545 | "votes": 1841
1546 | },
1547 | "scale_factor": 1,
1548 | "size": [
1549 | 55,
1550 | 55
1551 | ],
1552 | "styles": {
1553 | "label_styles": {
1554 | "backgroundColor": "rgba(0,0,0,0.7)",
1555 | "color": "#FFFFFF",
1556 | "fontSize": 20,
1557 | "fontWeight": "lighter",
1558 | "maximumWidth": 130,
1559 | "position": "north",
1560 | "textAlignment": "center",
1561 | "wrapping": "word"
1562 | }
1563 | },
1564 | "type": "#4CAF50"
1565 | },
1566 | {
1567 | "color": "#4CAF50",
1568 | "id": 10,
1569 | "label": "Title:\nThe Matrix Revolutions",
1570 | "parentId": "GroupNodemovies",
1571 | "position": [
1572 | 0,
1573 | 0
1574 | ],
1575 | "properties": {
1576 | "label": "Movie",
1577 | "released": 2003,
1578 | "tagline": "Everything that has a beginning has an end",
1579 | "title": "The Matrix Revolutions",
1580 | "votes": 1534
1581 | },
1582 | "scale_factor": 1,
1583 | "size": [
1584 | 55,
1585 | 55
1586 | ],
1587 | "styles": {
1588 | "label_styles": {
1589 | "backgroundColor": "rgba(0,0,0,0.7)",
1590 | "color": "#FFFFFF",
1591 | "fontSize": 20,
1592 | "fontWeight": "lighter",
1593 | "maximumWidth": 130,
1594 | "position": "north",
1595 | "textAlignment": "center",
1596 | "wrapping": "word"
1597 | }
1598 | },
1599 | "type": "#4CAF50"
1600 | },
1601 | {
1602 | "color": "#cca9ff",
1603 | "id": "GroupNodepeople",
1604 | "label": "people",
1605 | "position": [
1606 | 0,
1607 | 0
1608 | ],
1609 | "properties": {
1610 | "label": "people"
1611 | },
1612 | "scale_factor": 1,
1613 | "size": [
1614 | 55,
1615 | 55
1616 | ],
1617 | "styles": {},
1618 | "type": "#cca9ff"
1619 | },
1620 | {
1621 | "color": "#F44336",
1622 | "id": "GroupNodemovies",
1623 | "label": "movies",
1624 | "position": [
1625 | 0,
1626 | 0
1627 | ],
1628 | "properties": {
1629 | "label": "movies"
1630 | },
1631 | "scale_factor": 1,
1632 | "size": [
1633 | 55,
1634 | 55
1635 | ],
1636 | "styles": {},
1637 | "type": "#F44336"
1638 | }
1639 | ],
1640 | "_overview": {
1641 | "enabled": null,
1642 | "overview_set": false
1643 | },
1644 | "_selected_graph": [
1645 | [],
1646 | []
1647 | ],
1648 | "_sidebar": {
1649 | "enabled": false,
1650 | "start_with": "About"
1651 | },
1652 | "_view_module_version": "^1.10.2",
1653 | "layout": "IPY_MODEL_d5976e6a66da45828c79552aad4df578"
1654 | }
1655 | },
1656 | "68f8e05eabb5405c932e35fc3d52cda8": {
1657 | "model_module": "@jupyter-widgets/base",
1658 | "model_module_version": "2.0.0",
1659 | "model_name": "LayoutModel",
1660 | "state": {
1661 | "height": "500px",
1662 | "width": "100%"
1663 | }
1664 | },
1665 | "6bdf055e98594e2599fa5156c3f65956": {
1666 | "model_module": "@jupyter-widgets/base",
1667 | "model_module_version": "2.0.0",
1668 | "model_name": "LayoutModel",
1669 | "state": {
1670 | "height": "630px",
1671 | "width": "100%"
1672 | }
1673 | },
1674 | "77f79c1038da486ebdf3db3693fa1df7": {
1675 | "model_module": "@jupyter-widgets/base",
1676 | "model_module_version": "2.0.0",
1677 | "model_name": "LayoutModel",
1678 | "state": {
1679 | "height": "610px",
1680 | "width": "100%"
1681 | }
1682 | },
1683 | "7e45a2312586497fbc5d6ed31e553dc3": {
1684 | "model_module": "@jupyter-widgets/base",
1685 | "model_module_version": "2.0.0",
1686 | "model_name": "LayoutModel",
1687 | "state": {
1688 | "height": "630px",
1689 | "width": "100%"
1690 | }
1691 | },
1692 | "80ee4436b5b1406c9e8c025864afecdc": {
1693 | "model_module": "@jupyter-widgets/base",
1694 | "model_module_version": "2.0.0",
1695 | "model_name": "LayoutModel",
1696 | "state": {
1697 | "height": "500px",
1698 | "width": "100%"
1699 | }
1700 | },
1701 | "9737c7185d0842118fca7fb4141a5cff": {
1702 | "model_module": "@jupyter-widgets/base",
1703 | "model_module_version": "2.0.0",
1704 | "model_name": "LayoutModel",
1705 | "state": {
1706 | "height": "500px",
1707 | "width": "100%"
1708 | }
1709 | },
1710 | "9bbb8051faf243dbb3ed865d02d79116": {
1711 | "model_module": "yfiles-jupyter-graphs",
1712 | "model_module_version": "^1.10.2",
1713 | "model_name": "GraphModel",
1714 | "state": {
1715 | "_context_pane_mapping": [
1716 | {
1717 | "id": "Neighborhood",
1718 | "title": "Neighborhood"
1719 | },
1720 | {
1721 | "id": "Data",
1722 | "title": "Data"
1723 | },
1724 | {
1725 | "id": "Search",
1726 | "title": "Search"
1727 | },
1728 | {
1729 | "id": "About",
1730 | "title": "About"
1731 | }
1732 | ],
1733 | "_data_importer": "neo4j",
1734 | "_directed": true,
1735 | "_edges": [
1736 | {
1737 | "color": "#AC94F4",
1738 | "directed": true,
1739 | "end": 0,
1740 | "id": 0,
1741 | "label": "ACTED_IN",
1742 | "properties": {
1743 | "label": "ACTED_IN",
1744 | "roles": [
1745 | "Neo"
1746 | ]
1747 | },
1748 | "start": 1,
1749 | "styles": {},
1750 | "thickness_factor": 2
1751 | },
1752 | {
1753 | "color": "#AC94F4",
1754 | "directed": true,
1755 | "end": 0,
1756 | "id": 1,
1757 | "label": "ACTED_IN",
1758 | "properties": {
1759 | "label": "ACTED_IN",
1760 | "roles": [
1761 | "Trinity"
1762 | ]
1763 | },
1764 | "start": 2,
1765 | "styles": {},
1766 | "thickness_factor": 2
1767 | },
1768 | {
1769 | "color": "#AC94F4",
1770 | "directed": true,
1771 | "end": 0,
1772 | "id": 2,
1773 | "label": "ACTED_IN",
1774 | "properties": {
1775 | "label": "ACTED_IN",
1776 | "roles": [
1777 | "Morpheus"
1778 | ]
1779 | },
1780 | "start": 3,
1781 | "styles": {},
1782 | "thickness_factor": 2
1783 | },
1784 | {
1785 | "color": "#AC94F4",
1786 | "directed": true,
1787 | "end": 0,
1788 | "id": 3,
1789 | "label": "ACTED_IN",
1790 | "properties": {
1791 | "label": "ACTED_IN",
1792 | "roles": [
1793 | "Agent Smith"
1794 | ]
1795 | },
1796 | "start": 4,
1797 | "styles": {},
1798 | "thickness_factor": 2
1799 | },
1800 | {
1801 | "color": "blue",
1802 | "directed": true,
1803 | "end": 0,
1804 | "id": 4,
1805 | "label": "DIRECTED",
1806 | "properties": {
1807 | "label": "DIRECTED"
1808 | },
1809 | "start": 5,
1810 | "styles": {
1811 | "dashStyle": "dot"
1812 | },
1813 | "thickness_factor": 1
1814 | },
1815 | {
1816 | "color": "blue",
1817 | "directed": true,
1818 | "end": 0,
1819 | "id": 5,
1820 | "label": "DIRECTED",
1821 | "properties": {
1822 | "label": "DIRECTED"
1823 | },
1824 | "start": 6,
1825 | "styles": {
1826 | "dashStyle": "dot"
1827 | },
1828 | "thickness_factor": 1
1829 | },
1830 | {
1831 | "color": "#AC94F4",
1832 | "directed": true,
1833 | "end": 0,
1834 | "id": 6,
1835 | "label": "produced",
1836 | "properties": {
1837 | "label": "PRODUCED"
1838 | },
1839 | "start": 7,
1840 | "styles": {
1841 | "label_styles": {
1842 | "fontWeight": "bolder"
1843 | }
1844 | },
1845 | "thickness_factor": 1
1846 | },
1847 | {
1848 | "color": "#AC94F4",
1849 | "directed": true,
1850 | "end": 0,
1851 | "id": 7,
1852 | "label": "ACTED_IN",
1853 | "properties": {
1854 | "label": "ACTED_IN",
1855 | "roles": [
1856 | "Emil"
1857 | ]
1858 | },
1859 | "start": 8,
1860 | "styles": {},
1861 | "thickness_factor": 2
1862 | },
1863 | {
1864 | "color": "#AC94F4",
1865 | "directed": true,
1866 | "end": 9,
1867 | "id": 8,
1868 | "label": "ACTED_IN",
1869 | "properties": {
1870 | "label": "ACTED_IN",
1871 | "roles": [
1872 | "Neo"
1873 | ]
1874 | },
1875 | "start": 1,
1876 | "styles": {},
1877 | "thickness_factor": 2
1878 | },
1879 | {
1880 | "color": "#AC94F4",
1881 | "directed": true,
1882 | "end": 9,
1883 | "id": 9,
1884 | "label": "ACTED_IN",
1885 | "properties": {
1886 | "label": "ACTED_IN",
1887 | "roles": [
1888 | "Trinity"
1889 | ]
1890 | },
1891 | "start": 2,
1892 | "styles": {},
1893 | "thickness_factor": 2
1894 | },
1895 | {
1896 | "color": "#AC94F4",
1897 | "directed": true,
1898 | "end": 9,
1899 | "id": 10,
1900 | "label": "ACTED_IN",
1901 | "properties": {
1902 | "label": "ACTED_IN",
1903 | "roles": [
1904 | "Morpheus"
1905 | ]
1906 | },
1907 | "start": 3,
1908 | "styles": {},
1909 | "thickness_factor": 2
1910 | },
1911 | {
1912 | "color": "#AC94F4",
1913 | "directed": true,
1914 | "end": 9,
1915 | "id": 11,
1916 | "label": "ACTED_IN",
1917 | "properties": {
1918 | "label": "ACTED_IN",
1919 | "roles": [
1920 | "Agent Smith"
1921 | ]
1922 | },
1923 | "start": 4,
1924 | "styles": {},
1925 | "thickness_factor": 2
1926 | },
1927 | {
1928 | "color": "blue",
1929 | "directed": true,
1930 | "end": 9,
1931 | "id": 12,
1932 | "label": "DIRECTED",
1933 | "properties": {
1934 | "label": "DIRECTED"
1935 | },
1936 | "start": 5,
1937 | "styles": {
1938 | "dashStyle": "dot"
1939 | },
1940 | "thickness_factor": 1
1941 | },
1942 | {
1943 | "color": "blue",
1944 | "directed": true,
1945 | "end": 9,
1946 | "id": 13,
1947 | "label": "DIRECTED",
1948 | "properties": {
1949 | "label": "DIRECTED"
1950 | },
1951 | "start": 6,
1952 | "styles": {
1953 | "dashStyle": "dot"
1954 | },
1955 | "thickness_factor": 1
1956 | },
1957 | {
1958 | "color": "#AC94F4",
1959 | "directed": true,
1960 | "end": 9,
1961 | "id": 14,
1962 | "label": "produced",
1963 | "properties": {
1964 | "label": "PRODUCED"
1965 | },
1966 | "start": 7,
1967 | "styles": {
1968 | "label_styles": {
1969 | "fontWeight": "bolder"
1970 | }
1971 | },
1972 | "thickness_factor": 1
1973 | },
1974 | {
1975 | "color": "#AC94F4",
1976 | "directed": true,
1977 | "end": 10,
1978 | "id": 15,
1979 | "label": "ACTED_IN",
1980 | "properties": {
1981 | "label": "ACTED_IN",
1982 | "roles": [
1983 | "Neo"
1984 | ]
1985 | },
1986 | "start": 1,
1987 | "styles": {},
1988 | "thickness_factor": 2
1989 | },
1990 | {
1991 | "color": "#AC94F4",
1992 | "directed": true,
1993 | "end": 10,
1994 | "id": 16,
1995 | "label": "ACTED_IN",
1996 | "properties": {
1997 | "label": "ACTED_IN",
1998 | "roles": [
1999 | "Trinity"
2000 | ]
2001 | },
2002 | "start": 2,
2003 | "styles": {},
2004 | "thickness_factor": 2
2005 | },
2006 | {
2007 | "color": "#AC94F4",
2008 | "directed": true,
2009 | "end": 10,
2010 | "id": 17,
2011 | "label": "ACTED_IN",
2012 | "properties": {
2013 | "label": "ACTED_IN",
2014 | "roles": [
2015 | "Morpheus"
2016 | ]
2017 | },
2018 | "start": 3,
2019 | "styles": {},
2020 | "thickness_factor": 2
2021 | },
2022 | {
2023 | "color": "#AC94F4",
2024 | "directed": true,
2025 | "end": 10,
2026 | "id": 18,
2027 | "label": "ACTED_IN",
2028 | "properties": {
2029 | "label": "ACTED_IN",
2030 | "roles": [
2031 | "Agent Smith"
2032 | ]
2033 | },
2034 | "start": 4,
2035 | "styles": {},
2036 | "thickness_factor": 2
2037 | },
2038 | {
2039 | "color": "blue",
2040 | "directed": true,
2041 | "end": 10,
2042 | "id": 19,
2043 | "label": "DIRECTED",
2044 | "properties": {
2045 | "label": "DIRECTED"
2046 | },
2047 | "start": 5,
2048 | "styles": {
2049 | "dashStyle": "dot"
2050 | },
2051 | "thickness_factor": 1
2052 | }
2053 | ],
2054 | "_graph_layout": {
2055 | "algorithm": "organic",
2056 | "options": {}
2057 | },
2058 | "_model_module_version": "^1.10.2",
2059 | "_nodes": [
2060 | {
2061 | "color": "#2196F3",
2062 | "id": 1,
2063 | "label": "Keanu Reeves",
2064 | "parentId": "GroupNodepeople",
2065 | "position": [
2066 | 0,
2067 | 0
2068 | ],
2069 | "properties": {
2070 | "born": 1964,
2071 | "label": "Person",
2072 | "name": "Keanu Reeves"
2073 | },
2074 | "scale_factor": 1,
2075 | "size": [
2076 | 100,
2077 | 100
2078 | ],
2079 | "styles": {
2080 | "shape": "rectangle"
2081 | },
2082 | "type": "#2196F3"
2083 | },
2084 | {
2085 | "color": "#4CAF50",
2086 | "id": 0,
2087 | "label": "Title:\nThe Matrix",
2088 | "parentId": "GroupNodemovies",
2089 | "position": [
2090 | 0,
2091 | 0
2092 | ],
2093 | "properties": {
2094 | "label": "Movie",
2095 | "released": 1999,
2096 | "tagline": "Welcome to the Real World",
2097 | "title": "The Matrix",
2098 | "votes": 6345
2099 | },
2100 | "scale_factor": 1,
2101 | "size": [
2102 | 55,
2103 | 55
2104 | ],
2105 | "styles": {
2106 | "label_styles": {
2107 | "backgroundColor": "rgba(0,0,0,0.7)",
2108 | "color": "#FFFFFF",
2109 | "fontSize": 20,
2110 | "fontWeight": "lighter",
2111 | "maximumWidth": 130,
2112 | "position": "north",
2113 | "textAlignment": "center",
2114 | "wrapping": "word"
2115 | }
2116 | },
2117 | "type": "#4CAF50"
2118 | },
2119 | {
2120 | "color": "#2196F3",
2121 | "id": 2,
2122 | "label": "Carrie-Anne Moss",
2123 | "parentId": "GroupNodepeople",
2124 | "position": [
2125 | 0,
2126 | 0
2127 | ],
2128 | "properties": {
2129 | "born": 1967,
2130 | "label": "Person",
2131 | "name": "Carrie-Anne Moss"
2132 | },
2133 | "scale_factor": 1,
2134 | "size": [
2135 | 100,
2136 | 100
2137 | ],
2138 | "styles": {
2139 | "shape": "rectangle"
2140 | },
2141 | "type": "#2196F3"
2142 | },
2143 | {
2144 | "color": "#2196F3",
2145 | "id": 3,
2146 | "label": "Laurence Fishburne",
2147 | "parentId": "GroupNodepeople",
2148 | "position": [
2149 | 0,
2150 | 0
2151 | ],
2152 | "properties": {
2153 | "born": 1961,
2154 | "label": "Person",
2155 | "name": "Laurence Fishburne"
2156 | },
2157 | "scale_factor": 1,
2158 | "size": [
2159 | 100,
2160 | 100
2161 | ],
2162 | "styles": {
2163 | "shape": "rectangle"
2164 | },
2165 | "type": "#2196F3"
2166 | },
2167 | {
2168 | "color": "#2196F3",
2169 | "id": 4,
2170 | "label": "Hugo Weaving",
2171 | "parentId": "GroupNodepeople",
2172 | "position": [
2173 | 0,
2174 | 0
2175 | ],
2176 | "properties": {
2177 | "born": 1960,
2178 | "label": "Person",
2179 | "name": "Hugo Weaving"
2180 | },
2181 | "scale_factor": 1,
2182 | "size": [
2183 | 100,
2184 | 100
2185 | ],
2186 | "styles": {
2187 | "shape": "rectangle"
2188 | },
2189 | "type": "#2196F3"
2190 | },
2191 | {
2192 | "color": "#2196F3",
2193 | "id": 5,
2194 | "label": "Lilly Wachowski",
2195 | "parentId": "GroupNodepeople",
2196 | "position": [
2197 | 0,
2198 | 0
2199 | ],
2200 | "properties": {
2201 | "born": 1967,
2202 | "label": "Person",
2203 | "name": "Lilly Wachowski"
2204 | },
2205 | "scale_factor": 1,
2206 | "size": [
2207 | 100,
2208 | 100
2209 | ],
2210 | "styles": {
2211 | "shape": "rectangle"
2212 | },
2213 | "type": "#2196F3"
2214 | },
2215 | {
2216 | "color": "#2196F3",
2217 | "id": 6,
2218 | "label": "Lana Wachowski",
2219 | "parentId": "GroupNodepeople",
2220 | "position": [
2221 | 0,
2222 | 0
2223 | ],
2224 | "properties": {
2225 | "born": 1965,
2226 | "label": "Person",
2227 | "name": "Lana Wachowski"
2228 | },
2229 | "scale_factor": 1,
2230 | "size": [
2231 | 100,
2232 | 100
2233 | ],
2234 | "styles": {
2235 | "shape": "rectangle"
2236 | },
2237 | "type": "#2196F3"
2238 | },
2239 | {
2240 | "color": "#2196F3",
2241 | "id": 7,
2242 | "label": "Joel Silver",
2243 | "parentId": "GroupNodepeople",
2244 | "position": [
2245 | 0,
2246 | 0
2247 | ],
2248 | "properties": {
2249 | "born": 1952,
2250 | "label": "Person",
2251 | "name": "Joel Silver"
2252 | },
2253 | "scale_factor": 1,
2254 | "size": [
2255 | 100,
2256 | 100
2257 | ],
2258 | "styles": {
2259 | "shape": "rectangle"
2260 | },
2261 | "type": "#2196F3"
2262 | },
2263 | {
2264 | "color": "#2196F3",
2265 | "id": 8,
2266 | "label": "Emil Eifrem",
2267 | "parentId": "GroupNodepeople",
2268 | "position": [
2269 | 0,
2270 | 0
2271 | ],
2272 | "properties": {
2273 | "born": 1978,
2274 | "label": "Person",
2275 | "name": "Emil Eifrem"
2276 | },
2277 | "scale_factor": 1,
2278 | "size": [
2279 | 100,
2280 | 100
2281 | ],
2282 | "styles": {
2283 | "shape": "rectangle"
2284 | },
2285 | "type": "#2196F3"
2286 | },
2287 | {
2288 | "color": "#4CAF50",
2289 | "id": 9,
2290 | "label": "Title:\nThe Matrix Reloaded",
2291 | "parentId": "GroupNodemovies",
2292 | "position": [
2293 | 0,
2294 | 0
2295 | ],
2296 | "properties": {
2297 | "label": "Movie",
2298 | "released": 2003,
2299 | "tagline": "Free your mind",
2300 | "title": "The Matrix Reloaded",
2301 | "votes": 1841
2302 | },
2303 | "scale_factor": 1,
2304 | "size": [
2305 | 55,
2306 | 55
2307 | ],
2308 | "styles": {
2309 | "label_styles": {
2310 | "backgroundColor": "rgba(0,0,0,0.7)",
2311 | "color": "#FFFFFF",
2312 | "fontSize": 20,
2313 | "fontWeight": "lighter",
2314 | "maximumWidth": 130,
2315 | "position": "north",
2316 | "textAlignment": "center",
2317 | "wrapping": "word"
2318 | }
2319 | },
2320 | "type": "#4CAF50"
2321 | },
2322 | {
2323 | "color": "#4CAF50",
2324 | "id": 10,
2325 | "label": "Title:\nThe Matrix Revolutions",
2326 | "parentId": "GroupNodemovies",
2327 | "position": [
2328 | 0,
2329 | 0
2330 | ],
2331 | "properties": {
2332 | "label": "Movie",
2333 | "released": 2003,
2334 | "tagline": "Everything that has a beginning has an end",
2335 | "title": "The Matrix Revolutions",
2336 | "votes": 1534
2337 | },
2338 | "scale_factor": 1,
2339 | "size": [
2340 | 55,
2341 | 55
2342 | ],
2343 | "styles": {
2344 | "label_styles": {
2345 | "backgroundColor": "rgba(0,0,0,0.7)",
2346 | "color": "#FFFFFF",
2347 | "fontSize": 20,
2348 | "fontWeight": "lighter",
2349 | "maximumWidth": 130,
2350 | "position": "north",
2351 | "textAlignment": "center",
2352 | "wrapping": "word"
2353 | }
2354 | },
2355 | "type": "#4CAF50"
2356 | },
2357 | {
2358 | "color": "#cca9ff",
2359 | "id": "GroupNodepeople",
2360 | "label": "people",
2361 | "position": [
2362 | 0,
2363 | 0
2364 | ],
2365 | "properties": {
2366 | "label": "people"
2367 | },
2368 | "scale_factor": 1,
2369 | "size": [
2370 | 55,
2371 | 55
2372 | ],
2373 | "styles": {
2374 | "color": "#cca9ff"
2375 | },
2376 | "type": "#cca9ff"
2377 | },
2378 | {
2379 | "color": "#F44336",
2380 | "id": "GroupNodemovies",
2381 | "label": "movies",
2382 | "position": [
2383 | 0,
2384 | 0
2385 | ],
2386 | "properties": {
2387 | "label": "movies"
2388 | },
2389 | "scale_factor": 1,
2390 | "size": [
2391 | 55,
2392 | 55
2393 | ],
2394 | "styles": {},
2395 | "type": "#F44336"
2396 | }
2397 | ],
2398 | "_overview": {
2399 | "enabled": null,
2400 | "overview_set": false
2401 | },
2402 | "_selected_graph": [
2403 | [
2404 | {
2405 | "id": "GroupNodepeople",
2406 | "properties": {
2407 | "label": "people"
2408 | }
2409 | }
2410 | ],
2411 | []
2412 | ],
2413 | "_sidebar": {
2414 | "enabled": false,
2415 | "start_with": "About"
2416 | },
2417 | "_view_module_version": "^1.10.2",
2418 | "layout": "IPY_MODEL_7e45a2312586497fbc5d6ed31e553dc3"
2419 | }
2420 | },
2421 | "9f6b785b62394938ae19744a4479b796": {
2422 | "model_module": "yfiles-jupyter-graphs",
2423 | "model_module_version": "^1.10.2",
2424 | "model_name": "GraphModel",
2425 | "state": {
2426 | "_context_pane_mapping": [
2427 | {
2428 | "id": "Neighborhood",
2429 | "title": "Neighborhood"
2430 | },
2431 | {
2432 | "id": "Data",
2433 | "title": "Data"
2434 | },
2435 | {
2436 | "id": "Search",
2437 | "title": "Search"
2438 | },
2439 | {
2440 | "id": "About",
2441 | "title": "About"
2442 | }
2443 | ],
2444 | "_data_importer": "neo4j",
2445 | "_directed": true,
2446 | "_edges": [
2447 | {
2448 | "color": "blue",
2449 | "directed": true,
2450 | "end": 0,
2451 | "id": 4,
2452 | "label": "DIRECTED",
2453 | "properties": {
2454 | "label": "DIRECTED"
2455 | },
2456 | "start": 5,
2457 | "styles": {
2458 | "dashStyle": "dot"
2459 | },
2460 | "thickness_factor": 1
2461 | },
2462 | {
2463 | "color": "blue",
2464 | "directed": true,
2465 | "end": 0,
2466 | "id": 5,
2467 | "label": "DIRECTED",
2468 | "properties": {
2469 | "label": "DIRECTED"
2470 | },
2471 | "start": 6,
2472 | "styles": {
2473 | "dashStyle": "dot"
2474 | },
2475 | "thickness_factor": 1
2476 | },
2477 | {
2478 | "color": "#AC94F4",
2479 | "directed": true,
2480 | "end": 0,
2481 | "id": 6,
2482 | "label": "produced",
2483 | "properties": {
2484 | "label": "PRODUCED"
2485 | },
2486 | "start": 7,
2487 | "styles": {
2488 | "label_styles": {
2489 | "fontWeight": "bolder"
2490 | }
2491 | },
2492 | "thickness_factor": 1
2493 | },
2494 | {
2495 | "color": "blue",
2496 | "directed": true,
2497 | "end": 9,
2498 | "id": 12,
2499 | "label": "DIRECTED",
2500 | "properties": {
2501 | "label": "DIRECTED"
2502 | },
2503 | "start": 5,
2504 | "styles": {
2505 | "dashStyle": "dot"
2506 | },
2507 | "thickness_factor": 1
2508 | },
2509 | {
2510 | "color": "blue",
2511 | "directed": true,
2512 | "end": 9,
2513 | "id": 13,
2514 | "label": "DIRECTED",
2515 | "properties": {
2516 | "label": "DIRECTED"
2517 | },
2518 | "start": 6,
2519 | "styles": {
2520 | "dashStyle": "dot"
2521 | },
2522 | "thickness_factor": 1
2523 | },
2524 | {
2525 | "color": "#AC94F4",
2526 | "directed": true,
2527 | "end": 9,
2528 | "id": 14,
2529 | "label": "produced",
2530 | "properties": {
2531 | "label": "PRODUCED"
2532 | },
2533 | "start": 7,
2534 | "styles": {
2535 | "label_styles": {
2536 | "fontWeight": "bolder"
2537 | }
2538 | },
2539 | "thickness_factor": 1
2540 | },
2541 | {
2542 | "color": "blue",
2543 | "directed": true,
2544 | "end": 10,
2545 | "id": 19,
2546 | "label": "DIRECTED",
2547 | "properties": {
2548 | "label": "DIRECTED"
2549 | },
2550 | "start": 5,
2551 | "styles": {
2552 | "dashStyle": "dot"
2553 | },
2554 | "thickness_factor": 1
2555 | }
2556 | ],
2557 | "_graph_layout": {
2558 | "algorithm": "organic",
2559 | "options": {}
2560 | },
2561 | "_model_module_version": "^1.10.2",
2562 | "_nodes": [
2563 | {
2564 | "color": "#2196F3",
2565 | "id": 1,
2566 | "label": "Keanu Reeves",
2567 | "parentId": 10,
2568 | "position": [
2569 | 0,
2570 | 0
2571 | ],
2572 | "properties": {
2573 | "born": 1964,
2574 | "label": "Person",
2575 | "name": "Keanu Reeves"
2576 | },
2577 | "scale_factor": 1,
2578 | "size": [
2579 | 100,
2580 | 100
2581 | ],
2582 | "styles": {
2583 | "shape": "rectangle"
2584 | },
2585 | "type": "#2196F3"
2586 | },
2587 | {
2588 | "color": "#4CAF50",
2589 | "id": 0,
2590 | "label": "Title:\nThe Matrix",
2591 | "parentId": "GroupNodemovies",
2592 | "position": [
2593 | 0,
2594 | 0
2595 | ],
2596 | "properties": {
2597 | "label": "Movie",
2598 | "released": 1999,
2599 | "tagline": "Welcome to the Real World",
2600 | "title": "The Matrix",
2601 | "votes": 6345
2602 | },
2603 | "scale_factor": 1,
2604 | "size": [
2605 | 55,
2606 | 55
2607 | ],
2608 | "styles": {
2609 | "label_styles": {
2610 | "backgroundColor": "rgba(0,0,0,0.7)",
2611 | "color": "#FFFFFF",
2612 | "fontSize": 20,
2613 | "fontWeight": "lighter",
2614 | "maximumWidth": 130,
2615 | "position": "north",
2616 | "textAlignment": "center",
2617 | "wrapping": "word"
2618 | }
2619 | },
2620 | "type": "#4CAF50"
2621 | },
2622 | {
2623 | "color": "#2196F3",
2624 | "id": 2,
2625 | "label": "Carrie-Anne Moss",
2626 | "parentId": 10,
2627 | "position": [
2628 | 0,
2629 | 0
2630 | ],
2631 | "properties": {
2632 | "born": 1967,
2633 | "label": "Person",
2634 | "name": "Carrie-Anne Moss"
2635 | },
2636 | "scale_factor": 1,
2637 | "size": [
2638 | 100,
2639 | 100
2640 | ],
2641 | "styles": {
2642 | "shape": "rectangle"
2643 | },
2644 | "type": "#2196F3"
2645 | },
2646 | {
2647 | "color": "#2196F3",
2648 | "id": 3,
2649 | "label": "Laurence Fishburne",
2650 | "parentId": 10,
2651 | "position": [
2652 | 0,
2653 | 0
2654 | ],
2655 | "properties": {
2656 | "born": 1961,
2657 | "label": "Person",
2658 | "name": "Laurence Fishburne"
2659 | },
2660 | "scale_factor": 1,
2661 | "size": [
2662 | 100,
2663 | 100
2664 | ],
2665 | "styles": {
2666 | "shape": "rectangle"
2667 | },
2668 | "type": "#2196F3"
2669 | },
2670 | {
2671 | "color": "#2196F3",
2672 | "id": 4,
2673 | "label": "Hugo Weaving",
2674 | "parentId": 10,
2675 | "position": [
2676 | 0,
2677 | 0
2678 | ],
2679 | "properties": {
2680 | "born": 1960,
2681 | "label": "Person",
2682 | "name": "Hugo Weaving"
2683 | },
2684 | "scale_factor": 1,
2685 | "size": [
2686 | 100,
2687 | 100
2688 | ],
2689 | "styles": {
2690 | "shape": "rectangle"
2691 | },
2692 | "type": "#2196F3"
2693 | },
2694 | {
2695 | "color": "#2196F3",
2696 | "id": 5,
2697 | "label": "Lilly Wachowski",
2698 | "parentId": "GroupNodepeople",
2699 | "position": [
2700 | 0,
2701 | 0
2702 | ],
2703 | "properties": {
2704 | "born": 1967,
2705 | "label": "Person",
2706 | "name": "Lilly Wachowski"
2707 | },
2708 | "scale_factor": 1,
2709 | "size": [
2710 | 100,
2711 | 100
2712 | ],
2713 | "styles": {
2714 | "shape": "rectangle"
2715 | },
2716 | "type": "#2196F3"
2717 | },
2718 | {
2719 | "color": "#2196F3",
2720 | "id": 6,
2721 | "label": "Lana Wachowski",
2722 | "parentId": "GroupNodepeople",
2723 | "position": [
2724 | 0,
2725 | 0
2726 | ],
2727 | "properties": {
2728 | "born": 1965,
2729 | "label": "Person",
2730 | "name": "Lana Wachowski"
2731 | },
2732 | "scale_factor": 1,
2733 | "size": [
2734 | 100,
2735 | 100
2736 | ],
2737 | "styles": {
2738 | "shape": "rectangle"
2739 | },
2740 | "type": "#2196F3"
2741 | },
2742 | {
2743 | "color": "#2196F3",
2744 | "id": 7,
2745 | "label": "Joel Silver",
2746 | "parentId": "GroupNodepeople",
2747 | "position": [
2748 | 0,
2749 | 0
2750 | ],
2751 | "properties": {
2752 | "born": 1952,
2753 | "label": "Person",
2754 | "name": "Joel Silver"
2755 | },
2756 | "scale_factor": 1,
2757 | "size": [
2758 | 100,
2759 | 100
2760 | ],
2761 | "styles": {
2762 | "shape": "rectangle"
2763 | },
2764 | "type": "#2196F3"
2765 | },
2766 | {
2767 | "color": "#2196F3",
2768 | "id": 8,
2769 | "label": "Emil Eifrem",
2770 | "parentId": 0,
2771 | "position": [
2772 | 0,
2773 | 0
2774 | ],
2775 | "properties": {
2776 | "born": 1978,
2777 | "label": "Person",
2778 | "name": "Emil Eifrem"
2779 | },
2780 | "scale_factor": 1,
2781 | "size": [
2782 | 100,
2783 | 100
2784 | ],
2785 | "styles": {
2786 | "shape": "rectangle"
2787 | },
2788 | "type": "#2196F3"
2789 | },
2790 | {
2791 | "color": "#4CAF50",
2792 | "id": 9,
2793 | "label": "Title:\nThe Matrix Reloaded",
2794 | "parentId": "GroupNodemovies",
2795 | "position": [
2796 | 0,
2797 | 0
2798 | ],
2799 | "properties": {
2800 | "label": "Movie",
2801 | "released": 2003,
2802 | "tagline": "Free your mind",
2803 | "title": "The Matrix Reloaded",
2804 | "votes": 1841
2805 | },
2806 | "scale_factor": 1,
2807 | "size": [
2808 | 55,
2809 | 55
2810 | ],
2811 | "styles": {
2812 | "label_styles": {
2813 | "backgroundColor": "rgba(0,0,0,0.7)",
2814 | "color": "#FFFFFF",
2815 | "fontSize": 20,
2816 | "fontWeight": "lighter",
2817 | "maximumWidth": 130,
2818 | "position": "north",
2819 | "textAlignment": "center",
2820 | "wrapping": "word"
2821 | }
2822 | },
2823 | "type": "#4CAF50"
2824 | },
2825 | {
2826 | "color": "#4CAF50",
2827 | "id": 10,
2828 | "label": "Title:\nThe Matrix Revolutions",
2829 | "parentId": "GroupNodemovies",
2830 | "position": [
2831 | 0,
2832 | 0
2833 | ],
2834 | "properties": {
2835 | "label": "Movie",
2836 | "released": 2003,
2837 | "tagline": "Everything that has a beginning has an end",
2838 | "title": "The Matrix Revolutions",
2839 | "votes": 1534
2840 | },
2841 | "scale_factor": 1,
2842 | "size": [
2843 | 55,
2844 | 55
2845 | ],
2846 | "styles": {
2847 | "label_styles": {
2848 | "backgroundColor": "rgba(0,0,0,0.7)",
2849 | "color": "#FFFFFF",
2850 | "fontSize": 20,
2851 | "fontWeight": "lighter",
2852 | "maximumWidth": 130,
2853 | "position": "north",
2854 | "textAlignment": "center",
2855 | "wrapping": "word"
2856 | }
2857 | },
2858 | "type": "#4CAF50"
2859 | },
2860 | {
2861 | "color": "#cca9ff",
2862 | "id": "GroupNodepeople",
2863 | "label": "people",
2864 | "position": [
2865 | 0,
2866 | 0
2867 | ],
2868 | "properties": {
2869 | "label": "people"
2870 | },
2871 | "scale_factor": 1,
2872 | "size": [
2873 | 55,
2874 | 55
2875 | ],
2876 | "styles": {},
2877 | "type": "#cca9ff"
2878 | },
2879 | {
2880 | "color": "#F44336",
2881 | "id": "GroupNodemovies",
2882 | "label": "movies",
2883 | "position": [
2884 | 0,
2885 | 0
2886 | ],
2887 | "properties": {
2888 | "label": "movies"
2889 | },
2890 | "scale_factor": 1,
2891 | "size": [
2892 | 55,
2893 | 55
2894 | ],
2895 | "styles": {},
2896 | "type": "#F44336"
2897 | }
2898 | ],
2899 | "_overview": {
2900 | "enabled": null,
2901 | "overview_set": false
2902 | },
2903 | "_selected_graph": [
2904 | [],
2905 | []
2906 | ],
2907 | "_sidebar": {
2908 | "enabled": false,
2909 | "start_with": "About"
2910 | },
2911 | "_view_module_version": "^1.10.2",
2912 | "layout": "IPY_MODEL_dbc205b3693047f4832f11957c0ede1e"
2913 | }
2914 | },
2915 | "a4c9e65aa82e4824a015a17bf52ba2a5": {
2916 | "model_module": "@jupyter-widgets/base",
2917 | "model_module_version": "2.0.0",
2918 | "model_name": "LayoutModel",
2919 | "state": {
2920 | "height": "500px",
2921 | "width": "100%"
2922 | }
2923 | },
2924 | "b75d05a06bf5430eb7bcd20497262a02": {
2925 | "model_module": "@jupyter-widgets/base",
2926 | "model_module_version": "2.0.0",
2927 | "model_name": "LayoutModel",
2928 | "state": {
2929 | "height": "630px",
2930 | "width": "100%"
2931 | }
2932 | },
2933 | "c83fe4af42f84c83b12f829ff46ac16e": {
2934 | "model_module": "yfiles-jupyter-graphs",
2935 | "model_module_version": "^1.10.2",
2936 | "model_name": "GraphModel",
2937 | "state": {
2938 | "_context_pane_mapping": [
2939 | {
2940 | "id": "Neighborhood",
2941 | "title": "Neighborhood"
2942 | },
2943 | {
2944 | "id": "Data",
2945 | "title": "Data"
2946 | },
2947 | {
2948 | "id": "Search",
2949 | "title": "Search"
2950 | },
2951 | {
2952 | "id": "About",
2953 | "title": "About"
2954 | }
2955 | ],
2956 | "_data_importer": "neo4j",
2957 | "_directed": true,
2958 | "_edges": [
2959 | {
2960 | "color": "blue",
2961 | "directed": true,
2962 | "end": 0,
2963 | "id": 4,
2964 | "label": "DIRECTED",
2965 | "properties": {
2966 | "label": "DIRECTED"
2967 | },
2968 | "start": 5,
2969 | "styles": {
2970 | "dashStyle": "dot"
2971 | },
2972 | "thickness_factor": 1
2973 | },
2974 | {
2975 | "color": "blue",
2976 | "directed": true,
2977 | "end": 0,
2978 | "id": 5,
2979 | "label": "DIRECTED",
2980 | "properties": {
2981 | "label": "DIRECTED"
2982 | },
2983 | "start": 6,
2984 | "styles": {
2985 | "dashStyle": "dot"
2986 | },
2987 | "thickness_factor": 1
2988 | },
2989 | {
2990 | "color": "#AC94F4",
2991 | "directed": true,
2992 | "end": 0,
2993 | "id": 6,
2994 | "label": "produced",
2995 | "properties": {
2996 | "label": "PRODUCED"
2997 | },
2998 | "start": 7,
2999 | "styles": {
3000 | "label_styles": {
3001 | "fontSize": 16,
3002 | "fontWeight": "bolder"
3003 | }
3004 | },
3005 | "thickness_factor": 1
3006 | },
3007 | {
3008 | "color": "blue",
3009 | "directed": true,
3010 | "end": 9,
3011 | "id": 12,
3012 | "label": "DIRECTED",
3013 | "properties": {
3014 | "label": "DIRECTED"
3015 | },
3016 | "start": 5,
3017 | "styles": {
3018 | "dashStyle": "dot"
3019 | },
3020 | "thickness_factor": 1
3021 | },
3022 | {
3023 | "color": "blue",
3024 | "directed": true,
3025 | "end": 9,
3026 | "id": 13,
3027 | "label": "DIRECTED",
3028 | "properties": {
3029 | "label": "DIRECTED"
3030 | },
3031 | "start": 6,
3032 | "styles": {
3033 | "dashStyle": "dot"
3034 | },
3035 | "thickness_factor": 1
3036 | },
3037 | {
3038 | "color": "#AC94F4",
3039 | "directed": true,
3040 | "end": 9,
3041 | "id": 14,
3042 | "label": "produced",
3043 | "properties": {
3044 | "label": "PRODUCED"
3045 | },
3046 | "start": 7,
3047 | "styles": {
3048 | "label_styles": {
3049 | "fontSize": 16,
3050 | "fontWeight": "bolder"
3051 | }
3052 | },
3053 | "thickness_factor": 1
3054 | },
3055 | {
3056 | "color": "blue",
3057 | "directed": true,
3058 | "end": 10,
3059 | "id": 19,
3060 | "label": "DIRECTED",
3061 | "properties": {
3062 | "label": "DIRECTED"
3063 | },
3064 | "start": 5,
3065 | "styles": {
3066 | "dashStyle": "dot"
3067 | },
3068 | "thickness_factor": 1
3069 | }
3070 | ],
3071 | "_graph_layout": {
3072 | "algorithm": "organic",
3073 | "options": {}
3074 | },
3075 | "_model_module_version": "^1.10.2",
3076 | "_nodes": [
3077 | {
3078 | "color": "#2196F3",
3079 | "id": 1,
3080 | "label": "Keanu Reeves",
3081 | "parentId": 10,
3082 | "position": [
3083 | 0,
3084 | 0
3085 | ],
3086 | "properties": {
3087 | "born": 1964,
3088 | "label": "Person",
3089 | "name": "Keanu Reeves"
3090 | },
3091 | "scale_factor": 1,
3092 | "size": [
3093 | 100,
3094 | 100
3095 | ],
3096 | "styles": {
3097 | "shape": "rectangle"
3098 | },
3099 | "type": "#2196F3"
3100 | },
3101 | {
3102 | "color": "#4CAF50",
3103 | "id": 0,
3104 | "label": "Title:\nThe Matrix",
3105 | "parentId": "GroupNodemovies",
3106 | "position": [
3107 | 0,
3108 | 0
3109 | ],
3110 | "properties": {
3111 | "label": "Movie",
3112 | "released": 1999,
3113 | "tagline": "Welcome to the Real World",
3114 | "title": "The Matrix",
3115 | "votes": 6345
3116 | },
3117 | "scale_factor": 1,
3118 | "size": [
3119 | 55,
3120 | 55
3121 | ],
3122 | "styles": {
3123 | "label_styles": {
3124 | "backgroundColor": "rgba(0,0,0,0.7)",
3125 | "color": "#FFFFFF",
3126 | "fontSize": 20,
3127 | "fontWeight": "lighter",
3128 | "maximumWidth": 130,
3129 | "position": "north",
3130 | "textAlignment": "center",
3131 | "wrapping": "word"
3132 | }
3133 | },
3134 | "type": "#4CAF50"
3135 | },
3136 | {
3137 | "color": "#2196F3",
3138 | "id": 2,
3139 | "label": "Carrie-Anne Moss",
3140 | "parentId": 10,
3141 | "position": [
3142 | 0,
3143 | 0
3144 | ],
3145 | "properties": {
3146 | "born": 1967,
3147 | "label": "Person",
3148 | "name": "Carrie-Anne Moss"
3149 | },
3150 | "scale_factor": 1,
3151 | "size": [
3152 | 100,
3153 | 100
3154 | ],
3155 | "styles": {
3156 | "shape": "rectangle"
3157 | },
3158 | "type": "#2196F3"
3159 | },
3160 | {
3161 | "color": "#2196F3",
3162 | "id": 3,
3163 | "label": "Laurence Fishburne",
3164 | "parentId": 10,
3165 | "position": [
3166 | 0,
3167 | 0
3168 | ],
3169 | "properties": {
3170 | "born": 1961,
3171 | "label": "Person",
3172 | "name": "Laurence Fishburne"
3173 | },
3174 | "scale_factor": 1,
3175 | "size": [
3176 | 100,
3177 | 100
3178 | ],
3179 | "styles": {
3180 | "shape": "rectangle"
3181 | },
3182 | "type": "#2196F3"
3183 | },
3184 | {
3185 | "color": "#2196F3",
3186 | "id": 4,
3187 | "label": "Hugo Weaving",
3188 | "parentId": 10,
3189 | "position": [
3190 | 0,
3191 | 0
3192 | ],
3193 | "properties": {
3194 | "born": 1960,
3195 | "label": "Person",
3196 | "name": "Hugo Weaving"
3197 | },
3198 | "scale_factor": 1,
3199 | "size": [
3200 | 100,
3201 | 100
3202 | ],
3203 | "styles": {
3204 | "shape": "rectangle"
3205 | },
3206 | "type": "#2196F3"
3207 | },
3208 | {
3209 | "color": "#2196F3",
3210 | "id": 5,
3211 | "label": "Lilly Wachowski",
3212 | "parentId": "GroupNodepeople",
3213 | "position": [
3214 | 0,
3215 | 0
3216 | ],
3217 | "properties": {
3218 | "born": 1967,
3219 | "label": "Person",
3220 | "name": "Lilly Wachowski"
3221 | },
3222 | "scale_factor": 1,
3223 | "size": [
3224 | 100,
3225 | 100
3226 | ],
3227 | "styles": {
3228 | "shape": "rectangle"
3229 | },
3230 | "type": "#2196F3"
3231 | },
3232 | {
3233 | "color": "#2196F3",
3234 | "id": 6,
3235 | "label": "Lana Wachowski",
3236 | "parentId": "GroupNodepeople",
3237 | "position": [
3238 | 0,
3239 | 0
3240 | ],
3241 | "properties": {
3242 | "born": 1965,
3243 | "label": "Person",
3244 | "name": "Lana Wachowski"
3245 | },
3246 | "scale_factor": 1,
3247 | "size": [
3248 | 100,
3249 | 100
3250 | ],
3251 | "styles": {
3252 | "shape": "rectangle"
3253 | },
3254 | "type": "#2196F3"
3255 | },
3256 | {
3257 | "color": "#2196F3",
3258 | "id": 7,
3259 | "label": "Joel Silver",
3260 | "parentId": "GroupNodepeople",
3261 | "position": [
3262 | 0,
3263 | 0
3264 | ],
3265 | "properties": {
3266 | "born": 1952,
3267 | "label": "Person",
3268 | "name": "Joel Silver"
3269 | },
3270 | "scale_factor": 1,
3271 | "size": [
3272 | 100,
3273 | 100
3274 | ],
3275 | "styles": {
3276 | "shape": "rectangle"
3277 | },
3278 | "type": "#2196F3"
3279 | },
3280 | {
3281 | "color": "#2196F3",
3282 | "id": 8,
3283 | "label": "Emil Eifrem",
3284 | "parentId": 0,
3285 | "position": [
3286 | 0,
3287 | 0
3288 | ],
3289 | "properties": {
3290 | "born": 1978,
3291 | "label": "Person",
3292 | "name": "Emil Eifrem"
3293 | },
3294 | "scale_factor": 1,
3295 | "size": [
3296 | 100,
3297 | 100
3298 | ],
3299 | "styles": {
3300 | "shape": "rectangle"
3301 | },
3302 | "type": "#2196F3"
3303 | },
3304 | {
3305 | "color": "#4CAF50",
3306 | "id": 9,
3307 | "label": "Title:\nThe Matrix Reloaded",
3308 | "parentId": "GroupNodemovies",
3309 | "position": [
3310 | 0,
3311 | 0
3312 | ],
3313 | "properties": {
3314 | "label": "Movie",
3315 | "released": 2003,
3316 | "tagline": "Free your mind",
3317 | "title": "The Matrix Reloaded",
3318 | "votes": 1841
3319 | },
3320 | "scale_factor": 1,
3321 | "size": [
3322 | 55,
3323 | 55
3324 | ],
3325 | "styles": {
3326 | "label_styles": {
3327 | "backgroundColor": "rgba(0,0,0,0.7)",
3328 | "color": "#FFFFFF",
3329 | "fontSize": 20,
3330 | "fontWeight": "lighter",
3331 | "maximumWidth": 130,
3332 | "position": "north",
3333 | "textAlignment": "center",
3334 | "wrapping": "word"
3335 | }
3336 | },
3337 | "type": "#4CAF50"
3338 | },
3339 | {
3340 | "color": "#4CAF50",
3341 | "id": 10,
3342 | "label": "Title:\nThe Matrix Revolutions",
3343 | "parentId": "GroupNodemovies",
3344 | "position": [
3345 | 0,
3346 | 0
3347 | ],
3348 | "properties": {
3349 | "label": "Movie",
3350 | "released": 2003,
3351 | "tagline": "Everything that has a beginning has an end",
3352 | "title": "The Matrix Revolutions",
3353 | "votes": 1534
3354 | },
3355 | "scale_factor": 1,
3356 | "size": [
3357 | 55,
3358 | 55
3359 | ],
3360 | "styles": {
3361 | "label_styles": {
3362 | "backgroundColor": "rgba(0,0,0,0.7)",
3363 | "color": "#FFFFFF",
3364 | "fontSize": 20,
3365 | "fontWeight": "lighter",
3366 | "maximumWidth": 130,
3367 | "position": "north",
3368 | "textAlignment": "center",
3369 | "wrapping": "word"
3370 | }
3371 | },
3372 | "type": "#4CAF50"
3373 | },
3374 | {
3375 | "color": "#cca9ff",
3376 | "id": "GroupNodepeople",
3377 | "label": "people",
3378 | "position": [
3379 | 0,
3380 | 0
3381 | ],
3382 | "properties": {
3383 | "label": "people"
3384 | },
3385 | "scale_factor": 1,
3386 | "size": [
3387 | 55,
3388 | 55
3389 | ],
3390 | "styles": {},
3391 | "type": "#cca9ff"
3392 | },
3393 | {
3394 | "color": "#F44336",
3395 | "id": "GroupNodemovies",
3396 | "label": "movies",
3397 | "position": [
3398 | 0,
3399 | 0
3400 | ],
3401 | "properties": {
3402 | "label": "movies"
3403 | },
3404 | "scale_factor": 1,
3405 | "size": [
3406 | 55,
3407 | 55
3408 | ],
3409 | "styles": {},
3410 | "type": "#F44336"
3411 | }
3412 | ],
3413 | "_overview": {
3414 | "enabled": null,
3415 | "overview_set": false
3416 | },
3417 | "_selected_graph": [
3418 | [],
3419 | []
3420 | ],
3421 | "_sidebar": {
3422 | "enabled": false,
3423 | "start_with": "About"
3424 | },
3425 | "_view_module_version": "^1.10.2",
3426 | "layout": "IPY_MODEL_5fd3993005a24254b3a3d6b0ee922944"
3427 | }
3428 | },
3429 | "d5976e6a66da45828c79552aad4df578": {
3430 | "model_module": "@jupyter-widgets/base",
3431 | "model_module_version": "2.0.0",
3432 | "model_name": "LayoutModel",
3433 | "state": {
3434 | "height": "630px",
3435 | "width": "100%"
3436 | }
3437 | },
3438 | "d59c8897feff427aaf6f106244f5258c": {
3439 | "model_module": "@jupyter-widgets/base",
3440 | "model_module_version": "2.0.0",
3441 | "model_name": "LayoutModel",
3442 | "state": {
3443 | "height": "500px",
3444 | "width": "100%"
3445 | }
3446 | },
3447 | "dbc205b3693047f4832f11957c0ede1e": {
3448 | "model_module": "@jupyter-widgets/base",
3449 | "model_module_version": "2.0.0",
3450 | "model_name": "LayoutModel",
3451 | "state": {
3452 | "height": "630px",
3453 | "width": "100%"
3454 | }
3455 | },
3456 | "df8c3d8d361541ab919ea476b8e66e3d": {
3457 | "model_module": "yfiles-jupyter-graphs",
3458 | "model_module_version": "^1.10.2",
3459 | "model_name": "GraphModel",
3460 | "state": {
3461 | "_context_pane_mapping": [
3462 | {
3463 | "id": "Neighborhood",
3464 | "title": "Neighborhood"
3465 | },
3466 | {
3467 | "id": "Data",
3468 | "title": "Data"
3469 | },
3470 | {
3471 | "id": "Search",
3472 | "title": "Search"
3473 | },
3474 | {
3475 | "id": "About",
3476 | "title": "About"
3477 | }
3478 | ],
3479 | "_data_importer": "neo4j",
3480 | "_directed": true,
3481 | "_edges": [
3482 | {
3483 | "color": "#AC94F4",
3484 | "directed": true,
3485 | "end": 0,
3486 | "id": 6,
3487 | "label": "produced",
3488 | "properties": {
3489 | "label": "PRODUCED"
3490 | },
3491 | "start": 7,
3492 | "styles": {
3493 | "label_styles": {
3494 | "fontWeight": "bolder"
3495 | }
3496 | },
3497 | "thickness_factor": 1
3498 | },
3499 | {
3500 | "color": "blue",
3501 | "directed": true,
3502 | "end": 0,
3503 | "id": 5,
3504 | "label": "DIRECTED",
3505 | "properties": {
3506 | "label": "DIRECTED"
3507 | },
3508 | "start": 6,
3509 | "styles": {
3510 | "dashStyle": "dot"
3511 | },
3512 | "thickness_factor": 1
3513 | },
3514 | {
3515 | "color": "blue",
3516 | "directed": true,
3517 | "end": 0,
3518 | "id": 4,
3519 | "label": "DIRECTED",
3520 | "properties": {
3521 | "label": "DIRECTED"
3522 | },
3523 | "start": 5,
3524 | "styles": {
3525 | "dashStyle": "dot"
3526 | },
3527 | "thickness_factor": 1
3528 | }
3529 | ],
3530 | "_graph_layout": {
3531 | "algorithm": "organic",
3532 | "options": {}
3533 | },
3534 | "_model_module_version": "^1.10.2",
3535 | "_nodes": [
3536 | {
3537 | "color": "#2196F3",
3538 | "id": 8,
3539 | "label": "Emil Eifrem",
3540 | "parentId": 0,
3541 | "position": [
3542 | 0,
3543 | 0
3544 | ],
3545 | "properties": {
3546 | "born": 1978,
3547 | "label": "Person",
3548 | "name": "Emil Eifrem"
3549 | },
3550 | "scale_factor": 1,
3551 | "size": [
3552 | 100,
3553 | 100
3554 | ],
3555 | "styles": {
3556 | "shape": "rectangle"
3557 | },
3558 | "type": "#2196F3"
3559 | },
3560 | {
3561 | "color": "#4CAF50",
3562 | "id": 0,
3563 | "label": "Title:\nThe Matrix",
3564 | "parentId": "GroupNodemovies",
3565 | "position": [
3566 | 0,
3567 | 0
3568 | ],
3569 | "properties": {
3570 | "label": "Movie",
3571 | "released": 1999,
3572 | "tagline": "Welcome to the Real World",
3573 | "title": "The Matrix",
3574 | "votes": 6345
3575 | },
3576 | "scale_factor": 1,
3577 | "size": [
3578 | 55,
3579 | 55
3580 | ],
3581 | "styles": {
3582 | "label_styles": {
3583 | "backgroundColor": "rgba(0,0,0,0.7)",
3584 | "color": "#FFFFFF",
3585 | "fontSize": 20,
3586 | "fontWeight": "lighter",
3587 | "maximumWidth": 130,
3588 | "position": "north",
3589 | "textAlignment": "center",
3590 | "wrapping": "word"
3591 | }
3592 | },
3593 | "type": "#4CAF50"
3594 | },
3595 | {
3596 | "color": "#2196F3",
3597 | "id": 7,
3598 | "label": "Joel Silver",
3599 | "parentId": "GroupNodepeople",
3600 | "position": [
3601 | 0,
3602 | 0
3603 | ],
3604 | "properties": {
3605 | "born": 1952,
3606 | "label": "Person",
3607 | "name": "Joel Silver"
3608 | },
3609 | "scale_factor": 1,
3610 | "size": [
3611 | 100,
3612 | 100
3613 | ],
3614 | "styles": {
3615 | "shape": "rectangle"
3616 | },
3617 | "type": "#2196F3"
3618 | },
3619 | {
3620 | "color": "#2196F3",
3621 | "id": 6,
3622 | "label": "Lana Wachowski",
3623 | "parentId": "GroupNodepeople",
3624 | "position": [
3625 | 0,
3626 | 0
3627 | ],
3628 | "properties": {
3629 | "born": 1965,
3630 | "label": "Person",
3631 | "name": "Lana Wachowski"
3632 | },
3633 | "scale_factor": 1,
3634 | "size": [
3635 | 100,
3636 | 100
3637 | ],
3638 | "styles": {
3639 | "shape": "rectangle"
3640 | },
3641 | "type": "#2196F3"
3642 | },
3643 | {
3644 | "color": "#2196F3",
3645 | "id": 5,
3646 | "label": "Lilly Wachowski",
3647 | "parentId": "GroupNodepeople",
3648 | "position": [
3649 | 0,
3650 | 0
3651 | ],
3652 | "properties": {
3653 | "born": 1967,
3654 | "label": "Person",
3655 | "name": "Lilly Wachowski"
3656 | },
3657 | "scale_factor": 1,
3658 | "size": [
3659 | 100,
3660 | 100
3661 | ],
3662 | "styles": {
3663 | "shape": "rectangle"
3664 | },
3665 | "type": "#2196F3"
3666 | },
3667 | {
3668 | "color": "#2196F3",
3669 | "id": 4,
3670 | "label": "Hugo Weaving",
3671 | "parentId": 0,
3672 | "position": [
3673 | 0,
3674 | 0
3675 | ],
3676 | "properties": {
3677 | "born": 1960,
3678 | "label": "Person",
3679 | "name": "Hugo Weaving"
3680 | },
3681 | "scale_factor": 1,
3682 | "size": [
3683 | 100,
3684 | 100
3685 | ],
3686 | "styles": {
3687 | "shape": "rectangle"
3688 | },
3689 | "type": "#2196F3"
3690 | },
3691 | {
3692 | "color": "#2196F3",
3693 | "id": 3,
3694 | "label": "Laurence Fishburne",
3695 | "parentId": 0,
3696 | "position": [
3697 | 0,
3698 | 0
3699 | ],
3700 | "properties": {
3701 | "born": 1961,
3702 | "label": "Person",
3703 | "name": "Laurence Fishburne"
3704 | },
3705 | "scale_factor": 1,
3706 | "size": [
3707 | 100,
3708 | 100
3709 | ],
3710 | "styles": {
3711 | "shape": "rectangle"
3712 | },
3713 | "type": "#2196F3"
3714 | },
3715 | {
3716 | "color": "#2196F3",
3717 | "id": 2,
3718 | "label": "Carrie-Anne Moss",
3719 | "parentId": 0,
3720 | "position": [
3721 | 0,
3722 | 0
3723 | ],
3724 | "properties": {
3725 | "born": 1967,
3726 | "label": "Person",
3727 | "name": "Carrie-Anne Moss"
3728 | },
3729 | "scale_factor": 1,
3730 | "size": [
3731 | 100,
3732 | 100
3733 | ],
3734 | "styles": {
3735 | "shape": "rectangle"
3736 | },
3737 | "type": "#2196F3"
3738 | },
3739 | {
3740 | "color": "#2196F3",
3741 | "id": 1,
3742 | "label": "Keanu Reeves",
3743 | "parentId": 0,
3744 | "position": [
3745 | 0,
3746 | 0
3747 | ],
3748 | "properties": {
3749 | "born": 1964,
3750 | "label": "Person",
3751 | "name": "Keanu Reeves"
3752 | },
3753 | "scale_factor": 1,
3754 | "size": [
3755 | 100,
3756 | 100
3757 | ],
3758 | "styles": {
3759 | "shape": "rectangle"
3760 | },
3761 | "type": "#2196F3"
3762 | },
3763 | {
3764 | "color": "#cca9ff",
3765 | "id": "GroupNodepeople",
3766 | "label": "people",
3767 | "position": [
3768 | 0,
3769 | 0
3770 | ],
3771 | "properties": {
3772 | "label": "people"
3773 | },
3774 | "scale_factor": 1,
3775 | "size": [
3776 | 55,
3777 | 55
3778 | ],
3779 | "styles": {},
3780 | "type": "#cca9ff"
3781 | },
3782 | {
3783 | "color": "#F44336",
3784 | "id": "GroupNodemovies",
3785 | "label": "movies",
3786 | "position": [
3787 | 0,
3788 | 0
3789 | ],
3790 | "properties": {
3791 | "label": "movies"
3792 | },
3793 | "scale_factor": 1,
3794 | "size": [
3795 | 55,
3796 | 55
3797 | ],
3798 | "styles": {},
3799 | "type": "#F44336"
3800 | }
3801 | ],
3802 | "_overview": {
3803 | "enabled": null,
3804 | "overview_set": false
3805 | },
3806 | "_selected_graph": [
3807 | [],
3808 | []
3809 | ],
3810 | "_sidebar": {
3811 | "enabled": false,
3812 | "start_with": "About"
3813 | },
3814 | "_view_module_version": "^1.10.2",
3815 | "layout": "IPY_MODEL_77f79c1038da486ebdf3db3693fa1df7"
3816 | }
3817 | },
3818 | "e6099874e34449e1b898bc4e878bee7c": {
3819 | "model_module": "yfiles-jupyter-graphs",
3820 | "model_module_version": "^1.10.2",
3821 | "model_name": "GraphModel",
3822 | "state": {
3823 | "_context_pane_mapping": [
3824 | {
3825 | "id": "Neighborhood",
3826 | "title": "Neighborhood"
3827 | },
3828 | {
3829 | "id": "Data",
3830 | "title": "Data"
3831 | },
3832 | {
3833 | "id": "Search",
3834 | "title": "Search"
3835 | },
3836 | {
3837 | "id": "About",
3838 | "title": "About"
3839 | }
3840 | ],
3841 | "_data_importer": "neo4j",
3842 | "_directed": true,
3843 | "_edges": [
3844 | {
3845 | "color": "blue",
3846 | "directed": true,
3847 | "end": 0,
3848 | "id": 4,
3849 | "label": "DIRECTED",
3850 | "properties": {
3851 | "label": "DIRECTED"
3852 | },
3853 | "start": 5,
3854 | "styles": {
3855 | "dashStyle": "dot"
3856 | },
3857 | "thickness_factor": 1
3858 | },
3859 | {
3860 | "color": "blue",
3861 | "directed": true,
3862 | "end": 0,
3863 | "id": 5,
3864 | "label": "DIRECTED",
3865 | "properties": {
3866 | "label": "DIRECTED"
3867 | },
3868 | "start": 6,
3869 | "styles": {
3870 | "dashStyle": "dot"
3871 | },
3872 | "thickness_factor": 1
3873 | },
3874 | {
3875 | "color": "#AC94F4",
3876 | "directed": true,
3877 | "end": 0,
3878 | "id": 6,
3879 | "label": "produced",
3880 | "properties": {
3881 | "label": "PRODUCED"
3882 | },
3883 | "start": 7,
3884 | "styles": {
3885 | "label_styles": {
3886 | "fontSize": 16,
3887 | "fontWeight": "bolder"
3888 | }
3889 | },
3890 | "thickness_factor": 1
3891 | },
3892 | {
3893 | "color": "blue",
3894 | "directed": true,
3895 | "end": 9,
3896 | "id": 12,
3897 | "label": "DIRECTED",
3898 | "properties": {
3899 | "label": "DIRECTED"
3900 | },
3901 | "start": 5,
3902 | "styles": {
3903 | "dashStyle": "dot"
3904 | },
3905 | "thickness_factor": 1
3906 | },
3907 | {
3908 | "color": "blue",
3909 | "directed": true,
3910 | "end": 9,
3911 | "id": 13,
3912 | "label": "DIRECTED",
3913 | "properties": {
3914 | "label": "DIRECTED"
3915 | },
3916 | "start": 6,
3917 | "styles": {
3918 | "dashStyle": "dot"
3919 | },
3920 | "thickness_factor": 1
3921 | },
3922 | {
3923 | "color": "#AC94F4",
3924 | "directed": true,
3925 | "end": 9,
3926 | "id": 14,
3927 | "label": "produced",
3928 | "properties": {
3929 | "label": "PRODUCED"
3930 | },
3931 | "start": 7,
3932 | "styles": {
3933 | "label_styles": {
3934 | "fontSize": 16,
3935 | "fontWeight": "bolder"
3936 | }
3937 | },
3938 | "thickness_factor": 1
3939 | },
3940 | {
3941 | "color": "blue",
3942 | "directed": true,
3943 | "end": 10,
3944 | "id": 19,
3945 | "label": "DIRECTED",
3946 | "properties": {
3947 | "label": "DIRECTED"
3948 | },
3949 | "start": 5,
3950 | "styles": {
3951 | "dashStyle": "dot"
3952 | },
3953 | "thickness_factor": 1
3954 | }
3955 | ],
3956 | "_graph_layout": {
3957 | "algorithm": "organic",
3958 | "options": {}
3959 | },
3960 | "_model_module_version": "^1.10.2",
3961 | "_nodes": [
3962 | {
3963 | "color": "#2196F3",
3964 | "id": 1,
3965 | "label": "Keanu Reeves",
3966 | "parentId": 10,
3967 | "position": [
3968 | 0,
3969 | 0
3970 | ],
3971 | "properties": {
3972 | "born": 1964,
3973 | "label": "Person",
3974 | "name": "Keanu Reeves"
3975 | },
3976 | "scale_factor": 1,
3977 | "size": [
3978 | 100,
3979 | 100
3980 | ],
3981 | "styles": {
3982 | "shape": "rectangle"
3983 | },
3984 | "type": "#2196F3"
3985 | },
3986 | {
3987 | "color": "#4CAF50",
3988 | "id": 0,
3989 | "label": "Title:\nThe Matrix",
3990 | "parentId": "GroupNodemovies",
3991 | "position": [
3992 | 0,
3993 | 0
3994 | ],
3995 | "properties": {
3996 | "label": "Movie",
3997 | "released": 1999,
3998 | "tagline": "Welcome to the Real World",
3999 | "title": "The Matrix",
4000 | "votes": 6345
4001 | },
4002 | "scale_factor": 1,
4003 | "size": [
4004 | 55,
4005 | 55
4006 | ],
4007 | "styles": {
4008 | "label_styles": {
4009 | "backgroundColor": "rgba(0,0,0,0.7)",
4010 | "color": "#FFFFFF",
4011 | "fontSize": 20,
4012 | "fontWeight": "lighter",
4013 | "maximumWidth": 130,
4014 | "position": "north",
4015 | "textAlignment": "center",
4016 | "wrapping": "word"
4017 | }
4018 | },
4019 | "type": "#4CAF50"
4020 | },
4021 | {
4022 | "color": "#2196F3",
4023 | "id": 2,
4024 | "label": "Carrie-Anne Moss",
4025 | "parentId": 10,
4026 | "position": [
4027 | 0,
4028 | 0
4029 | ],
4030 | "properties": {
4031 | "born": 1967,
4032 | "label": "Person",
4033 | "name": "Carrie-Anne Moss"
4034 | },
4035 | "scale_factor": 1,
4036 | "size": [
4037 | 100,
4038 | 100
4039 | ],
4040 | "styles": {
4041 | "shape": "rectangle"
4042 | },
4043 | "type": "#2196F3"
4044 | },
4045 | {
4046 | "color": "#2196F3",
4047 | "id": 3,
4048 | "label": "Laurence Fishburne",
4049 | "parentId": 10,
4050 | "position": [
4051 | 0,
4052 | 0
4053 | ],
4054 | "properties": {
4055 | "born": 1961,
4056 | "label": "Person",
4057 | "name": "Laurence Fishburne"
4058 | },
4059 | "scale_factor": 1,
4060 | "size": [
4061 | 100,
4062 | 100
4063 | ],
4064 | "styles": {
4065 | "shape": "rectangle"
4066 | },
4067 | "type": "#2196F3"
4068 | },
4069 | {
4070 | "color": "#2196F3",
4071 | "id": 4,
4072 | "label": "Hugo Weaving",
4073 | "parentId": 10,
4074 | "position": [
4075 | 0,
4076 | 0
4077 | ],
4078 | "properties": {
4079 | "born": 1960,
4080 | "label": "Person",
4081 | "name": "Hugo Weaving"
4082 | },
4083 | "scale_factor": 1,
4084 | "size": [
4085 | 100,
4086 | 100
4087 | ],
4088 | "styles": {
4089 | "shape": "rectangle"
4090 | },
4091 | "type": "#2196F3"
4092 | },
4093 | {
4094 | "color": "#2196F3",
4095 | "id": 5,
4096 | "label": "Lilly Wachowski",
4097 | "parentId": "GroupNodepeople",
4098 | "position": [
4099 | 0,
4100 | 0
4101 | ],
4102 | "properties": {
4103 | "born": 1967,
4104 | "label": "Person",
4105 | "name": "Lilly Wachowski"
4106 | },
4107 | "scale_factor": 1,
4108 | "size": [
4109 | 100,
4110 | 100
4111 | ],
4112 | "styles": {
4113 | "shape": "rectangle"
4114 | },
4115 | "type": "#2196F3"
4116 | },
4117 | {
4118 | "color": "#2196F3",
4119 | "id": 6,
4120 | "label": "Lana Wachowski",
4121 | "parentId": "GroupNodepeople",
4122 | "position": [
4123 | 0,
4124 | 0
4125 | ],
4126 | "properties": {
4127 | "born": 1965,
4128 | "label": "Person",
4129 | "name": "Lana Wachowski"
4130 | },
4131 | "scale_factor": 1,
4132 | "size": [
4133 | 100,
4134 | 100
4135 | ],
4136 | "styles": {
4137 | "shape": "rectangle"
4138 | },
4139 | "type": "#2196F3"
4140 | },
4141 | {
4142 | "color": "#2196F3",
4143 | "id": 7,
4144 | "label": "Joel Silver",
4145 | "parentId": "GroupNodepeople",
4146 | "position": [
4147 | 0,
4148 | 0
4149 | ],
4150 | "properties": {
4151 | "born": 1952,
4152 | "label": "Person",
4153 | "name": "Joel Silver"
4154 | },
4155 | "scale_factor": 1,
4156 | "size": [
4157 | 100,
4158 | 100
4159 | ],
4160 | "styles": {
4161 | "shape": "rectangle"
4162 | },
4163 | "type": "#2196F3"
4164 | },
4165 | {
4166 | "color": "#2196F3",
4167 | "id": 8,
4168 | "label": "Emil Eifrem",
4169 | "parentId": 0,
4170 | "position": [
4171 | 0,
4172 | 0
4173 | ],
4174 | "properties": {
4175 | "born": 1978,
4176 | "label": "Person",
4177 | "name": "Emil Eifrem"
4178 | },
4179 | "scale_factor": 1,
4180 | "size": [
4181 | 100,
4182 | 100
4183 | ],
4184 | "styles": {
4185 | "shape": "rectangle"
4186 | },
4187 | "type": "#2196F3"
4188 | },
4189 | {
4190 | "color": "#4CAF50",
4191 | "id": 9,
4192 | "label": "Title:\nThe Matrix Reloaded",
4193 | "parentId": "GroupNodemovies",
4194 | "position": [
4195 | 0,
4196 | 0
4197 | ],
4198 | "properties": {
4199 | "label": "Movie",
4200 | "released": 2003,
4201 | "tagline": "Free your mind",
4202 | "title": "The Matrix Reloaded",
4203 | "votes": 1841
4204 | },
4205 | "scale_factor": 1,
4206 | "size": [
4207 | 55,
4208 | 55
4209 | ],
4210 | "styles": {
4211 | "label_styles": {
4212 | "backgroundColor": "rgba(0,0,0,0.7)",
4213 | "color": "#FFFFFF",
4214 | "fontSize": 20,
4215 | "fontWeight": "lighter",
4216 | "maximumWidth": 130,
4217 | "position": "north",
4218 | "textAlignment": "center",
4219 | "wrapping": "word"
4220 | }
4221 | },
4222 | "type": "#4CAF50"
4223 | },
4224 | {
4225 | "color": "#4CAF50",
4226 | "id": 10,
4227 | "label": "Title:\nThe Matrix Revolutions",
4228 | "parentId": "GroupNodemovies",
4229 | "position": [
4230 | 0,
4231 | 0
4232 | ],
4233 | "properties": {
4234 | "label": "Movie",
4235 | "released": 2003,
4236 | "tagline": "Everything that has a beginning has an end",
4237 | "title": "The Matrix Revolutions",
4238 | "votes": 1534
4239 | },
4240 | "scale_factor": 1,
4241 | "size": [
4242 | 55,
4243 | 55
4244 | ],
4245 | "styles": {
4246 | "label_styles": {
4247 | "backgroundColor": "rgba(0,0,0,0.7)",
4248 | "color": "#FFFFFF",
4249 | "fontSize": 20,
4250 | "fontWeight": "lighter",
4251 | "maximumWidth": 130,
4252 | "position": "north",
4253 | "textAlignment": "center",
4254 | "wrapping": "word"
4255 | }
4256 | },
4257 | "type": "#4CAF50"
4258 | },
4259 | {
4260 | "color": "#cca9ff",
4261 | "id": "GroupNodepeople",
4262 | "label": "people",
4263 | "position": [
4264 | 0,
4265 | 0
4266 | ],
4267 | "properties": {
4268 | "label": "people"
4269 | },
4270 | "scale_factor": 1,
4271 | "size": [
4272 | 55,
4273 | 55
4274 | ],
4275 | "styles": {},
4276 | "type": "#cca9ff"
4277 | },
4278 | {
4279 | "color": "#F44336",
4280 | "id": "GroupNodemovies",
4281 | "label": "movies",
4282 | "position": [
4283 | 0,
4284 | 0
4285 | ],
4286 | "properties": {
4287 | "label": "movies"
4288 | },
4289 | "scale_factor": 1,
4290 | "size": [
4291 | 55,
4292 | 55
4293 | ],
4294 | "styles": {},
4295 | "type": "#F44336"
4296 | }
4297 | ],
4298 | "_overview": {
4299 | "enabled": null,
4300 | "overview_set": false
4301 | },
4302 | "_selected_graph": [
4303 | [],
4304 | []
4305 | ],
4306 | "_sidebar": {
4307 | "enabled": false,
4308 | "start_with": "About"
4309 | },
4310 | "_view_module_version": "^1.10.2",
4311 | "layout": "IPY_MODEL_b75d05a06bf5430eb7bcd20497262a02"
4312 | }
4313 | },
4314 | "ee5c26d1e68147d6a0d613aaf7c9ae0e": {
4315 | "model_module": "@jupyter-widgets/base",
4316 | "model_module_version": "2.0.0",
4317 | "model_name": "LayoutModel",
4318 | "state": {
4319 | "height": "500px",
4320 | "width": "100%"
4321 | }
4322 | },
4323 | "f3a094e36f354e40827d35b439895f5a": {
4324 | "model_module": "yfiles-jupyter-graphs",
4325 | "model_module_version": "^1.10.2",
4326 | "model_name": "GraphModel",
4327 | "state": {
4328 | "_context_pane_mapping": [
4329 | {
4330 | "id": "Neighborhood",
4331 | "title": "Neighborhood"
4332 | },
4333 | {
4334 | "id": "Data",
4335 | "title": "Data"
4336 | },
4337 | {
4338 | "id": "Search",
4339 | "title": "Search"
4340 | },
4341 | {
4342 | "id": "About",
4343 | "title": "About"
4344 | }
4345 | ],
4346 | "_directed": false,
4347 | "_model_module_version": "^1.10.2",
4348 | "_overview": {
4349 | "enabled": null,
4350 | "overview_set": false
4351 | },
4352 | "_sidebar": {
4353 | "enabled": false,
4354 | "start_with": null
4355 | },
4356 | "_view_module_version": "^1.10.2",
4357 | "layout": "IPY_MODEL_ee5c26d1e68147d6a0d613aaf7c9ae0e"
4358 | }
4359 | },
4360 | "fff11c7266aa4e2fa93490fad39be98a": {
4361 | "model_module": "yfiles-jupyter-graphs",
4362 | "model_module_version": "^1.10.2",
4363 | "model_name": "GraphModel",
4364 | "state": {
4365 | "_context_pane_mapping": [
4366 | {
4367 | "id": "Neighborhood",
4368 | "title": "Neighborhood"
4369 | },
4370 | {
4371 | "id": "Data",
4372 | "title": "Data"
4373 | },
4374 | {
4375 | "id": "Search",
4376 | "title": "Search"
4377 | },
4378 | {
4379 | "id": "About",
4380 | "title": "About"
4381 | }
4382 | ],
4383 | "_directed": false,
4384 | "_model_module_version": "^1.10.2",
4385 | "_overview": {
4386 | "enabled": null,
4387 | "overview_set": false
4388 | },
4389 | "_sidebar": {
4390 | "enabled": false,
4391 | "start_with": null
4392 | },
4393 | "_view_module_version": "^1.10.2",
4394 | "layout": "IPY_MODEL_34bbd98495ca4f32aae4dbb34758f654"
4395 | }
4396 | }
4397 | },
4398 | "version_major": 2,
4399 | "version_minor": 0
4400 | }
4401 | }
4402 | },
4403 | "nbformat": 4,
4404 | "nbformat_minor": 5
4405 | }
4406 |
--------------------------------------------------------------------------------
/examples/feature_example.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "8778641ccdd52ee5",
6 | "metadata": {},
7 | "source": [
8 | "# Features
\n",
9 | "\n",
10 | "This notebook showcases some of the various features of `yfiles-jupyter-graphs-for-neo4j`. \n",
11 | "\n",
12 | "For a detailed description for the different mappings, check out the widget [documentation](https://yworks.github.io/yfiles-jupyter-graphs/)\n"
13 | ]
14 | },
15 | {
16 | "cell_type": "code",
17 | "execution_count": null,
18 | "id": "547d100c-0e8c-4600-875d-33877fbea626",
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "%pip install yfiles_jupyter_graphs_for_neo4j --quiet\n",
23 | "%pip install neo4j --quiet"
24 | ]
25 | },
26 | {
27 | "cell_type": "markdown",
28 | "id": "698b0bf0-118e-4f23-95b5-a8f1c7b2774f",
29 | "metadata": {},
30 | "source": [
31 | "You can also open this notebook in Google Colab when Google Colab's custom widget manager is enabled:"
32 | ]
33 | },
34 | {
35 | "cell_type": "code",
36 | "execution_count": null,
37 | "id": "91c4e84f-a1b5-43c2-ad7f-80581e3ae26a",
38 | "metadata": {},
39 | "outputs": [],
40 | "source": [
41 | "try:\n",
42 | " import google.colab\n",
43 | " from google.colab import output\n",
44 | " output.enable_custom_widget_manager()\n",
45 | "except:\n",
46 | " pass"
47 | ]
48 | },
49 | {
50 | "cell_type": "markdown",
51 | "id": "da7913db-4807-4631-a82c-a09ab6999383",
52 | "metadata": {},
53 | "source": [
54 | "
"
55 | ]
56 | },
57 | {
58 | "cell_type": "markdown",
59 | "id": "4f84e6a7-616d-400c-a297-c9d6c67932b9",
60 | "metadata": {},
61 | "source": [
62 | "## Connect to the database"
63 | ]
64 | },
65 | {
66 | "cell_type": "code",
67 | "execution_count": null,
68 | "id": "1b30c433-5541-4bb5-8dc6-631987280e5f",
69 | "metadata": {},
70 | "outputs": [],
71 | "source": [
72 | "from yfiles_jupyter_graphs_for_neo4j import Neo4jGraphWidget\n",
73 | "from neo4j import GraphDatabase\n",
74 | "\n",
75 | "NEO4J_URI = \"neo4j+ssc://demo.neo4jlabs.com\" \n",
76 | "NEO4J_USERNAME = \"fincen\"\n",
77 | "NEO4J_PASSWORD = \"fincen\"\n",
78 | "driver = GraphDatabase.driver(uri = NEO4J_URI, auth = (NEO4J_USERNAME, NEO4J_PASSWORD), database = \"fincen\")\n",
79 | "\n",
80 | "g = Neo4jGraphWidget(driver)"
81 | ]
82 | },
83 | {
84 | "cell_type": "markdown",
85 | "id": "2c8fb293-c638-4a5a-b321-eeb4907b6432",
86 | "metadata": {},
87 | "source": [
88 | "## Use heat mapping"
89 | ]
90 | },
91 | {
92 | "cell_type": "code",
93 | "execution_count": null,
94 | "id": "ebdabe1b-6503-49a7-8039-8d469b4caeea",
95 | "metadata": {},
96 | "outputs": [],
97 | "source": [
98 | "max_amount = 120000000\n",
99 | "min_amount = 50000\n",
100 | "\n",
101 | "def heat_mapping(element):\n",
102 | " if \"amount\" in element[\"properties\"]:\n",
103 | " amount = element[\"properties\"][\"amount\"]\n",
104 | " normalized_value = (amount - min_amount) / (max_amount - min_amount)\n",
105 | " transformed_value = -1 * (1 - normalized_value) ** 2 + 1\n",
106 | " return max(0, min(1, transformed_value))\n",
107 | "\n",
108 | "g.add_node_configuration(\"Filing\", heat=heat_mapping)\n",
109 | "\n",
110 | "g.show_cypher(\"MATCH (s)-[r]->(t) RETURN s,r,t LIMIT 25\")"
111 | ]
112 | },
113 | {
114 | "cell_type": "markdown",
115 | "id": "41002892-a638-4544-842b-1881287d04e0",
116 | "metadata": {},
117 | "source": [
118 | "## Visualize geospatial data"
119 | ]
120 | },
121 | {
122 | "cell_type": "code",
123 | "execution_count": null,
124 | "id": "3f9de7f7-bacc-4c5b-989e-ed4211584725",
125 | "metadata": {},
126 | "outputs": [],
127 | "source": [
128 | "def node_coordinate_mapping(node):\n",
129 | " return (node[\"properties\"][\"location\"][\"y\"], node[\"properties\"][\"location\"][\"x\"]) if \"location\" in node[\"properties\"].keys() else None\n",
130 | "\n",
131 | "def filing_coordinate_mapping(node):\n",
132 | " return (node[\"properties\"][\"beneficiary_lat\"], (node[\"properties\"][\"beneficiary_lng\"])) if \"beneficiary_lat\" in node[\"properties\"].keys() else None\n",
133 | "\n",
134 | "g.add_node_configuration([\"Country\", \"Entity\"], coordinate=node_coordinate_mapping)\n",
135 | "g.add_node_configuration(\"Filing\", coordinate=filing_coordinate_mapping)\n",
136 | "\n",
137 | "g.show_cypher(\"MATCH (s)-[r]->(t) RETURN s,r,t LIMIT 50\", layout=\"map\")"
138 | ]
139 | },
140 | {
141 | "cell_type": "markdown",
142 | "id": "3879ccf9-4223-41bd-ae38-d4b726097413",
143 | "metadata": {},
144 | "source": [
145 | "## Configure item visualization"
146 | ]
147 | },
148 | {
149 | "cell_type": "code",
150 | "execution_count": null,
151 | "id": "de1ca154-7735-476e-920b-d5b45b61a5ac",
152 | "metadata": {},
153 | "outputs": [],
154 | "source": [
155 | "\n",
156 | "g.add_relationship_configuration(\"CONCERNS\", thickness_factor= 0.5)\n",
157 | "g.add_relationship_configuration(\"FILED\", thickness_factor= 2)\n",
158 | "g.del_node_configuration(\"Entity\")\n",
159 | "g.add_node_configuration(\"Filing\", size= lambda node: (55 * (1 + heat_mapping(node)), 55 * (1 + heat_mapping(node))))\n",
160 | "\n",
161 | "g.show_cypher(\"MATCH (s)-[r]->(t) RETURN s,r,t LIMIT 25\")"
162 | ]
163 | },
164 | {
165 | "cell_type": "markdown",
166 | "id": "7484387b-5b1e-429d-afa2-7df01bd3b4e2",
167 | "metadata": {},
168 | "source": [
169 | "## Configure grouping\n",
170 | "\n",
171 | "The widget supports various grouping options, see [grouping.ipynb](./grouping.ipynb) for more details."
172 | ]
173 | },
174 | {
175 | "cell_type": "code",
176 | "execution_count": null,
177 | "id": "c70e5897-c369-4132-8c30-5e7a2c712ed9",
178 | "metadata": {},
179 | "outputs": [],
180 | "source": [
181 | "# replaces \"COUNTRY\" relationships with a grouped hierarchy\n",
182 | "g.add_parent_relationship_configuration(\"COUNTRY\")\n",
183 | "\n",
184 | "g.show_cypher(\"MATCH (s)-[r]->(t) RETURN s,r,t LIMIT 25\")"
185 | ]
186 | },
187 | {
188 | "cell_type": "markdown",
189 | "id": "58927c27-4a76-4835-bd6e-f0dd0f2fce04",
190 | "metadata": {},
191 | "source": [
192 | "## Configure node-to-cell mapping\n",
193 | "\n",
194 | "The node-to-cell mapping allows to fine-tune layout results by assigning preferred cell constraints for specific nodes.\n",
195 | "\n",
196 | "This is particularly useful to highlight specific items structurally aside from visual features like color or size."
197 | ]
198 | },
199 | {
200 | "cell_type": "code",
201 | "execution_count": null,
202 | "id": "77d47bf2-5f34-4034-ad40-cb0c32a63fee",
203 | "metadata": {},
204 | "outputs": [],
205 | "source": [
206 | "g.del_parent_relationship_configuration(\"COUNTRY\")\n",
207 | "\n",
208 | "# highlight Bank of New York and China Construction Bank\n",
209 | "def get_cell_mapping(node):\n",
210 | " name = node[\"properties\"].get(\"name\")\n",
211 | " if name == \"The Bank of New York Mellon Corp.\" or name == \"China Construction Bank Corporation\":\n",
212 | " return (0,0)\n",
213 | " return (1,0)\n",
214 | "\n",
215 | "g.node_cell_mapping = get_cell_mapping\n",
216 | "\n",
217 | "# layouts interpret node-to-cell mapping differently\n",
218 | "g.show_cypher(\"MATCH (s)-[r]->(t) RETURN s,r,t LIMIT 25\", layout='hierarchic')"
219 | ]
220 | }
221 | ],
222 | "metadata": {
223 | "kernelspec": {
224 | "display_name": "Python 3 (ipykernel)",
225 | "language": "python",
226 | "name": "python3"
227 | },
228 | "language_info": {
229 | "codemirror_mode": {
230 | "name": "ipython",
231 | "version": 3
232 | },
233 | "file_extension": ".py",
234 | "mimetype": "text/x-python",
235 | "name": "python",
236 | "nbconvert_exporter": "python",
237 | "pygments_lexer": "ipython3",
238 | "version": "3.9.19"
239 | }
240 | },
241 | "nbformat": 4,
242 | "nbformat_minor": 5
243 | }
244 |
--------------------------------------------------------------------------------
/examples/selection_example.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "3e6c94df-2ee0-40ac-a0eb-97ddb6c18556",
6 | "metadata": {},
7 | "source": [
8 | "# Obtain selection from widget
"
9 | ]
10 | },
11 | {
12 | "cell_type": "code",
13 | "execution_count": null,
14 | "id": "7e709b40-0119-498d-879f-cc324b992320",
15 | "metadata": {},
16 | "outputs": [],
17 | "source": [
18 | "%pip install yfiles_jupyter_graphs_for_neo4j --quiet\n",
19 | "%pip install neo4j --quiet\n",
20 | "from yfiles_jupyter_graphs_for_neo4j import Neo4jGraphWidget\n",
21 | "from neo4j import GraphDatabase"
22 | ]
23 | },
24 | {
25 | "cell_type": "markdown",
26 | "id": "56afeb89-1e79-4318-a000-67f80e1f4983",
27 | "metadata": {},
28 | "source": [
29 | "You can also open this notebook in Google Colab when Google Colab's custom widget manager is enabled:"
30 | ]
31 | },
32 | {
33 | "cell_type": "code",
34 | "execution_count": null,
35 | "id": "4932f2d4-ad5e-49e8-a9f3-d72a74f21cd4",
36 | "metadata": {},
37 | "outputs": [],
38 | "source": [
39 | "try:\n",
40 | " import google.colab\n",
41 | " from google.colab import output\n",
42 | " output.enable_custom_widget_manager()\n",
43 | "except:\n",
44 | " pass"
45 | ]
46 | },
47 | {
48 | "cell_type": "markdown",
49 | "id": "1e7fcf65-a062-4d84-9888-11793857c4ab",
50 | "metadata": {},
51 | "source": [
52 | "## Connect with Neo4j driver"
53 | ]
54 | },
55 | {
56 | "cell_type": "code",
57 | "execution_count": null,
58 | "id": "6660c53f-3041-4931-919b-18789dcd0667",
59 | "metadata": {},
60 | "outputs": [],
61 | "source": [
62 | "NEO4J_URI = \"neo4j+ssc://demo.neo4jlabs.com\" \n",
63 | "NEO4J_USERNAME = \"movies\"\n",
64 | "NEO4J_PASSWORD = \"movies\"\n",
65 | "driver = GraphDatabase.driver(uri = NEO4J_URI, auth = (NEO4J_USERNAME, NEO4J_PASSWORD), database = 'movies')\n",
66 | "\n",
67 | "g = Neo4jGraphWidget(driver)"
68 | ]
69 | },
70 | {
71 | "cell_type": "markdown",
72 | "id": "cf79e4f2-7a6b-4197-9f94-902f38c303bd",
73 | "metadata": {},
74 | "source": [
75 | "## Query database and interactively select nodes\n",
76 | "\n",
77 | "Select your nodes in the following diagram:"
78 | ]
79 | },
80 | {
81 | "cell_type": "code",
82 | "execution_count": null,
83 | "id": "a4c2eac8-3547-4db8-b72d-c0a0ed987b4b",
84 | "metadata": {},
85 | "outputs": [],
86 | "source": [
87 | "g.show_cypher(\"MATCH (s)-[r]->(t) RETURN s,r,t LIMIT 20\")"
88 | ]
89 | },
90 | {
91 | "cell_type": "markdown",
92 | "id": "8725e3b8-9d09-40a3-b728-fecb962a6aa4",
93 | "metadata": {},
94 | "source": [
95 | "## Use interactively selected nodes\n",
96 | "\n",
97 | "Recompile the following code cell to update your ids with your selection:"
98 | ]
99 | },
100 | {
101 | "cell_type": "code",
102 | "execution_count": null,
103 | "id": "4f56b914-4ad5-4cfe-ad9b-f8785a86c561",
104 | "metadata": {},
105 | "outputs": [],
106 | "source": [
107 | "ids = g.get_selected_node_ids()"
108 | ]
109 | },
110 | {
111 | "cell_type": "markdown",
112 | "id": "d6263574-7dfc-4977-95ed-e66677ceeb8c",
113 | "metadata": {},
114 | "source": [
115 | "Now, `ids` should contain a list of the currently selected nodes in the widget, otherwise make sure to (click-)select elements in the above diagram and recompile the `get_selected_node_ids()` cell:"
116 | ]
117 | },
118 | {
119 | "cell_type": "code",
120 | "execution_count": null,
121 | "id": "52292a3d-823b-47dd-a105-890f7a636079",
122 | "metadata": {},
123 | "outputs": [],
124 | "source": [
125 | "print(ids)"
126 | ]
127 | },
128 | {
129 | "cell_type": "markdown",
130 | "id": "b7d427c8-a467-4a40-9094-a0fd62894d5f",
131 | "metadata": {},
132 | "source": [
133 | "Make sure not to restart the whole notebook as your selection is being reset as well."
134 | ]
135 | },
136 | {
137 | "cell_type": "code",
138 | "execution_count": null,
139 | "id": "f4e0de71-069d-4a74-98c1-3e100cdd05ed",
140 | "metadata": {},
141 | "outputs": [],
142 | "source": [
143 | "g.show_cypher(\"MATCH (s)-[r]->(t) WHERE ID(s) IN $ids RETURN s,r,t LIMIT 20\", ids=ids)"
144 | ]
145 | }
146 | ],
147 | "metadata": {
148 | "kernelspec": {
149 | "display_name": "Python 3 (ipykernel)",
150 | "language": "python",
151 | "name": "python3"
152 | },
153 | "language_info": {
154 | "codemirror_mode": {
155 | "name": "ipython",
156 | "version": 3
157 | },
158 | "file_extension": ".py",
159 | "mimetype": "text/x-python",
160 | "name": "python",
161 | "nbconvert_exporter": "python",
162 | "pygments_lexer": "ipython3",
163 | "version": "3.9.19"
164 | }
165 | },
166 | "nbformat": 4,
167 | "nbformat_minor": 5
168 | }
169 |
--------------------------------------------------------------------------------
/images/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yWorks/yfiles-jupyter-graphs-for-neo4j/f5a463dddb57aa778dbc0f0e30dafbf5ce2acb02/images/example.png
--------------------------------------------------------------------------------
/images/features/grouping_feature.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yWorks/yfiles-jupyter-graphs-for-neo4j/f5a463dddb57aa778dbc0f0e30dafbf5ce2acb02/images/features/grouping_feature.png
--------------------------------------------------------------------------------
/images/features/heat_feature.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yWorks/yfiles-jupyter-graphs-for-neo4j/f5a463dddb57aa778dbc0f0e30dafbf5ce2acb02/images/features/heat_feature.png
--------------------------------------------------------------------------------
/images/features/map_feature.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yWorks/yfiles-jupyter-graphs-for-neo4j/f5a463dddb57aa778dbc0f0e30dafbf5ce2acb02/images/features/map_feature.png
--------------------------------------------------------------------------------
/images/features/size_feature.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yWorks/yfiles-jupyter-graphs-for-neo4j/f5a463dddb57aa778dbc0f0e30dafbf5ce2acb02/images/features/size_feature.png
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "yfiles_jupyter_graphs_for_neo4j"
3 | version = "1.7.0"
4 | authors = [
5 | {name = "yWorks Support Team", email= "yfileshtml@yworks.com"},
6 | ]
7 | description = "A Neo4j graph visualization package that uses the yfiles-jupyter-graphs widget"
8 | readme = "README.md"
9 | requires-python = ">=3.8"
10 | classifiers = [
11 | "Development Status :: 5 - Production/Stable",
12 | "Framework :: IPython",
13 | "Intended Audience :: Developers",
14 | "Intended Audience :: Science/Research",
15 | "Programming Language :: Python :: 3",
16 | "Operating System :: OS Independent",
17 | "Framework :: Jupyter",
18 | "Framework :: Jupyter :: JupyterLab",
19 | "Framework :: Jupyter :: JupyterLab :: 3",
20 | "Framework :: Jupyter :: JupyterLab :: Extensions",
21 | "Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt",
22 | ]
23 | license = {file = "LICENSE.md"}
24 | dependencies = [
25 | "yfiles_jupyter_graphs>=1.10.0"
26 | ]
27 |
28 | [project.urls]
29 | License = "https://github.com/yWorks/yfiles-jupyter-graphs-for-neo4j/blob/main/LICENSE.md"
30 | "Bug Tracker" = "https://github.com/yWorks/yfiles-jupyter-graphs-for-neo4j/issues"
31 | Documentation = "https://github.com/yWorks/yfiles-jupyter-graphs-for-neo4j/"
32 | Repository = "https://github.com/yWorks/yfiles-jupyter-graphs-for-neo4j.git"
33 |
34 | [build-system]
35 | requires = ["setuptools>=61.0"]
36 | build-backend = "setuptools.build_meta"
37 |
--------------------------------------------------------------------------------
/src/yfiles_jupyter_graphs_for_neo4j/Yfiles_Neo4j_Graphs.py:
--------------------------------------------------------------------------------
1 | """Jupyter (ipy)widget powered by yFiles.
2 |
3 | The main Neo4jGraphWidget class is defined in this module.
4 |
5 | """
6 | from typing import Any, Callable, Dict, Union, Type, Optional, List
7 | from types import FunctionType, MethodType
8 | import inspect
9 |
10 | from yfiles_jupyter_graphs import GraphWidget
11 |
12 | # TODO maybe change to get dynamically when adding bindings
13 |
14 | POSSIBLE_NODE_BINDINGS = {'coordinate', 'color', 'size', 'type', 'styles', 'scale_factor', 'position',
15 | 'layout', 'property', 'label'}
16 | POSSIBLE_EDGE_BINDINGS = {'color', 'thickness_factor', 'styles', 'property', 'label'}
17 | NEO4J_LABEL_KEYS = ['name', 'title', 'text', 'description', 'caption', 'label']
18 |
19 | class Neo4jGraphWidget:
20 | """
21 | A yFiles Graphs for Jupyter widget that is tailored to visualize Cypher queries resolved against a Neo4j database.
22 | """
23 |
24 | # noinspection PyShadowingBuiltins
25 | def __init__(self, driver: Optional[Any] = None, widget_layout: Optional[Any] = None,
26 | overview_enabled: Optional[bool] = None, context_start_with: Optional[str] = None,
27 | license: Optional[Dict] = None,
28 | autocomplete_relationships: Optional[bool] = False, layout: Optional[str] = 'organic'):
29 | """
30 | Initializes a new instance of the Neo4jGraphWidget class.
31 |
32 | Args:
33 | driver (Optional[neo4j._sync.driver.Neo4jDriver]): The Neo4j driver to resolve the Cypher queries.
34 | widget_layout (Optional[ipywidgets.Layout]): Can be used to specify general widget appearance through css attributes.
35 | See ipywidgets documentation for the available keywords.
36 | overview_enabled (Optional[bool]): Whether the graph overview is enabled or not.
37 | context_start_with (Optional[str]): Start with a specific side-panel opened in the interactive widget. Starts with closed side-panel by default.
38 | license (Optional[Dict]): The widget works on common public domains without a specific license.
39 | For unknown domains, a license can be obtained by the creators of the widget.
40 | autocomplete_relationships (Optional[bool]): Whether missing relationships in the Cypher's return value are automatically added.
41 | layout (Optional[str]): Specifies the default automatic graph arrangement. Can be overwritten for each
42 | cypher separately. By default, an "organic" layout is used. Supported values are:
43 | - "circular"
44 | - "circular_straight_line"
45 | - "hierarchic"
46 | - "organic"
47 | - "interactive_organic"
48 | - "orthogonal"
49 | - "radial"
50 | - "tree"
51 | - "map"
52 | - "orthogonal_edge_router"
53 | - "organic_edge_router"
54 | """
55 |
56 | self._widget = GraphWidget()
57 | self._driver = driver
58 | self._session = driver.session()
59 | self._license = license
60 | self._overview = overview_enabled
61 | self._layout = widget_layout
62 | self._context_start_with = context_start_with
63 | self.set_autocomplete_relationships(autocomplete_relationships)
64 | self._graph_layout = layout
65 |
66 | self._node_configurations = {}
67 | self._edge_configurations = {}
68 | self._parent_configurations = set()
69 |
70 | def set_driver(self, driver: Any) -> None:
71 | """
72 | The Neo4j driver that is used to resolve the Cypher queries. A new session is created when set.
73 |
74 | Args:
75 | driver (neo4j._sync.driver.Neo4jDriver): The Neo4j driver to resolve the Cypher queries.
76 |
77 | Returns:
78 | None
79 | """
80 | self._driver = driver
81 | self._session = driver.session()
82 |
83 | def get_driver(self) -> Any:
84 | """
85 | Gets the configured Neo4j driver.
86 |
87 | Returns:
88 | neo4j._sync.driver.Neo4jDriver
89 | """
90 | return self._driver
91 |
92 | def set_autocomplete_relationships(self, autocomplete_relationships: Union[bool, str, list[str]]) -> None:
93 | """
94 | Sets the flag to enable or disable autocomplete for relationships.
95 | When autocomplete is enabled, relationships are automatically completed in the graph,
96 | similar to the behavior in Neo4j Browser.
97 | This can be set to True/False to enable or disable for all relationships,
98 | or a single relationship type or a list of relationship types to enable for specific relationships.
99 |
100 | Args:
101 | autocomplete_relationships (Union[bool, str, list[str]]): Enable autocompletion for relationships
102 | in general, or for a single type, or for multiple types.
103 |
104 | Returns:
105 | None
106 | """
107 | if not isinstance(autocomplete_relationships, (bool, str, list)):
108 | raise ValueError("autocomplete_relationships must be a bool, a string, or a list of strings")
109 | if isinstance(autocomplete_relationships, str):
110 | self._autocomplete_relationships = [autocomplete_relationships]
111 | else:
112 | self._autocomplete_relationships = autocomplete_relationships
113 |
114 | def _is_autocomplete_enabled(self) -> bool:
115 | if isinstance(self._autocomplete_relationships, bool):
116 | return self._autocomplete_relationships
117 | return len(self._autocomplete_relationships) > 0
118 |
119 | def _get_relationship_types_expression(self) -> str:
120 | if isinstance(self._autocomplete_relationships, list) and len(self._autocomplete_relationships) > 0:
121 | return "AND type(rel) IN $relationship_types"
122 | return ""
123 |
124 | def show_cypher(self, cypher: str, layout: Optional[str] = None, **kwargs: Dict[str, Any]) -> None:
125 | """
126 | Displays the given Cypher query as interactive graph.
127 |
128 | Args:
129 | cypher (str): The Cypher query whose result should be visualized as graph.
130 | layout (Optional[str]): The graph layout for this request. Overwrites the general default `layout` that was
131 | specified when initializing the class. Supported values are:
132 | - "circular"
133 | - "circular_straight_line"
134 | - "hierarchic"
135 | - "organic"
136 | - "interactive_organic"
137 | - "orthogonal"
138 | - "radial"
139 | - "tree"
140 | - "map"
141 | - "orthogonal_edge_router"
142 | - "organic_edge_router"
143 | **kwargs (Dict[str, Any]): Additional parameters that should be passed to the Cypher query.
144 |
145 | Returns:
146 | None
147 |
148 | Raises:
149 | Exception: If no driver was specified.
150 | """
151 | if self._driver is not None:
152 | if self._is_autocomplete_enabled():
153 | nodes = self._session.run(cypher, **kwargs).graph().nodes
154 | node_ids = [node.element_id for node in nodes]
155 | reltypes_expr = self._get_relationship_types_expression()
156 | cypher = f"""
157 | MATCH (n) WHERE elementId(n) IN $node_ids
158 | RETURN n as start, NULL as rel, NULL as end
159 | UNION ALL
160 | MATCH (n)-[rel]-(m)
161 | WHERE elementId(n) IN $node_ids
162 | AND elementId(m) IN $node_ids
163 | {reltypes_expr}
164 | RETURN n as start, rel, m as end
165 | """
166 | kwargs = {"node_ids": node_ids}
167 | if reltypes_expr:
168 | kwargs["relationship_types"] = self._autocomplete_relationships
169 | widget = GraphWidget(overview_enabled=self._overview, context_start_with=self._context_start_with,
170 | widget_layout=self._layout, license=self._license,
171 | graph=self._session.run(cypher, **kwargs).graph())
172 | self.__create_group_nodes(self._node_configurations, widget)
173 | self.__apply_node_mappings(widget)
174 | self.__apply_edge_mappings(widget)
175 | self.__apply_heat_mapping({**self._node_configurations, **self._edge_configurations}, widget)
176 | self.__apply_parent_mapping(widget)
177 | if layout is None:
178 | widget.set_graph_layout(self._graph_layout)
179 | else:
180 | widget.set_graph_layout(layout)
181 |
182 | widget.node_cell_mapping = self.node_cell_mapping
183 |
184 | self._widget = widget
185 | widget.show()
186 | else:
187 | raise Exception("no driver specified")
188 |
189 | @staticmethod
190 | def __get_neo4j_item_text(element: Dict) -> Union[str, None]:
191 | lowercase_element_props = {key.lower(): value for key, value in element.get('properties', {}).items()}
192 | for key in NEO4J_LABEL_KEYS:
193 | if key in lowercase_element_props:
194 | return str(lowercase_element_props[key])
195 | return None
196 |
197 | @staticmethod
198 | def __configuration_mapper_factory(binding_key: str, configurations: Dict[str, Dict[str, str]],
199 | default_mapping: Callable) -> Callable[[int, Dict], Union[Dict, str]]:
200 | """
201 | This is called once for each POSSIBLE_NODE_BINDINGS or POSSIBLE_EDGE_BINDINGS (as `binding_key` argument) and
202 | sets the returned mapping function for the `binding_key` on the core yFiles Graphs for Jupyter widget.
203 |
204 | Args:
205 | binding_key (str): One of POSSIBLE_NODE_BINDINGS or POSSIBLE_EDGE_BINDINGS
206 | configurations (Dict): All configured node or relationship configurations by the user, keyed by the node label or relationship type.
207 | For example, a dictionary built like:
208 | {
209 | "Movie": { "color": "red", ... },
210 | "Person": { "color": "blue", ... },
211 | "*": { "color": "gray", ... }
212 | }
213 | default_mapping (MethodType): A reference to the default binding of the yFiles Graphs for Jupyter core widget that should be used when the binding_key is not specified otherwise.
214 |
215 | Returns:
216 | FunctionType: A mapping function that can used in the yFiles Graphs for Jupyter core widget.
217 | """
218 |
219 | def mapping(index: int, item: Dict) -> Union[Dict, str]:
220 | label = item["properties"]["label"] # yjg stores the neo4j node/relationship type in properties["label"]
221 | if ((label in configurations or '*' in configurations)
222 | and binding_key in configurations.get(label, configurations.get('*'))):
223 | type_configuration = configurations.get(label, configurations.get('*'))
224 | if binding_key == 'parent_configuration':
225 | # the binding may be a lambda that must be resolved first
226 | binding = type_configuration.get(binding_key)
227 | if callable(binding):
228 | binding = binding(item)
229 | # parent_configuration binding may either resolve to a dict or a string
230 | if isinstance(binding, dict):
231 | group_label = binding.get('text', '')
232 | else:
233 | group_label = binding
234 | result = 'GroupNode' + group_label
235 | # mapping
236 | elif callable(type_configuration[binding_key]):
237 | result = type_configuration[binding_key](item)
238 | # property name
239 | elif (not isinstance(type_configuration[binding_key], dict) and
240 | type_configuration[binding_key] in item["properties"]):
241 | result = item["properties"][type_configuration.get(binding_key)]
242 | # constant value
243 | else:
244 | result = type_configuration.get(binding_key)
245 |
246 | return result
247 |
248 | if binding_key == "label":
249 | return Neo4jGraphWidget.__get_neo4j_item_text(item)
250 | else:
251 | # call default mapping
252 | # some default mappings do not support "index" as first parameter
253 | parameters = inspect.signature(default_mapping).parameters
254 | if len(parameters) > 1 and parameters[list(parameters)[0]].annotation == int:
255 | return default_mapping(index, item)
256 | else:
257 | return default_mapping(item)
258 |
259 | return mapping
260 |
261 | def __apply_heat_mapping(self, configuration, widget: GraphWidget) -> None:
262 | setattr(widget, "_heat_mapping",
263 | Neo4jGraphWidget.__configuration_mapper_factory('heat', configuration,
264 | getattr(widget, 'default_heat_mapping')))
265 |
266 | def __create_group_nodes(self, configurations, widget: GraphWidget) -> None:
267 | group_node_properties = set()
268 | group_node_values = set()
269 | key = 'parent_configuration'
270 | for node in widget.nodes:
271 | label = node['properties']['label']
272 | if label in configurations and key in configurations.get(label):
273 | group_node = configurations.get(label).get(key)
274 |
275 | if callable(group_node):
276 | group_node = group_node(node)
277 |
278 | if isinstance(group_node, str):
279 | # string or property value
280 | if group_node in node["properties"]:
281 | group_node_properties.add(str(node["properties"][group_node]))
282 | else:
283 | group_node_values.add(group_node)
284 | else:
285 | # dictionary with values
286 | text = group_node.get('text', '')
287 | group_node_values.add(text)
288 | configuration = {k: v for k, v in group_node.items() if k != 'text'}
289 | self.add_node_configuration(text, **configuration)
290 |
291 | for group_label in group_node_properties.union(group_node_values):
292 | node = {'id': 'GroupNode' + group_label, 'properties': {'label': group_label}}
293 | widget.nodes = [*widget.nodes, node]
294 |
295 | def __apply_parent_mapping(self, widget: GraphWidget) -> None:
296 | node_to_parent = {}
297 | edge_ids_to_remove = set()
298 | for edge in widget.edges[:]:
299 | rel_type = edge["properties"]["label"]
300 | for (parent_type, is_reversed) in self._parent_configurations:
301 | if rel_type == parent_type:
302 | start = edge['start'] # child node id
303 | end = edge['end'] # parent node id
304 | if is_reversed:
305 | node_to_parent[end] = start
306 | else:
307 | node_to_parent[start] = end
308 | edge_ids_to_remove.add(edge['id'])
309 | break
310 |
311 | # use list comprehension to filter out the edges to automatically trigger model sync with the frontend
312 | widget.edges = [edge for edge in widget.edges if edge['id'] not in edge_ids_to_remove]
313 | current_parent_mapping = getattr(widget, '_node_parent_mapping')
314 | setattr(widget, "_node_parent_mapping",
315 | lambda index, node: node_to_parent.get(node['id'], current_parent_mapping(index, node)))
316 |
317 | def __apply_node_mappings(self, widget: GraphWidget) -> None:
318 | for key in POSSIBLE_NODE_BINDINGS:
319 | default_mapping = getattr(widget, f"default_node_{key}_mapping")
320 | setattr(widget, f"_node_{key}_mapping",
321 | Neo4jGraphWidget.__configuration_mapper_factory(key, self._node_configurations, default_mapping))
322 | # manually set parent configuration
323 | setattr(widget, f"_node_parent_mapping",
324 | Neo4jGraphWidget.__configuration_mapper_factory('parent_configuration',
325 | self._node_configurations, lambda node: None))
326 |
327 | def __apply_edge_mappings(self, widget: GraphWidget) -> None:
328 | for key in POSSIBLE_EDGE_BINDINGS:
329 | default_mapping = getattr(widget, f"default_edge_{key}_mapping")
330 | setattr(widget, f"_edge_{key}_mapping",
331 | Neo4jGraphWidget.__configuration_mapper_factory(key, self._edge_configurations, default_mapping))
332 |
333 | def add_node_configuration(self, label: Union[str, list[str]], **kwargs: Dict[str, Any]) -> None:
334 | """
335 | Adds a configuration object for the given node `label`(s).
336 |
337 | Args:
338 | label (Union[str, list[str]]): The node label(s) for which this configuration should be used. Supports `*` to address all labels.
339 | **kwargs (Dict[str, Any]): Visualization configuration for the given node label. The following arguments are supported:
340 |
341 | - `text` (Union[str, Callable]): The text to be displayed on the node. By default, the node's label is used.
342 | - `color` (Union[str, Callable]): A convenience color binding for the node (see also styles kwarg).
343 | - `size` (Union[str, Callable]): The size of the node.
344 | - `styles` (Union[Dict, Callable]): A dictionary that may contain the following attributes color, shape (one of 'ellipse', ' hexagon', 'hexagon2', 'octagon', 'pill', 'rectangle', 'round-rectangle' or 'triangle'), image.
345 | - `property` (Union[Dict, Callable]): Allows to specify additional properties on the node, which may be bound by other bindings.
346 | - `type` (Union[Dict, Callable]): Defines a specific "type" for the node which affects the automatic positioning of nodes (same "type"s are preferred to be placed next to each other).
347 | - `parent_configuration` (Union[str, Callable]): Configure grouping for this node label.
348 |
349 | Returns:
350 | None
351 | """
352 | # this wrapper uses "text" as text binding in the graph
353 | # in contrast to "label" which is used in yfiles-jupyter-graphs
354 | text_binding = kwargs.pop("text", None)
355 | config = kwargs
356 | if text_binding is not None:
357 | config["label"] = text_binding
358 |
359 | cloned_config = {key: value for key, value in config.items()}
360 | if isinstance(label, list):
361 | for l in label:
362 | self._node_configurations[l] = cloned_config
363 | else:
364 | self._node_configurations[label] = cloned_config
365 |
366 | # noinspection PyShadowingBuiltins
367 | def add_relationship_configuration(self, type: Union[str, list[str]], **kwargs: Dict[str, Any]) -> None:
368 | """
369 | Adds a configuration object for the given relationship `type`(s).
370 |
371 | Args:
372 | type (Union[str, list[str]]): The relationship type(s) for which this configuration should be used. Supports `*` to address all labels.
373 | **kwargs (Dict): Visualization configuration for the given node label. The following arguments are supported:
374 |
375 | - `text` (Union[str, Callable]): The text to be displayed on the node. By default, the relationship's type is used.
376 | - `color` (Union[str, Callable]): The relationship's color.
377 | - `thickness_factor` (Union[str, Callable]): The relationship's stroke thickness factor. By default, 1.
378 | - `property` (Union[Dict, Callable]): Allows to specify additional properties on the relationship, which may be bound by other bindings.
379 |
380 | Returns:
381 | None
382 | """
383 | # this wrapper uses "text" as text binding in the graph
384 | # in contrast to "label" which is used in yfiles-jupyter-graphs
385 | text_binding = kwargs.pop("text", None)
386 | config = kwargs
387 | if text_binding is not None:
388 | config["label"] = text_binding
389 |
390 | cloned_config = {key: value for key, value in config.items()}
391 | if isinstance(type, list):
392 | for t in type:
393 | self._edge_configurations[t] = cloned_config
394 | else:
395 | self._edge_configurations[type] = cloned_config
396 |
397 | # noinspection PyShadowingBuiltins
398 | def add_parent_relationship_configuration(self, type: Union[str, list[str]], reverse: Optional[bool] = False) -> None:
399 | """
400 | Configure specific relationship types to visualize as nested hierarchies. This removes these relationships from
401 | the graph and instead groups the related nodes (source and target) as parent-child.
402 |
403 | Args:
404 | type (Union[str, list[str]]): The relationship type(s) that should be visualized as node grouping hierarchy instead of the actual relationship.
405 | reverse (bool): Which node should be considered as parent. By default, the target node is considered as parent which can be reverted with this argument.
406 |
407 | Returns:
408 | None
409 | """
410 | if isinstance(type, list):
411 | for t in type:
412 | self._parent_configurations.add((t, reverse))
413 | else:
414 | self._parent_configurations.add((type, reverse))
415 |
416 | # noinspection PyShadowingBuiltins
417 | def del_node_configuration(self, label: Union[str, list[str]]) -> None:
418 | """
419 | Deletes the configuration object for the given node `label`(s).
420 |
421 | Args:
422 | label (Union[str, list[str]]): The node label(s) for which the configuration should be deleted. Supports `*` to address all labels.
423 |
424 | Returns:
425 | None
426 | """
427 | if isinstance(label, list):
428 | for l in label:
429 | Neo4jGraphWidget.__safe_delete_configuration(l, self._node_configurations)
430 | else:
431 | Neo4jGraphWidget.__safe_delete_configuration(label, self._node_configurations)
432 |
433 | # noinspection PyShadowingBuiltins
434 | def del_relationship_configuration(self, type: Union[str, list[str]]) -> None:
435 | """
436 | Deletes the configuration object for the given relationship `type`(s).
437 |
438 | Args:
439 | type (Union[str, list[str]]): The relationship type(s) for which the configuration should be deleted. Supports `*` to address all types.
440 |
441 | Returns:
442 | None
443 | """
444 | if isinstance(type, list):
445 | for t in type:
446 | Neo4jGraphWidget.__safe_delete_configuration(t, self._edge_configurations)
447 | else:
448 | Neo4jGraphWidget.__safe_delete_configuration(type, self._edge_configurations)
449 |
450 | @staticmethod
451 | def __safe_delete_configuration(key: str, configurations: Dict[str, Any]) -> None:
452 | if key == "*":
453 | configurations.clear()
454 | if key in configurations:
455 | del configurations[key]
456 |
457 | # noinspection PyShadowingBuiltins
458 | def del_parent_relationship_configuration(self, type: Union[str, list[str]]) -> None:
459 | """
460 | Deletes the relationship configuration for the given `type`(s).
461 |
462 | Args:
463 | type (Union[str, list[str]]): The relationship type(s) for which the configuration should be deleted.
464 |
465 | Returns:
466 | None
467 | """
468 | if isinstance(type, list):
469 | self._parent_configurations = {
470 | rel_type for rel_type in self._parent_configurations if rel_type[0] not in type
471 | }
472 | else:
473 | self._parent_configurations = {
474 | rel_type for rel_type in self._parent_configurations if rel_type[0] != type
475 | }
476 |
477 | def get_selected_node_ids(self, widget: Optional[Type["Neo4jGraphWidget"]] = None) -> List[str]:
478 | """
479 | Returns the list of node ids that are currently selected in the most recently shown widget, or in the given `widget`.
480 |
481 | Args:
482 | widget (Optional[Type["Neo4jGraphWidget"]]): The widget from which the selected ids should be retrieved.
483 | If not specified, the most recently shown widget will be used.
484 |
485 | Returns:
486 | List[str]: The list of node ids currently selected in the widget.
487 | """
488 | graph = widget if widget is not None else self._widget
489 | nodes, edges = graph.get_selection()
490 | return list(map(lambda node: node['id'], nodes))
491 |
492 | def get_selected_relationship_ids(self, widget: Optional[Type["Neo4jGraphWidget"]] = None) -> List[str]:
493 | """
494 | Returns the list of relationship ids that are currently selected in the most recently shown widget, or in the given `widget`.
495 |
496 | Args:
497 | widget (Optional[Type["Neo4jGraphWidget"]]): The widget from which the selected ids should be retrieved.
498 | If not specified, the most recently shown widget will be used.
499 |
500 | Returns:
501 | List[str]: The list of relationship ids currently selected in the widget.
502 | """
503 | graph = widget if widget is not None else self._widget
504 | nodes, edges = graph.get_selection()
505 | return list(map(lambda edge: edge['id'], edges))
506 |
507 | def get_node_cell_mapping(self) -> Union[str, Callable, None]:
508 | """
509 | Returns the currently specified node cell mapping.
510 |
511 | Returns:
512 | Union[str, Callable, None]: The currently specified node cell mapping.
513 | """
514 | return self._node_cell_mapping if hasattr(self, '_node_cell_mapping') else None
515 |
516 | def set_node_cell_mapping(self, node_cell_mapping: Union[str, Callable]) -> None:
517 | """
518 | Specify a node to cell mapping to fine-tune automatic layout algorithms. The mapping should resolve to a row,
519 | column tuple that is used as a cell into which the node is placed.
520 |
521 | Args:
522 | node_cell_mapping (Union[str, Callable]): Specifies a node to cell mapping. Must resolve to a row, column tuple or None.
523 |
524 | Returns:
525 | None
526 | """
527 | # noinspection PyAttributeOutsideInit
528 | self._node_cell_mapping = node_cell_mapping
529 |
530 | def del_node_cell_mapping(self) -> None:
531 | """
532 | Deletes the node cell mapping.
533 |
534 | Returns:
535 | None
536 | """
537 | if hasattr(self, '_node_cell_mapping'):
538 | delattr(self, '_node_cell_mapping')
539 |
540 | node_cell_mapping = property(get_node_cell_mapping, set_node_cell_mapping, del_node_cell_mapping)
--------------------------------------------------------------------------------
/src/yfiles_jupyter_graphs_for_neo4j/__init__.py:
--------------------------------------------------------------------------------
1 | # see Yfiles_Neo4j_Graphs.py
2 |
3 | from .Yfiles_Neo4j_Graphs import Neo4jGraphWidget
4 |
--------------------------------------------------------------------------------