├── .gitignore ├── test ├── fixtures │ ├── emphasis.adoc │ ├── monospaced.adoc │ ├── thematic-break.adoc │ ├── xrefs.adoc │ ├── inline-style.adoc │ ├── images │ │ ├── MovieNodes.png │ │ ├── PersonNodes.png │ │ ├── UseFTIndex.png │ │ ├── UseFTIndex2.png │ │ ├── run-button.png │ │ ├── ReturnAllNodes.png │ │ ├── call_db.schema.png │ │ ├── database-icon.png │ │ ├── InitialDatabase.png │ │ └── overlap.svg │ ├── example-block.adoc │ ├── nested-blocks.adoc │ ├── admonition-block.adoc │ ├── unsupported-syntax.adoc │ ├── colist.adoc │ ├── literal.adoc │ ├── passthrough.adoc │ ├── description-list-without-text.adoc │ ├── admonitions.adoc │ ├── complex-dlist.adoc │ ├── basic-dlist.adoc │ ├── multiple-codeblocks.adoc │ ├── table.adoc │ ├── quote.adoc │ ├── list-continuation.adoc │ ├── retain-order.adoc │ ├── lorenz-differential-equations.adoc │ ├── references │ │ ├── lorenz.ipynb │ │ └── hello-world.ipynb │ ├── intro-neo4j-guides-01.adoc │ ├── hello-world.adoc │ └── scripts │ │ └── initialDatabase.cypher ├── register.spec.js └── converter.spec.js ├── jupyter-lorenz-notebook.png ├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── package.json ├── README.md ├── examples ├── hello-world.adoc └── hello-world.ipynb └── src └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | -------------------------------------------------------------------------------- /test/fixtures/emphasis.adoc: -------------------------------------------------------------------------------- 1 | Happy werewolves are _really_ slobbery. -------------------------------------------------------------------------------- /test/fixtures/monospaced.adoc: -------------------------------------------------------------------------------- 1 | = Monospaced 2 | 3 | Install `icypher` then connect to your database! -------------------------------------------------------------------------------- /test/fixtures/thematic-break.adoc: -------------------------------------------------------------------------------- 1 | = Title 2 | :author: Guillaume Grosseti 3 | 4 | ''' 5 | 6 | == Introduction -------------------------------------------------------------------------------- /test/fixtures/xrefs.adoc: -------------------------------------------------------------------------------- 1 | xref:refcard.pdf[Refcard] 2 | 3 | xref:sect-a[] 4 | 5 | [[sect-a]] 6 | == Section A -------------------------------------------------------------------------------- /test/fixtures/inline-style.adoc: -------------------------------------------------------------------------------- 1 | For all query tuning measurements, you must [underline]#always# run the query twice. -------------------------------------------------------------------------------- /jupyter-lorenz-notebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-documentation/asciidoctor-jupyter/HEAD/jupyter-lorenz-notebook.png -------------------------------------------------------------------------------- /test/fixtures/images/MovieNodes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-documentation/asciidoctor-jupyter/HEAD/test/fixtures/images/MovieNodes.png -------------------------------------------------------------------------------- /test/fixtures/images/PersonNodes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-documentation/asciidoctor-jupyter/HEAD/test/fixtures/images/PersonNodes.png -------------------------------------------------------------------------------- /test/fixtures/images/UseFTIndex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-documentation/asciidoctor-jupyter/HEAD/test/fixtures/images/UseFTIndex.png -------------------------------------------------------------------------------- /test/fixtures/images/UseFTIndex2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-documentation/asciidoctor-jupyter/HEAD/test/fixtures/images/UseFTIndex2.png -------------------------------------------------------------------------------- /test/fixtures/images/run-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-documentation/asciidoctor-jupyter/HEAD/test/fixtures/images/run-button.png -------------------------------------------------------------------------------- /test/fixtures/images/ReturnAllNodes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-documentation/asciidoctor-jupyter/HEAD/test/fixtures/images/ReturnAllNodes.png -------------------------------------------------------------------------------- /test/fixtures/images/call_db.schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-documentation/asciidoctor-jupyter/HEAD/test/fixtures/images/call_db.schema.png -------------------------------------------------------------------------------- /test/fixtures/images/database-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-documentation/asciidoctor-jupyter/HEAD/test/fixtures/images/database-icon.png -------------------------------------------------------------------------------- /test/fixtures/images/InitialDatabase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-documentation/asciidoctor-jupyter/HEAD/test/fixtures/images/InitialDatabase.png -------------------------------------------------------------------------------- /test/fixtures/example-block.adoc: -------------------------------------------------------------------------------- 1 | .Onomatopoeia 2 | ==== 3 | The book hit the floor with a *thud*. 4 | 5 | He could hear doves *cooing* in the pine trees`' branches. 6 | ==== -------------------------------------------------------------------------------- /test/fixtures/nested-blocks.adoc: -------------------------------------------------------------------------------- 1 | = Nested Blocks 2 | 3 | .Title 4 | [.style] 5 | **** 6 | Example... 7 | [stem] 8 | ++++ 9 | A_1=\left(\begin{array}{lll} 10 | ++++ 11 | **** 12 | -------------------------------------------------------------------------------- /test/fixtures/admonition-block.adoc: -------------------------------------------------------------------------------- 1 | [NOTE] 2 | ==== 3 | An admonition block may contain complex content. 4 | 5 | .A list 6 | - one 7 | - two 8 | - three 9 | 10 | Another paragraph. 11 | ==== -------------------------------------------------------------------------------- /test/fixtures/unsupported-syntax.adoc: -------------------------------------------------------------------------------- 1 | "`Well the H~2~O formula written on their whiteboard could be part 2 | of a shopping list, but I don't think the local bodega sells 3 | E=mc^2^,`" Lazarus replied. -------------------------------------------------------------------------------- /test/fixtures/colist.adoc: -------------------------------------------------------------------------------- 1 | ---- 2 | git clone https://github.com/feelpp/book.feelpp.org.git # <1> 3 | git clone https://github.com/feelpp/toolbox.git # <2> 4 | ---- 5 | <1> clone the source for the website 6 | <2> clone the source for toolbox cases -------------------------------------------------------------------------------- /test/fixtures/literal.adoc: -------------------------------------------------------------------------------- 1 | .... 2 | { 3 | name: , 4 | born: , 5 | roles: [roles that this actor has played in all of his/her movies] 6 | movies: [titles of the movies this actor has acted in] 7 | } 8 | .... 9 | -------------------------------------------------------------------------------- /test/fixtures/passthrough.adoc: -------------------------------------------------------------------------------- 1 | = Title 2 | 3 | == Section 4 | 5 | ++++ 6 | 7 | ++++ 8 | image::https://upload.wikimedia.org/wikipedia/commons/5/59/Firefox_Project_Logo%2C_2019.svg[width=100] 9 | ++++ 10 | 11 | ++++ -------------------------------------------------------------------------------- /test/fixtures/description-list-without-text.adoc: -------------------------------------------------------------------------------- 1 | = Basics 2 | 3 | == Introduction 4 | 5 | Basic Matrix Creation:: 6 | [%dynamic,python] 7 | ---- 8 | import numpy as np 9 | matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) 10 | print(f"matrix={matrix}") 11 | ---- -------------------------------------------------------------------------------- /test/fixtures/admonitions.adoc: -------------------------------------------------------------------------------- 1 | NOTE: An admonition draws attention to auxiliary information. 2 | 3 | Here are the other built-in admonition types: 4 | 5 | TIP: Pro tip... 6 | 7 | IMPORTANT: Don't forget... 8 | 9 | WARNING: Watch out for... 10 | 11 | CAUTION: Ensure that... -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [test/fixtures/**] 13 | insert_final_newline = false 14 | -------------------------------------------------------------------------------- /test/fixtures/complex-dlist.adoc: -------------------------------------------------------------------------------- 1 | CPU:: 2 | The brain of the computer. 3 | It performs operations on an external data source, usually memory or some other data stream. 4 | + 5 | Traditional processors are typically based on silicon. 6 | + 7 | [source,js] 8 | ---- 9 | console.log('hello') 10 | ---- 11 | Hard drive:: Permanent storage for operating system and/or user files. -------------------------------------------------------------------------------- /test/fixtures/basic-dlist.adoc: -------------------------------------------------------------------------------- 1 | CPU:: The brain of the computer. 2 | Hard drive:: Permanent storage for operating system and/or user files. 3 | RAM:: Temporarily stores information the CPU uses during operation. 4 | Keyboard:: Used to enter text or control items on the screen. 5 | Mouse:: Used to point to and select items on your computer screen. 6 | Monitor:: Displays information in visual form using text and graphics. -------------------------------------------------------------------------------- /test/fixtures/multiple-codeblocks.adoc: -------------------------------------------------------------------------------- 1 | .myfunc.hpp+ 2 | [source,cpp] 3 | ---- 4 | #ifndef __MYFUNC_HPP__ #define __MYFUNC_HPP__ 5 | 6 | void mymsg(); 7 | 8 | #endif 9 | ---- 10 | 11 | .myfunc.cpp 12 | [source,cpp] 13 | ---- 14 | #include 15 | 16 | void mymsg() 17 | { 18 | std::cout << "Hello, world"; 19 | } 20 | ---- 21 | 22 | .mymain.cpp 23 | [source,cpp] 24 | ---- 25 | #include "myfunc.hpp" 26 | 27 | int main() 28 | { 29 | mymsg(); 30 | return 0; 31 | } 32 | ---- -------------------------------------------------------------------------------- /test/fixtures/table.adoc: -------------------------------------------------------------------------------- 1 | == Exercise 5.2: Determine how large the datasets are that you will be loading. (Instructions) 2 | 3 | The datasets containing the normalized data are at these locations: 4 | 5 | [cols='1,4',opts=header] 6 | |=== 7 | |Dataset 8 | |URL 9 | 10 | |Movies 11 | |https://data.neo4j.com/advanced-cypher/movies1.csv 12 | 13 | |People 14 | |https://data.neo4j.com/advanced-cypher/people.csv 15 | 16 | |Roles 17 | |https://data.neo4j.com/advanced-cypher/roles.csv 18 | 19 | |Directors 20 | |https://data.neo4j.com/advanced-cypher/directors.csv 21 | |=== 22 | 23 | *Write Cypher code to return the number of lines in each of these CSV files.* -------------------------------------------------------------------------------- /test/fixtures/quote.adoc: -------------------------------------------------------------------------------- 1 | == Category Hierarchy 2 | 3 | This structure between categories is already hiding in the data, we just need to extract it. 4 | The Overlap Similarity algorithm is the perfect choice for this type of problem. 5 | 6 | [quote] 7 | ____ 8 | The overlap coefficient, or Szymkiewicz–Simpson coefficient, is a similarity measure that measures the overlap between two sets. 9 | It is defined as the size of the intersection divided by the smaller of the size of the two sets. 10 | ____ 11 | 12 | It is computed using the following formula: 13 | 14 | image::overlap.svg[] 15 | 16 | If set X is a subset of Y or vice versa then the overlap coefficient is equal to one. -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build JavaScript 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | build: 13 | strategy: 14 | matrix: 15 | os: [ubuntu-latest] 16 | node-version: ['16', '18'] 17 | include: 18 | - os: macos-latest 19 | node-version: 18 20 | - os: windows-latest 21 | node-version: 18 22 | runs-on: ${{ matrix.os }} 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Set up Node ${{ matrix.node-version }} 26 | uses: actions/setup-node@v3 27 | with: 28 | node-version: ${{ matrix.node-version }} 29 | - name: Install dependencies 30 | run: | 31 | npm ci 32 | - name: Lint and test 33 | run: | 34 | npm run lint 35 | npm t 36 | -------------------------------------------------------------------------------- /test/fixtures/list-continuation.adoc: -------------------------------------------------------------------------------- 1 | = Exercise 14 2 | 3 | == Exercise 14.6: Perform a query that uses the full-text schema index (Solution) 4 | 5 | . *Write and execute a query to find all movies with taglines that contain the strings "real" or "world".* 6 | + 7 | [source,Cypher] 8 | ---- 9 | CALL db.index.fulltext.queryNodes('MovieTaglineFTIndex', 'real OR world') YIELD node 10 | RETURN node.title, node.tagline 11 | ---- 12 | + 13 | The result returned should be: 14 | + 15 | [.thumb] 16 | image::UseFTIndex.png[UseFTIndex,width=600] 17 | + 18 | . *Write and execute a query to find all movies with taglines that contain the strings "real" and "world".* 19 | + 20 | [source,Cypher] 21 | ---- 22 | CALL db.index.fulltext.queryNodes('MovieTaglineFTIndex', 'real AND world') YIELD node 23 | RETURN node.title, node.tagline 24 | ---- 25 | + 26 | The result returned should be: 27 | + 28 | [.thumb] 29 | image::UseFTIndex2.png[UseFTIndex2,width=800] 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asciidoctor-jupyter", 3 | "version": "0.7.0", 4 | "description": "A Jupyter converter for Asciidoctor.js. Write your Notebook in AsciiDoc!", 5 | "engines": { 6 | "node": ">=12" 7 | }, 8 | "main": "src/index.js", 9 | "files": [ 10 | "src/*" 11 | ], 12 | "scripts": { 13 | "test": "mocha test/**.spec.js", 14 | "lint": "standard src/**.js test/**.js" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/neo4j-documentation/asciidoctor-jupyter.git" 19 | }, 20 | "keywords": [], 21 | "author": "", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/neo4j-documentation/asciidoctor-jupyter/issues" 25 | }, 26 | "homepage": "https://github.com/neo4j-documentation/asciidoctor-jupyter#readme", 27 | "devDependencies": { 28 | "@asciidoctor/core": "~2.2", 29 | "chai": "~4.3", 30 | "dirty-chai": "~2.0", 31 | "mocha": "~10.0", 32 | "standard": "~17.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/fixtures/retain-order.adoc: -------------------------------------------------------------------------------- 1 | ++++ 2 | 7 | ++++ 8 | 9 | = Title 10 | 11 | == Section 1 12 | 13 | 1. 14 | 15 | == Section 2 16 | 17 | 2. 18 | 19 | == Section 3 20 | 21 | 3. 22 | 23 | === Section 3.1 24 | 25 | Paragraph. 26 | 27 | * Item 28 | ** Subitem 29 | ** Subitem 30 | * Item 31 | 32 | Another paragraph. 33 | 34 | [source, python] 35 | ---- 36 | First query 37 | ---- 38 | // graph 39 | 40 | Third paragraph. 41 | 42 | [source, py] 43 | ---- 44 | Second Query 45 | ---- 46 | // graph 47 | ++++ 48 |
49 | ++++ 50 | image::https://upload.wikimedia.org/wikipedia/commons/5/59/Firefox_Project_Logo%2C_2019.svg[width=100] 51 | ++++ 52 |
53 | ++++ 54 | 55 | Fourth paragraph. 56 | 57 | === Section 3.2 58 | 59 | [source, python] 60 | ---- 61 | Third Query 62 | ---- 63 | // graph 64 | 65 | Fifth paragraph. 66 | 67 | === Section 3.3 68 | 69 | Sixth paragraph. 70 | 71 | [source, python] 72 | ---- 73 | Fourth Query 74 | ---- 75 | // graph 76 | 77 | Paragraph. -------------------------------------------------------------------------------- /test/fixtures/lorenz-differential-equations.adoc: -------------------------------------------------------------------------------- 1 | = The Lorenz Differential Equations 2 | :jupyter-language-name: python 3 | :jupyter-language-version: 3.7.8 4 | :url-ipywidgets: https://ipywidgets.readthedocs.io/en/stable/ 5 | 6 | Before we start, we import some preliminary libraries. 7 | We will also import (below) the accompanying `lorenz.py` file, 8 | which contains the actual solver and plotting routine. 9 | 10 | [source,python] 11 | ---- 12 | %matplotlib inline 13 | from ipywidgets import interactive, fixed 14 | ---- 15 | 16 | We explore the Lorenz system of differential equations: 17 | 18 | [latexmath] 19 | ++++ 20 | \begin{aligned} 21 | \dot{x} & = \sigma(y-x) \\ 22 | \dot{y} & = \rho x - y - xz \\ 23 | \dot{z} & = -\beta z + xy 24 | \end{aligned} 25 | ++++ 26 | 27 | Let's change (stem:[\sigma], stem:[\beta], stem:[\rho]) with {url-ipywidgets}[ipywidgets] and examine the trajectories. 28 | 29 | [source,python] 30 | ---- 31 | from lorenz import solve_lorenz 32 | w=interactive(solve_lorenz,sigma=(0.0,60.0),rho=(0.0,50.0)) 33 | w 34 | ---- 35 | 36 | For the default set of parameters, we see the trajectories swirling around two points, called attractors. 37 | 38 | The object returned by `interactive` is a `Widget` object and it has attributes that contain the current result and arguments: 39 | 40 | [source,python] 41 | ---- 42 | t, x_t = w.result 43 | ---- 44 | 45 | [source,python] 46 | ---- 47 | w.kwargs 48 | ---- -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Asciidoctor Jupyter Converter 2 | 3 | A Jupyter converter for Asciidoctor.js. Write your Notebook in AsciiDoc! 4 | 5 | ![Build JavaScript](https://github.com/neo4j-documentation/asciidoctor-jupyter/workflows/Build%20JavaScript/badge.svg) 6 | [![npm version](http://img.shields.io/npm/v/asciidoctor-jupyter.svg)](https://www.npmjs.com/package/asciidoctor-jupyter) 7 | 8 | ## Install 9 | 10 | $ npm i asciidoctor-jupyter 11 | 12 | ## Usage 13 | 14 | In order to use this converter, you will need to install [Asciidoctor.js](https://docs.asciidoctor.org/asciidoctor.js/latest/setup/install/). 15 | Verify that the `asciidoctor` command is now available on your `PATH` by running: 16 | 17 | $ asciidoctor -v 18 | 19 | ### From the CLI (Command Line Interface) 20 | 21 | To convert an AsciiDoc file to a Jupyter Notebook, open a terminal and type: 22 | 23 | $ asciidoctor -r asciidoctor-jupyter -b jupyter notebook.adoc 24 | 25 | The above command will generate a file named `notebook.ipynb`. 26 | 27 | ### From the API 28 | 29 | ```js 30 | const asciidoctor = require('@asciidoctor/core')() 31 | const JupyterConverter = require('asciidoctor-jupyter') 32 | 33 | // register the converter 34 | asciidoctor.ConverterFactory.register(JupyterConverter, ['jupyter']) 35 | 36 | // convert an AsciiDoc file 37 | asciidoctor.convertFile('notebook.adoc', { backend: 'jupyter' }) 38 | ``` 39 | 40 | ## Write your Notebook 41 | 42 | Here's a basic example of notebook written in AsciiDoc: 43 | 44 | ```adoc 45 | = The Lorenz Differential Equations 46 | :jupyter-language-name: python 47 | :jupyter-language-version: 3.7.8 48 | :url-ipywidgets: https://ipywidgets.readthedocs.io/en/stable/ 49 | 50 | Before we start, we import some preliminary libraries. 51 | We will also import (below) the accompanying `lorenz.py` file, 52 | which contains the actual solver and plotting routine. 53 | 54 | [source,python] 55 | ---- 56 | %matplotlib inline 57 | from ipywidgets import interactive, fixed 58 | ---- 59 | 60 | We explore the Lorenz system of differential equations: 61 | 62 | [latexmath] 63 | ++++ 64 | \begin{aligned} 65 | \dot{x} & = \sigma(y-x) \\ 66 | \dot{y} & = \rho x - y - xz \\ 67 | \dot{z} & = -\beta z + xy 68 | \end{aligned} 69 | ++++ 70 | 71 | Let's change (stem:[\sigma], stem:[\beta], stem:[\rho]) with {url-ipywidgets}[ipywidgets] and examine the trajectories. 72 | 73 | [source,python] 74 | ---- 75 | from lorenz import solve_lorenz 76 | w=interactive(solve_lorenz,sigma=(0.0,60.0),rho=(0.0,50.0)) 77 | w 78 | ---- 79 | 80 | For the default set of parameters, we see the trajectories swirling around two points, called attractors. 81 | 82 | The object returned by `interactive` is a `Widget` object and it has attributes that contain the current result and arguments: 83 | 84 | [source,python] 85 | ---- 86 | t, x_t = w.result 87 | ---- 88 | 89 | [source,python] 90 | ---- 91 | w.kwargs 92 | ---- 93 | ``` 94 | 95 | And here's the result: 96 | 97 | ![](jupyter-lorenz-notebook.png) 98 | 99 | ## Document attributes 100 | 101 | | Name | Default Value | Mapping | 102 | |----------------------------|---------------|----------------------------------| 103 | | `jupyter-language-name` | `python` | `metadata.language_info.name` | 104 | | `jupyter-language-version` | `3.9.1` | `metadata.language_info.version` | 105 | | `jupyter-kernel-name` | `python3` | `metadata.kernelspec.name` | 106 | | `jupyter-kernel-language` | `python` | `metadata.kernelspec.language` | 107 | 108 | **IMPORTANT:** The language name defined in `jupyter-language-name` will be used to decide which AsciiDoc source blocks will be converted to Notebook code cells and which will be converted to Markdown cells. 109 | For instance, if the Jupyter language name is `python` the converter will convert source blocks that have the language `python` to code cells. Source blocks with other languages will be converted as Markdown cells. 110 | 111 | ## Notebook file format 112 | 113 | This converter generates [Jupyter notebooks] using [Notebook file format](https://nbformat.readthedocs.io/en/latest/format_description.html) version 4.4. 114 | -------------------------------------------------------------------------------- /test/fixtures/references/lorenz.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "source": "# The Lorenz Differential Equations", 6 | "metadata": {} 7 | }, 8 | { 9 | "cell_type": "markdown", 10 | "source": "Before we start, we import some preliminary libraries. We will also import (below) the accompanying `lorenz.py` file, which contains the actual solver and plotting routine.", 11 | "metadata": {} 12 | }, 13 | { 14 | "cell_type": "code", 15 | "source": "%matplotlib inline\nfrom ipywidgets import interactive, fixed", 16 | "metadata": { 17 | "trusted": true 18 | }, 19 | "execution_count": null, 20 | "outputs": [] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "source": "We explore the Lorenz system of differential equations:\n\n$$\n\\begin{aligned}\n\\dot{x} & = \\sigma(y-x) \\\\\n\\dot{y} & = \\rho x - y - xz \\\\\n\\dot{z} & = -\\beta z + xy\n\\end{aligned}\n$$\n\nLet's change (\\\\(\\sigma\\\\), \\\\(\\beta\\\\), \\\\(\\rho\\\\)) with ipywidgets and examine the trajectories.", 25 | "metadata": {} 26 | }, 27 | { 28 | "cell_type": "code", 29 | "source": "from lorenz import solve_lorenz\nw=interactive(solve_lorenz,sigma=(0.0,60.0),rho=(0.0,50.0))\nw", 30 | "metadata": { 31 | "trusted": true 32 | }, 33 | "execution_count": 4, 34 | "outputs": [ 35 | { 36 | "output_type": "display_data", 37 | "data": { 38 | "text/plain": "interactive(children=(FloatSlider(value=10.0, description='sigma', max=60.0), FloatSlider(value=2.666666666666…", 39 | "application/vnd.jupyter.widget-view+json": { 40 | "version_major": 2, 41 | "version_minor": 0, 42 | "model_id": "583c22f6774f4e948d2bb40cfc3ff526" 43 | } 44 | }, 45 | "metadata": {} 46 | } 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "source": "For the default set of parameters, we see the trajectories swirling around two points, called attractors. ", 52 | "metadata": {} 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "source": "The object returned by `interactive` is a `Widget` object and it has attributes that contain the current result and arguments:", 57 | "metadata": {} 58 | }, 59 | { 60 | "cell_type": "code", 61 | "source": "t, x_t = w.result", 62 | "metadata": {}, 63 | "execution_count": null, 64 | "outputs": [] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "source": "w.kwargs", 69 | "metadata": {}, 70 | "execution_count": null, 71 | "outputs": [] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "source": "After interacting with the system, we can take the result and perform further computations. In this case, we compute the average positions in \\\\(x\\\\), \\\\(y\\\\) and \\\\(z\\\\).", 76 | "metadata": {} 77 | }, 78 | { 79 | "cell_type": "code", 80 | "source": "xyz_avg = x_t.mean(axis=1)", 81 | "metadata": {}, 82 | "execution_count": null, 83 | "outputs": [] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "source": "xyz_avg.shape", 88 | "metadata": {}, 89 | "execution_count": null, 90 | "outputs": [] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "source": "Creating histograms of the average positions (across different trajectories) show that, on average, the trajectories swirl about the attractors.", 95 | "metadata": {} 96 | }, 97 | { 98 | "cell_type": "code", 99 | "source": "from matplotlib import pyplot as plt", 100 | "metadata": {}, 101 | "execution_count": null, 102 | "outputs": [] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "source": "plt.hist(xyz_avg[:,0])\nplt.title('Average $x(t)$');", 107 | "metadata": {}, 108 | "execution_count": null, 109 | "outputs": [] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "source": "plt.hist(xyz_avg[:,1])\nplt.title('Average $y(t)$');", 114 | "metadata": {}, 115 | "execution_count": null, 116 | "outputs": [] 117 | } 118 | ], 119 | "nbformat_minor": 4, 120 | "nbformat": 4 121 | } -------------------------------------------------------------------------------- /test/fixtures/intro-neo4j-guides-01.adoc: -------------------------------------------------------------------------------- 1 | = Exercise 1 2 | :icons: font 3 | :imagesdir: images 4 | 5 | == Exercise 1: Retrieving Nodes (Preparations) 6 | 7 | Make sure you have the Movie database, which contains 171 nodes and 253 relationships. 8 | 9 | This is what you should see when you click the database icon image:database-icon.png[]. 10 | 11 | image::InitialDatabase.png[InitialDatabase,300, role=left] 12 | 13 | If your database does not have this number of nodes and relationships, you can use the script below to reset it: 14 | 15 | [source, cypher] 16 | ---- 17 | include::scripts/initialDatabase.cypher[] 18 | ---- 19 | 20 | == Exercise 1: Retrieving Nodes (Overview) 21 | 22 | In this exercise, you will perform some basic retrievals from the Movie database. 23 | 24 | * *Exercise 1.1*: Retrieve all nodes from the database. 25 | * *Exercise 1.2*: Examine the data model for the graph. 26 | * *Exercise 1.3*: Retrieve all _Person_ nodes. 27 | * *Exercise 1.4*: Retrieve all _Movie_ nodes. 28 | 29 | Go to the next page to start this exercise. 30 | 31 | == Exercise 1.1: Retrieve all nodes from the database (Instructions) 32 | 33 | *Write a query to retrieve _all_ nodes from the database.* 34 | 35 | *Hints:* 36 | 37 | . Write the query in the query pane at the top of Neo4j Browser. 38 | . Click the _Run_ button image:run-button.png[] in order to execute the code. 39 | 40 | == Exercise 1.1: Retrieve all nodes from the database (Solution) 41 | 42 | *Write a query to retrieve _all_ nodes from the database.* 43 | 44 | . Click the code block below where it appears in query pane at the top of Neo4j Browser. 45 | . Click the _Run_ button image:run-button.png[] in order to execute the code. 46 | 47 | [source, cypher] 48 | ---- 49 | MATCH (n) RETURN n 50 | ---- 51 | 52 | You should now see all nodes in the graph in the result pane. 53 | In this query, _n_ is a variable, which is a container that holds the result. 54 | 55 | The result returned should be: 56 | 57 | [.thumb] 58 | image::ReturnAllNodes.png[ReturnAllNodes,width=500] 59 | 60 | *Notes:* 61 | 62 | * If the nodes displayed are not connected, make sure that _Connect result nodes_ is selected in your Neo4j Browser settings. 63 | * If no nodes are returned, then perhaps you did not load the _Movie_ database (see _Preparations_). 64 | 65 | 66 | == Exercise 1.2: Examine the data model of the graph (Instructions) 67 | 68 | *Write a query to display the _schema_ of your database.* 69 | 70 | 71 | == Exercise 1.2: Examine the _schema_ of your database (Solution) 72 | 73 | *Write a query to display the _schema_ of your database.* 74 | 75 | [source, cypher] 76 | ---- 77 | CALL db.schema() 78 | ---- 79 | 80 | The result returned should be: 81 | 82 | [.thumb] 83 | image::call_db.schema.png[call_db.schema,width=500] 84 | 85 | 86 | == Exercise 1.3: Retrieve all _Person_ nodes (Instructions) 87 | 88 | *Write a query to retrieve all _Person_ nodes.* 89 | 90 | == Exercise 1.3: Retrieve all _Person_ nodes (Solution) 91 | 92 | *Write a query to retrieve all _Person_ nodes.* 93 | 94 | [source, cypher] 95 | ---- 96 | MATCH (p:Person) RETURN p 97 | ---- 98 | 99 | The result returned should be: 100 | 101 | [.thumb] 102 | image::PersonNodes.png[PersonNodes,width=600] 103 | 104 | Here we use _p_ as the variable representing all nodes retrieved and what is returned for the query. 105 | Notice also, that very few relationships are displayed. 106 | That is because most _Person_ nodes have no relationships with other _Person_ nodes. 107 | 108 | 109 | == Exercise 1.4: Retrieve all _Movie_ nodes (Instructions) 110 | 111 | *Write a query to retrieve all _Movie_ nodes.* 112 | 113 | == Exercise 1.4: Retrieve all _Movie_ nodes (Solution) 114 | 115 | *Write a query to retrieve all _Movie_ nodes.* 116 | 117 | [source, cypher] 118 | ---- 119 | MATCH (m:Movie) RETURN m 120 | ---- 121 | 122 | The result returned should be: 123 | 124 | [.thumb] 125 | image::MovieNodes.png[MovieNodes,width=600] 126 | 127 | 128 | Here we use _m_ as the variable representing all nodes retrieved and what is returned for the query. 129 | Notice also, that no relationships are displayed. 130 | That is because a _Movie_ node has no relationships with another _Movie_ node. 131 | 132 | 133 | == Exercise 1: Retrieving Nodes (Summary) 134 | 135 | 136 | In this exercise, you performed some basic retrievals from the Movie database. 137 | You also called the procedure to examine the schema of the graph. 138 | 139 | ifdef::env-guide[] 140 | pass:a[Continue to Exercise 2] 141 | endif::[] -------------------------------------------------------------------------------- /test/register.spec.js: -------------------------------------------------------------------------------- 1 | /* global describe it */ 2 | const fs = require('fs').promises 3 | const path = require('path') 4 | const chai = require('chai') 5 | const expect = chai.expect 6 | const dirtyChai = require('dirty-chai') 7 | 8 | chai.use(dirtyChai) 9 | 10 | const { register: registerJupyterConverter } = require('../src/index.js') 11 | const asciidoctor = require('@asciidoctor/core')() 12 | 13 | describe('Jupyter converter', () => { 14 | it('should register the converter', async () => { 15 | try { 16 | // this function is called by Asciidoctor.js CLI 17 | registerJupyterConverter(asciidoctor.Extensions) 18 | const content = await fs.readFile(path.join(__dirname, 'fixtures', 'hello-world.adoc')) 19 | const result = asciidoctor.convert(content, { backend: 'jupyter' }) 20 | expect(result).is.not.empty() 21 | const ipynb = JSON.parse(result) 22 | expect(ipynb.metadata.language_info.name).is.equal('python') 23 | expect(ipynb.metadata.language_info.version).is.equal('2.7.10') 24 | expect(ipynb.cells.length).is.equal(32) 25 | const codeCells = ipynb.cells.filter(cell => cell.cell_type === 'code') 26 | expect(codeCells.length).is.equal(21) 27 | expect(codeCells[0].source.join('')).is.equal(`from py2neo import Graph 28 | 29 | graph = Graph() 30 | `) 31 | } finally { 32 | asciidoctor.Extensions.unregisterAll() 33 | } 34 | }) 35 | it('should send logs through Asciidoctor logger', async () => { 36 | const loggerManager = asciidoctor.LoggerManager 37 | const initialLogger = loggerManager.getLogger() 38 | try { 39 | const memoryLogger = asciidoctor.MemoryLogger.create() 40 | loggerManager.setLogger(memoryLogger) 41 | // this function is called by Asciidoctor.js CLI 42 | registerJupyterConverter(asciidoctor.Extensions) 43 | const content = await fs.readFile(path.join(__dirname, 'fixtures', 'unsupported-syntax.adoc')) 44 | const result = asciidoctor.convert(content, { backend: 'jupyter' }) 45 | expect(result).is.not.empty() 46 | expect(memoryLogger.getMessages().map(msg => ({ severity: msg.getSeverity(), message: msg.getText() }))).to.deep.equals([ 47 | { 48 | severity: 'INFO', 49 | message: 'Unsupported inline type: subscript, using raw text.' 50 | }, 51 | { 52 | severity: 'INFO', 53 | message: 'Unsupported inline type: superscript, using raw text.' 54 | } 55 | ]) 56 | } finally { 57 | asciidoctor.Extensions.unregisterAll() 58 | loggerManager.setLogger(initialLogger) 59 | } 60 | }) 61 | it('should send logs through Asciidoctor logger using a registry instance', async () => { 62 | const loggerManager = asciidoctor.LoggerManager 63 | const initialLogger = loggerManager.getLogger() 64 | try { 65 | const memoryLogger = asciidoctor.MemoryLogger.create() 66 | loggerManager.setLogger(memoryLogger) 67 | const registry = asciidoctor.Extensions.create() 68 | registerJupyterConverter(registry) 69 | const content = await fs.readFile(path.join(__dirname, 'fixtures', 'unsupported-syntax.adoc')) 70 | const result = asciidoctor.convert(content, { backend: 'jupyter', extension_registry: registry }) 71 | expect(result).is.not.empty() 72 | expect(memoryLogger.getMessages().map(msg => ({ severity: msg.getSeverity(), message: msg.getText() }))).to.deep.equals([ 73 | { 74 | severity: 'INFO', 75 | message: 'Unsupported inline type: subscript, using raw text.' 76 | }, 77 | { 78 | severity: 'INFO', 79 | message: 'Unsupported inline type: superscript, using raw text.' 80 | } 81 | ]) 82 | } finally { 83 | loggerManager.setLogger(initialLogger) 84 | } 85 | }) 86 | it('should send logs through a logger', async () => { 87 | const registry = asciidoctor.Extensions.create() 88 | const messages = [] 89 | registerJupyterConverter(registry, { 90 | logger: { 91 | info: (msg) => messages.push(msg) 92 | } 93 | }) 94 | const content = await fs.readFile(path.join(__dirname, 'fixtures', 'unsupported-syntax.adoc')) 95 | const result = asciidoctor.convert(content, { backend: 'jupyter', extension_registry: registry }) 96 | expect(result).is.not.empty() 97 | expect(messages).to.deep.equals([ 98 | 'Unsupported inline type: subscript, using raw text.', 99 | 'Unsupported inline type: superscript, using raw text.' 100 | ]) 101 | }) 102 | }) 103 | -------------------------------------------------------------------------------- /examples/hello-world.adoc: -------------------------------------------------------------------------------- 1 | = Hello World 2 | :jupyter-language-name: python 3 | :jupyter-language-version: 2.7.10 4 | 5 | This notebook walks through basic code examples for integrating various packages with Neo4j, including `py2neo`, `ipython-cypher`, `pandas`, `networkx`, `igraph`, and `jgraph`. 6 | 7 | == py2neo 8 | 9 | `py2neo` is one of Neo4j's Python drivers. It offers a fully-featured interface for interacting with your data in Neo4j. Install `py2neo` with `pip install py2neo`. 10 | 11 | == Connect 12 | 13 | Connect to Neo4j with the Graph class. 14 | 15 | [source,py] 16 | ---- 17 | from py2neo import Graph 18 | 19 | graph = Graph() 20 | ---- 21 | 22 | [source,py] 23 | ---- 24 | graph.delete_all() 25 | ---- 26 | 27 | == Nodes 28 | 29 | Create nodes with the Node class. 30 | The first argument is the node's label. 31 | The remaining arguments are an arbitrary amount of node properties or key-value pairs. 32 | 33 | [source,py] 34 | ---- 35 | from py2neo import Node 36 | 37 | nicole = Node("Person", name="Nicole", age=24) 38 | drew = Node("Person", name="Drew", age=20) 39 | 40 | mtdew = Node("Drink", name="Mountain Dew", calories=9000) 41 | cokezero = Node("Drink", name="Coke Zero", calories=0) 42 | 43 | coke = Node("Manufacturer", name="Coca Cola") 44 | pepsi = Node("Manufacturer", name="Pepsi") 45 | 46 | graph.create(nicole | drew | mtdew | cokezero | coke | pepsi) 47 | ---- 48 | 49 | [source,py] 50 | ---- 51 | from scripts.vis import draw 52 | 53 | options = {"Person": "name", "Drink": "name", "Manufacturer": "name"} 54 | draw(graph, options) 55 | ---- 56 | 57 | 58 | P.S. - If you want to check out what's going on behind the scenes for the `draw()` function used above, 59 | take a look at https://github.com/nicolewhite/neo4j-jupyter/blob/master/scripts/vis.py[scripts/vis.py]. 60 | 61 | == Relationships 62 | 63 | Create relationships between nodes with the Relationship class. 64 | 65 | [source,py] 66 | ---- 67 | from py2neo import Relationship 68 | 69 | graph.create(Relationship(nicole, "LIKES", cokezero)) 70 | graph.create(Relationship(nicole, "LIKES", mtdew)) 71 | graph.create(Relationship(drew, "LIKES", mtdew)) 72 | graph.create(Relationship(coke, "MAKES", cokezero)) 73 | graph.create(Relationship(pepsi, "MAKES", mtdew)) 74 | 75 | draw(graph, options) 76 | ---- 77 | 78 | == Cypher 79 | 80 | Retrieve Cypher query results with `Graph.cypher.execute`. 81 | 82 | [source,py] 83 | ---- 84 | query = """ 85 | MATCH (person:Person)-[:LIKES]->(drink:Drink) 86 | RETURN person.name AS name, drink.name AS drink 87 | """ 88 | 89 | data = graph.run(query) 90 | 91 | for d in data: 92 | print(d) 93 | ---- 94 | 95 | == Parameterized Cypher 96 | 97 | Pass parameters to Cypher queries by passing additional key-value arguments to `Graph.cypher.execute`. 98 | Parameters in Cypher are named and are wrapped in curly braces. 99 | 100 | [source,py] 101 | ---- 102 | query = """ 103 | MATCH (p:Person)-[:LIKES]->(drink:Drink) 104 | WHERE p.name = {name} 105 | RETURN p.name AS name, AVG(drink.calories) AS avg_calories 106 | """ 107 | 108 | data = graph.run(query, name="Nicole") 109 | 110 | for d in data: 111 | print(d) 112 | ---- 113 | 114 | == ipython-cypher 115 | 116 | `ipython-cypher` exposes `%cypher` magic in Jupyter. 117 | Install `ipython-cypher` with `pip install ipython-cypher`. 118 | 119 | [source,py] 120 | ---- 121 | %load_ext cypher 122 | ---- 123 | 124 | == Cypher 125 | 126 | `%cypher` is intended for one-line Cypher queries and `%%cypher` is intended for multi-line Cypher queries. 127 | Placing `%%cypher` above a Cypher query will display that query's results. 128 | 129 | [source,py] 130 | ---- 131 | %%cypher 132 | MATCH (person:Person)-[:LIKES]->(drink:Drink) 133 | RETURN person.name, drink.name, drink.calories 134 | ---- 135 | 136 | == Pandas Data Frames 137 | 138 | Cypher query results can be coerced to pandas data frames with the `get_dataframe` method. 139 | To assign Cypher query results to a variable, you need to use `%cypher` and separate lines with \\. 140 | You'll first need to install pandas with `pip install pandas`. 141 | 142 | [source,py] 143 | ---- 144 | results = %cypher MATCH (person:Person)-[:LIKES]->(drink:Drink) \ 145 | RETURN person.name AS name, drink.name AS drink 146 | 147 | df = results.get_dataframe() 148 | 149 | df 150 | ---- 151 | 152 | [source,py] 153 | ---- 154 | df.index 155 | ---- 156 | 157 | [source,py] 158 | ---- 159 | df.iloc[[1]] 160 | ---- 161 | 162 | [source,py] 163 | ---- 164 | df["name"] 165 | ---- 166 | 167 | == NetworkX Graphs 168 | 169 | Cypher query results can be coerced to `NetworkX` MultiDiGraphs, graphs that permit multiple edges between nodes, with the get_graph method. 170 | You'll first need to install `NetworkX` with `pip install networkx`. 171 | 172 | [source,py] 173 | ---- 174 | import networkx as nx 175 | %matplotlib inline 176 | 177 | results = %cypher MATCH p = (:Person)-[:LIKES]->(:Drink) RETURN p 178 | 179 | g = results.get_graph() 180 | 181 | nx.draw(g) 182 | ---- 183 | 184 | [source,py] 185 | ---- 186 | g.nodes(data=True) 187 | ---- 188 | 189 | [source,py] 190 | ---- 191 | nx.degree(g) 192 | ---- 193 | 194 | == igraph 195 | 196 | Cypher query results can be imported into igraph with `py2neo`. 197 | You'll need to install `igraph` with `pip install python-igraph`. 198 | Query results should be returned as edgelists, as igraph has a method for building an igraph object from a list of tuples representing edges between nodes. 199 | 200 | [source,py] 201 | ---- 202 | from py2neo import Graph as PGraph 203 | from igraph import Graph as IGraph 204 | 205 | neo4j = PGraph() 206 | 207 | query = """ 208 | MATCH (person:Person)-[:LIKES]->(drink:Drink) 209 | RETURN person.name AS source, drink.name AS target 210 | """ 211 | 212 | data = neo4j.run(query) 213 | tups = [] 214 | 215 | for d in data: 216 | tups.append((d["source"], d["target"])) 217 | ---- 218 | 219 | [source,py] 220 | ---- 221 | ig = IGraph.TupleList(tups) 222 | 223 | ig 224 | ---- 225 | 226 | [source,py] 227 | ---- 228 | best = ig.vs.select(_degree = ig.maxdegree())["name"] 229 | best 230 | ---- 231 | 232 | == jgraph 233 | 234 | `jgraph` will plot tuple lists as 3D graphs. 235 | 236 | [source,py] 237 | ---- 238 | import jgraph 239 | 240 | jgraph.draw([(1, 2), (2, 3), (3, 4), (4, 1), (4, 5), (5, 2)]) 241 | ---- 242 | 243 | [source,py] 244 | ---- 245 | data = graph.run("MATCH (n)-->(m) RETURN ID(n), ID(m)") 246 | data = [tuple(x) for x in data] 247 | 248 | jgraph.draw(data) 249 | ---- 250 | -------------------------------------------------------------------------------- /test/fixtures/hello-world.adoc: -------------------------------------------------------------------------------- 1 | = Hello World 2 | :jupyter-language-name: python 3 | :jupyter-language-version: 2.7.10 4 | 5 | This notebook walks through basic code examples for integrating various packages with Neo4j, including `py2neo`, `ipython-cypher`, `pandas`, `networkx`, `igraph`, and `jgraph`. 6 | 7 | == py2neo 8 | 9 | `py2neo` is one of Neo4j's Python drivers. It offers a fully-featured interface for interacting with your data in Neo4j. Install `py2neo` with `pip install py2neo`. 10 | 11 | == Connect 12 | 13 | Connect to Neo4j with the Graph class. 14 | 15 | [source,py] 16 | ---- 17 | from py2neo import Graph 18 | 19 | graph = Graph() 20 | ---- 21 | 22 | [source,py] 23 | ---- 24 | graph.delete_all() 25 | ---- 26 | 27 | == Nodes 28 | 29 | Create nodes with the Node class. 30 | The first argument is the node's label. 31 | The remaining arguments are an arbitrary amount of node properties or key-value pairs. 32 | 33 | [source,py] 34 | ---- 35 | from py2neo import Node 36 | 37 | nicole = Node("Person", name="Nicole", age=24) 38 | drew = Node("Person", name="Drew", age=20) 39 | 40 | mtdew = Node("Drink", name="Mountain Dew", calories=9000) 41 | cokezero = Node("Drink", name="Coke Zero", calories=0) 42 | 43 | coke = Node("Manufacturer", name="Coca Cola") 44 | pepsi = Node("Manufacturer", name="Pepsi") 45 | 46 | graph.create(nicole | drew | mtdew | cokezero | coke | pepsi) 47 | ---- 48 | 49 | [source,py] 50 | ---- 51 | from scripts.vis import draw 52 | 53 | options = {"Person": "name", "Drink": "name", "Manufacturer": "name"} 54 | draw(graph, options) 55 | ---- 56 | 57 | 58 | P.S. - If you want to check out what's going on behind the scenes for the `draw()` function used above, 59 | take a look at https://github.com/nicolewhite/neo4j-jupyter/blob/master/scripts/vis.py[scripts/vis.py]. 60 | 61 | == Relationships 62 | 63 | Create relationships between nodes with the Relationship class. 64 | 65 | [source,py] 66 | ---- 67 | from py2neo import Relationship 68 | 69 | graph.create(Relationship(nicole, "LIKES", cokezero)) 70 | graph.create(Relationship(nicole, "LIKES", mtdew)) 71 | graph.create(Relationship(drew, "LIKES", mtdew)) 72 | graph.create(Relationship(coke, "MAKES", cokezero)) 73 | graph.create(Relationship(pepsi, "MAKES", mtdew)) 74 | 75 | draw(graph, options) 76 | ---- 77 | 78 | == Cypher 79 | 80 | Retrieve Cypher query results with `Graph.cypher.execute`. 81 | 82 | [source,py] 83 | ---- 84 | query = """ 85 | MATCH (person:Person)-[:LIKES]->(drink:Drink) 86 | RETURN person.name AS name, drink.name AS drink 87 | """ 88 | 89 | data = graph.run(query) 90 | 91 | for d in data: 92 | print(d) 93 | ---- 94 | 95 | == Parameterized Cypher 96 | 97 | Pass parameters to Cypher queries by passing additional key-value arguments to `Graph.cypher.execute`. 98 | Parameters in Cypher are named and are wrapped in curly braces. 99 | 100 | [source,py] 101 | ---- 102 | query = """ 103 | MATCH (p:Person)-[:LIKES]->(drink:Drink) 104 | WHERE p.name = {name} 105 | RETURN p.name AS name, AVG(drink.calories) AS avg_calories 106 | """ 107 | 108 | data = graph.run(query, name="Nicole") 109 | 110 | for d in data: 111 | print(d) 112 | ---- 113 | 114 | == ipython-cypher 115 | 116 | `ipython-cypher` exposes `%cypher` magic in Jupyter. 117 | Install `ipython-cypher` with `pip install ipython-cypher`. 118 | 119 | [source,py] 120 | ---- 121 | %load_ext cypher 122 | ---- 123 | 124 | == Cypher 125 | 126 | `%cypher` is intended for one-line Cypher queries and `%%cypher` is intended for multi-line Cypher queries. 127 | Placing `%%cypher` above a Cypher query will display that query's results. 128 | 129 | [source,py] 130 | ---- 131 | %%cypher 132 | MATCH (person:Person)-[:LIKES]->(drink:Drink) 133 | RETURN person.name, drink.name, drink.calories 134 | ---- 135 | 136 | == Pandas Data Frames 137 | 138 | Cypher query results can be coerced to pandas data frames with the `get_dataframe` method. 139 | To assign Cypher query results to a variable, you need to use `%cypher` and separate lines with \\. 140 | You'll first need to install pandas with `pip install pandas`. 141 | 142 | [source,py] 143 | ---- 144 | results = %cypher MATCH (person:Person)-[:LIKES]->(drink:Drink) \ 145 | RETURN person.name AS name, drink.name AS drink 146 | 147 | df = results.get_dataframe() 148 | 149 | df 150 | ---- 151 | 152 | [source,py] 153 | ---- 154 | df.index 155 | ---- 156 | 157 | [source,py] 158 | ---- 159 | df.iloc[[1]] 160 | ---- 161 | 162 | [source,py] 163 | ---- 164 | df["name"] 165 | ---- 166 | 167 | == NetworkX Graphs 168 | 169 | Cypher query results can be coerced to `NetworkX` MultiDiGraphs, graphs that permit multiple edges between nodes, with the get_graph method. 170 | You'll first need to install `NetworkX` with `pip install networkx`. 171 | 172 | [source,py] 173 | ---- 174 | import networkx as nx 175 | %matplotlib inline 176 | 177 | results = %cypher MATCH p = (:Person)-[:LIKES]->(:Drink) RETURN p 178 | 179 | g = results.get_graph() 180 | 181 | nx.draw(g) 182 | ---- 183 | 184 | [source,py] 185 | ---- 186 | g.nodes(data=True) 187 | ---- 188 | 189 | [source,py] 190 | ---- 191 | nx.degree(g) 192 | ---- 193 | 194 | == igraph 195 | 196 | Cypher query results can be imported into igraph with `py2neo`. 197 | You'll need to install `igraph` with `pip install python-igraph`. 198 | Query results should be returned as edgelists, as igraph has a method for building an igraph object from a list of tuples representing edges between nodes. 199 | 200 | [source,py] 201 | ---- 202 | from py2neo import Graph as PGraph 203 | from igraph import Graph as IGraph 204 | 205 | neo4j = PGraph() 206 | 207 | query = """ 208 | MATCH (person:Person)-[:LIKES]->(drink:Drink) 209 | RETURN person.name AS source, drink.name AS target 210 | """ 211 | 212 | data = neo4j.run(query) 213 | tups = [] 214 | 215 | for d in data: 216 | tups.append((d["source"], d["target"])) 217 | ---- 218 | 219 | [source,py] 220 | ---- 221 | ig = IGraph.TupleList(tups) 222 | 223 | ig 224 | ---- 225 | 226 | [source,py] 227 | ---- 228 | best = ig.vs.select(_degree = ig.maxdegree())["name"] 229 | best 230 | ---- 231 | 232 | == jgraph 233 | 234 | `jgraph` will plot tuple lists as 3D graphs. 235 | 236 | [source,py] 237 | ---- 238 | import jgraph 239 | 240 | jgraph.draw([(1, 2), (2, 3), (3, 4), (4, 1), (4, 5), (5, 2)]) 241 | ---- 242 | 243 | [source,py] 244 | ---- 245 | data = graph.run("MATCH (n)-->(m) RETURN ID(n), ID(m)") 246 | data = [tuple(x) for x in data] 247 | 248 | jgraph.draw(data) 249 | ---- 250 | -------------------------------------------------------------------------------- /test/fixtures/images/overlap.svg: -------------------------------------------------------------------------------- 1 | 2 | {\displaystyle \mathrm {overlap} (X,Y)={\frac {|X\cap Y|}{\min(|X|,|Y|)}}} 3 | 23 | 64 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* global Opal */ 2 | class JupyterConverter { 3 | constructor (backend) { 4 | this.backend = backend 5 | // this.basebackend = 'json' 6 | this.outfilesuffix = '.ipynb' 7 | this.filetype = 'json' 8 | this.ignoredNodes = [] 9 | } 10 | 11 | convert (node, transform) { 12 | const nodeName = transform || node.getNodeName() 13 | if (nodeName === 'document' || nodeName === 'embedded') { 14 | this.ignoredNodes = [] 15 | const languageName = node.getAttribute('jupyter-language-name', 'python') 16 | const languageVersion = node.getAttribute('jupyter-language-version', '3.9.1') 17 | const kernelName = node.getAttribute('jupyter-kernel-name', 'python3') 18 | const kernelLanguage = node.getAttribute('jupyter-kernel-language', 'python') 19 | const blocks = node.getBlocks() 20 | const cells = [] 21 | let lastCell = {} 22 | for (const [index, block] of blocks.entries()) { 23 | const result = block.convert() 24 | if (lastCell.cell_type === 'markdown') { 25 | lastCell = this.mergeAdjacentMarkdownCells(result, lastCell, cells, index < blocks.length ? '\n' : '') 26 | } else { 27 | if (cells.length === 0) { 28 | const firstCell = result[0] 29 | // attach document title 30 | if (node.hasHeader() && node.getDocumentTitle()) { 31 | if (firstCell && firstCell.cell_type === 'markdown') { 32 | firstCell.source.unshift(`# ${node.getDocumentTitle()}\n\n`) 33 | } else { 34 | cells.push({ 35 | cell_type: 'markdown', 36 | source: [`# ${node.getDocumentTitle()}\n\n`], 37 | metadata: {} 38 | }) 39 | } 40 | } 41 | } 42 | cells.push(...result) 43 | if (cells.length > 0) { 44 | lastCell = cells[cells.length - 1] 45 | } 46 | } 47 | } 48 | const result = { 49 | cells, 50 | metadata: { 51 | language_info: { 52 | name: languageName, 53 | version: languageVersion 54 | }, 55 | kernelspec: { 56 | name: kernelName, 57 | language: kernelLanguage 58 | } 59 | }, 60 | nbformat: 4, 61 | nbformat_minor: 4 62 | } 63 | if (this.ignoredNodes.length > 0) { 64 | logger.warn(`Unsupported nodes [${Array.from(new Set(this.ignoredNodes.map(item => item.name))).join(', ')}], some content might be missing!`) 65 | } 66 | return JSON.stringify(result) 67 | } 68 | if (nodeName === 'paragraph') { 69 | const source = node.lines.map(l => node.applySubstitutions(l) + '\n') 70 | return [{ 71 | cell_type: 'markdown', 72 | source, 73 | metadata: {} 74 | }] 75 | } 76 | if (nodeName === 'pass' || nodeName === 'thematic_break') { 77 | return [] // ignore 78 | } 79 | if (nodeName === 'preamble') { 80 | const blocks = node.getBlocks() 81 | return blocks.map((b) => b.convert()).filter(v => v.length !== 0).reduce((acc, val) => acc.concat(val), []) // flat Node > 10 82 | } 83 | if (nodeName === 'section') { 84 | const blocks = node.getBlocks() 85 | const cells = [] 86 | let lastCell = {} 87 | for (const block of blocks) { 88 | const result = block.convert() 89 | // merge adjacent cells with the same type 90 | if (lastCell.cell_type === 'markdown') { 91 | lastCell = this.mergeAdjacentMarkdownCells(result, lastCell, cells) 92 | } else { 93 | if (cells.length === 0) { 94 | const firstCell = result[0] 95 | // attach section title 96 | if (node.getTitle()) { 97 | if (firstCell && firstCell.cell_type === 'markdown') { 98 | firstCell.source.unshift(`## ${node.getTitle()}\n\n`) 99 | } else { 100 | cells.push({ 101 | cell_type: 'markdown', 102 | source: [`## ${node.getTitle()}\n\n`], 103 | metadata: {} 104 | }) 105 | } 106 | } 107 | } 108 | cells.push(...result) 109 | lastCell = cells[cells.length - 1] 110 | } 111 | } 112 | return cells 113 | } 114 | if (nodeName === 'example' || nodeName === 'sidebar') { 115 | const blocks = node.getBlocks() 116 | const cells = [] 117 | let lastCell = {} 118 | for (const block of blocks) { 119 | const result = block.convert() 120 | // merge adjacent cells with the same type 121 | if (lastCell.cell_type === 'markdown') { 122 | lastCell = this.mergeAdjacentMarkdownCells(result, lastCell, cells, '\n') 123 | } else { 124 | if (cells.length === 0) { 125 | const firstCell = result[0] 126 | // attach section title 127 | if (node.getTitle()) { 128 | if (firstCell && firstCell.cell_type === 'markdown') { 129 | firstCell.source.unshift(`*${node.getTitle()}*\\\n`) 130 | } else { 131 | cells.push({ 132 | cell_type: 'markdown', 133 | source: [`*${node.getTitle()}*\\\n`], 134 | metadata: {} 135 | }) 136 | } 137 | } 138 | } 139 | cells.push(...result) 140 | lastCell = cells[cells.length - 1] 141 | } 142 | } 143 | return cells 144 | } 145 | if (nodeName === 'stem') { 146 | return [{ 147 | cell_type: 'markdown', 148 | source: [`\n$$\n${node.lines.join('\n')}\n$$\n`], 149 | metadata: {} 150 | }] 151 | } 152 | if (nodeName === 'literal') { 153 | const lines = node.lines 154 | const length = lines.length 155 | const source = lines 156 | .map((l, index) => length === index + 1 ? l : l + '\n') 157 | return [{ 158 | cell_type: 'markdown', 159 | source: ['\n```\n', ...source, '\n```\n'], 160 | metadata: {} 161 | }] 162 | } 163 | if (nodeName === 'listing') { 164 | const lines = node.lines 165 | const source = lines.map((l) => l + '\n') 166 | const language = node.getAttribute('language') 167 | const languageName = node.getDocument().getAttribute('jupyter-language-name', 'python') 168 | let languages 169 | if (languageName === 'python' || languageName === 'py') { 170 | languages = ['python', 'py'] 171 | } else if (languageName === 'c++' || languageName === 'cpp') { 172 | languages = ['c++', 'cpp'] 173 | } else { 174 | languages = [languageName] 175 | } 176 | if (languages.includes(language)) { 177 | return [{ 178 | cell_type: 'code', 179 | execution_count: 0, 180 | metadata: { 181 | slideshow: { 182 | slide_type: 'fragment' 183 | } 184 | }, 185 | outputs: [], 186 | source 187 | }] 188 | } else { 189 | return [{ 190 | cell_type: 'markdown', 191 | source: ['```' + (language || '') + '\n', ...source, '```'], 192 | metadata: { 193 | node_name: nodeName 194 | } 195 | }] 196 | } 197 | } 198 | if (nodeName === 'image') { 199 | const image = `![${node.getAttribute('alt')}](${node.getImageUri(node.getAttribute('target'))})` 200 | if (node.hasAttribute('link')) { 201 | return [`[${image}](${node.getAttribute('link')})\n`] 202 | } 203 | return [{ 204 | cell_type: 'markdown', 205 | source: ['\n', `${image}\n`, '\n'], 206 | metadata: { 207 | node_name: nodeName 208 | } 209 | }] 210 | } 211 | if (nodeName === 'ulist' || nodeName === 'olist') { 212 | const symbol = nodeName === 'ulist' ? '-' : '1.' 213 | let lastCell = {} 214 | const cells = [] 215 | for (const item of node.getItems()) { 216 | cells.push({ 217 | cell_type: 'markdown', 218 | source: [`${symbol} ${item.getText()}\n`], 219 | metadata: { 220 | node_name: nodeName 221 | } 222 | }) 223 | if (item.hasBlocks()) { 224 | const blocks = item.getBlocks() 225 | for (const block of blocks) { 226 | const result = block.convert() 227 | // merge adjacent cells with the same type 228 | if (lastCell.cell_type === 'markdown') { 229 | lastCell = this.mergeAdjacentMarkdownCells(result, lastCell, cells) 230 | } else { 231 | cells.push(...result) 232 | lastCell = cells[cells.length - 1] 233 | } 234 | } 235 | } 236 | } 237 | // make room around a list 238 | if (cells.length > 0) { 239 | cells[0].source.unshift('\n') 240 | } 241 | if (cells.length > 1) { 242 | cells[cells.length - 1].source.push('\n') 243 | } 244 | return cells 245 | } 246 | if (nodeName === 'table') { 247 | const lines = ['\n'] 248 | const headRows = node.rows.head // getHeadRows() 249 | const bodyRows = node.rows.body.concat(node.rows.foot) // getBodyRows() | getFootRows() 250 | if (headRows.length === 0 && bodyRows.length === 0) { 251 | // empty table! 252 | return '' 253 | } 254 | let headRow 255 | if (headRows.length === 0) { 256 | headRow = bodyRows.shift() 257 | } else { 258 | headRow = headRows.shift() 259 | } 260 | let headLine = '| ' 261 | for (const headCell of headRow) { 262 | headLine += headCell.$text() + ' | ' // getText() 263 | } 264 | lines.push(headLine.trim() + '\n') 265 | lines.push('| ' + headRow.map(c => '-'.repeat(c.$text().length)).join(' | ') + ' |\n') // getText() 266 | if (headRows && headRows.length > 0) { 267 | for (const headRow of headRows) { 268 | let line = '| ' 269 | for (const headCell of headRow) { 270 | line += headCell.$text() + ' | ' // getText() 271 | } 272 | line = line.trim() + '\n' 273 | lines.push(line) 274 | } 275 | } 276 | if (bodyRows && bodyRows.length > 0) { 277 | for (const bodyRow of bodyRows) { 278 | let line = '| ' 279 | for (const cell of bodyRow) { 280 | line += cell.$text() + ' | ' // getText() 281 | } 282 | line = line.trim() + '\n' 283 | lines.push(line) 284 | } 285 | } 286 | lines.push('\n') 287 | return [{ 288 | cell_type: 'markdown', 289 | source: lines, 290 | metadata: { 291 | node_name: nodeName 292 | } 293 | }] 294 | } 295 | if (nodeName === 'quote') { 296 | const cells = [] 297 | let lastCell = {} 298 | const blocks = node.getBlocks() 299 | for (const block of blocks) { 300 | const result = block.convert() 301 | // merge adjacent cells with the same type 302 | if (lastCell.cell_type === 'markdown') { 303 | lastCell = this.mergeAdjacentMarkdownCells(result, lastCell, cells) 304 | } else { 305 | cells.push(...result) 306 | lastCell = cells[cells.length - 1] 307 | } 308 | } 309 | return cells.map(cell => { 310 | if (cell.cell_type === 'markdown') { 311 | cell.source = cell.source.map(l => `> ${l}`) 312 | cell.source.unshift('\n') 313 | cell.source.push('\n') 314 | } 315 | return cell 316 | }) 317 | } 318 | if (nodeName === 'admonition') { 319 | const cells = [] 320 | cells.push({ 321 | cell_type: 'markdown', 322 | source: [`*${node.getAttribute('textlabel')}:* `], 323 | metadata: { 324 | node_name: nodeName 325 | } 326 | }) 327 | let lastCell = {} 328 | if (node.hasBlocks()) { 329 | const blocks = node.getBlocks() 330 | for (const block of blocks) { 331 | const result = block.convert() 332 | // merge adjacent cells with the same type 333 | if (lastCell.cell_type === 'markdown') { 334 | lastCell = this.mergeAdjacentMarkdownCells(result, lastCell, cells) 335 | } else { 336 | cells.push(...result) 337 | lastCell = cells[cells.length - 1] 338 | } 339 | } 340 | } else { 341 | cells[0].source[0] += node.getContent() + '\n' 342 | } 343 | return cells 344 | } 345 | // inline 346 | if (nodeName === 'inline_break') { 347 | return '\n' 348 | } 349 | if (nodeName === 'inline_anchor') { 350 | const type = node.getType() 351 | if (type === 'link') { 352 | return `[${node.getText()}](${node.getTarget()})` 353 | } 354 | if (type === 'xref') { 355 | const path = node.getAttribute('path') 356 | let text 357 | if (path) { 358 | text = node.getText() || path 359 | } else { 360 | text = node.getText() 361 | if (!text) { 362 | const refId = node.getAttribute('refid') 363 | text = `[${refId}]` 364 | } 365 | } 366 | return `[${text}](${node.getTarget()})` 367 | } 368 | // unsupported link type! 369 | this.ignoredNodes.push({ name: `inline_anchor>${type}` }) 370 | return '' 371 | } 372 | if (nodeName === 'inline_quoted') { 373 | const type = node.getType() 374 | if (type === 'emphasis') { 375 | return `*${node.getText()}*` 376 | } 377 | if (type === 'monospaced') { 378 | return `\`${node.getText()}\`` 379 | } 380 | if (type === 'strong') { 381 | return `**${node.getText()}**` 382 | } 383 | if (type === 'asciimath' || type === 'latexmath') { 384 | return `$${node.getText()}$` 385 | } 386 | if (type === 'unquoted') { 387 | return node.getText() 388 | } 389 | logger.info(`Unsupported inline type: ${type}, using raw text.`) 390 | return node.getText() 391 | } 392 | if (nodeName === 'inline_image') { 393 | const image = `![${node.getAttribute('alt')}](${node.getImageUri(node.getTarget())})` 394 | if (node.hasAttribute('link')) { 395 | return `[${image}](${node.getAttribute('link')})` 396 | } 397 | return image 398 | } 399 | if (nodeName === 'dlist') { 400 | let source = '' 401 | for (const [terms, dd] of node.getItems()) { 402 | for (const term of terms) { 403 | source += `* **${term.getText()}**\\` 404 | } 405 | if (dd && dd !== Opal.nil) { 406 | if (dd.hasText()) { 407 | source += ` 408 | ${dd.getText()} 409 | ` 410 | } 411 | if (dd.hasBlocks()) { 412 | source += '\n' 413 | for (const block of dd.getBlocks()) { 414 | const content = block.convert() 415 | if (content && content.length === 1 && content[0].cell_type === 'markdown') { 416 | source += ` ${content[0].source.join(' ')}\n` 417 | } else { 418 | // ignore 419 | this.ignoredNodes.push({ name: `dlist>${block.getNodeName()}` }) 420 | } 421 | } 422 | source += '\n' 423 | } 424 | } 425 | } 426 | return [{ 427 | cell_type: 'markdown', 428 | source: [source], 429 | metadata: { 430 | node_name: nodeName 431 | } 432 | }] 433 | } 434 | if (nodeName === 'colist') { 435 | const source = [] 436 | for (const [index, item] of node.getItems().entries()) { 437 | source.push(`\n${index + 1}. ${item.text}`) 438 | } 439 | source.push('') 440 | return [{ 441 | cell_type: 'markdown', 442 | source, 443 | metadata: { 444 | node_name: nodeName 445 | } 446 | }] 447 | } 448 | 449 | this.ignoredNodes.push({ name: nodeName }) 450 | return '' 451 | } 452 | 453 | /** 454 | * Merge adjacent cells with the same type 455 | * @param result 456 | * @param lastCell 457 | * @param cells 458 | * @param joinCharacter {string} '' 459 | * @returns lastCell 460 | */ 461 | mergeAdjacentMarkdownCells (result, lastCell, cells, joinCharacter = '') { 462 | if (result && result.length > 0) { 463 | if (!result.find(cell => cell.cell_type !== 'markdown')) { 464 | if (joinCharacter !== '' && result[0].metadata && result[0].metadata.node_name !== 'colist') { 465 | const blockJoiner = result[0].metadata.node_name === 'listing' && lastCell.metadata.node_name === 'listing' 466 | ? '\n' 467 | : '' 468 | lastCell.source[lastCell.source.length - 1] = lastCell.source[lastCell.source.length - 1] + joinCharacter + blockJoiner 469 | } 470 | lastCell.source.push(...result.reduce((acc, cell) => acc.concat(cell.source), [])) // flatMap Node > 11 471 | } else { 472 | const adjacentMarkdownCells = [] 473 | const remainingCells = [] 474 | let adjacentCells = true 475 | for (const cell of result) { 476 | if (adjacentCells && cell.cell_type === 'markdown') { 477 | adjacentMarkdownCells.push(cell) 478 | } else { 479 | remainingCells.push(cell) 480 | adjacentCells = false 481 | } 482 | } 483 | lastCell.source.push(...adjacentMarkdownCells.reduce((acc, cell) => acc.concat(cell.source), [])) // flatMap Node > 11 484 | if (remainingCells.length > 0) { 485 | cells.push(...remainingCells) 486 | lastCell = cells[cells.length - 1] 487 | } 488 | } 489 | } 490 | return lastCell 491 | } 492 | } 493 | 494 | let logger = console 495 | 496 | module.exports = JupyterConverter 497 | module.exports.register = function (registry, context) { 498 | let AsciidoctorModule 499 | if (registry.$$meta && registry.$$meta.$$is_class) { 500 | // registry is a class 501 | AsciidoctorModule = registry.$$base_module 502 | } else { 503 | // instance 504 | AsciidoctorModule = registry.$$class.$$base_module.$$base_module 505 | } 506 | if (context && context.logger) { 507 | logger = context.logger 508 | } else if (AsciidoctorModule.LoggerManager) { 509 | logger = AsciidoctorModule.LoggerManager.getLogger() 510 | } 511 | let ConverterFactory 512 | if (typeof AsciidoctorModule.ConverterFactory !== 'undefined') { 513 | // Asciidoctor.js >= 2 514 | ConverterFactory = AsciidoctorModule.ConverterFactory 515 | } else { 516 | // Asciidoctor.js < 2 517 | ConverterFactory = AsciidoctorModule.$$.ConverterFactory 518 | } 519 | ConverterFactory.register(JupyterConverter, ['jupyter']) 520 | } 521 | -------------------------------------------------------------------------------- /test/converter.spec.js: -------------------------------------------------------------------------------- 1 | /* global describe it */ 2 | const util = require('util') 3 | const fs = require('fs').promises 4 | const path = require('path') 5 | const chai = require('chai') 6 | const expect = chai.expect 7 | const dirtyChai = require('dirty-chai') 8 | 9 | chai.use(dirtyChai) 10 | 11 | const JupyterConverter = require('../src/index.js') 12 | const asciidoctor = require('@asciidoctor/core')() 13 | asciidoctor.ConverterFactory.register(JupyterConverter, ['jupyter']) 14 | 15 | const debug = async (result, path) => { 16 | if (process.env.DEBUG) { 17 | if (path) { 18 | await fs.writeFile(path, result, 'utf8') 19 | } 20 | const ipynb = JSON.parse(result) 21 | console.log(util.inspect(ipynb, false, Infinity, true)) 22 | } 23 | } 24 | 25 | describe('Jupyter converter', () => { 26 | it('should convert to ipynb', async () => { 27 | const content = await fs.readFile(path.join(__dirname, 'fixtures', 'hello-world.adoc')) 28 | const result = asciidoctor.convert(content, { backend: 'jupyter' }) 29 | expect(result).is.not.empty() 30 | const ipynb = JSON.parse(result) 31 | expect(ipynb.metadata.language_info.name).is.equal('python') 32 | expect(ipynb.metadata.language_info.version).is.equal('2.7.10') 33 | expect(ipynb.metadata.kernelspec.name).is.equal('python3') 34 | expect(ipynb.metadata.kernelspec.language).is.equal('python') 35 | expect(ipynb.cells.length).is.equal(32) 36 | const codeCells = ipynb.cells.filter(cell => cell.cell_type === 'code') 37 | expect(codeCells.length).is.equal(21) 38 | expect(codeCells[0].source.join('')).is.equal(`from py2neo import Graph 39 | 40 | graph = Graph() 41 | `) 42 | }) 43 | it('should configure language with document attributes', async () => { 44 | const result = asciidoctor.convert(`= Hello World 45 | :jupyter-language-name: c++ 46 | :jupyter-language-version: 17 47 | 48 | `, { backend: 'jupyter' }) 49 | expect(result).is.not.empty() 50 | const ipynb = JSON.parse(result) 51 | expect(ipynb.metadata.language_info.name).is.equal('c++') 52 | expect(ipynb.metadata.language_info.version).is.equal('17') 53 | }) 54 | it('should configure kernelspec with document attributes', async () => { 55 | const result = asciidoctor.convert(`= Hello World 56 | :jupyter-language-name: c++ 57 | :jupyter-language-version: 17 58 | :jupyter-kernel-name: xcpp17 59 | :jupyter-kernel-language: C++17 60 | 61 | `, { backend: 'jupyter' }) 62 | expect(result).is.not.empty() 63 | const ipynb = JSON.parse(result) 64 | expect(ipynb.metadata.language_info.name).is.equal('c++') 65 | expect(ipynb.metadata.language_info.version).is.equal('17') 66 | expect(ipynb.metadata.kernelspec.name).is.equal('xcpp17') 67 | expect(ipynb.metadata.kernelspec.language).is.equal('C++17') 68 | }) 69 | it('should convert source blocks depending on language name (C++)', async () => { 70 | const result = asciidoctor.convert(`= Hello World 71 | :jupyter-language-name: c++ 72 | :jupyter-language-version: 17 73 | :jupyter-kernel-name: xcpp17 74 | :jupyter-kernel-language: C++17 75 | 76 | .Python 77 | [source,py] 78 | ---- 79 | print('hello') 80 | ---- 81 | 82 | .C{pp} 83 | [source,cpp] 84 | ---- 85 | int i=1; 86 | ---- 87 | `, { backend: 'jupyter' }) 88 | expect(result).is.not.empty() 89 | const ipynb = JSON.parse(result) 90 | expect(ipynb.cells.length).is.equal(2) 91 | expect(ipynb.cells[0].cell_type).is.equal('markdown') 92 | expect(ipynb.cells[0].source.join('')).is.equal(`# Hello World 93 | 94 | \`\`\`py 95 | print('hello') 96 | \`\`\``) 97 | expect(ipynb.cells[1].cell_type).is.equal('code') 98 | expect(ipynb.cells[1].source.join('')).is.equal(`int i=1; 99 | `) 100 | }) 101 | it('should convert source blocks depending on language name (Python)', async () => { 102 | const result = asciidoctor.convert(`= Hello World 103 | :jupyter-language-name: python 104 | :jupyter-language-version: 3.11.5 105 | 106 | .Python 107 | [source,py] 108 | ---- 109 | print('hello') 110 | ---- 111 | 112 | .C{pp} 113 | [source,cpp] 114 | ---- 115 | int i=1; 116 | ---- 117 | `, { backend: 'jupyter' }) 118 | expect(result).is.not.empty() 119 | const ipynb = JSON.parse(result) 120 | expect(ipynb.cells.length).is.equal(3) 121 | expect(ipynb.cells[0].cell_type).is.equal('markdown') 122 | expect(ipynb.cells[0].source.join('')).is.equal(`# Hello World 123 | 124 | `) 125 | expect(ipynb.cells[1].cell_type).is.equal('code') 126 | expect(ipynb.cells[1].source.join('')).is.equal(`print('hello') 127 | `) 128 | expect(ipynb.cells[2].cell_type).is.equal('markdown') 129 | expect(ipynb.cells[2].source.join('')).is.equal(`\`\`\`cpp 130 | int i=1; 131 | \`\`\``) 132 | }) 133 | it('should convert source blocks depending on language name (default -> Python)', async () => { 134 | const result = asciidoctor.convert(`= Hello World 135 | 136 | .Python 137 | [source,py] 138 | ---- 139 | print('hello') 140 | ---- 141 | 142 | .C{pp} 143 | [source,cpp] 144 | ---- 145 | int i=1; 146 | ---- 147 | `, { backend: 'jupyter' }) 148 | expect(result).is.not.empty() 149 | const ipynb = JSON.parse(result) 150 | expect(ipynb.cells.length).is.equal(3) 151 | expect(ipynb.cells[0].cell_type).is.equal('markdown') 152 | expect(ipynb.cells[0].source.join('')).is.equal(`# Hello World 153 | 154 | `) 155 | expect(ipynb.cells[1].cell_type).is.equal('code') 156 | expect(ipynb.cells[1].source.join('')).is.equal(`print('hello') 157 | `) 158 | expect(ipynb.cells[2].cell_type).is.equal('markdown') 159 | expect(ipynb.cells[2].source.join('')).is.equal(`\`\`\`cpp 160 | int i=1; 161 | \`\`\``) 162 | }) 163 | it('should convert an exercise guide to ipynb', async () => { 164 | const inputFile = path.join(__dirname, 'fixtures', 'intro-neo4j-guides-01.adoc') 165 | const result = asciidoctor.convertFile(inputFile, { 166 | safe: 'safe', 167 | backend: 'jupyter', 168 | to_file: false 169 | }) 170 | expect(result).is.not.empty() 171 | const ipynb = JSON.parse(result) 172 | expect(ipynb.metadata.language_info.name).is.equal('python') 173 | expect(ipynb.metadata.language_info.version).is.equal('3.9.1') 174 | expect(ipynb.cells.length).is.equal(1) 175 | }) 176 | it('should convert stem blocks to ipynb', async () => { 177 | const inputFile = path.join(__dirname, 'fixtures', 'lorenz-differential-equations.adoc') 178 | const result = asciidoctor.convertFile(inputFile, { 179 | safe: 'safe', 180 | backend: 'jupyter', 181 | to_file: false 182 | }) 183 | expect(result).is.not.empty() 184 | const ipynb = JSON.parse(result) 185 | expect(ipynb.metadata.language_info.name).is.equal('python') 186 | expect(ipynb.metadata.language_info.version).is.equal('3.7.8') 187 | expect(ipynb.cells.length).is.equal(7) 188 | }) 189 | it('should convert an exercise guide with a complex list to ipynb', async () => { 190 | const inputFile = path.join(__dirname, 'fixtures', 'list-continuation.adoc') 191 | const result = asciidoctor.convertFile(inputFile, { 192 | safe: 'safe', 193 | backend: 'jupyter', 194 | to_file: false 195 | }) 196 | expect(result).is.not.empty() 197 | const ipynb = JSON.parse(result) 198 | expect(ipynb.metadata.language_info.name).is.equal('python') 199 | expect(ipynb.metadata.language_info.version).is.equal('3.9.1') 200 | expect(ipynb.cells.length).is.equal(3) 201 | }) 202 | it('should convert an exercise guide with a inline style list to ipynb', async () => { 203 | const inputFile = path.join(__dirname, 'fixtures', 'inline-style.adoc') 204 | const result = asciidoctor.convertFile(inputFile, { 205 | safe: 'safe', 206 | backend: 'jupyter', 207 | to_file: false 208 | }) 209 | expect(result).is.not.empty() 210 | const ipynb = JSON.parse(result) 211 | expect(ipynb.metadata.language_info.name).is.equal('python') 212 | expect(ipynb.metadata.language_info.version).is.equal('3.9.1') 213 | expect(ipynb.cells.length).is.equal(1) 214 | expect(ipynb.cells[0].source[0]).is.equal('For all query tuning measurements, you must always run the query twice.\n') 215 | }) 216 | it('should convert an exercise guide with a table to ipynb', async () => { 217 | const inputFile = path.join(__dirname, 'fixtures', 'table.adoc') 218 | const result = asciidoctor.convertFile(inputFile, { 219 | safe: 'safe', 220 | backend: 'jupyter', 221 | to_file: false 222 | }) 223 | expect(result).is.not.empty() 224 | const ipynb = JSON.parse(result) 225 | expect(ipynb.metadata.language_info.name).is.equal('python') 226 | expect(ipynb.metadata.language_info.version).is.equal('3.9.1') 227 | expect(ipynb.cells.length).is.equal(1) 228 | expect(ipynb.cells[0].source.join('')).is.equal(`## Exercise 5.2: Determine how large the datasets are that you will be loading. (Instructions) 229 | 230 | The datasets containing the normalized data are at these locations: 231 | 232 | | Dataset | URL | 233 | | ------- | --- | 234 | | Movies | [https://data.neo4j.com/advanced-cypher/movies1.csv](https://data.neo4j.com/advanced-cypher/movies1.csv) | 235 | | People | [https://data.neo4j.com/advanced-cypher/people.csv](https://data.neo4j.com/advanced-cypher/people.csv) | 236 | | Roles | [https://data.neo4j.com/advanced-cypher/roles.csv](https://data.neo4j.com/advanced-cypher/roles.csv) | 237 | | Directors | [https://data.neo4j.com/advanced-cypher/directors.csv](https://data.neo4j.com/advanced-cypher/directors.csv) | 238 | 239 | **Write Cypher code to return the number of lines in each of these CSV files.** 240 | `) 241 | }) 242 | it('should convert an exercise guide with a quote to ipynb', async () => { 243 | const inputFile = path.join(__dirname, 'fixtures', 'quote.adoc') 244 | const result = asciidoctor.convertFile(inputFile, { 245 | safe: 'safe', 246 | backend: 'jupyter', 247 | to_file: false 248 | }) 249 | expect(result).is.not.empty() 250 | const ipynb = JSON.parse(result) 251 | expect(ipynb.metadata.language_info.name).is.equal('python') 252 | expect(ipynb.metadata.language_info.version).is.equal('3.9.1') 253 | expect(ipynb.cells.length).is.equal(1) 254 | expect(ipynb.cells[0].source.join('')).is.equal(`## Category Hierarchy 255 | 256 | This structure between categories is already hiding in the data, we just need to extract it. 257 | The Overlap Similarity algorithm is the perfect choice for this type of problem. 258 | 259 | > The overlap coefficient, or Szymkiewicz–Simpson coefficient, is a similarity measure that measures the overlap between two sets. 260 | > It is defined as the size of the intersection divided by the smaller of the size of the two sets. 261 | 262 | It is computed using the following formula: 263 | 264 | ![overlap](overlap.svg) 265 | 266 | If set X is a subset of Y or vice versa then the overlap coefficient is equal to one. 267 | `) 268 | }) 269 | it('should convert an exercise guide with admonitions to ipynb', async () => { 270 | const inputFile = path.join(__dirname, 'fixtures', 'admonitions.adoc') 271 | const result = asciidoctor.convertFile(inputFile, { 272 | safe: 'safe', 273 | backend: 'jupyter', 274 | to_file: false 275 | }) 276 | expect(result).is.not.empty() 277 | const ipynb = JSON.parse(result) 278 | expect(ipynb.metadata.language_info.name).is.equal('python') 279 | expect(ipynb.metadata.language_info.version).is.equal('3.9.1') 280 | expect(ipynb.cells.length).is.equal(1) 281 | expect(ipynb.cells[0].source.join('')).is.equal(`*Note:* An admonition draws attention to auxiliary information. 282 | 283 | Here are the other built-in admonition types: 284 | 285 | *Tip:* Pro tip…​ 286 | 287 | *Important:* Don’t forget…​ 288 | 289 | *Warning:* Watch out for…​ 290 | 291 | *Caution:* Ensure that…​ 292 | `) 293 | }) 294 | it('should convert an exercise guide with an admonition block to ipynb', async () => { 295 | const inputFile = path.join(__dirname, 'fixtures', 'admonition-block.adoc') 296 | const result = asciidoctor.convertFile(inputFile, { 297 | safe: 'safe', 298 | backend: 'jupyter', 299 | to_file: false 300 | }) 301 | expect(result).is.not.empty() 302 | const ipynb = JSON.parse(result) 303 | expect(ipynb.metadata.language_info.name).is.equal('python') 304 | expect(ipynb.metadata.language_info.version).is.equal('3.9.1') 305 | expect(ipynb.cells.length).is.equal(2) 306 | expect(ipynb.cells[0].source.join('')).is.equal('*Note:* ') 307 | expect(ipynb.cells[1].source.join('')).is.equal(`An admonition block may contain complex content. 308 | 309 | - one 310 | - two 311 | - three 312 | 313 | Another paragraph. 314 | `) 315 | }) 316 | it('should convert an exercise guide with a literal block to ipynb', async () => { 317 | const inputFile = path.join(__dirname, 'fixtures', 'literal.adoc') 318 | const result = asciidoctor.convertFile(inputFile, { 319 | safe: 'safe', 320 | backend: 'jupyter', 321 | to_file: false 322 | }) 323 | expect(result).is.not.empty() 324 | const ipynb = JSON.parse(result) 325 | expect(ipynb.metadata.language_info.name).is.equal('python') 326 | expect(ipynb.metadata.language_info.version).is.equal('3.9.1') 327 | expect(ipynb.cells.length).is.equal(1) 328 | expect(ipynb.cells[0].cell_type).is.equal('markdown') 329 | expect(ipynb.cells[0].source.join('')).is.equal(` 330 | \`\`\` 331 | { 332 | name: , 333 | born: , 334 | roles: [roles that this actor has played in all of his/her movies] 335 | movies: [titles of the movies this actor has acted in] 336 | } 337 | \`\`\` 338 | `) 339 | }) 340 | it('should ignore passthrough blocks', async () => { 341 | const inputFile = path.join(__dirname, 'fixtures', 'passthrough.adoc') 342 | const result = asciidoctor.convertFile(inputFile, { 343 | safe: 'safe', 344 | backend: 'jupyter', 345 | to_file: false 346 | }) 347 | expect(result).is.not.empty() 348 | }) 349 | it('should ignore thematic break blocks', async () => { 350 | const inputFile = path.join(__dirname, 'fixtures', 'thematic-break.adoc') 351 | const result = asciidoctor.convertFile(inputFile, { 352 | safe: 'safe', 353 | backend: 'jupyter', 354 | to_file: false 355 | }) 356 | expect(result).is.not.empty() 357 | }) 358 | it('should retain blocks order', async () => { 359 | const inputFile = path.join(__dirname, 'fixtures', 'retain-order.adoc') 360 | const result = asciidoctor.convertFile(inputFile, { 361 | safe: 'safe', 362 | backend: 'jupyter', 363 | to_file: false 364 | }) 365 | expect(result).is.not.empty() 366 | const ipynb = JSON.parse(result) 367 | expect(ipynb.cells.length).is.equal(9) 368 | expect(ipynb.cells[0].cell_type).is.equal('markdown') 369 | expect(ipynb.cells[1].cell_type).is.equal('code') 370 | expect(ipynb.cells[2].cell_type).is.equal('markdown') 371 | expect(ipynb.cells[3].cell_type).is.equal('code') 372 | expect(ipynb.cells[4].cell_type).is.equal('markdown') 373 | expect(ipynb.cells[5].cell_type).is.equal('code') 374 | expect(ipynb.cells[6].cell_type).is.equal('markdown') 375 | expect(ipynb.cells[7].cell_type).is.equal('code') 376 | expect(ipynb.cells[8].cell_type).is.equal('markdown') 377 | }) 378 | it('should convert monospaced', async () => { 379 | const inputFile = path.join(__dirname, 'fixtures', 'monospaced.adoc') 380 | const result = asciidoctor.convertFile(inputFile, { 381 | safe: 'safe', 382 | backend: 'jupyter', 383 | to_file: false 384 | }) 385 | expect(result).is.not.empty() 386 | const ipynb = JSON.parse(result) 387 | expect(ipynb.cells[0].source[1]).is.equal('Install `icypher` then connect to your database!\n') 388 | }) 389 | it('should convert emphasis', async () => { 390 | const inputFile = path.join(__dirname, 'fixtures', 'emphasis.adoc') 391 | const result = asciidoctor.convertFile(inputFile, { 392 | safe: 'safe', 393 | backend: 'jupyter', 394 | to_file: false 395 | }) 396 | expect(result).is.not.empty() 397 | const ipynb = JSON.parse(result) 398 | expect(ipynb.cells[0].source[0]).is.equal('Happy werewolves are *really* slobbery.\n') 399 | }) 400 | it('should convert basic dlist', async () => { 401 | const inputFile = path.join(__dirname, 'fixtures', 'basic-dlist.adoc') 402 | const result = asciidoctor.convertFile(inputFile, { 403 | safe: 'safe', 404 | backend: 'jupyter', 405 | to_file: false 406 | }) 407 | expect(result).is.not.empty() 408 | const ipynb = JSON.parse(result) 409 | expect(ipynb.cells[0].source[0]).is.equal(`* **CPU**\\ 410 | The brain of the computer. 411 | * **Hard drive**\\ 412 | Permanent storage for operating system and/or user files. 413 | * **RAM**\\ 414 | Temporarily stores information the CPU uses during operation. 415 | * **Keyboard**\\ 416 | Used to enter text or control items on the screen. 417 | * **Mouse**\\ 418 | Used to point to and select items on your computer screen. 419 | * **Monitor**\\ 420 | Displays information in visual form using text and graphics. 421 | `) 422 | }) 423 | it('should convert complex dlist', async () => { 424 | const inputFile = path.join(__dirname, 'fixtures', 'complex-dlist.adoc') 425 | const result = asciidoctor.convertFile(inputFile, { 426 | safe: 'safe', 427 | backend: 'jupyter', 428 | to_file: false 429 | }) 430 | expect(result).is.not.empty() 431 | const ipynb = JSON.parse(result) 432 | expect(ipynb.cells[0].source[0]).is.equal(`* **CPU**\\ 433 | The brain of the computer. 434 | It performs operations on an external data source, usually memory or some other data stream. 435 | 436 | Traditional processors are typically based on silicon. 437 | 438 | \`\`\`js 439 | console.log('hello') 440 | \`\`\` 441 | 442 | * **Hard drive**\\ 443 | Permanent storage for operating system and/or user files. 444 | `) 445 | }) 446 | it('should convert colist', async () => { 447 | const inputFile = path.join(__dirname, 'fixtures', 'colist.adoc') 448 | const result = asciidoctor.convertFile(inputFile, { 449 | safe: 'safe', 450 | backend: 'jupyter', 451 | to_file: false 452 | }) 453 | expect(result).is.not.empty() 454 | const ipynb = JSON.parse(result) 455 | expect(ipynb.cells[0].source.join('')).is.equal(`\`\`\` 456 | git clone https://github.com/feelpp/book.feelpp.org.git # <1> 457 | git clone https://github.com/feelpp/toolbox.git # <2> 458 | \`\`\` 459 | 1. clone the source for the website 460 | 2. clone the source for toolbox cases`) 461 | }) 462 | it('should convert example block', async () => { 463 | const inputFile = path.join(__dirname, 'fixtures', 'example-block.adoc') 464 | const result = asciidoctor.convertFile(inputFile, { 465 | safe: 'safe', 466 | backend: 'jupyter', 467 | to_file: false 468 | }) 469 | expect(result).is.not.empty() 470 | const ipynb = JSON.parse(result) 471 | expect(ipynb.cells[0].source.join('')).is.equal(`*Onomatopoeia*\\ 472 | The book hit the floor with a **thud**. 473 | 474 | He could hear doves **cooing** in the pine trees’ branches. 475 | `) 476 | }) 477 | it('should convert xrefs', async () => { 478 | const inputFile = path.join(__dirname, 'fixtures', 'xrefs.adoc') 479 | const result = asciidoctor.convertFile(inputFile, { 480 | safe: 'safe', 481 | backend: 'jupyter', 482 | to_file: false 483 | }) 484 | expect(result).is.not.empty() 485 | const ipynb = JSON.parse(result) 486 | expect(ipynb.cells[0].source.join('')).is.equal(`[Refcard](refcard.pdf) 487 | 488 | [[sect-a]](#sect-a) 489 | `) 490 | await debug(result, 'xrefs.ipynb') 491 | }) 492 | it('should convert nested blocks', async () => { 493 | const inputFile = path.join(__dirname, 'fixtures', 'nested-blocks.adoc') 494 | const result = asciidoctor.convertFile(inputFile, { 495 | safe: 'safe', 496 | backend: 'jupyter', 497 | to_file: false 498 | }) 499 | expect(result).is.not.empty() 500 | const ipynb = JSON.parse(result) 501 | expect(ipynb.cells[0].source.join('')).is.equal(`# Nested Blocks 502 | 503 | *Title*\\ 504 | Example…​ 505 | 506 | 507 | $$ 508 | A_1=\\left(\\begin{array}{lll} 509 | $$ 510 | `) 511 | await debug(result, 'nested-blocks.ipynb') 512 | }) 513 | it('should convert a description list without text', () => { 514 | const inputFile = path.join(__dirname, 'fixtures', 'description-list-without-text.adoc') 515 | const result = asciidoctor.convertFile(inputFile, { 516 | safe: 'safe', 517 | backend: 'jupyter', 518 | to_file: false 519 | }) 520 | expect(result).is.not.empty() 521 | const ipynb = JSON.parse(result) 522 | expect(ipynb.cells[0].source.join('')).is.equal(`# Basics 523 | 524 | ## Introduction 525 | 526 | * **Basic Matrix Creation**\\`) 527 | expect(ipynb.cells[1].source.join('')).is.equal(`import numpy as np 528 | matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) 529 | print(f"matrix={matrix}") 530 | `) 531 | }) 532 | it('should convert code blocks as Markdown', () => { 533 | const inputFile = path.join(__dirname, 'fixtures', 'multiple-codeblocks.adoc') 534 | const result = asciidoctor.convertFile(inputFile, { 535 | safe: 'safe', 536 | backend: 'jupyter', 537 | to_file: false 538 | }) 539 | expect(result).is.not.empty() 540 | const ipynb = JSON.parse(result) 541 | expect(ipynb.cells[0].source.join('')).is.equal(`\`\`\`cpp 542 | #ifndef __MYFUNC_HPP__ #define __MYFUNC_HPP__ 543 | 544 | void mymsg(); 545 | 546 | #endif 547 | \`\`\` 548 | 549 | \`\`\`cpp 550 | #include 551 | 552 | void mymsg() 553 | { 554 | std::cout << "Hello, world"; 555 | } 556 | \`\`\` 557 | 558 | \`\`\`cpp 559 | #include "myfunc.hpp" 560 | 561 | int main() 562 | { 563 | mymsg(); 564 | return 0; 565 | } 566 | \`\`\``) 567 | }) 568 | }) 569 | -------------------------------------------------------------------------------- /test/fixtures/scripts/initialDatabase.cypher: -------------------------------------------------------------------------------- 1 | MATCH (n) DETACH DELETE n; 2 | CREATE (TheMatrix:Movie {title:'The Matrix', released:1999, tagline:'Welcome to the Real World'}) 3 | CREATE (Keanu:Person {name:'Keanu Reeves', born:1964}) 4 | CREATE (Carrie:Person {name:'Carrie-Anne Moss', born:1967}) 5 | CREATE (Laurence:Person {name:'Laurence Fishburne', born:1961}) 6 | CREATE (Hugo:Person {name:'Hugo Weaving', born:1960}) 7 | CREATE (LillyW:Person {name:'Lilly Wachowski', born:1967}) 8 | CREATE (LanaW:Person {name:'Lana Wachowski', born:1965}) 9 | CREATE (JoelS:Person {name:'Joel Silver', born:1952}) 10 | CREATE 11 | (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrix), 12 | (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrix), 13 | (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrix), 14 | (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrix), 15 | (LillyW)-[:DIRECTED]->(TheMatrix), 16 | (LanaW)-[:DIRECTED]->(TheMatrix), 17 | (JoelS)-[:PRODUCED]->(TheMatrix) 18 | 19 | CREATE (Emil:Person {name:"Emil Eifrem", born:1978}) 20 | CREATE (Emil)-[:ACTED_IN {roles:["Emil"]}]->(TheMatrix) 21 | 22 | CREATE (TheMatrixReloaded:Movie {title:'The Matrix Reloaded', released:2003, tagline:'Free your mind'}) 23 | CREATE 24 | (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixReloaded), 25 | (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixReloaded), 26 | (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixReloaded), 27 | (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixReloaded), 28 | (LillyW)-[:DIRECTED]->(TheMatrixReloaded), 29 | (LanaW)-[:DIRECTED]->(TheMatrixReloaded), 30 | (JoelS)-[:PRODUCED]->(TheMatrixReloaded) 31 | 32 | CREATE (TheMatrixRevolutions:Movie {title:'The Matrix Revolutions', released:2003, tagline:'Everything that has a beginning has an end'}) 33 | CREATE 34 | (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixRevolutions), 35 | (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixRevolutions), 36 | (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixRevolutions), 37 | (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixRevolutions), 38 | (LillyW)-[:DIRECTED]->(TheMatrixRevolutions), 39 | (LanaW)-[:DIRECTED]->(TheMatrixRevolutions), 40 | (JoelS)-[:PRODUCED]->(TheMatrixRevolutions) 41 | 42 | CREATE (TheDevilsAdvocate:Movie {title:"The Devil's Advocate", released:1997, tagline:'Evil has its winning ways'}) 43 | CREATE (Charlize:Person {name:'Charlize Theron', born:1975}) 44 | CREATE (Al:Person {name:'Al Pacino', born:1940}) 45 | CREATE (Taylor:Person {name:'Taylor Hackford', born:1944}) 46 | CREATE 47 | (Keanu)-[:ACTED_IN {roles:['Kevin Lomax']}]->(TheDevilsAdvocate), 48 | (Charlize)-[:ACTED_IN {roles:['Mary Ann Lomax']}]->(TheDevilsAdvocate), 49 | (Al)-[:ACTED_IN {roles:['John Milton']}]->(TheDevilsAdvocate), 50 | (Taylor)-[:DIRECTED]->(TheDevilsAdvocate) 51 | 52 | CREATE (AFewGoodMen:Movie {title:"A Few Good Men", released:1992, tagline:"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth."}) 53 | CREATE (TomC:Person {name:'Tom Cruise', born:1962}) 54 | CREATE (JackN:Person {name:'Jack Nicholson', born:1937}) 55 | CREATE (DemiM:Person {name:'Demi Moore', born:1962}) 56 | CREATE (KevinB:Person {name:'Kevin Bacon', born:1958}) 57 | CREATE (KieferS:Person {name:'Kiefer Sutherland', born:1966}) 58 | CREATE (NoahW:Person {name:'Noah Wyle', born:1971}) 59 | CREATE (CubaG:Person {name:'Cuba Gooding Jr.', born:1968}) 60 | CREATE (KevinP:Person {name:'Kevin Pollak', born:1957}) 61 | CREATE (JTW:Person {name:'J.T. Walsh', born:1943}) 62 | CREATE (JamesM:Person {name:'James Marshall', born:1967}) 63 | CREATE (ChristopherG:Person {name:'Christopher Guest', born:1948}) 64 | CREATE (RobR:Person {name:'Rob Reiner', born:1947}) 65 | CREATE (AaronS:Person {name:'Aaron Sorkin', born:1961}) 66 | CREATE 67 | (TomC)-[:ACTED_IN {roles:['Lt. Daniel Kaffee']}]->(AFewGoodMen), 68 | (JackN)-[:ACTED_IN {roles:['Col. Nathan R. Jessup']}]->(AFewGoodMen), 69 | (DemiM)-[:ACTED_IN {roles:['Lt. Cdr. JoAnne Galloway']}]->(AFewGoodMen), 70 | (KevinB)-[:ACTED_IN {roles:['Capt. Jack Ross']}]->(AFewGoodMen), 71 | (KieferS)-[:ACTED_IN {roles:['Lt. Jonathan Kendrick']}]->(AFewGoodMen), 72 | (NoahW)-[:ACTED_IN {roles:['Cpl. Jeffrey Barnes']}]->(AFewGoodMen), 73 | (CubaG)-[:ACTED_IN {roles:['Cpl. Carl Hammaker']}]->(AFewGoodMen), 74 | (KevinP)-[:ACTED_IN {roles:['Lt. Sam Weinberg']}]->(AFewGoodMen), 75 | (JTW)-[:ACTED_IN {roles:['Lt. Col. Matthew Andrew Markinson']}]->(AFewGoodMen), 76 | (JamesM)-[:ACTED_IN {roles:['Pfc. Louden Downey']}]->(AFewGoodMen), 77 | (ChristopherG)-[:ACTED_IN {roles:['Dr. Stone']}]->(AFewGoodMen), 78 | (AaronS)-[:ACTED_IN {roles:['Man in Bar']}]->(AFewGoodMen), 79 | (RobR)-[:DIRECTED]->(AFewGoodMen), 80 | (AaronS)-[:WROTE]->(AFewGoodMen) 81 | 82 | CREATE (TopGun:Movie {title:"Top Gun", released:1986, tagline:'I feel the need, the need for speed.'}) 83 | CREATE (KellyM:Person {name:'Kelly McGillis', born:1957}) 84 | CREATE (ValK:Person {name:'Val Kilmer', born:1959}) 85 | CREATE (AnthonyE:Person {name:'Anthony Edwards', born:1962}) 86 | CREATE (TomS:Person {name:'Tom Skerritt', born:1933}) 87 | CREATE (MegR:Person {name:'Meg Ryan', born:1961}) 88 | CREATE (TonyS:Person {name:'Tony Scott', born:1944}) 89 | CREATE (JimC:Person {name:'Jim Cash', born:1941}) 90 | CREATE 91 | (TomC)-[:ACTED_IN {roles:['Maverick']}]->(TopGun), 92 | (KellyM)-[:ACTED_IN {roles:['Charlie']}]->(TopGun), 93 | (ValK)-[:ACTED_IN {roles:['Iceman']}]->(TopGun), 94 | (AnthonyE)-[:ACTED_IN {roles:['Goose']}]->(TopGun), 95 | (TomS)-[:ACTED_IN {roles:['Viper']}]->(TopGun), 96 | (MegR)-[:ACTED_IN {roles:['Carole']}]->(TopGun), 97 | (TonyS)-[:DIRECTED]->(TopGun), 98 | (JimC)-[:WROTE]->(TopGun) 99 | 100 | CREATE (JerryMaguire:Movie {title:'Jerry Maguire', released:2000, tagline:'The rest of his life begins now.'}) 101 | CREATE (ReneeZ:Person {name:'Renee Zellweger', born:1969}) 102 | CREATE (KellyP:Person {name:'Kelly Preston', born:1962}) 103 | CREATE (JerryO:Person {name:"Jerry O'Connell", born:1974}) 104 | CREATE (JayM:Person {name:'Jay Mohr', born:1970}) 105 | CREATE (BonnieH:Person {name:'Bonnie Hunt', born:1961}) 106 | CREATE (ReginaK:Person {name:'Regina King', born:1971}) 107 | CREATE (JonathanL:Person {name:'Jonathan Lipnicki', born:1996}) 108 | CREATE (CameronC:Person {name:'Cameron Crowe', born:1957}) 109 | CREATE 110 | (TomC)-[:ACTED_IN {roles:['Jerry Maguire']}]->(JerryMaguire), 111 | (CubaG)-[:ACTED_IN {roles:['Rod Tidwell']}]->(JerryMaguire), 112 | (ReneeZ)-[:ACTED_IN {roles:['Dorothy Boyd']}]->(JerryMaguire), 113 | (KellyP)-[:ACTED_IN {roles:['Avery Bishop']}]->(JerryMaguire), 114 | (JerryO)-[:ACTED_IN {roles:['Frank Cushman']}]->(JerryMaguire), 115 | (JayM)-[:ACTED_IN {roles:['Bob Sugar']}]->(JerryMaguire), 116 | (BonnieH)-[:ACTED_IN {roles:['Laurel Boyd']}]->(JerryMaguire), 117 | (ReginaK)-[:ACTED_IN {roles:['Marcee Tidwell']}]->(JerryMaguire), 118 | (JonathanL)-[:ACTED_IN {roles:['Ray Boyd']}]->(JerryMaguire), 119 | (CameronC)-[:DIRECTED]->(JerryMaguire), 120 | (CameronC)-[:PRODUCED]->(JerryMaguire), 121 | (CameronC)-[:WROTE]->(JerryMaguire) 122 | 123 | CREATE (StandByMe:Movie {title:"Stand By Me", released:1986, tagline:"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of."}) 124 | CREATE (RiverP:Person {name:'River Phoenix', born:1970}) 125 | CREATE (CoreyF:Person {name:'Corey Feldman', born:1971}) 126 | CREATE (WilW:Person {name:'Wil Wheaton', born:1972}) 127 | CREATE (JohnC:Person {name:'John Cusack', born:1966}) 128 | CREATE (MarshallB:Person {name:'Marshall Bell', born:1942}) 129 | CREATE 130 | (WilW)-[:ACTED_IN {roles:['Gordie Lachance']}]->(StandByMe), 131 | (RiverP)-[:ACTED_IN {roles:['Chris Chambers']}]->(StandByMe), 132 | (JerryO)-[:ACTED_IN {roles:['Vern Tessio']}]->(StandByMe), 133 | (CoreyF)-[:ACTED_IN {roles:['Teddy Duchamp']}]->(StandByMe), 134 | (JohnC)-[:ACTED_IN {roles:['Denny Lachance']}]->(StandByMe), 135 | (KieferS)-[:ACTED_IN {roles:['Ace Merrill']}]->(StandByMe), 136 | (MarshallB)-[:ACTED_IN {roles:['Mr. Lachance']}]->(StandByMe), 137 | (RobR)-[:DIRECTED]->(StandByMe) 138 | 139 | CREATE (AsGoodAsItGets:Movie {title:'As Good as It Gets', released:1997, tagline:'A comedy from the heart that goes for the throat.'}) 140 | CREATE (HelenH:Person {name:'Helen Hunt', born:1963}) 141 | CREATE (GregK:Person {name:'Greg Kinnear', born:1963}) 142 | CREATE (JamesB:Person {name:'James L. Brooks', born:1940}) 143 | CREATE 144 | (JackN)-[:ACTED_IN {roles:['Melvin Udall']}]->(AsGoodAsItGets), 145 | (HelenH)-[:ACTED_IN {roles:['Carol Connelly']}]->(AsGoodAsItGets), 146 | (GregK)-[:ACTED_IN {roles:['Simon Bishop']}]->(AsGoodAsItGets), 147 | (CubaG)-[:ACTED_IN {roles:['Frank Sachs']}]->(AsGoodAsItGets), 148 | (JamesB)-[:DIRECTED]->(AsGoodAsItGets) 149 | 150 | CREATE (WhatDreamsMayCome:Movie {title:'What Dreams May Come', released:1998, tagline:'After life there is more. The end is just the beginning.'}) 151 | CREATE (AnnabellaS:Person {name:'Annabella Sciorra', born:1960}) 152 | CREATE (MaxS:Person {name:'Max von Sydow', born:1929}) 153 | CREATE (WernerH:Person {name:'Werner Herzog', born:1942}) 154 | CREATE (Robin:Person {name:'Robin Williams', born:1951}) 155 | CREATE (VincentW:Person {name:'Vincent Ward', born:1956}) 156 | CREATE 157 | (Robin)-[:ACTED_IN {roles:['Chris Nielsen']}]->(WhatDreamsMayCome), 158 | (CubaG)-[:ACTED_IN {roles:['Albert Lewis']}]->(WhatDreamsMayCome), 159 | (AnnabellaS)-[:ACTED_IN {roles:['Annie Collins-Nielsen']}]->(WhatDreamsMayCome), 160 | (MaxS)-[:ACTED_IN {roles:['The Tracker']}]->(WhatDreamsMayCome), 161 | (WernerH)-[:ACTED_IN {roles:['The Face']}]->(WhatDreamsMayCome), 162 | (VincentW)-[:DIRECTED]->(WhatDreamsMayCome) 163 | 164 | CREATE (SnowFallingonCedars:Movie {title:'Snow Falling on Cedars', released:1999, tagline:'First loves last. Forever.'}) 165 | CREATE (EthanH:Person {name:'Ethan Hawke', born:1970}) 166 | CREATE (RickY:Person {name:'Rick Yune', born:1971}) 167 | CREATE (JamesC:Person {name:'James Cromwell', born:1940}) 168 | CREATE (ScottH:Person {name:'Scott Hicks', born:1953}) 169 | CREATE 170 | (EthanH)-[:ACTED_IN {roles:['Ishmael Chambers']}]->(SnowFallingonCedars), 171 | (RickY)-[:ACTED_IN {roles:['Kazuo Miyamoto']}]->(SnowFallingonCedars), 172 | (MaxS)-[:ACTED_IN {roles:['Nels Gudmundsson']}]->(SnowFallingonCedars), 173 | (JamesC)-[:ACTED_IN {roles:['Judge Fielding']}]->(SnowFallingonCedars), 174 | (ScottH)-[:DIRECTED]->(SnowFallingonCedars) 175 | 176 | CREATE (YouveGotMail:Movie {title:"You've Got Mail", released:1998, tagline:'At odds in life... in love on-line.'}) 177 | CREATE (ParkerP:Person {name:'Parker Posey', born:1968}) 178 | CREATE (DaveC:Person {name:'Dave Chappelle', born:1973}) 179 | CREATE (SteveZ:Person {name:'Steve Zahn', born:1967}) 180 | CREATE (TomH:Person {name:'Tom Hanks', born:1956}) 181 | CREATE (NoraE:Person {name:'Nora Ephron', born:1941}) 182 | CREATE 183 | (TomH)-[:ACTED_IN {roles:['Joe Fox']}]->(YouveGotMail), 184 | (MegR)-[:ACTED_IN {roles:['Kathleen Kelly']}]->(YouveGotMail), 185 | (GregK)-[:ACTED_IN {roles:['Frank Navasky']}]->(YouveGotMail), 186 | (ParkerP)-[:ACTED_IN {roles:['Patricia Eden']}]->(YouveGotMail), 187 | (DaveC)-[:ACTED_IN {roles:['Kevin Jackson']}]->(YouveGotMail), 188 | (SteveZ)-[:ACTED_IN {roles:['George Pappas']}]->(YouveGotMail), 189 | (NoraE)-[:DIRECTED]->(YouveGotMail) 190 | 191 | CREATE (SleeplessInSeattle:Movie {title:'Sleepless in Seattle', released:1993, tagline:'What if someone you never met, someone you never saw, someone you never knew was the only someone for you?'}) 192 | CREATE (RitaW:Person {name:'Rita Wilson', born:1956}) 193 | CREATE (BillPull:Person {name:'Bill Pullman', born:1953}) 194 | CREATE (VictorG:Person {name:'Victor Garber', born:1949}) 195 | CREATE (RosieO:Person {name:"Rosie O'Donnell", born:1962}) 196 | CREATE 197 | (TomH)-[:ACTED_IN {roles:['Sam Baldwin']}]->(SleeplessInSeattle), 198 | (MegR)-[:ACTED_IN {roles:['Annie Reed']}]->(SleeplessInSeattle), 199 | (RitaW)-[:ACTED_IN {roles:['Suzy']}]->(SleeplessInSeattle), 200 | (BillPull)-[:ACTED_IN {roles:['Walter']}]->(SleeplessInSeattle), 201 | (VictorG)-[:ACTED_IN {roles:['Greg']}]->(SleeplessInSeattle), 202 | (RosieO)-[:ACTED_IN {roles:['Becky']}]->(SleeplessInSeattle), 203 | (NoraE)-[:DIRECTED]->(SleeplessInSeattle) 204 | 205 | CREATE (JoeVersustheVolcano:Movie {title:'Joe Versus the Volcano', released:1990, tagline:'A story of love, lava and burning desire.'}) 206 | CREATE (JohnS:Person {name:'John Patrick Stanley', born:1950}) 207 | CREATE (Nathan:Person {name:'Nathan Lane', born:1956}) 208 | CREATE 209 | (TomH)-[:ACTED_IN {roles:['Joe Banks']}]->(JoeVersustheVolcano), 210 | (MegR)-[:ACTED_IN {roles:['DeDe', 'Angelica Graynamore', 'Patricia Graynamore']}]->(JoeVersustheVolcano), 211 | (Nathan)-[:ACTED_IN {roles:['Baw']}]->(JoeVersustheVolcano), 212 | (JohnS)-[:DIRECTED]->(JoeVersustheVolcano) 213 | 214 | CREATE (WhenHarryMetSally:Movie {title:'When Harry Met Sally', released:1998, tagline:'At odds in life... in love on-line.'}) 215 | CREATE (BillyC:Person {name:'Billy Crystal', born:1948}) 216 | CREATE (CarrieF:Person {name:'Carrie Fisher', born:1956}) 217 | CREATE (BrunoK:Person {name:'Bruno Kirby', born:1949}) 218 | CREATE 219 | (BillyC)-[:ACTED_IN {roles:['Harry Burns']}]->(WhenHarryMetSally), 220 | (MegR)-[:ACTED_IN {roles:['Sally Albright']}]->(WhenHarryMetSally), 221 | (CarrieF)-[:ACTED_IN {roles:['Marie']}]->(WhenHarryMetSally), 222 | (BrunoK)-[:ACTED_IN {roles:['Jess']}]->(WhenHarryMetSally), 223 | (RobR)-[:DIRECTED]->(WhenHarryMetSally), 224 | (RobR)-[:PRODUCED]->(WhenHarryMetSally), 225 | (NoraE)-[:PRODUCED]->(WhenHarryMetSally), 226 | (NoraE)-[:WROTE]->(WhenHarryMetSally) 227 | 228 | CREATE (ThatThingYouDo:Movie {title:'That Thing You Do', released:1996, tagline:'In every life there comes a time when that thing you dream becomes that thing you do'}) 229 | CREATE (LivT:Person {name:'Liv Tyler', born:1977}) 230 | CREATE 231 | (TomH)-[:ACTED_IN {roles:['Mr. White']}]->(ThatThingYouDo), 232 | (LivT)-[:ACTED_IN {roles:['Faye Dolan']}]->(ThatThingYouDo), 233 | (Charlize)-[:ACTED_IN {roles:['Tina']}]->(ThatThingYouDo), 234 | (TomH)-[:DIRECTED]->(ThatThingYouDo) 235 | 236 | CREATE (TheReplacements:Movie {title:'The Replacements', released:2000, tagline:'Pain heals, Chicks dig scars... Glory lasts forever'}) 237 | CREATE (Brooke:Person {name:'Brooke Langton', born:1970}) 238 | CREATE (Gene:Person {name:'Gene Hackman', born:1930}) 239 | CREATE (Orlando:Person {name:'Orlando Jones', born:1968}) 240 | CREATE (Howard:Person {name:'Howard Deutch', born:1950}) 241 | CREATE 242 | (Keanu)-[:ACTED_IN {roles:['Shane Falco']}]->(TheReplacements), 243 | (Brooke)-[:ACTED_IN {roles:['Annabelle Farrell']}]->(TheReplacements), 244 | (Gene)-[:ACTED_IN {roles:['Jimmy McGinty']}]->(TheReplacements), 245 | (Orlando)-[:ACTED_IN {roles:['Clifford Franklin']}]->(TheReplacements), 246 | (Howard)-[:DIRECTED]->(TheReplacements) 247 | 248 | CREATE (RescueDawn:Movie {title:'RescueDawn', released:2006, tagline:"Based on the extraordinary true story of one man's fight for freedom"}) 249 | CREATE (ChristianB:Person {name:'Christian Bale', born:1974}) 250 | CREATE (ZachG:Person {name:'Zach Grenier', born:1954}) 251 | CREATE 252 | (MarshallB)-[:ACTED_IN {roles:['Admiral']}]->(RescueDawn), 253 | (ChristianB)-[:ACTED_IN {roles:['Dieter Dengler']}]->(RescueDawn), 254 | (ZachG)-[:ACTED_IN {roles:['Squad Leader']}]->(RescueDawn), 255 | (SteveZ)-[:ACTED_IN {roles:['Duane']}]->(RescueDawn), 256 | (WernerH)-[:DIRECTED]->(RescueDawn) 257 | 258 | CREATE (TheBirdcage:Movie {title:'The Birdcage', released:1996, tagline:'Come as you are'}) 259 | CREATE (MikeN:Person {name:'Mike Nichols', born:1931}) 260 | CREATE 261 | (Robin)-[:ACTED_IN {roles:['Armand Goldman']}]->(TheBirdcage), 262 | (Nathan)-[:ACTED_IN {roles:['Albert Goldman']}]->(TheBirdcage), 263 | (Gene)-[:ACTED_IN {roles:['Sen. Kevin Keeley']}]->(TheBirdcage), 264 | (MikeN)-[:DIRECTED]->(TheBirdcage) 265 | 266 | CREATE (Unforgiven:Movie {title:'Unforgiven', released:1992, tagline:"It's a hell of a thing, killing a man"}) 267 | CREATE (RichardH:Person {name:'Richard Harris', born:1930}) 268 | CREATE (ClintE:Person {name:'Clint Eastwood', born:1930}) 269 | CREATE 270 | (RichardH)-[:ACTED_IN {roles:['English Bob']}]->(Unforgiven), 271 | (ClintE)-[:ACTED_IN {roles:['Bill Munny']}]->(Unforgiven), 272 | (Gene)-[:ACTED_IN {roles:['Little Bill Daggett']}]->(Unforgiven), 273 | (ClintE)-[:DIRECTED]->(Unforgiven) 274 | 275 | CREATE (JohnnyMnemonic:Movie {title:'Johnny Mnemonic', released:1995, tagline:'The hottest data on earth. In the coolest head in town'}) 276 | CREATE (Takeshi:Person {name:'Takeshi Kitano', born:1947}) 277 | CREATE (Dina:Person {name:'Dina Meyer', born:1968}) 278 | CREATE (IceT:Person {name:'Ice-T', born:1958}) 279 | CREATE (RobertL:Person {name:'Robert Longo', born:1953}) 280 | CREATE 281 | (Keanu)-[:ACTED_IN {roles:['Johnny Mnemonic']}]->(JohnnyMnemonic), 282 | (Takeshi)-[:ACTED_IN {roles:['Takahashi']}]->(JohnnyMnemonic), 283 | (Dina)-[:ACTED_IN {roles:['Jane']}]->(JohnnyMnemonic), 284 | (IceT)-[:ACTED_IN {roles:['J-Bone']}]->(JohnnyMnemonic), 285 | (RobertL)-[:DIRECTED]->(JohnnyMnemonic) 286 | 287 | CREATE (CloudAtlas:Movie {title:'Cloud Atlas', released:2012, tagline:'Everything is connected'}) 288 | CREATE (HalleB:Person {name:'Halle Berry', born:1966}) 289 | CREATE (JimB:Person {name:'Jim Broadbent', born:1949}) 290 | CREATE (TomT:Person {name:'Tom Tykwer', born:1965}) 291 | CREATE (DavidMitchell:Person {name:'David Mitchell', born:1969}) 292 | CREATE (StefanArndt:Person {name:'Stefan Arndt', born:1961}) 293 | CREATE 294 | (TomH)-[:ACTED_IN {roles:['Zachry', 'Dr. Henry Goose', 'Isaac Sachs', 'Dermot Hoggins']}]->(CloudAtlas), 295 | (Hugo)-[:ACTED_IN {roles:['Bill Smoke', 'Haskell Moore', 'Tadeusz Kesselring', 'Nurse Noakes', 'Boardman Mephi', 'Old Georgie']}]->(CloudAtlas), 296 | (HalleB)-[:ACTED_IN {roles:['Luisa Rey', 'Jocasta Ayrs', 'Ovid', 'Meronym']}]->(CloudAtlas), 297 | (JimB)-[:ACTED_IN {roles:['Vyvyan Ayrs', 'Captain Molyneux', 'Timothy Cavendish']}]->(CloudAtlas), 298 | (TomT)-[:DIRECTED]->(CloudAtlas), 299 | (LillyW)-[:DIRECTED]->(CloudAtlas), 300 | (LanaW)-[:DIRECTED]->(CloudAtlas), 301 | (DavidMitchell)-[:WROTE]->(CloudAtlas), 302 | (StefanArndt)-[:PRODUCED]->(CloudAtlas) 303 | 304 | CREATE (TheDaVinciCode:Movie {title:'The Da Vinci Code', released:2006, tagline:'Break The Codes'}) 305 | CREATE (IanM:Person {name:'Ian McKellen', born:1939}) 306 | CREATE (AudreyT:Person {name:'Audrey Tautou', born:1976}) 307 | CREATE (PaulB:Person {name:'Paul Bettany', born:1971}) 308 | CREATE (RonH:Person {name:'Ron Howard', born:1954}) 309 | CREATE 310 | (TomH)-[:ACTED_IN {roles:['Dr. Robert Langdon']}]->(TheDaVinciCode), 311 | (IanM)-[:ACTED_IN {roles:['Sir Leight Teabing']}]->(TheDaVinciCode), 312 | (AudreyT)-[:ACTED_IN {roles:['Sophie Neveu']}]->(TheDaVinciCode), 313 | (PaulB)-[:ACTED_IN {roles:['Silas']}]->(TheDaVinciCode), 314 | (RonH)-[:DIRECTED]->(TheDaVinciCode) 315 | 316 | CREATE (VforVendetta:Movie {title:'V for Vendetta', released:2006, tagline:'Freedom! Forever!'}) 317 | CREATE (NatalieP:Person {name:'Natalie Portman', born:1981}) 318 | CREATE (StephenR:Person {name:'Stephen Rea', born:1946}) 319 | CREATE (JohnH:Person {name:'John Hurt', born:1940}) 320 | CREATE (BenM:Person {name: 'Ben Miles', born:1967}) 321 | CREATE 322 | (Hugo)-[:ACTED_IN {roles:['V']}]->(VforVendetta), 323 | (NatalieP)-[:ACTED_IN {roles:['Evey Hammond']}]->(VforVendetta), 324 | (StephenR)-[:ACTED_IN {roles:['Eric Finch']}]->(VforVendetta), 325 | (JohnH)-[:ACTED_IN {roles:['High Chancellor Adam Sutler']}]->(VforVendetta), 326 | (BenM)-[:ACTED_IN {roles:['Dascomb']}]->(VforVendetta), 327 | (JamesM)-[:DIRECTED]->(VforVendetta), 328 | (LillyW)-[:PRODUCED]->(VforVendetta), 329 | (LanaW)-[:PRODUCED]->(VforVendetta), 330 | (JoelS)-[:PRODUCED]->(VforVendetta), 331 | (LillyW)-[:WROTE]->(VforVendetta), 332 | (LanaW)-[:WROTE]->(VforVendetta) 333 | 334 | CREATE (SpeedRacer:Movie {title:'Speed Racer', released:2008, tagline:'Speed has no limits'}) 335 | CREATE (EmileH:Person {name:'Emile Hirsch', born:1985}) 336 | CREATE (JohnG:Person {name:'John Goodman', born:1960}) 337 | CREATE (SusanS:Person {name:'Susan Sarandon', born:1946}) 338 | CREATE (MatthewF:Person {name:'Matthew Fox', born:1966}) 339 | CREATE (ChristinaR:Person {name:'Christina Ricci', born:1980}) 340 | CREATE (Rain:Person {name:'Rain', born:1982}) 341 | CREATE 342 | (EmileH)-[:ACTED_IN {roles:['Speed Racer']}]->(SpeedRacer), 343 | (JohnG)-[:ACTED_IN {roles:['Pops']}]->(SpeedRacer), 344 | (SusanS)-[:ACTED_IN {roles:['Mom']}]->(SpeedRacer), 345 | (MatthewF)-[:ACTED_IN {roles:['Racer X']}]->(SpeedRacer), 346 | (ChristinaR)-[:ACTED_IN {roles:['Trixie']}]->(SpeedRacer), 347 | (Rain)-[:ACTED_IN {roles:['Taejo Togokahn']}]->(SpeedRacer), 348 | (BenM)-[:ACTED_IN {roles:['Cass Jones']}]->(SpeedRacer), 349 | (LillyW)-[:DIRECTED]->(SpeedRacer), 350 | (LanaW)-[:DIRECTED]->(SpeedRacer), 351 | (LillyW)-[:WROTE]->(SpeedRacer), 352 | (LanaW)-[:WROTE]->(SpeedRacer), 353 | (JoelS)-[:PRODUCED]->(SpeedRacer) 354 | 355 | CREATE (NinjaAssassin:Movie {title:'Ninja Assassin', released:2009, tagline:'Prepare to enter a secret world of assassins'}) 356 | CREATE (NaomieH:Person {name:'Naomie Harris'}) 357 | CREATE 358 | (Rain)-[:ACTED_IN {roles:['Raizo']}]->(NinjaAssassin), 359 | (NaomieH)-[:ACTED_IN {roles:['Mika Coretti']}]->(NinjaAssassin), 360 | (RickY)-[:ACTED_IN {roles:['Takeshi']}]->(NinjaAssassin), 361 | (BenM)-[:ACTED_IN {roles:['Ryan Maslow']}]->(NinjaAssassin), 362 | (JamesM)-[:DIRECTED]->(NinjaAssassin), 363 | (LillyW)-[:PRODUCED]->(NinjaAssassin), 364 | (LanaW)-[:PRODUCED]->(NinjaAssassin), 365 | (JoelS)-[:PRODUCED]->(NinjaAssassin) 366 | 367 | CREATE (TheGreenMile:Movie {title:'The Green Mile', released:1999, tagline:"Walk a mile you'll never forget."}) 368 | CREATE (MichaelD:Person {name:'Michael Clarke Duncan', born:1957}) 369 | CREATE (DavidM:Person {name:'David Morse', born:1953}) 370 | CREATE (SamR:Person {name:'Sam Rockwell', born:1968}) 371 | CREATE (GaryS:Person {name:'Gary Sinise', born:1955}) 372 | CREATE (PatriciaC:Person {name:'Patricia Clarkson', born:1959}) 373 | CREATE (FrankD:Person {name:'Frank Darabont', born:1959}) 374 | CREATE 375 | (TomH)-[:ACTED_IN {roles:['Paul Edgecomb']}]->(TheGreenMile), 376 | (MichaelD)-[:ACTED_IN {roles:['John Coffey']}]->(TheGreenMile), 377 | (DavidM)-[:ACTED_IN {roles:['Brutus "Brutal" Howell']}]->(TheGreenMile), 378 | (BonnieH)-[:ACTED_IN {roles:['Jan Edgecomb']}]->(TheGreenMile), 379 | (JamesC)-[:ACTED_IN {roles:['Warden Hal Moores']}]->(TheGreenMile), 380 | (SamR)-[:ACTED_IN {roles:['"Wild Bill" Wharton']}]->(TheGreenMile), 381 | (GaryS)-[:ACTED_IN {roles:['Burt Hammersmith']}]->(TheGreenMile), 382 | (PatriciaC)-[:ACTED_IN {roles:['Melinda Moores']}]->(TheGreenMile), 383 | (FrankD)-[:DIRECTED]->(TheGreenMile) 384 | 385 | CREATE (FrostNixon:Movie {title:'Frost/Nixon', released:2008, tagline:'400 million people were waiting for the truth.'}) 386 | CREATE (FrankL:Person {name:'Frank Langella', born:1938}) 387 | CREATE (MichaelS:Person {name:'Michael Sheen', born:1969}) 388 | CREATE (OliverP:Person {name:'Oliver Platt', born:1960}) 389 | CREATE 390 | (FrankL)-[:ACTED_IN {roles:['Richard Nixon']}]->(FrostNixon), 391 | (MichaelS)-[:ACTED_IN {roles:['David Frost']}]->(FrostNixon), 392 | (KevinB)-[:ACTED_IN {roles:['Jack Brennan']}]->(FrostNixon), 393 | (OliverP)-[:ACTED_IN {roles:['Bob Zelnick']}]->(FrostNixon), 394 | (SamR)-[:ACTED_IN {roles:['James Reston, Jr.']}]->(FrostNixon), 395 | (RonH)-[:DIRECTED]->(FrostNixon) 396 | 397 | CREATE (Hoffa:Movie {title:'Hoffa', released:1992, tagline:"He didn't want law. He wanted justice."}) 398 | CREATE (DannyD:Person {name:'Danny DeVito', born:1944}) 399 | CREATE (JohnR:Person {name:'John C. Reilly', born:1965}) 400 | CREATE 401 | (JackN)-[:ACTED_IN {roles:['Hoffa']}]->(Hoffa), 402 | (DannyD)-[:ACTED_IN {roles:['Robert "Bobby" Ciaro']}]->(Hoffa), 403 | (JTW)-[:ACTED_IN {roles:['Frank Fitzsimmons']}]->(Hoffa), 404 | (JohnR)-[:ACTED_IN {roles:['Peter "Pete" Connelly']}]->(Hoffa), 405 | (DannyD)-[:DIRECTED]->(Hoffa) 406 | 407 | CREATE (Apollo13:Movie {title:'Apollo 13', released:1995, tagline:'Houston, we have a problem.'}) 408 | CREATE (EdH:Person {name:'Ed Harris', born:1950}) 409 | CREATE (BillPax:Person {name:'Bill Paxton', born:1955}) 410 | CREATE 411 | (TomH)-[:ACTED_IN {roles:['Jim Lovell']}]->(Apollo13), 412 | (KevinB)-[:ACTED_IN {roles:['Jack Swigert']}]->(Apollo13), 413 | (EdH)-[:ACTED_IN {roles:['Gene Kranz']}]->(Apollo13), 414 | (BillPax)-[:ACTED_IN {roles:['Fred Haise']}]->(Apollo13), 415 | (GaryS)-[:ACTED_IN {roles:['Ken Mattingly']}]->(Apollo13), 416 | (RonH)-[:DIRECTED]->(Apollo13) 417 | 418 | CREATE (Twister:Movie {title:'Twister', released:1996, tagline:"Don't Breathe. Don't Look Back."}) 419 | CREATE (PhilipH:Person {name:'Philip Seymour Hoffman', born:1967}) 420 | CREATE (JanB:Person {name:'Jan de Bont', born:1943}) 421 | CREATE 422 | (BillPax)-[:ACTED_IN {roles:['Bill Harding']}]->(Twister), 423 | (HelenH)-[:ACTED_IN {roles:['Dr. Jo Harding']}]->(Twister), 424 | (ZachG)-[:ACTED_IN {roles:['Eddie']}]->(Twister), 425 | (PhilipH)-[:ACTED_IN {roles:['Dustin "Dusty" Davis']}]->(Twister), 426 | (JanB)-[:DIRECTED]->(Twister) 427 | 428 | CREATE (CastAway:Movie {title:'Cast Away', released:2000, tagline:'At the edge of the world, his journey begins.'}) 429 | CREATE (RobertZ:Person {name:'Robert Zemeckis', born:1951}) 430 | CREATE 431 | (TomH)-[:ACTED_IN {roles:['Chuck Noland']}]->(CastAway), 432 | (HelenH)-[:ACTED_IN {roles:['Kelly Frears']}]->(CastAway), 433 | (RobertZ)-[:DIRECTED]->(CastAway) 434 | 435 | CREATE (OneFlewOvertheCuckoosNest:Movie {title:"One Flew Over the Cuckoo's Nest", released:1975, tagline:"If he's crazy, what does that make you?"}) 436 | CREATE (MilosF:Person {name:'Milos Forman', born:1932}) 437 | CREATE 438 | (JackN)-[:ACTED_IN {roles:['Randle McMurphy']}]->(OneFlewOvertheCuckoosNest), 439 | (DannyD)-[:ACTED_IN {roles:['Martini']}]->(OneFlewOvertheCuckoosNest), 440 | (MilosF)-[:DIRECTED]->(OneFlewOvertheCuckoosNest) 441 | 442 | CREATE (SomethingsGottaGive:Movie {title:"Something's Gotta Give", released:2003}) 443 | CREATE (DianeK:Person {name:'Diane Keaton', born:1946}) 444 | CREATE (NancyM:Person {name:'Nancy Meyers', born:1949}) 445 | CREATE 446 | (JackN)-[:ACTED_IN {roles:['Harry Sanborn']}]->(SomethingsGottaGive), 447 | (DianeK)-[:ACTED_IN {roles:['Erica Barry']}]->(SomethingsGottaGive), 448 | (Keanu)-[:ACTED_IN {roles:['Julian Mercer']}]->(SomethingsGottaGive), 449 | (NancyM)-[:DIRECTED]->(SomethingsGottaGive), 450 | (NancyM)-[:PRODUCED]->(SomethingsGottaGive), 451 | (NancyM)-[:WROTE]->(SomethingsGottaGive) 452 | 453 | CREATE (BicentennialMan:Movie {title:'Bicentennial Man', released:1999, tagline:"One robot's 200 year journey to become an ordinary man."}) 454 | CREATE (ChrisC:Person {name:'Chris Columbus', born:1958}) 455 | CREATE 456 | (Robin)-[:ACTED_IN {roles:['Andrew Marin']}]->(BicentennialMan), 457 | (OliverP)-[:ACTED_IN {roles:['Rupert Burns']}]->(BicentennialMan), 458 | (ChrisC)-[:DIRECTED]->(BicentennialMan) 459 | 460 | CREATE (CharlieWilsonsWar:Movie {title:"Charlie Wilson's War", released:2007, tagline:"A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire."}) 461 | CREATE (JuliaR:Person {name:'Julia Roberts', born:1967}) 462 | CREATE 463 | (TomH)-[:ACTED_IN {roles:['Rep. Charlie Wilson']}]->(CharlieWilsonsWar), 464 | (JuliaR)-[:ACTED_IN {roles:['Joanne Herring']}]->(CharlieWilsonsWar), 465 | (PhilipH)-[:ACTED_IN {roles:['Gust Avrakotos']}]->(CharlieWilsonsWar), 466 | (MikeN)-[:DIRECTED]->(CharlieWilsonsWar) 467 | 468 | CREATE (ThePolarExpress:Movie {title:'The Polar Express', released:2004, tagline:'This Holiday Season… Believe'}) 469 | CREATE 470 | (TomH)-[:ACTED_IN {roles:['Hero Boy', 'Father', 'Conductor', 'Hobo', 'Scrooge', 'Santa Claus']}]->(ThePolarExpress), 471 | (RobertZ)-[:DIRECTED]->(ThePolarExpress) 472 | 473 | CREATE (ALeagueofTheirOwn:Movie {title:'A League of Their Own', released:1992, tagline:'Once in a lifetime you get a chance to do something different.'}) 474 | CREATE (Madonna:Person {name:'Madonna', born:1954}) 475 | CREATE (GeenaD:Person {name:'Geena Davis', born:1956}) 476 | CREATE (LoriP:Person {name:'Lori Petty', born:1963}) 477 | CREATE (PennyM:Person {name:'Penny Marshall', born:1943}) 478 | CREATE 479 | (TomH)-[:ACTED_IN {roles:['Jimmy Dugan']}]->(ALeagueofTheirOwn), 480 | (GeenaD)-[:ACTED_IN {roles:['Dottie Hinson']}]->(ALeagueofTheirOwn), 481 | (LoriP)-[:ACTED_IN {roles:['Kit Keller']}]->(ALeagueofTheirOwn), 482 | (RosieO)-[:ACTED_IN {roles:['Doris Murphy']}]->(ALeagueofTheirOwn), 483 | (Madonna)-[:ACTED_IN {roles:['"All the Way" Mae Mordabito']}]->(ALeagueofTheirOwn), 484 | (BillPax)-[:ACTED_IN {roles:['Bob Hinson']}]->(ALeagueofTheirOwn), 485 | (PennyM)-[:DIRECTED]->(ALeagueofTheirOwn) 486 | 487 | CREATE (PaulBlythe:Person {name:'Paul Blythe'}) 488 | CREATE (AngelaScope:Person {name:'Angela Scope'}) 489 | CREATE (JessicaThompson:Person {name:'Jessica Thompson'}) 490 | CREATE (JamesThompson:Person {name:'James Thompson'}) 491 | 492 | CREATE 493 | (JamesThompson)-[:FOLLOWS]->(JessicaThompson), 494 | (AngelaScope)-[:FOLLOWS]->(JessicaThompson), 495 | (PaulBlythe)-[:FOLLOWS]->(AngelaScope) 496 | 497 | CREATE 498 | (JessicaThompson)-[:REVIEWED {summary:'An amazing journey', rating:95}]->(CloudAtlas), 499 | (JessicaThompson)-[:REVIEWED {summary:'Silly, but fun', rating:65}]->(TheReplacements), 500 | (JamesThompson)-[:REVIEWED {summary:'The coolest football movie ever', rating:100}]->(TheReplacements), 501 | (AngelaScope)-[:REVIEWED {summary:'Pretty funny at times', rating:62}]->(TheReplacements), 502 | (JessicaThompson)-[:REVIEWED {summary:'Dark, but compelling', rating:85}]->(Unforgiven), 503 | (JessicaThompson)-[:REVIEWED {summary:"Slapstick redeemed only by the Robin Williams and Gene Hackman's stellar performances", rating:45}]->(TheBirdcage), 504 | (JessicaThompson)-[:REVIEWED {summary:'A solid romp', rating:68}]->(TheDaVinciCode), 505 | (JamesThompson)-[:REVIEWED {summary:'Fun, but a little far fetched', rating:65}]->(TheDaVinciCode), 506 | (JessicaThompson)-[:REVIEWED {summary:'You had me at Jerry', rating:92}]->(JerryMaguire) -------------------------------------------------------------------------------- /examples/hello-world.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "# Hello World\n", 12 | "\n", 13 | "This notebook walks through basic code examples for integrating various packages with Neo4j, including `py2neo`, `ipython-cypher`, `pandas`, `networkx`, `igraph`, and `jgraph`." 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": { 19 | "slideshow": { 20 | "slide_type": "slide" 21 | } 22 | }, 23 | "source": [ 24 | "# py2neo\n", 25 | "\n", 26 | "`py2neo` is one of Neo4j's Python drivers. It offers a fully-featured interface for interacting with your data in Neo4j. Install `py2neo` with `pip install py2neo`." 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "metadata": { 32 | "slideshow": { 33 | "slide_type": "subslide" 34 | } 35 | }, 36 | "source": [ 37 | "## Connect\n", 38 | "\n", 39 | "Connect to Neo4j with the `Graph` class." 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 1, 45 | "metadata": { 46 | "collapsed": false, 47 | "slideshow": { 48 | "slide_type": "fragment" 49 | } 50 | }, 51 | "outputs": [], 52 | "source": [ 53 | "from py2neo import Graph\n", 54 | "\n", 55 | "graph = Graph()" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 2, 61 | "metadata": { 62 | "collapsed": true, 63 | "slideshow": { 64 | "slide_type": "skip" 65 | } 66 | }, 67 | "outputs": [], 68 | "source": [ 69 | "graph.delete_all()" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": { 75 | "slideshow": { 76 | "slide_type": "subslide" 77 | } 78 | }, 79 | "source": [ 80 | "## Nodes\n", 81 | "\n", 82 | "Create nodes with the `Node` class. The first argument is the node's label. The remaining arguments are an arbitrary amount of node properties or key-value pairs." 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 3, 88 | "metadata": { 89 | "collapsed": false, 90 | "slideshow": { 91 | "slide_type": "fragment" 92 | } 93 | }, 94 | "outputs": [], 95 | "source": [ 96 | "from py2neo import Node\n", 97 | "\n", 98 | "nicole = Node(\"Person\", name=\"Nicole\", age=24)\n", 99 | "drew = Node(\"Person\", name=\"Drew\", age=20)\n", 100 | "\n", 101 | "mtdew = Node(\"Drink\", name=\"Mountain Dew\", calories=9000)\n", 102 | "cokezero = Node(\"Drink\", name=\"Coke Zero\", calories=0)\n", 103 | "\n", 104 | "coke = Node(\"Manufacturer\", name=\"Coca Cola\")\n", 105 | "pepsi = Node(\"Manufacturer\", name=\"Pepsi\")\n", 106 | "\n", 107 | "graph.create(nicole | drew | mtdew | cokezero | coke | pepsi)" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": 4, 113 | "metadata": { 114 | "collapsed": false, 115 | "slideshow": { 116 | "slide_type": "subslide" 117 | } 118 | }, 119 | "outputs": [ 120 | { 121 | "data": { 122 | "text/html": [ 123 | "\n", 124 | " \n", 131 | " " 132 | ], 133 | "text/plain": [ 134 | "" 135 | ] 136 | }, 137 | "execution_count": 4, 138 | "metadata": {}, 139 | "output_type": "execute_result" 140 | } 141 | ], 142 | "source": [ 143 | "from scripts.vis import draw\n", 144 | "\n", 145 | "options = {\"Person\": \"name\", \"Drink\": \"name\", \"Manufacturer\": \"name\"}\n", 146 | "draw(graph, options)" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "metadata": { 152 | "slideshow": { 153 | "slide_type": "skip" 154 | } 155 | }, 156 | "source": [ 157 | "P.S. - If you want to check out what's going on behind the scenes for the `draw()` function used above, take a look at [`scripts/vis.py`](https://github.com/nicolewhite/neo4j-jupyter/blob/master/scripts/vis.py)." 158 | ] 159 | }, 160 | { 161 | "cell_type": "markdown", 162 | "metadata": { 163 | "slideshow": { 164 | "slide_type": "subslide" 165 | } 166 | }, 167 | "source": [ 168 | "## Relationships\n", 169 | "\n", 170 | "Create relationships between nodes with the `Relationship` class." 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 5, 176 | "metadata": { 177 | "collapsed": false, 178 | "slideshow": { 179 | "slide_type": "fragment" 180 | } 181 | }, 182 | "outputs": [ 183 | { 184 | "data": { 185 | "text/html": [ 186 | "\n", 187 | " \n", 194 | " " 195 | ], 196 | "text/plain": [ 197 | "" 198 | ] 199 | }, 200 | "execution_count": 5, 201 | "metadata": {}, 202 | "output_type": "execute_result" 203 | } 204 | ], 205 | "source": [ 206 | "from py2neo import Relationship\n", 207 | "\n", 208 | "graph.create(Relationship(nicole, \"LIKES\", cokezero))\n", 209 | "graph.create(Relationship(nicole, \"LIKES\", mtdew))\n", 210 | "graph.create(Relationship(drew, \"LIKES\", mtdew))\n", 211 | "graph.create(Relationship(coke, \"MAKES\", cokezero))\n", 212 | "graph.create(Relationship(pepsi, \"MAKES\", mtdew))\n", 213 | "\n", 214 | "draw(graph, options)" 215 | ] 216 | }, 217 | { 218 | "cell_type": "markdown", 219 | "metadata": { 220 | "slideshow": { 221 | "slide_type": "subslide" 222 | } 223 | }, 224 | "source": [ 225 | "## Cypher\n", 226 | "\n", 227 | "Retrieve Cypher query results with `Graph.cypher.execute`." 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": 6, 233 | "metadata": { 234 | "collapsed": false, 235 | "slideshow": { 236 | "slide_type": "fragment" 237 | } 238 | }, 239 | "outputs": [ 240 | { 241 | "name": "stdout", 242 | "output_type": "stream", 243 | "text": [ 244 | "('name': 'Drew', 'drink': 'Mountain Dew')\n", 245 | "('name': 'Nicole', 'drink': 'Mountain Dew')\n", 246 | "('name': 'Nicole', 'drink': 'Coke Zero')\n" 247 | ] 248 | } 249 | ], 250 | "source": [ 251 | "query = \"\"\"\n", 252 | "MATCH (person:Person)-[:LIKES]->(drink:Drink)\n", 253 | "RETURN person.name AS name, drink.name AS drink\n", 254 | "\"\"\"\n", 255 | "\n", 256 | "data = graph.run(query)\n", 257 | "\n", 258 | "for d in data:\n", 259 | " print(d)" 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": { 265 | "slideshow": { 266 | "slide_type": "subslide" 267 | } 268 | }, 269 | "source": [ 270 | "## Parameterized Cypher\n", 271 | "\n", 272 | "Pass parameters to Cypher queries by passing additional key-value arguments to `Graph.cypher.execute.` Parameters in Cypher are named and are wrapped in curly braces." 273 | ] 274 | }, 275 | { 276 | "cell_type": "code", 277 | "execution_count": 7, 278 | "metadata": { 279 | "collapsed": false, 280 | "slideshow": { 281 | "slide_type": "fragment" 282 | } 283 | }, 284 | "outputs": [ 285 | { 286 | "name": "stdout", 287 | "output_type": "stream", 288 | "text": [ 289 | "('name': 'Nicole', 'avg_calories': 4500.0)\n" 290 | ] 291 | } 292 | ], 293 | "source": [ 294 | "query = \"\"\"\n", 295 | "MATCH (p:Person)-[:LIKES]->(drink:Drink)\n", 296 | "WHERE p.name = {name}\n", 297 | "RETURN p.name AS name, AVG(drink.calories) AS avg_calories\n", 298 | "\"\"\"\n", 299 | "\n", 300 | "data = graph.run(query, name=\"Nicole\")\n", 301 | "\n", 302 | "for d in data:\n", 303 | " print(d)" 304 | ] 305 | }, 306 | { 307 | "cell_type": "markdown", 308 | "metadata": { 309 | "slideshow": { 310 | "slide_type": "slide" 311 | } 312 | }, 313 | "source": [ 314 | "# ipython-cypher\n", 315 | "\n", 316 | "`ipython-cypher` exposes `%cypher` magic in Jupyter. Install `ipython-cypher` with `pip install ipython-cypher`." 317 | ] 318 | }, 319 | { 320 | "cell_type": "code", 321 | "execution_count": 8, 322 | "metadata": { 323 | "collapsed": true, 324 | "slideshow": { 325 | "slide_type": "fragment" 326 | } 327 | }, 328 | "outputs": [], 329 | "source": [ 330 | "%load_ext cypher" 331 | ] 332 | }, 333 | { 334 | "cell_type": "markdown", 335 | "metadata": { 336 | "slideshow": { 337 | "slide_type": "subslide" 338 | } 339 | }, 340 | "source": [ 341 | "## Cypher\n", 342 | "\n", 343 | "`%cypher` is intended for one-line Cypher queries and `%%cypher` is intended for multi-line Cypher queries. Placing `%%cypher` above a Cypher query will display that query's results." 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": 9, 349 | "metadata": { 350 | "collapsed": false, 351 | "slideshow": { 352 | "slide_type": "fragment" 353 | } 354 | }, 355 | "outputs": [ 356 | { 357 | "name": "stdout", 358 | "output_type": "stream", 359 | "text": [ 360 | "3 rows affected.\n" 361 | ] 362 | }, 363 | { 364 | "data": { 365 | "text/html": [ 366 | "\n", 367 | " \n", 368 | " \n", 369 | " \n", 370 | " \n", 371 | " \n", 372 | " \n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " \n", 377 | " \n", 378 | " \n", 379 | " \n", 380 | " \n", 381 | " \n", 382 | " \n", 383 | " \n", 384 | " \n", 385 | " \n", 386 | " \n", 387 | "
person.namedrink.namedrink.calories
DrewMountain Dew9000
NicoleMountain Dew9000
NicoleCoke Zero0
" 388 | ], 389 | "text/plain": [ 390 | "[['Drew', 'Mountain Dew', 9000],\n", 391 | " ['Nicole', 'Mountain Dew', 9000],\n", 392 | " ['Nicole', 'Coke Zero', 0]]" 393 | ] 394 | }, 395 | "execution_count": 9, 396 | "metadata": {}, 397 | "output_type": "execute_result" 398 | } 399 | ], 400 | "source": [ 401 | "%%cypher\n", 402 | "MATCH (person:Person)-[:LIKES]->(drink:Drink)\n", 403 | "RETURN person.name, drink.name, drink.calories" 404 | ] 405 | }, 406 | { 407 | "cell_type": "markdown", 408 | "metadata": { 409 | "slideshow": { 410 | "slide_type": "subslide" 411 | } 412 | }, 413 | "source": [ 414 | "## Pandas Data Frames\n", 415 | "\n", 416 | "Cypher query results can be coerced to `pandas` data frames with the `get_dataframe` method. To assign Cypher query results to a variable, you need to use `%cypher` and separate lines with \\\\. You'll first need to install `pandas` with `pip install pandas`." 417 | ] 418 | }, 419 | { 420 | "cell_type": "code", 421 | "execution_count": 10, 422 | "metadata": { 423 | "collapsed": false, 424 | "slideshow": { 425 | "slide_type": "fragment" 426 | } 427 | }, 428 | "outputs": [ 429 | { 430 | "name": "stdout", 431 | "output_type": "stream", 432 | "text": [ 433 | "3 rows affected.\n" 434 | ] 435 | }, 436 | { 437 | "data": { 438 | "text/html": [ 439 | "
\n", 440 | "\n", 441 | " \n", 442 | " \n", 443 | " \n", 444 | " \n", 445 | " \n", 446 | " \n", 447 | " \n", 448 | " \n", 449 | " \n", 450 | " \n", 451 | " \n", 452 | " \n", 453 | " \n", 454 | " \n", 455 | " \n", 456 | " \n", 457 | " \n", 458 | " \n", 459 | " \n", 460 | " \n", 461 | " \n", 462 | " \n", 463 | " \n", 464 | " \n", 465 | "
namedrink
0DrewMountain Dew
1NicoleMountain Dew
2NicoleCoke Zero
\n", 466 | "
" 467 | ], 468 | "text/plain": [ 469 | " name drink\n", 470 | "0 Drew Mountain Dew\n", 471 | "1 Nicole Mountain Dew\n", 472 | "2 Nicole Coke Zero" 473 | ] 474 | }, 475 | "execution_count": 10, 476 | "metadata": {}, 477 | "output_type": "execute_result" 478 | } 479 | ], 480 | "source": [ 481 | "results = %cypher MATCH (person:Person)-[:LIKES]->(drink:Drink) \\\n", 482 | " RETURN person.name AS name, drink.name AS drink\n", 483 | " \n", 484 | "df = results.get_dataframe()\n", 485 | "\n", 486 | "df" 487 | ] 488 | }, 489 | { 490 | "cell_type": "code", 491 | "execution_count": 11, 492 | "metadata": { 493 | "collapsed": false, 494 | "slideshow": { 495 | "slide_type": "subslide" 496 | } 497 | }, 498 | "outputs": [ 499 | { 500 | "data": { 501 | "text/plain": [ 502 | "RangeIndex(start=0, stop=3, step=1)" 503 | ] 504 | }, 505 | "execution_count": 11, 506 | "metadata": {}, 507 | "output_type": "execute_result" 508 | } 509 | ], 510 | "source": [ 511 | "df.index" 512 | ] 513 | }, 514 | { 515 | "cell_type": "code", 516 | "execution_count": 12, 517 | "metadata": { 518 | "collapsed": false, 519 | "slideshow": { 520 | "slide_type": "subslide" 521 | } 522 | }, 523 | "outputs": [ 524 | { 525 | "data": { 526 | "text/html": [ 527 | "
\n", 528 | "\n", 529 | " \n", 530 | " \n", 531 | " \n", 532 | " \n", 533 | " \n", 534 | " \n", 535 | " \n", 536 | " \n", 537 | " \n", 538 | " \n", 539 | " \n", 540 | " \n", 541 | " \n", 542 | " \n", 543 | "
namedrink
1NicoleCoke Zero
\n", 544 | "
" 545 | ], 546 | "text/plain": [ 547 | " name drink\n", 548 | "1 Nicole Coke Zero" 549 | ] 550 | }, 551 | "execution_count": 12, 552 | "metadata": {}, 553 | "output_type": "execute_result" 554 | } 555 | ], 556 | "source": [ 557 | "df.iloc[[1]]" 558 | ] 559 | }, 560 | { 561 | "cell_type": "code", 562 | "execution_count": 12, 563 | "metadata": { 564 | "collapsed": false, 565 | "slideshow": { 566 | "slide_type": "subslide" 567 | } 568 | }, 569 | "outputs": [ 570 | { 571 | "data": { 572 | "text/plain": [ 573 | "0 Drew\n", 574 | "1 Nicole\n", 575 | "2 Nicole\n", 576 | "Name: name, dtype: object" 577 | ] 578 | }, 579 | "execution_count": 12, 580 | "metadata": {}, 581 | "output_type": "execute_result" 582 | } 583 | ], 584 | "source": [ 585 | "df[\"name\"]" 586 | ] 587 | }, 588 | { 589 | "cell_type": "markdown", 590 | "metadata": { 591 | "slideshow": { 592 | "slide_type": "subslide" 593 | } 594 | }, 595 | "source": [ 596 | "## NetworkX Graphs\n", 597 | "\n", 598 | "Cypher query results can be coerced to `NetworkX` MultiDiGraphs, graphs that permit multiple edges between nodes, with the `get_graph` method. You'll first need to install `NetworkX` with `pip install networkx`." 599 | ] 600 | }, 601 | { 602 | "cell_type": "code", 603 | "execution_count": 13, 604 | "metadata": { 605 | "collapsed": false, 606 | "slideshow": { 607 | "slide_type": "fragment" 608 | } 609 | }, 610 | "outputs": [ 611 | { 612 | "name": "stdout", 613 | "output_type": "stream", 614 | "text": [ 615 | "3 rows affected.\n" 616 | ] 617 | }, 618 | { 619 | "data": { 620 | "image/png": "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\n", 621 | "text/plain": [ 622 | "" 623 | ] 624 | }, 625 | "metadata": {}, 626 | "output_type": "display_data" 627 | } 628 | ], 629 | "source": [ 630 | "import networkx as nx\n", 631 | "%matplotlib inline\n", 632 | "\n", 633 | "results = %cypher MATCH p = (:Person)-[:LIKES]->(:Drink) RETURN p\n", 634 | "\n", 635 | "g = results.get_graph()\n", 636 | "\n", 637 | "nx.draw(g)" 638 | ] 639 | }, 640 | { 641 | "cell_type": "code", 642 | "execution_count": 14, 643 | "metadata": { 644 | "collapsed": false, 645 | "slideshow": { 646 | "slide_type": "subslide" 647 | } 648 | }, 649 | "outputs": [ 650 | { 651 | "data": { 652 | "text/plain": [ 653 | "[('4013', {'age': 24, 'labels': ['Person'], 'name': 'Nicole'}),\n", 654 | " ('4011', {'calories': 9000, 'labels': ['Drink'], 'name': 'Mountain Dew'}),\n", 655 | " ('4012', {'calories': 0, 'labels': ['Drink'], 'name': 'Coke Zero'}),\n", 656 | " ('4010', {'age': 20, 'labels': ['Person'], 'name': 'Drew'})]" 657 | ] 658 | }, 659 | "execution_count": 14, 660 | "metadata": {}, 661 | "output_type": "execute_result" 662 | } 663 | ], 664 | "source": [ 665 | "g.nodes(data=True)" 666 | ] 667 | }, 668 | { 669 | "cell_type": "code", 670 | "execution_count": 15, 671 | "metadata": { 672 | "collapsed": false, 673 | "slideshow": { 674 | "slide_type": "subslide" 675 | } 676 | }, 677 | "outputs": [ 678 | { 679 | "data": { 680 | "text/plain": [ 681 | "{'4010': 1, '4011': 2, '4012': 1, '4013': 2}" 682 | ] 683 | }, 684 | "execution_count": 15, 685 | "metadata": {}, 686 | "output_type": "execute_result" 687 | } 688 | ], 689 | "source": [ 690 | "nx.degree(g)" 691 | ] 692 | }, 693 | { 694 | "cell_type": "markdown", 695 | "metadata": { 696 | "slideshow": { 697 | "slide_type": "slide" 698 | } 699 | }, 700 | "source": [ 701 | "# igraph\n", 702 | "\n", 703 | "Cypher query results can be imported into `igraph` with `py2neo`. You'll need to install `igraph` with `pip install python-igraph`. Query results should be returned as edgelists, as `igraph` has a method for building an `igraph` object from a list of tuples representing edges between nodes." 704 | ] 705 | }, 706 | { 707 | "cell_type": "code", 708 | "execution_count": 26, 709 | "metadata": { 710 | "collapsed": false, 711 | "slideshow": { 712 | "slide_type": "fragment" 713 | } 714 | }, 715 | "outputs": [], 716 | "source": [ 717 | "from py2neo import Graph as PGraph\n", 718 | "from igraph import Graph as IGraph\n", 719 | "\n", 720 | "neo4j = PGraph()\n", 721 | "\n", 722 | "query = \"\"\"\n", 723 | "MATCH (person:Person)-[:LIKES]->(drink:Drink)\n", 724 | "RETURN person.name AS source, drink.name AS target\n", 725 | "\"\"\"\n", 726 | "\n", 727 | "data = neo4j.run(query)\n", 728 | "tups = []\n", 729 | "\n", 730 | "for d in data:\n", 731 | " tups.append((d[\"source\"], d[\"target\"]))" 732 | ] 733 | }, 734 | { 735 | "cell_type": "code", 736 | "execution_count": 27, 737 | "metadata": { 738 | "collapsed": false, 739 | "slideshow": { 740 | "slide_type": "subslide" 741 | } 742 | }, 743 | "outputs": [ 744 | { 745 | "data": { 746 | "text/plain": [ 747 | "" 748 | ] 749 | }, 750 | "execution_count": 27, 751 | "metadata": {}, 752 | "output_type": "execute_result" 753 | } 754 | ], 755 | "source": [ 756 | "ig = IGraph.TupleList(tups)\n", 757 | "\n", 758 | "ig" 759 | ] 760 | }, 761 | { 762 | "cell_type": "code", 763 | "execution_count": 28, 764 | "metadata": { 765 | "collapsed": false, 766 | "slideshow": { 767 | "slide_type": "subslide" 768 | } 769 | }, 770 | "outputs": [ 771 | { 772 | "data": { 773 | "text/plain": [ 774 | "['Mountain Dew', 'Nicole']" 775 | ] 776 | }, 777 | "execution_count": 28, 778 | "metadata": {}, 779 | "output_type": "execute_result" 780 | } 781 | ], 782 | "source": [ 783 | "best = ig.vs.select(_degree = ig.maxdegree())[\"name\"]\n", 784 | "best" 785 | ] 786 | }, 787 | { 788 | "cell_type": "markdown", 789 | "metadata": { 790 | "collapsed": true, 791 | "slideshow": { 792 | "slide_type": "slide" 793 | } 794 | }, 795 | "source": [ 796 | "# jgraph\n", 797 | "\n", 798 | "`jgraph` will plot tuple lists as 3D graphs." 799 | ] 800 | }, 801 | { 802 | "cell_type": "code", 803 | "execution_count": 20, 804 | "metadata": { 805 | "collapsed": false, 806 | "slideshow": { 807 | "slide_type": "fragment" 808 | } 809 | }, 810 | "outputs": [ 811 | { 812 | "data": { 813 | "text/html": [ 814 | "
\n", 815 | " " 858 | ], 859 | "text/plain": [ 860 | "" 861 | ] 862 | }, 863 | "metadata": {}, 864 | "output_type": "display_data" 865 | } 866 | ], 867 | "source": [ 868 | "import jgraph\n", 869 | "\n", 870 | "jgraph.draw([(1, 2), (2, 3), (3, 4), (4, 1), (4, 5), (5, 2)])" 871 | ] 872 | }, 873 | { 874 | "cell_type": "code", 875 | "execution_count": 21, 876 | "metadata": { 877 | "collapsed": false, 878 | "slideshow": { 879 | "slide_type": "subslide" 880 | } 881 | }, 882 | "outputs": [ 883 | { 884 | "data": { 885 | "text/html": [ 886 | "
\n", 887 | " " 930 | ], 931 | "text/plain": [ 932 | "" 933 | ] 934 | }, 935 | "metadata": {}, 936 | "output_type": "display_data" 937 | } 938 | ], 939 | "source": [ 940 | "data = graph.run(\"MATCH (n)-->(m) RETURN ID(n), ID(m)\")\n", 941 | "data = [tuple(x) for x in data]\n", 942 | "\n", 943 | "jgraph.draw(data)" 944 | ] 945 | } 946 | ], 947 | "metadata": { 948 | "celltoolbar": "Slideshow", 949 | "kernelspec": { 950 | "display_name": "Python 2", 951 | "language": "python", 952 | "name": "python2" 953 | }, 954 | "language_info": { 955 | "codemirror_mode": { 956 | "name": "ipython", 957 | "version": 2 958 | }, 959 | "file_extension": ".py", 960 | "mimetype": "text/x-python", 961 | "name": "python", 962 | "nbconvert_exporter": "python", 963 | "pygments_lexer": "ipython2", 964 | "version": "2.7.10" 965 | } 966 | }, 967 | "nbformat": 4, 968 | "nbformat_minor": 0 969 | } 970 | -------------------------------------------------------------------------------- /test/fixtures/references/hello-world.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "# Hello World\n", 12 | "\n", 13 | "This notebook walks through basic code examples for integrating various packages with Neo4j, including `py2neo`, `ipython-cypher`, `pandas`, `networkx`, `igraph`, and `jgraph`." 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": { 19 | "slideshow": { 20 | "slide_type": "slide" 21 | } 22 | }, 23 | "source": [ 24 | "# py2neo\n", 25 | "\n", 26 | "`py2neo` is one of Neo4j's Python drivers. It offers a fully-featured interface for interacting with your data in Neo4j. Install `py2neo` with `pip install py2neo`." 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "metadata": { 32 | "slideshow": { 33 | "slide_type": "subslide" 34 | } 35 | }, 36 | "source": [ 37 | "## Connect\n", 38 | "\n", 39 | "Connect to Neo4j with the `Graph` class." 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 1, 45 | "metadata": { 46 | "collapsed": false, 47 | "slideshow": { 48 | "slide_type": "fragment" 49 | } 50 | }, 51 | "outputs": [], 52 | "source": [ 53 | "from py2neo import Graph\n", 54 | "\n", 55 | "graph = Graph()" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 2, 61 | "metadata": { 62 | "collapsed": true, 63 | "slideshow": { 64 | "slide_type": "skip" 65 | } 66 | }, 67 | "outputs": [], 68 | "source": [ 69 | "graph.delete_all()" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": { 75 | "slideshow": { 76 | "slide_type": "subslide" 77 | } 78 | }, 79 | "source": [ 80 | "## Nodes\n", 81 | "\n", 82 | "Create nodes with the `Node` class. The first argument is the node's label. The remaining arguments are an arbitrary amount of node properties or key-value pairs." 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 3, 88 | "metadata": { 89 | "collapsed": false, 90 | "slideshow": { 91 | "slide_type": "fragment" 92 | } 93 | }, 94 | "outputs": [], 95 | "source": [ 96 | "from py2neo import Node\n", 97 | "\n", 98 | "nicole = Node(\"Person\", name=\"Nicole\", age=24)\n", 99 | "drew = Node(\"Person\", name=\"Drew\", age=20)\n", 100 | "\n", 101 | "mtdew = Node(\"Drink\", name=\"Mountain Dew\", calories=9000)\n", 102 | "cokezero = Node(\"Drink\", name=\"Coke Zero\", calories=0)\n", 103 | "\n", 104 | "coke = Node(\"Manufacturer\", name=\"Coca Cola\")\n", 105 | "pepsi = Node(\"Manufacturer\", name=\"Pepsi\")\n", 106 | "\n", 107 | "graph.create(nicole | drew | mtdew | cokezero | coke | pepsi)" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": 4, 113 | "metadata": { 114 | "collapsed": false, 115 | "slideshow": { 116 | "slide_type": "subslide" 117 | } 118 | }, 119 | "outputs": [ 120 | { 121 | "data": { 122 | "text/html": [ 123 | "\n", 124 | " \n", 131 | " " 132 | ], 133 | "text/plain": [ 134 | "" 135 | ] 136 | }, 137 | "execution_count": 4, 138 | "metadata": {}, 139 | "output_type": "execute_result" 140 | } 141 | ], 142 | "source": [ 143 | "from scripts.vis import draw\n", 144 | "\n", 145 | "options = {\"Person\": \"name\", \"Drink\": \"name\", \"Manufacturer\": \"name\"}\n", 146 | "draw(graph, options)" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "metadata": { 152 | "slideshow": { 153 | "slide_type": "skip" 154 | } 155 | }, 156 | "source": [ 157 | "P.S. - If you want to check out what's going on behind the scenes for the `draw()` function used above, take a look at [`scripts/vis.py`](https://github.com/nicolewhite/neo4j-jupyter/blob/master/scripts/vis.py)." 158 | ] 159 | }, 160 | { 161 | "cell_type": "markdown", 162 | "metadata": { 163 | "slideshow": { 164 | "slide_type": "subslide" 165 | } 166 | }, 167 | "source": [ 168 | "## Relationships\n", 169 | "\n", 170 | "Create relationships between nodes with the `Relationship` class." 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 5, 176 | "metadata": { 177 | "collapsed": false, 178 | "slideshow": { 179 | "slide_type": "fragment" 180 | } 181 | }, 182 | "outputs": [ 183 | { 184 | "data": { 185 | "text/html": [ 186 | "\n", 187 | " \n", 194 | " " 195 | ], 196 | "text/plain": [ 197 | "" 198 | ] 199 | }, 200 | "execution_count": 5, 201 | "metadata": {}, 202 | "output_type": "execute_result" 203 | } 204 | ], 205 | "source": [ 206 | "from py2neo import Relationship\n", 207 | "\n", 208 | "graph.create(Relationship(nicole, \"LIKES\", cokezero))\n", 209 | "graph.create(Relationship(nicole, \"LIKES\", mtdew))\n", 210 | "graph.create(Relationship(drew, \"LIKES\", mtdew))\n", 211 | "graph.create(Relationship(coke, \"MAKES\", cokezero))\n", 212 | "graph.create(Relationship(pepsi, \"MAKES\", mtdew))\n", 213 | "\n", 214 | "draw(graph, options)" 215 | ] 216 | }, 217 | { 218 | "cell_type": "markdown", 219 | "metadata": { 220 | "slideshow": { 221 | "slide_type": "subslide" 222 | } 223 | }, 224 | "source": [ 225 | "## Cypher\n", 226 | "\n", 227 | "Retrieve Cypher query results with `Graph.cypher.execute`." 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": 6, 233 | "metadata": { 234 | "collapsed": false, 235 | "slideshow": { 236 | "slide_type": "fragment" 237 | } 238 | }, 239 | "outputs": [ 240 | { 241 | "name": "stdout", 242 | "output_type": "stream", 243 | "text": [ 244 | "('name': 'Drew', 'drink': 'Mountain Dew')\n", 245 | "('name': 'Nicole', 'drink': 'Mountain Dew')\n", 246 | "('name': 'Nicole', 'drink': 'Coke Zero')\n" 247 | ] 248 | } 249 | ], 250 | "source": [ 251 | "query = \"\"\"\n", 252 | "MATCH (person:Person)-[:LIKES]->(drink:Drink)\n", 253 | "RETURN person.name AS name, drink.name AS drink\n", 254 | "\"\"\"\n", 255 | "\n", 256 | "data = graph.run(query)\n", 257 | "\n", 258 | "for d in data:\n", 259 | " print(d)" 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": { 265 | "slideshow": { 266 | "slide_type": "subslide" 267 | } 268 | }, 269 | "source": [ 270 | "## Parameterized Cypher\n", 271 | "\n", 272 | "Pass parameters to Cypher queries by passing additional key-value arguments to `Graph.cypher.execute.` Parameters in Cypher are named and are wrapped in curly braces." 273 | ] 274 | }, 275 | { 276 | "cell_type": "code", 277 | "execution_count": 7, 278 | "metadata": { 279 | "collapsed": false, 280 | "slideshow": { 281 | "slide_type": "fragment" 282 | } 283 | }, 284 | "outputs": [ 285 | { 286 | "name": "stdout", 287 | "output_type": "stream", 288 | "text": [ 289 | "('name': 'Nicole', 'avg_calories': 4500.0)\n" 290 | ] 291 | } 292 | ], 293 | "source": [ 294 | "query = \"\"\"\n", 295 | "MATCH (p:Person)-[:LIKES]->(drink:Drink)\n", 296 | "WHERE p.name = {name}\n", 297 | "RETURN p.name AS name, AVG(drink.calories) AS avg_calories\n", 298 | "\"\"\"\n", 299 | "\n", 300 | "data = graph.run(query, name=\"Nicole\")\n", 301 | "\n", 302 | "for d in data:\n", 303 | " print(d)" 304 | ] 305 | }, 306 | { 307 | "cell_type": "markdown", 308 | "metadata": { 309 | "slideshow": { 310 | "slide_type": "slide" 311 | } 312 | }, 313 | "source": [ 314 | "# ipython-cypher\n", 315 | "\n", 316 | "`ipython-cypher` exposes `%cypher` magic in Jupyter. Install `ipython-cypher` with `pip install ipython-cypher`." 317 | ] 318 | }, 319 | { 320 | "cell_type": "code", 321 | "execution_count": 8, 322 | "metadata": { 323 | "collapsed": true, 324 | "slideshow": { 325 | "slide_type": "fragment" 326 | } 327 | }, 328 | "outputs": [], 329 | "source": [ 330 | "%load_ext cypher" 331 | ] 332 | }, 333 | { 334 | "cell_type": "markdown", 335 | "metadata": { 336 | "slideshow": { 337 | "slide_type": "subslide" 338 | } 339 | }, 340 | "source": [ 341 | "## Cypher\n", 342 | "\n", 343 | "`%cypher` is intended for one-line Cypher queries and `%%cypher` is intended for multi-line Cypher queries. Placing `%%cypher` above a Cypher query will display that query's results." 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": 9, 349 | "metadata": { 350 | "collapsed": false, 351 | "slideshow": { 352 | "slide_type": "fragment" 353 | } 354 | }, 355 | "outputs": [ 356 | { 357 | "name": "stdout", 358 | "output_type": "stream", 359 | "text": [ 360 | "3 rows affected.\n" 361 | ] 362 | }, 363 | { 364 | "data": { 365 | "text/html": [ 366 | "\n", 367 | " \n", 368 | " \n", 369 | " \n", 370 | " \n", 371 | " \n", 372 | " \n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " \n", 377 | " \n", 378 | " \n", 379 | " \n", 380 | " \n", 381 | " \n", 382 | " \n", 383 | " \n", 384 | " \n", 385 | " \n", 386 | " \n", 387 | "
person.namedrink.namedrink.calories
DrewMountain Dew9000
NicoleMountain Dew9000
NicoleCoke Zero0
" 388 | ], 389 | "text/plain": [ 390 | "[['Drew', 'Mountain Dew', 9000],\n", 391 | " ['Nicole', 'Mountain Dew', 9000],\n", 392 | " ['Nicole', 'Coke Zero', 0]]" 393 | ] 394 | }, 395 | "execution_count": 9, 396 | "metadata": {}, 397 | "output_type": "execute_result" 398 | } 399 | ], 400 | "source": [ 401 | "%%cypher\n", 402 | "MATCH (person:Person)-[:LIKES]->(drink:Drink)\n", 403 | "RETURN person.name, drink.name, drink.calories" 404 | ] 405 | }, 406 | { 407 | "cell_type": "markdown", 408 | "metadata": { 409 | "slideshow": { 410 | "slide_type": "subslide" 411 | } 412 | }, 413 | "source": [ 414 | "## Pandas Data Frames\n", 415 | "\n", 416 | "Cypher query results can be coerced to `pandas` data frames with the `get_dataframe` method. To assign Cypher query results to a variable, you need to use `%cypher` and separate lines with \\\\. You'll first need to install `pandas` with `pip install pandas`." 417 | ] 418 | }, 419 | { 420 | "cell_type": "code", 421 | "execution_count": 10, 422 | "metadata": { 423 | "collapsed": false, 424 | "slideshow": { 425 | "slide_type": "fragment" 426 | } 427 | }, 428 | "outputs": [ 429 | { 430 | "name": "stdout", 431 | "output_type": "stream", 432 | "text": [ 433 | "3 rows affected.\n" 434 | ] 435 | }, 436 | { 437 | "data": { 438 | "text/html": [ 439 | "
\n", 440 | "\n", 441 | " \n", 442 | " \n", 443 | " \n", 444 | " \n", 445 | " \n", 446 | " \n", 447 | " \n", 448 | " \n", 449 | " \n", 450 | " \n", 451 | " \n", 452 | " \n", 453 | " \n", 454 | " \n", 455 | " \n", 456 | " \n", 457 | " \n", 458 | " \n", 459 | " \n", 460 | " \n", 461 | " \n", 462 | " \n", 463 | " \n", 464 | " \n", 465 | "
namedrink
0DrewMountain Dew
1NicoleMountain Dew
2NicoleCoke Zero
\n", 466 | "
" 467 | ], 468 | "text/plain": [ 469 | " name drink\n", 470 | "0 Drew Mountain Dew\n", 471 | "1 Nicole Mountain Dew\n", 472 | "2 Nicole Coke Zero" 473 | ] 474 | }, 475 | "execution_count": 10, 476 | "metadata": {}, 477 | "output_type": "execute_result" 478 | } 479 | ], 480 | "source": [ 481 | "results = %cypher MATCH (person:Person)-[:LIKES]->(drink:Drink) \\\n", 482 | " RETURN person.name AS name, drink.name AS drink\n", 483 | " \n", 484 | "df = results.get_dataframe()\n", 485 | "\n", 486 | "df" 487 | ] 488 | }, 489 | { 490 | "cell_type": "code", 491 | "execution_count": 11, 492 | "metadata": { 493 | "collapsed": false, 494 | "slideshow": { 495 | "slide_type": "subslide" 496 | } 497 | }, 498 | "outputs": [ 499 | { 500 | "data": { 501 | "text/plain": [ 502 | "RangeIndex(start=0, stop=3, step=1)" 503 | ] 504 | }, 505 | "execution_count": 11, 506 | "metadata": {}, 507 | "output_type": "execute_result" 508 | } 509 | ], 510 | "source": [ 511 | "df.index" 512 | ] 513 | }, 514 | { 515 | "cell_type": "code", 516 | "execution_count": 12, 517 | "metadata": { 518 | "collapsed": false, 519 | "slideshow": { 520 | "slide_type": "subslide" 521 | } 522 | }, 523 | "outputs": [ 524 | { 525 | "data": { 526 | "text/html": [ 527 | "
\n", 528 | "\n", 529 | " \n", 530 | " \n", 531 | " \n", 532 | " \n", 533 | " \n", 534 | " \n", 535 | " \n", 536 | " \n", 537 | " \n", 538 | " \n", 539 | " \n", 540 | " \n", 541 | " \n", 542 | " \n", 543 | "
namedrink
1NicoleCoke Zero
\n", 544 | "
" 545 | ], 546 | "text/plain": [ 547 | " name drink\n", 548 | "1 Nicole Coke Zero" 549 | ] 550 | }, 551 | "execution_count": 12, 552 | "metadata": {}, 553 | "output_type": "execute_result" 554 | } 555 | ], 556 | "source": [ 557 | "df.iloc[[1]]" 558 | ] 559 | }, 560 | { 561 | "cell_type": "code", 562 | "execution_count": 12, 563 | "metadata": { 564 | "collapsed": false, 565 | "slideshow": { 566 | "slide_type": "subslide" 567 | } 568 | }, 569 | "outputs": [ 570 | { 571 | "data": { 572 | "text/plain": [ 573 | "0 Drew\n", 574 | "1 Nicole\n", 575 | "2 Nicole\n", 576 | "Name: name, dtype: object" 577 | ] 578 | }, 579 | "execution_count": 12, 580 | "metadata": {}, 581 | "output_type": "execute_result" 582 | } 583 | ], 584 | "source": [ 585 | "df[\"name\"]" 586 | ] 587 | }, 588 | { 589 | "cell_type": "markdown", 590 | "metadata": { 591 | "slideshow": { 592 | "slide_type": "subslide" 593 | } 594 | }, 595 | "source": [ 596 | "## NetworkX Graphs\n", 597 | "\n", 598 | "Cypher query results can be coerced to `NetworkX` MultiDiGraphs, graphs that permit multiple edges between nodes, with the `get_graph` method. You'll first need to install `NetworkX` with `pip install networkx`." 599 | ] 600 | }, 601 | { 602 | "cell_type": "code", 603 | "execution_count": 13, 604 | "metadata": { 605 | "collapsed": false, 606 | "slideshow": { 607 | "slide_type": "fragment" 608 | } 609 | }, 610 | "outputs": [ 611 | { 612 | "name": "stdout", 613 | "output_type": "stream", 614 | "text": [ 615 | "3 rows affected.\n" 616 | ] 617 | }, 618 | { 619 | "data": { 620 | "image/png": "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\n", 621 | "text/plain": [ 622 | "" 623 | ] 624 | }, 625 | "metadata": {}, 626 | "output_type": "display_data" 627 | } 628 | ], 629 | "source": [ 630 | "import networkx as nx\n", 631 | "%matplotlib inline\n", 632 | "\n", 633 | "results = %cypher MATCH p = (:Person)-[:LIKES]->(:Drink) RETURN p\n", 634 | "\n", 635 | "g = results.get_graph()\n", 636 | "\n", 637 | "nx.draw(g)" 638 | ] 639 | }, 640 | { 641 | "cell_type": "code", 642 | "execution_count": 14, 643 | "metadata": { 644 | "collapsed": false, 645 | "slideshow": { 646 | "slide_type": "subslide" 647 | } 648 | }, 649 | "outputs": [ 650 | { 651 | "data": { 652 | "text/plain": [ 653 | "[('4013', {'age': 24, 'labels': ['Person'], 'name': 'Nicole'}),\n", 654 | " ('4011', {'calories': 9000, 'labels': ['Drink'], 'name': 'Mountain Dew'}),\n", 655 | " ('4012', {'calories': 0, 'labels': ['Drink'], 'name': 'Coke Zero'}),\n", 656 | " ('4010', {'age': 20, 'labels': ['Person'], 'name': 'Drew'})]" 657 | ] 658 | }, 659 | "execution_count": 14, 660 | "metadata": {}, 661 | "output_type": "execute_result" 662 | } 663 | ], 664 | "source": [ 665 | "g.nodes(data=True)" 666 | ] 667 | }, 668 | { 669 | "cell_type": "code", 670 | "execution_count": 15, 671 | "metadata": { 672 | "collapsed": false, 673 | "slideshow": { 674 | "slide_type": "subslide" 675 | } 676 | }, 677 | "outputs": [ 678 | { 679 | "data": { 680 | "text/plain": [ 681 | "{'4010': 1, '4011': 2, '4012': 1, '4013': 2}" 682 | ] 683 | }, 684 | "execution_count": 15, 685 | "metadata": {}, 686 | "output_type": "execute_result" 687 | } 688 | ], 689 | "source": [ 690 | "nx.degree(g)" 691 | ] 692 | }, 693 | { 694 | "cell_type": "markdown", 695 | "metadata": { 696 | "slideshow": { 697 | "slide_type": "slide" 698 | } 699 | }, 700 | "source": [ 701 | "# igraph\n", 702 | "\n", 703 | "Cypher query results can be imported into `igraph` with `py2neo`. You'll need to install `igraph` with `pip install python-igraph`. Query results should be returned as edgelists, as `igraph` has a method for building an `igraph` object from a list of tuples representing edges between nodes." 704 | ] 705 | }, 706 | { 707 | "cell_type": "code", 708 | "execution_count": 26, 709 | "metadata": { 710 | "collapsed": false, 711 | "slideshow": { 712 | "slide_type": "fragment" 713 | } 714 | }, 715 | "outputs": [], 716 | "source": [ 717 | "from py2neo import Graph as PGraph\n", 718 | "from igraph import Graph as IGraph\n", 719 | "\n", 720 | "neo4j = PGraph()\n", 721 | "\n", 722 | "query = \"\"\"\n", 723 | "MATCH (person:Person)-[:LIKES]->(drink:Drink)\n", 724 | "RETURN person.name AS source, drink.name AS target\n", 725 | "\"\"\"\n", 726 | "\n", 727 | "data = neo4j.run(query)\n", 728 | "tups = []\n", 729 | "\n", 730 | "for d in data:\n", 731 | " tups.append((d[\"source\"], d[\"target\"]))" 732 | ] 733 | }, 734 | { 735 | "cell_type": "code", 736 | "execution_count": 27, 737 | "metadata": { 738 | "collapsed": false, 739 | "slideshow": { 740 | "slide_type": "subslide" 741 | } 742 | }, 743 | "outputs": [ 744 | { 745 | "data": { 746 | "text/plain": [ 747 | "" 748 | ] 749 | }, 750 | "execution_count": 27, 751 | "metadata": {}, 752 | "output_type": "execute_result" 753 | } 754 | ], 755 | "source": [ 756 | "ig = IGraph.TupleList(tups)\n", 757 | "\n", 758 | "ig" 759 | ] 760 | }, 761 | { 762 | "cell_type": "code", 763 | "execution_count": 28, 764 | "metadata": { 765 | "collapsed": false, 766 | "slideshow": { 767 | "slide_type": "subslide" 768 | } 769 | }, 770 | "outputs": [ 771 | { 772 | "data": { 773 | "text/plain": [ 774 | "['Mountain Dew', 'Nicole']" 775 | ] 776 | }, 777 | "execution_count": 28, 778 | "metadata": {}, 779 | "output_type": "execute_result" 780 | } 781 | ], 782 | "source": [ 783 | "best = ig.vs.select(_degree = ig.maxdegree())[\"name\"]\n", 784 | "best" 785 | ] 786 | }, 787 | { 788 | "cell_type": "markdown", 789 | "metadata": { 790 | "collapsed": true, 791 | "slideshow": { 792 | "slide_type": "slide" 793 | } 794 | }, 795 | "source": [ 796 | "# jgraph\n", 797 | "\n", 798 | "`jgraph` will plot tuple lists as 3D graphs." 799 | ] 800 | }, 801 | { 802 | "cell_type": "code", 803 | "execution_count": 20, 804 | "metadata": { 805 | "collapsed": false, 806 | "slideshow": { 807 | "slide_type": "fragment" 808 | } 809 | }, 810 | "outputs": [ 811 | { 812 | "data": { 813 | "text/html": [ 814 | "
\n", 815 | " " 858 | ], 859 | "text/plain": [ 860 | "" 861 | ] 862 | }, 863 | "metadata": {}, 864 | "output_type": "display_data" 865 | } 866 | ], 867 | "source": [ 868 | "import jgraph\n", 869 | "\n", 870 | "jgraph.draw([(1, 2), (2, 3), (3, 4), (4, 1), (4, 5), (5, 2)])" 871 | ] 872 | }, 873 | { 874 | "cell_type": "code", 875 | "execution_count": 21, 876 | "metadata": { 877 | "collapsed": false, 878 | "slideshow": { 879 | "slide_type": "subslide" 880 | } 881 | }, 882 | "outputs": [ 883 | { 884 | "data": { 885 | "text/html": [ 886 | "
\n", 887 | " " 930 | ], 931 | "text/plain": [ 932 | "" 933 | ] 934 | }, 935 | "metadata": {}, 936 | "output_type": "display_data" 937 | } 938 | ], 939 | "source": [ 940 | "data = graph.run(\"MATCH (n)-->(m) RETURN ID(n), ID(m)\")\n", 941 | "data = [tuple(x) for x in data]\n", 942 | "\n", 943 | "jgraph.draw(data)" 944 | ] 945 | } 946 | ], 947 | "metadata": { 948 | "celltoolbar": "Slideshow", 949 | "kernelspec": { 950 | "display_name": "Python 2", 951 | "language": "python", 952 | "name": "python2" 953 | }, 954 | "language_info": { 955 | "codemirror_mode": { 956 | "name": "ipython", 957 | "version": 2 958 | }, 959 | "file_extension": ".py", 960 | "mimetype": "text/x-python", 961 | "name": "python", 962 | "nbconvert_exporter": "python", 963 | "pygments_lexer": "ipython2", 964 | "version": "2.7.10" 965 | } 966 | }, 967 | "nbformat": 4, 968 | "nbformat_minor": 0 969 | } 970 | --------------------------------------------------------------------------------