From e7bfd099d796667a6de20a5b52e37574a80080b4 Mon Sep 17 00:00:00 2001 From: theneelshah Date: Tue, 19 Aug 2025 11:03:04 -0700 Subject: [PATCH 1/4] Fix expected output in graph query tutorials (#755) Update expected results for "friends of friends not connected to Dave" exercise in Gremlin and openCypher tutorials to correctly show two results (Denise, Paras) instead of three. Co-authored-by: Neel Shah --- .../01-Gremlin/01-Basic-Read-Queries.ipynb | 2 +- .../01-Gremlin/Gremlin-Exercises-Answer-Sheet.ipynb | 2 +- .../02-openCypher/01-Basic-Read-Queries.ipynb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/01-Basic-Read-Queries.ipynb b/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/01-Basic-Read-Queries.ipynb index 9d789c85..0f14eb49 100644 --- a/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/01-Basic-Read-Queries.ipynb +++ b/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/01-Basic-Read-Queries.ipynb @@ -1038,7 +1038,7 @@ "\n", "* Find all the friends of friends that do not have a connection to Dave\n", "\n", - "The correct answer contains three results: \"Hank\", \"Denise\", \"Paras\"" + "The correct answer contains two results: \"Denise\", \"Paras\"" ] }, { diff --git a/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/Gremlin-Exercises-Answer-Sheet.ipynb b/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/Gremlin-Exercises-Answer-Sheet.ipynb index 0d0cd9cd..66458baf 100644 --- a/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/Gremlin-Exercises-Answer-Sheet.ipynb +++ b/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/Gremlin-Exercises-Answer-Sheet.ipynb @@ -128,7 +128,7 @@ "\n", "* Find all the friends of friends that do not have a connection to Dave\n", "\n", - "The correct answer contains three results: \"Hank\", \"Denise\", \"Paras\"" + "The correct answer contains two results: \"Denise\", \"Paras\"" ] }, { diff --git a/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/01-Basic-Read-Queries.ipynb b/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/01-Basic-Read-Queries.ipynb index 6a7021c0..f567fb41 100644 --- a/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/01-Basic-Read-Queries.ipynb +++ b/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/01-Basic-Read-Queries.ipynb @@ -839,7 +839,7 @@ "\n", "* Find all the friends of friends that do not have a connection to Dave\n", "\n", - "The correct answer contains three results: \"Hank\", \"Denise\", \"Paras\"" + "The correct answer contains two results: \"Denise\", \"Paras\"" ] }, { From f0f172832e3445280ae6a320bcaf3dd4fe7aae66 Mon Sep 17 00:00:00 2001 From: Aditya Ramesh <30495471+adityaramesh12@users.noreply.github.com> Date: Tue, 19 Aug 2025 15:08:14 -0700 Subject: [PATCH 2/4] bump to 5.0.2 (#757) --- ChangeLog.md | 5 +++++ README.md | 4 ++-- pyproject.toml | 2 +- src/graph_notebook/__init__.py | 2 +- src/graph_notebook/widgets/package.json | 16 ++++++++-------- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 49e3435a..2992483d 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -4,6 +4,11 @@ Starting with v1.31.6, this file will contain a record of major features and upd ## Upcoming +## Release 5.0.2 (Aug 19, 2025) +- Support edgeOnlyLoad param for load magic ([Link to PR](https://github.com/aws/graph-notebook/pull/750)) +- Theme support for Graph Notebook Widgets ([Link to PR](https://github.com/aws/graph-notebook/pull/754)) +- Fix expected output in graph query tutorials ([Link to PR](https://github.com/aws/graph-notebook/pull/755)) + ## Release 5.0.1 (May 19, 2025) - Locked numba dependency to 0.60.0 to avoid numpy conflict ([Link to PR](https://github.com/aws/graph-notebook/pull/735)) - Fixed library target for nbclassic nbextension for graph_notebook_widget ([Link to PR](https://github.com/aws/graph-notebook/pull/739)) diff --git a/README.md b/README.md index d6433c6e..cc8a7b6f 100644 --- a/README.md +++ b/README.md @@ -429,7 +429,7 @@ python3 -m build . You should now be able to find the built distribution at -`./dist/graph_notebook-5.0.1-py3-none-any.whl` +`./dist/graph_notebook-5.0.2-py3-none-any.whl` And use it by following the [installation](https://github.com/aws/graph-notebook#installation) steps, replacing @@ -440,7 +440,7 @@ pip install graph-notebook with ``` python -pip install ./dist/graph_notebook-5.0.1-py3-none-any.whl --force-reinstall +pip install ./dist/graph_notebook-5.0.2-py3-none-any.whl --force-reinstall ``` ## Contributing Guidelines diff --git a/pyproject.toml b/pyproject.toml index 2cab94eb..d657cc07 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "hatchling.build" [project] name = "graph-notebook" -version = "5.0.1" +version = "5.0.2" description = "Jupyter notebook extension to connect to graph databases" readme = "README.md" license = { file = "LICENSE" } diff --git a/src/graph_notebook/__init__.py b/src/graph_notebook/__init__.py index 31f16d9b..bf9a9000 100644 --- a/src/graph_notebook/__init__.py +++ b/src/graph_notebook/__init__.py @@ -3,4 +3,4 @@ SPDX-License-Identifier: Apache-2.0 """ -__version__ = '5.0.1' +__version__ = '5.0.2' diff --git a/src/graph_notebook/widgets/package.json b/src/graph_notebook/widgets/package.json index 9d38c584..188780c4 100644 --- a/src/graph_notebook/widgets/package.json +++ b/src/graph_notebook/widgets/package.json @@ -1,6 +1,6 @@ { "name": "graph_notebook_widgets", - "version": "5.0.1", + "version": "5.0.2", "author": "amazon", "description": "A Custom Jupyter Library for rendering NetworkX MultiDiGraphs using vis-network", "dependencies": { @@ -12,7 +12,7 @@ "vis-data": "7.1.4", "vis-network": "9.1.6", "vis-util": "5.0.3", - "@jupyterlab/application": "^4.3.5" + "@jupyterlab/application": "^4.3.5" }, "devDependencies": { "@jupyterlab/builder": "^4.3.5", @@ -60,7 +60,7 @@ "stylelint-csstree-validator": "^3.0.0", "stylelint-prettier": "^4.0.0", "ts-loader": "^9.4.4", - "typescript": "~5.0.4", + "typescript": "~5.0.4", "webpack": "^5.88.2", "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.15.1", @@ -81,7 +81,7 @@ "version": "^6.0.4", "requiredVersion": "^6.0.4" }, - "@jupyterlab/application": { + "@jupyterlab/application": { "bundled": false, "singleton": true, "version": "^4.0.0", @@ -115,7 +115,7 @@ } }, "jupyter": { - "extension": "extension.js", + "extension": "extension.js", "outputDir": "nbextension" }, "keywords": [ @@ -127,10 +127,10 @@ ], "main": "lib/index.js", "types": "lib/index.d.ts", - "engines": { + "engines": { "node": ">=20.18.3", "npm": ">=10.8.2" - }, + }, "scripts": { "build": "npm run build:lib && npm run build:nbextension && npm run build:labextension:dev", "build:prod": "npm run clean && npm run build:lib && npm run build:nbextension && npm run build:labextension", @@ -151,4 +151,4 @@ "watch:nbextension": "webpack --watch --mode=development", "watch:labextension": "jupyter labextension watch ." } -} +} \ No newline at end of file From 6fc775c3b3bdad24ff2fe8c122b8af2b41ef7119 Mon Sep 17 00:00:00 2001 From: Adam Myatt Date: Mon, 8 Sep 2025 13:49:21 -0400 Subject: [PATCH 3/4] Fix typos in Sample Notebooks (#760) * Update 01-Basic-Read-Queries.ipynb Fix minor typos and missing words. * Update 02-Variable-Length-Paths.ipynb Fix minor typos * Update 03-Ordering-Functions-Grouping.ipynb fix typos * Update 04-Creating-Updating-Delete-Queries.ipynb Fix typos * Update 01-Basic-Read-Queries.ipynb Fix typos * Update 02-Variable-Length-Paths.ipynb Fix typos * Update 03-Ordering-Functions-Grouping.ipynb Fix typos * Update openCypher-Exercises-Answer-Key.ipynb Fix typos * Update 01-Basic-Read-Queries.ipynb Fix typos and wording * Update 02-Loops-Repeats.ipynb Fix typos and applied better standardization of US vs UK spelling to be more consistent with other docs in this repo. * Update 03-Ordering-Functions-Grouping.ipynb Fix typos and standardize US vs UK wording to be more consistent * Update 04-Creating-Updating-Deleting-Queries.ipynb Fix typos and consistency issues * Update Gremlin-Exercises-Answer-Sheet.ipynb Fix typos and wording * Update 01-SPARQL-Basics.ipynb Fix minor typos --- .../01-Gremlin/01-Basic-Read-Queries.ipynb | 24 +++++++++---------- .../01-Gremlin/02-Loops-Repeats.ipynb | 20 ++++++++-------- .../03-Ordering-Functions-Grouping.ipynb | 12 +++++----- ...4-Creating-Updating-Deleting-Queries.ipynb | 20 ++++++++-------- .../Gremlin-Exercises-Answer-Sheet.ipynb | 4 ++-- .../02-openCypher/01-Basic-Read-Queries.ipynb | 16 ++++++------- .../02-Variable-Length-Paths.ipynb | 12 +++++----- .../03-Ordering-Functions-Grouping.ipynb | 12 +++++----- .../04-Creating-Updating-Delete-Queries.ipynb | 18 +++++++------- .../openCypher-Exercises-Answer-Key.ipynb | 4 ++-- .../03-SPARQL/01-SPARQL-Basics.ipynb | 16 ++++++------- 11 files changed, 79 insertions(+), 79 deletions(-) diff --git a/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/01-Basic-Read-Queries.ipynb b/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/01-Basic-Read-Queries.ipynb index 0f14eb49..60e81b77 100644 --- a/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/01-Basic-Read-Queries.ipynb +++ b/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/01-Basic-Read-Queries.ipynb @@ -20,7 +20,7 @@ "For these notebooks, we will be leveraging a dataset from the book [Graph Databases in Action](https://www.manning.com/books/graph-databases-in-action?a_aid=bechberger) from Manning Publications. \n", "\n", "\n", - "**Note** These notebooks do not cover data modeling or building a data loading pipeline. If you would like a more detailed description about how this dataset is constructed and the design of the data model came from then please read the book.\n", + "**Note** These notebooks do not cover data modeling or building a data loading pipeline. If you would like a more detailed description about how this dataset is constructed and where the design of the data model came from then please read the book.\n", "\n", "To get started, the first step is to load data into the cluster. Assuming the cluster is empty, this can be accomplished by running the cell below which will load our Dining By Friends data." ] @@ -34,7 +34,7 @@ "\n", "Throughout all the **Learning Gremlin on Neptune** notebooks, you will notice that each code block starts with either a `%` or `%%` command. These are called *workbench magic* commands, and are essentially shortcuts to specific Neptune APIs. For example:\n", "\n", - "* `%%gremlin` - issues a Gremlin query to the Neptune endpoint usng WebSockets\n", + "* `%%gremlin` - issues a Gremlin query to the Neptune endpoint using WebSockets\n", "* `%seed` - provides a convenient way to add sample data to your Neptune endpoint\n", "* `%load` - generates a form that you can use to submit a bulk load request to Neptune\n", "\n", @@ -99,7 +99,7 @@ " \"restaurant\": {\n", " \"color\": \"#ffe6cc\"\n", " },\n", - " \"cusine\": {\n", + " \"cuisine\": {\n", " \"color\": \"#fff2cc\"\n", " }\n", " }\n", @@ -233,7 +233,7 @@ "\n", "### Finding Nodes\n", "\n", - "The simplest traversal you can do in Gremlin is to search for nodes. In Gremlin traversals, nodes are represented by `V()`. In our example, *review*, *restaurant*, *cuisine*, *person*, *state* and *city* as represented as nodes.\n", + "The simplest traversal you can do in Gremlin is to search for nodes. In Gremlin traversals, nodes are represented by `V()`. In our example, *review*, *restaurant*, *cuisine*, *person*, *state* and *city* are represented as nodes.\n", "\n", "Execute the query below to search for all nodes and return them, but limit the number returned to 10." ] @@ -320,7 +320,7 @@ "id": "a3093ad2", "metadata": {}, "source": [ - "We can also do the same using the combination of `bothE()` and `otherV()`, instead of explicitly stating whether to travese outgoing or incoming edges." + "We can also do the same using the combination of `bothE()` and `otherV()`, instead of explicitly stating whether to traverse outgoing or incoming edges." ] }, { @@ -484,7 +484,7 @@ "metadata": {}, "source": [ "### Filtering Edge by Label\n", - "Another common item you to filter on is the type or label associated with an edge. As with nodes, you can use the `hasLabel()` step associated with an edge." + "Another common item to filter on is the type or label associated with an edge. As with nodes, you can use the `hasLabel()` step associated with an edge." ] }, { @@ -523,7 +523,7 @@ "%%gremlin -d $node_labels\n", "g.V()\n", ".hasLabel('person')\n", - ".where(out().hasLabel('person').count().is(gte(2))) // <-- filter only people who have at 2 or more friend connections\n", + ".where(out().hasLabel('person').count().is(gte(2))) // <-- filter only people who have 2 or more friend connections\n", ".outE()\n", " .hasLabel('friends')\n", ".inV()\n", @@ -537,7 +537,7 @@ "id": "c5abc463", "metadata": {}, "source": [ - "What if we wanted to get a list of all the restaurants in order to find out which cuisine's they serve? After all, all this learning has made me hungry!" + "What if we wanted to get a list of all the restaurants in order to find out which cuisines they serve? After all, all this learning has made me hungry!" ] }, { @@ -597,7 +597,7 @@ "id": "8b998cd1", "metadata": {}, "source": [ - "Because there are no properties associated to any of our edges, running the following query won't return any records. However, you can use it to see how the same concept of filtering nodes based on properties can be applied to edge.s" + "Because there are no properties associated to any of our edges, running the following query won't return any records. However, you can use it to see how the same concept of filtering nodes based on properties can be applied to edges." ] }, { @@ -768,7 +768,7 @@ "id": "0452038d", "metadata": {}, "source": [ - "**Note** When using `by()` after a `select()` you must specify the same number of `by()` statements as there are variables in the `select()`. Failing to doing so, will cause Gremlin to re-use whichever by() statements have been specified, starting with the first one. Now, this may not always be a problem, as we can see in the next example:" + "**Note** When using `by()` after a `select()` you must specify the same number of `by()` statements as there are variables in the `select()`. Failing to do so, will cause Gremlin to re-use whichever by() statements have been specified, starting with the first one. Now, this may not always be a problem, as we can see in the next example:" ] }, { @@ -823,7 +823,7 @@ "id": "943bdceb", "metadata": {}, "source": [ - "Notice in the above query how we've combined `project()` and `select()` to provide us with the same results. This is because to we've needed to alias specific portions of the incoming traversal, e.g. the node representing *Dave*, and the nodes representing Dave's *friends*.\n", + "Notice in the above query how we've combined `project()` and `select()` to provide us with the same results. This is because we've needed to alias specific portions of the incoming traversal, e.g. the node representing *Dave*, and the nodes representing Dave's *friends*.\n", "\n", "If we were to run the following query, you'll notice something very odd happen with the results." ] @@ -912,7 +912,7 @@ "\n", "In addition to returning simple key-value pairs, we can construct more complex responses. This is a common requirement, especially when returning aggregations or when returning attributes from different variables in the matched patterns.\n", "\n", - "These new projections are created by using the `by()` step modulator (which is discussed more in the Loops-Repeats notebook). As we're previous seen, for each traversal step, we write a `by()` step to apply to it. The example below shows how we can return a custom string with the statement \"*person* is friends with *person*\"." + "These new projections are created by using the `by()` step modulator (which is discussed more in the Loops-Repeats notebook). As we've previously seen, for each traversal step, we write a `by()` step to apply to it. The example below shows how we can return a custom string with the statement \"*person* is friends with *person*\"." ] }, { diff --git a/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/02-Loops-Repeats.ipynb b/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/02-Loops-Repeats.ipynb index 5824bfda..a389a696 100644 --- a/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/02-Loops-Repeats.ipynb +++ b/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/02-Loops-Repeats.ipynb @@ -54,7 +54,7 @@ " \"restaurant\": {\n", " \"color\": \"#ffe6cc\"\n", " },\n", - " \"cusine\": {\n", + " \"cuisine\": {\n", " \"color\": \"#fff2cc\"\n", " }\n", " }\n", @@ -76,7 +76,7 @@ "id": "e5bebb15", "metadata": {}, "source": [ - "We'll be using the `node_labels` variable to provide a nicer visualisation when running the queries in this notebook. To use it, we need to pass it along with the query itself, as follows:\n", + "We'll be using the `node_labels` variable to provide a nicer visualization when running the queries in this notebook. To use it, we need to pass it along with the query itself, as follows:\n", "\n", "`%%gremlin -d node_labels`\n", "\n", @@ -132,7 +132,7 @@ "\n", "The `repeat()` step also supports two 'modulators'; `until()` and `emit()`, which can be both used before or after the `repeat()` step. Using the `until()` step before the `repeat()` is similar to the common [`while...do`](https://www.w3schools.com/java/java_while_loop.asp) programming paradigm, whereas using the `until()` _after_ the `repeat()` is similar to the [`do...while`](https://www.w3schools.com/cpp/cpp_do_while_loop.asp) concept.\n", "\n", - "The `emit()` modulator works by returning the results of a traversal as it is executed, and can be useful when used in conjunction with other looping-limiting steps such as `times()`. An example of this is the query below where we want to limit the `repeat()` to two hops, however we also want to return paths which include only one hops." + "The `emit()` modulator works by returning the results of a traversal as it is executed, and can be useful when used in conjunction with other looping-limiting steps such as `times()`. An example of this is the query below where we want to limit the `repeat()` to two hops, however we also want to return paths which include only one hop." ] }, { @@ -446,7 +446,7 @@ "\n", "When repeating a traversal in Gremlin using the `repeat()` it's common to come across a pattern whereby the path loops back on itself. This is called a cyclic path, and can lead to your Gremlin queries looping forever.\n", "\n", - "To stop this from occurring, it's good practise to include the `simplePath()` step. This removes paths with repeated objects, thus ensuring cyclic paths are not traversed.\n", + "To stop this from occurring, it's good practice to include the `simplePath()` step. This removes paths with repeated objects, thus ensuring cyclic paths are not traversed.\n", "\n", "**Important**. The `simplePath()` filters for repeated object based on the previous step, such as `in()` or `out()`.\n", "\n", @@ -481,14 +481,14 @@ "metadata": {}, "source": [ "\n", - "### Visualising Results in a Neptune Notebook\n", + "### Visualizing Results in a Neptune Notebook\n", "\n", - "A key part of using any graph database is being able to visualise the way the objects stored within it are connected to each other. We've already shown how to do this in previous examples, however it's important to understand which of the Gremlin steps support this type of functionality.\n", + "A key part of using any graph database is being able to visualize the way the objects stored within it are connected to each other. We've already shown how to do this in previous examples, however it's important to understand which of the Gremlin steps support this type of functionality.\n", "\n", "* `path()` - used to provide access to all nodes and edges within each unique path traversed\n", "* `simplePath()` - used to ensure we don't repeat a traversal across an object we've already covered (this can lead to infinite looping if the model supports circular references)\n", "\n", - "If you're running this in a Neptune Notebook, we can use the `path()` step we tell the notebook to automatically present a visualisation of the output of a query. The following query returns 10 paths visualising the connections between `person`, `city`, `restaurant` and `cuisine`. Run the following query, and a graphical visualisation will automatically appear." + "If you're running this in a Neptune Notebook, we can use the `path()` step we tell the notebook to automatically present a visualization of the output of a query. The following query returns 10 paths visualizing the connections between `person`, `city`, `restaurant` and `cuisine`. Run the following query, and a graphical visualization will automatically appear." ] }, { @@ -595,7 +595,7 @@ "* Find the friends of that person (i.e. traverse the `friends` edge)\n", "* Return the friends `first_name`\n", "\n", - "The correct answer is a three results: \"Hank\", \"Denise\", \"Paras\"" + "The correct answer is three results: \"Hank\", \"Denise\", \"Paras\"" ] }, { @@ -676,7 +676,7 @@ "source": [ "## Conclusion\n", "\n", - "In this notebook, we explored writing looping and repeat queries in Gremlin. These queries are a powerful and common way to explore connected data to answer questions, especially those where the exact number of connection is unknown. \n", + "In this notebook, we explored writing looping and repeat queries in Gremlin. These queries are a powerful and common way to explore connected data to answer questions, especially those where the exact number of connections are unknown. \n", "\n", "In the next notebook we will take what we have learned in this notebook and extend it to demonstrate how to order, group, and aggregate values in queries." ] @@ -703,4 +703,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/03-Ordering-Functions-Grouping.ipynb b/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/03-Ordering-Functions-Grouping.ipynb index 6083bf87..bde83a2b 100644 --- a/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/03-Ordering-Functions-Grouping.ipynb +++ b/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/03-Ordering-Functions-Grouping.ipynb @@ -53,7 +53,7 @@ " \"restaurant\": {\n", " \"color\": \"#ffe6cc\"\n", " },\n", - " \"cusine\": {\n", + " \"cuisine\": {\n", " \"color\": \"#fff2cc\"\n", " }\n", " }\n", @@ -75,7 +75,7 @@ "id": "111cfabb", "metadata": {}, "source": [ - "We'll be using the `node_labels` variable to provide a nicer visualisation when running the queries in this notebook. To use it, we need to pass it along with the query itself, as follows:\n", + "We'll be using the `node_labels` variable to provide a nicer visualization when running the queries in this notebook. To use it, we need to pass it along with the query itself, as follows:\n", "\n", "`%%gremlin -d node_labels`\n", "\n", @@ -109,7 +109,7 @@ "\n", "When working with data, one common requirement is to return that data in a consistent and ordered fashion. \n", "\n", - "By default, data returned from an Gremlin query does not have a specified order, and consistent order cannot be assumed across multiple executions of the same query. To give our data a consistent order we must use the combination of the `order()` and `by()` steps. These enable you sort your results using the values that a query can return, such as nodes/edges, ID values, as well as via many expressions. \n", + "By default, data returned from a Gremlin query does not have a specified order, and consistent order cannot be assumed across multiple executions of the same query. To give our data a consistent order we must use the combination of the `order()` and `by()` steps. These enable you to sort your results using the values that a query can return, such as nodes/edges, ID values, as well as via many expressions. \n", "\n", "**Note** When the data being ordered contains a `null` value, these will be sorted to the end of the results for ascending sort order and the beginning of the list for descending sort order.\n", "\n", @@ -436,7 +436,7 @@ ".fold()\n", "\n", "// Results in:\n", - "// [\"Perryman's\", 'Spicy Heat', 'Rare Choice', 'Super Delish', 'Eastern Winds', 'Saucy-Cheesy-Saucers', 'With Pasta', 'With Brine', 'With Wine', 'U-S-A', 'Pick & Go', 'Rare Bull', 'Satiated', 'Good Bull', 'Southern Fire', 'With Salsa', 'With Curry', 'With Shell', 'Taters', 'Awesome Suace', 'Prancing Pony', 'Mexican Hut', 'Rabbitfood', 'Hand Roll', 'Northern Quench', 'Western Granola', 'With Noodles', 'With Sauce', 'Without Chaser', 'With Rice', 'Food For Thought', \"Dave's Big Deluxe\", 'Quick N Greasy', 'Lonely Grape', 'Breaded & Fried', 'All Night Long', 'Black Pit of Des Pair', 'Without Heat', 'With Ginger', 'Fat Fried Fast']" + "// [\"Perryman's\", 'Spicy Heat', 'Rare Choice', 'Super Delish', 'Eastern Winds', 'Saucy-Cheesy-Saucers', 'With Pasta', 'With Brine', 'With Wine', 'U-S-A', 'Pick & Go', 'Rare Bull', 'Satiated', 'Good Bull', 'Southern Fire', 'With Salsa', 'With Curry', 'With Shell', 'Taters', 'Awesome Sauce', 'Prancing Pony', 'Mexican Hut', 'Rabbitfood', 'Hand Roll', 'Northern Quench', 'Western Granola', 'With Noodles', 'With Sauce', 'Without Chaser', 'With Rice', 'Food For Thought', \"Dave's Big Deluxe\", 'Quick N Greasy', 'Lonely Grape', 'Breaded & Fried', 'All Night Long', 'Black Pit of Des Pair', 'Without Heat', 'With Ginger', 'Fat Fried Fast']" ] }, { @@ -589,7 +589,7 @@ "id": "e31b0932", "metadata": {}, "source": [ - "An important point to note that `union` works in the same way as other Gremlin steps in that it uses the *incoming traversal* as its starting point. \n", + "An important point to note is that `union` works in the same way as other Gremlin steps in that it uses the *incoming traversal* as its starting point. \n", "\n", "The following query traverses to the `With Pasta` restaurant and then uses a `union` step to combine the results with a traversal to the `With Wine` restaurants. Let's take a look at the results." ] @@ -638,7 +638,7 @@ "\n", "* `Lookup Cache` - This is an always-on caching technique, but is **only available for D instances, e.g. R5d, and not Serverless**. It uses local instance SSD storage to store property values (strings) or RDF literals for fast retrieval. This can be useful when frequently returning or filtering on a large number of property values.\n", "\n", - "As a general rule, you should look to optimise your queries by only filtering on, and returning properties that you need. \n", + "As a general rule, you should look to optimize your queries by only filtering on, and returning properties that you need. \n", "\n", "In addition, monitoring your cluster and instance health using [CloudWatch metrics](https://docs.aws.amazon.com/neptune/latest/userguide/cw-metrics.html) can alert you to causes for query performance degradation.\n" ] diff --git a/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/04-Creating-Updating-Deleting-Queries.ipynb b/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/04-Creating-Updating-Deleting-Queries.ipynb index 6bf47551..0c1f0ca3 100644 --- a/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/04-Creating-Updating-Deleting-Queries.ipynb +++ b/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/04-Creating-Updating-Deleting-Queries.ipynb @@ -51,7 +51,7 @@ " \"restaurant\": {\n", " \"color\": \"#ffe6cc\"\n", " },\n", - " \"cusine\": {\n", + " \"cuisine\": {\n", " \"color\": \"#fff2cc\"\n", " }\n", " }\n", @@ -83,11 +83,11 @@ "\n", "## Creating Data\n", "\n", - "When working with any database, one of the most common tasks is adding new data. To add new nodes, edges, or path in Gremlin we use the [`addV()`](https://tinkerpop.apache.org/docs/current/reference/#addvertex-step) step. \n", + "When working with any database, one of the most common tasks is adding new data. To add new nodes, edges, or paths in Gremlin we use the [`addV()`](https://tinkerpop.apache.org/docs/current/reference/#addvertex-step) step. \n", "\n", "\n", "### Creating a node with a label and properties\n", - "The simpliest option to create a node in Gremlin is to do a query similar to this:\n", + "The simplest option to create a node in Gremlin is to do a query similar to this:\n", "\n", "```\n", "g.addV()\n", @@ -221,7 +221,7 @@ "id": "e0d3236d", "metadata": {}, "source": [ - "Another way of performing the above is to use `aliases`. This can save us time and effort (and potentially costly typo's), effectively removing the need re-type the `from()` and `to()` steps." + "Another way of performing the above is to use `aliases`. This can save us time and effort (and potentially costly typos), effectively removing the need to re-type the `from()` and `to()` steps." ] }, { @@ -431,7 +431,7 @@ "g.mergeV([(T.id): 'jamie-1'])\n", " .option(onCreate, [(T.label): 'person', first_name: 'Jamie'])\n", " .option(onMatch, [age: 39])\n", - ".id() //not necessary, but helps to optimise the serialization of the output" + ".id() //not necessary, but helps to optimize the serialization of the output" ] }, { @@ -577,7 +577,7 @@ "source": [ "### Conditional Upserts using `mergeV()` ###\n", "\n", - "Sometimes you may only want to update a property depending on its current value. An example of this is `last_update_date` where you only want to update it if it's less than or equal to the new value. Combining the `onMatch` option with `sideEffect`, you can check the existing value of a property, and choose whether or not to update it." + "Sometimes you may only want to update a property depending on its current value. An example of this is `last_update_at` where you only want to update it if it's less than or equal to the new value. Combining the `onMatch` option with `sideEffect`, you can check the existing value of a property, and choose whether or not to update it." ] }, { @@ -601,7 +601,7 @@ " )\n", " ).constant([:]) // finally return an empty map\n", " )\n", - " .id() // not necessary, but helps to optimise the serialization of the output" + " .id() // not necessary, but helps to optimize the serialization of the output" ] }, { @@ -635,7 +635,7 @@ "metadata": {}, "source": [ "### Removing an Edge\n", - "To remove an edge(s) in Gremlin is very similar to removing a node, except that we need to pass the edge to the `drop` step. In the example below, we will remove any edges associated with nodes with the `first_name` of `Joesph` from our graph." + "To remove an edge(s) in Gremlin is very similar to removing a node, except that we need to pass the edge to the `drop` step. In the example below, we will remove any edges associated with nodes with the `first_name` of `Joseph` from our graph." ] }, { @@ -749,7 +749,7 @@ "* If the edge is created write a property `creation` with a value `Created`\n", "* If the edge already exists write a property `creation` with a value `Updated`\n", "* Return the new edge elements\n", - "* This query should be re-runable without creating new nodes or edges\n", + "* This query should be re-runnable without creating new nodes or edges\n", "\n", "The results for this query are the three edge elements" ] @@ -796,7 +796,7 @@ "source": [ "## Conclusion\n", "\n", - "In this notebook, we explored how to write queries to mutate data in Gremlin. In the next notebook, we'll be discovering how to read explain and profile outputs from your Gremlin queries in order to understand the data contained within each section, and how to use that to write performant queries." + "In this notebook, we explored how to write queries to mutate data in Gremlin. In the next notebook, we'll be discovering how to read, explain, and profile outputs from your Gremlin queries in order to understand the data contained within each section, and how to use that to write performant queries." ] } ], diff --git a/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/Gremlin-Exercises-Answer-Sheet.ipynb b/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/Gremlin-Exercises-Answer-Sheet.ipynb index 66458baf..be4f6a2b 100644 --- a/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/Gremlin-Exercises-Answer-Sheet.ipynb +++ b/src/graph_notebook/notebooks/04-Language-Tutorials/01-Gremlin/Gremlin-Exercises-Answer-Sheet.ipynb @@ -173,7 +173,7 @@ "* Find the friends of that person (i.e. traverse the `friends` edge)\n", "* Return the friends `first_name`\n", "\n", - "The correct answer is a three results: \"Hank\", \"Denise\", \"Paras\"" + "The correct answer is three results: \"Hank\", \"Denise\", \"Paras\"" ] }, { @@ -556,7 +556,7 @@ "* If the edge is created write a property `creation` with a value `Created`\n", "* If the edge already exists write a property `creation` with a value `Updated`\n", "* Return the new edge elements\n", - "* This query should be re-runable without creating new nodes or edges\n", + "* This query should be re-runnable without creating new nodes or edges\n", "\n", "The results for this query are the three edge elements" ] diff --git a/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/01-Basic-Read-Queries.ipynb b/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/01-Basic-Read-Queries.ipynb index f567fb41..01273622 100644 --- a/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/01-Basic-Read-Queries.ipynb +++ b/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/01-Basic-Read-Queries.ipynb @@ -87,7 +87,7 @@ "\n", "* Users, represented by `person` nodes\n", "* Users connected to Users via `friends` edges\n", - "* Restaurants and their associated information (`city`, `state`, `cusine`)\n", + "* Restaurants and their associated information (`city`, `state`, `cuisine`)\n", "* Reviews include the body and ratings\n", "* Ratings of reviews (helpful/not helpful)\n", "\n", @@ -136,7 +136,7 @@ " \"restaurant\": {\n", " \"color\": \"#ffe6cc\"\n", " },\n", - " \"cusine\": {\n", + " \"cuisine\": {\n", " \"color\": \"#fff2cc\"\n", " }\n", " }\n", @@ -150,7 +150,7 @@ "metadata": {}, "outputs": [], "source": [ - "node_labels = '{\"person\":\"first_name\",\"city\":\"name\",\"state\":\"name\",\"restaurant\":\"name\",\"cusine\":\"name\"}'" + "node_labels = '{\"person\":\"first_name\",\"city\":\"name\",\"state\":\"name\",\"restaurant\":\"name\",\"cuisine\":\"name\"}'" ] }, { @@ -165,11 +165,11 @@ "\n", "* `MATCH` - specifies the pattern of data to look for, see below for the pattern syntax\n", "* `RETURN` - defines what and how the data will be returned to the user\n", - "* `LIMIT` - while not required, this is useful to minimze the data returned by specifying the maximum number of matching patterns returned\n", + "* `LIMIT` - while not required, this is useful to minimize the data returned by specifying the maximum number of matching patterns returned\n", "\n", "The `MATCH` clause is a fundamental part of every query. It uses an ASCII art-based syntax to define the pattern of nodes and edges that you would like to match within the graph. These matches are then used as the basis for the filter and format portions of the query. \n", "\n", - "The pattern matching syntax used in is highlighted in the table below.\n", + "The pattern matching syntax used in openCypher is highlighted in the table below.\n", "\n", "#### Pattern Matching Syntax\n", "\n", @@ -383,7 +383,7 @@ "One item you might notice is the use of `IN` to find if the label exists in the node instead of an equality (`=`) operator. In openCypher, nodes can have multiple labels associated with them. As a result, the `labels()` function returns a list of labels and the `IN` clause allows us to find a specific label inside that list. \n", "\n", "### Filtering Edge by Type\n", - "Another common item you to filter on is the type or label associated with an edge. As with nodes, there are two methods for adding edge label filters to a query, as shown here: \n", + "Another common item you may want to filter on is the type or label associated with an edge. As with nodes, there are two methods for adding edge label filters to a query, as shown here: \n", "\n", "* Inline the filter as part of the match clause, which is done by adding a colon (`:`) followed by one or more type names (separated by a |) \n", "* Use the `type()` function in a `WHERE` clause to filter. \n", @@ -646,7 +646,7 @@ "source": [ "### Returning unique values\n", "\n", - "To return unique values in the results, use the `DISTINCT` clause in the `RETURN` statement to return just the unique values for the specified propery." + "To return unique values in the results, use the `DISTINCT` clause in the `RETURN` statement to return just the unique values for the specified property." ] }, { @@ -694,7 +694,7 @@ "\n", "In addition to returning existing values, we can also perform operations on those values using functions, which will be discussed in the third notebook), such as `min()`, `max()`, `toUpper()`, or perform other operations using primitives, such as concatenating strings. \n", "\n", - "In the example below, we show how you can return a full name by concatenating the `first_name` and `last_name` using one these primitive operators. The exact primitives available and their behavior vary depending on the incoming data types." + "In the example below, we show how you can return a full name by concatenating the `first_name` and `last_name` using one of these primitive operators. The exact primitives available and their behavior vary depending on the incoming data types." ] }, { diff --git a/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/02-Variable-Length-Paths.ipynb b/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/02-Variable-Length-Paths.ipynb index a111e41e..e9f7d5b6 100644 --- a/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/02-Variable-Length-Paths.ipynb +++ b/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/02-Variable-Length-Paths.ipynb @@ -13,7 +13,7 @@ "This notebook is the second in a series of notebooks that walk through how to write queries using openCypher. In this notebook, we will examine the basics of how to perform variable length path queries in openCypher. \n", "\n", "\n", - "This notebook will build upon the items convered in the notebook \"01-Basic-Read-Queries\". If you have not loaded the data from those notebooks please follow the steps in the [Getting Started](#Getting-Started) section below. If you have loaded the data then you can jump ahead to the [Setting up the visualizations](#Setting-up-the-visualizations) section.\n", + "This notebook will build upon the items covered in the notebook \"01-Basic-Read-Queries\". If you have not loaded the data from those notebooks please follow the steps in the [Getting Started](#Getting-Started) section below. If you have loaded the data then you can jump ahead to the [Setting up the visualizations](#Setting-up-the-visualizations) section.\n", "\n" ] }, @@ -96,7 +96,7 @@ "\n", "* Users, represented by `person` nodes\n", "* Users connected to Users via `friends` edges\n", - "* Restaurants and their associated information (`city`, `state`, `cusine`)\n", + "* Restaurants and their associated information (`city`, `state`, `cuisine`)\n", "* Reviews include the body and ratings\n", "* Ratings of reviews (helpful/not helpful)\n", "\n", @@ -145,7 +145,7 @@ " \"restaurant\": {\n", " \"color\": \"#ffe6cc\"\n", " },\n", - " \"cusine\": {\n", + " \"cuisine\": {\n", " \"color\": \"#fff2cc\"\n", " }\n", " }\n", @@ -159,7 +159,7 @@ "metadata": {}, "outputs": [], "source": [ - "node_labels = '{\"person\":\"first_name\",\"city\":\"name\",\"state\":\"name\",\"restaurant\":\"name\",\"cusine\":\"name\"}'" + "node_labels = '{\"person\":\"first_name\",\"city\":\"name\",\"state\":\"name\",\"restaurant\":\"name\",\"cuisine\":\"name\"}'" ] }, { @@ -370,7 +370,7 @@ "\n", "Starting at a single node and trying to find all connected children (a.k.a. root to leaf) or trying to find the parent of any child node (a.k.a leaf to root) are two very common hierarchical graph query patterns. Commonly, these queries supported bill of materials, information organization, or compliance use cases.\n", "\n", - "In this exercise, we will be applying that same query pattern to find the hierarchy of people within our social network. We'll accomplish this vby writing a \"root to leaf\" type query where the root node is our `Dave` node in the social network.\n", + "In this exercise, we will be applying that same query pattern to find the hierarchy of people within our social network. We'll accomplish this by writing a \"root to leaf\" type query where the root node is our `Dave` node in the social network.\n", "\n", "Using the data model above, write a query that will:\n", "\n", @@ -460,7 +460,7 @@ "source": [ "## Conclusion\n", "\n", - "In this notebook, we explored writing variable length path queries in openCypher queries. These queries are a powerful and common way to explore connected data to answer questions, especially those where the exact number of connection is unknown. \n", + "In this notebook, we explored writing variable length path queries in openCypher queries. These queries are a powerful and common way to explore connected data to answer questions, especially those where the exact number of connections are unknown. \n", "\n", "In the next notebook, we will take what we have learned in this notebook and extend it to demonstrate how to order, group, and aggregate values in queries." ] diff --git a/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/03-Ordering-Functions-Grouping.ipynb b/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/03-Ordering-Functions-Grouping.ipynb index 206fa58d..b2122f49 100644 --- a/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/03-Ordering-Functions-Grouping.ipynb +++ b/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/03-Ordering-Functions-Grouping.ipynb @@ -12,7 +12,7 @@ "\n", "This notebook is the third in a series of notebooks that walk through how to write queries using openCypher. \n", "\n", - "This notebook will build upon the items convered in the notebook \"01-Basic-Read-Queries\" and \"02-Variable-Length-Paths\". If you have not loaded the data from those notebooks, please follow the steps in the [Getting Started](#Getting-Started) section below. If you have loaded the data, then you can jump ahead to the [Setting up the visualizations](#Setting-up-the-visualizations) section.\n", + "This notebook will build upon the items covered in the notebook \"01-Basic-Read-Queries\" and \"02-Variable-Length-Paths\". If you have not loaded the data from those notebooks, please follow the steps in the [Getting Started](#Getting-Started) section below. If you have loaded the data, then you can jump ahead to the [Setting up the visualizations](#Setting-up-the-visualizations) section.\n", "\n", "## Getting Started\n", "\n", @@ -93,7 +93,7 @@ " \"restaurant\": {\n", " \"color\": \"#ffe6cc\"\n", " },\n", - " \"cusine\": {\n", + " \"cuisine\": {\n", " \"color\": \"#fff2cc\"\n", " }\n", " }\n", @@ -109,7 +109,7 @@ }, "outputs": [], "source": [ - "node_labels = '{\"person\":\"first_name\",\"city\":\"name\",\"state\":\"name\",\"restaurant\":\"name\",\"cusine\":\"name\"}'" + "node_labels = '{\"person\":\"first_name\",\"city\":\"name\",\"state\":\"name\",\"restaurant\":\"name\",\"cuisine\":\"name\"}'" ] }, { @@ -122,7 +122,7 @@ "\n", "The second law of thermodynamics states that in any process the entropy of the system is always increasing. However, when working with data, one common requirement is to return that data in a consistent and ordered fashion. \n", "\n", - "By default, data returned from an openCypher query does not have a specified order. To give our data a consistent order we must uss the `ORDER BY` clause. This clause enables you sort your results using the values that a query can return, such as nodes/edges, ID values, as well as via many expressions. \n", + "By default, data returned from an openCypher query does not have a specified order. To give our data a consistent order we must use the `ORDER BY` clause. This clause enables you sort your results using the values that a query can return, such as nodes/edges, ID values, as well as via many expressions. \n", "\n", "**Note:** When the data being ordered contains a `null` value, these will be sorted to the end of the results for ascending sort order and the beginning of the list for descending sort order.\n", "\n", @@ -401,7 +401,7 @@ "source": [ "### Composing multiple functions\n", "\n", - "Often you many need to chain or compose functions together to create more complex computations. While functions can be chained together in any part of a query where they are supported, the example below shows how to compose several functions together in the `RETURN`. \n", + "Often you may need to chain or compose functions together to create more complex computations. While functions can be chained together in any part of a query where they are supported, the example below shows how to compose several functions together in the `RETURN`. \n", "\n", "\n", "In this example, our query will find the average number of words in the `name` for restaurants in the graph." @@ -439,7 +439,7 @@ "* A grouping key (`RETURN n, count(n)`)\n", "* A local variable\n", "\n", - "These expressions ensure that the aggregation is be computed over all the results within a group. Groups are determined through the grouping keys. Grouping keys are non-aggregate expressions, that are specfied in conjunction with the aggregate functions are are used to group the values. Let's look at an example to understand how this works.\n", + "These expressions ensure that the aggregation is be computed over all the results within a group. Groups are determined through the grouping keys. Grouping keys are non-aggregate expressions, that are specified in conjunction with the aggregate functions and are used to group the values. Let's look at an example to understand how this works.\n", "\n", "**Example**\n", "|id|name|\n", diff --git a/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/04-Creating-Updating-Delete-Queries.ipynb b/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/04-Creating-Updating-Delete-Queries.ipynb index ab4f3517..00dd30b3 100644 --- a/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/04-Creating-Updating-Delete-Queries.ipynb +++ b/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/04-Creating-Updating-Delete-Queries.ipynb @@ -12,7 +12,7 @@ "\n", "This notebook is the fourth in a series of notebooks that walk through how to write queries using openCypher.\n", "\n", - "This notebook will build upon the items convered in the notebook \"01-Basic-Read-Queries\", \"02-Variable-Length-Paths\", and \"03-Ordering-Functions-Grouping\". If you have not loaded the data from those notebooks, please follow the steps in the [Getting Started](#Getting-Started) section below. If you have loaded the data, then you can jump ahead to the [Setting up the visualizations](#Setting-up-the-visualizations) section.\n", + "This notebook will build upon the items covered in the notebook \"01-Basic-Read-Queries\", \"02-Variable-Length-Paths\", and \"03-Ordering-Functions-Grouping\". If you have not loaded the data from those notebooks, please follow the steps in the [Getting Started](#Getting-Started) section below. If you have loaded the data, then you can jump ahead to the [Setting up the visualizations](#Setting-up-the-visualizations) section.\n", "\n", "## Getting Started\n", "\n", @@ -86,7 +86,7 @@ "\n", "* Users, represented by `person` nodes\n", "* Users connected to Users via `friends` edges\n", - "* Restaurants and their associated information (`city`, `state`, `cusine`)\n", + "* Restaurants and their associated information (`city`, `state`, `cuisine`)\n", "* Reviews include the body and ratings\n", "* Ratings of reviews (helpful/not helpful)\n", "\n", @@ -135,7 +135,7 @@ " \"restaurant\": {\n", " \"color\": \"#ffe6cc\"\n", " },\n", - " \"cusine\": {\n", + " \"cuisine\": {\n", " \"color\": \"#fff2cc\"\n", " }\n", " }\n", @@ -149,7 +149,7 @@ "metadata": {}, "outputs": [], "source": [ - "node_labels = '{\"person\":\"first_name\",\"city\":\"name\",\"state\":\"name\",\"restaurant\":\"name\",\"cusine\":\"name\"}'" + "node_labels = '{\"person\":\"first_name\",\"city\":\"name\",\"state\":\"name\",\"restaurant\":\"name\",\"cuisine\":\"name\"}'" ] }, { @@ -164,7 +164,7 @@ "\n", "\n", "### Creating a node with a label and properties\n", - "The simpliest option to create a node in openCypher is to do a query similar to this:\n", + "The simplest option to create a node in openCypher is to do a query similar to this:\n", "\n", "```\n", "CREATE (n)\n", @@ -283,7 +283,7 @@ "CREATE (jim)-[:friends]->(joe)\n", "```\n", "\n", - "While this is reasonable approach, we can increase the readability of this query a bit by adding them all within a single statement as shown below." + "While this is a reasonable approach, we can increase the readability of this query a bit by adding them all within a single statement as shown below." ] }, { @@ -453,7 +453,7 @@ "source": [ "### Upserting edges\n", "\n", - "The `MERGE` clause can also be used on to upsert edges. Let's take the query we used above to create edges and update it to use `MERGE` instead." + "The `MERGE` clause can also be used to upsert edges. Let's take the query we used above to create edges and update it to use `MERGE` instead." ] }, { @@ -632,7 +632,7 @@ "id": "04ab5b59", "metadata": {}, "source": [ - "### Exercise M-2 Upsert the a list of followers and add an edge to `Dave`?\n", + "### Exercise M-2 Upsert a list of followers and add an edge to `Dave`?\n", "\n", "Using the data model above, write a query that will:\n", "\n", @@ -645,7 +645,7 @@ "* If the edge is created write a property `creation` with a value `Created`\n", "* If the edge already exists write a property `creation` with a value `Updated`\n", "* Return the new edge elements\n", - "* This query should be re-runable without creating new nodes or edges\n", + "* This query should be re-runnable without creating new nodes or edges\n", "\n", "The results for this query are the three edge elements" ] diff --git a/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/openCypher-Exercises-Answer-Key.ipynb b/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/openCypher-Exercises-Answer-Key.ipynb index ccb3c431..fe3c4756 100644 --- a/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/openCypher-Exercises-Answer-Key.ipynb +++ b/src/graph_notebook/notebooks/04-Language-Tutorials/02-openCypher/openCypher-Exercises-Answer-Key.ipynb @@ -40,7 +40,7 @@ " \"restaurant\": {\n", " \"color\": \"#ffe6cc\"\n", " },\n", - " \"cusine\": {\n", + " \"cuisine\": {\n", " \"color\": \"#fff2cc\"\n", " }\n", " }\n", @@ -359,7 +359,7 @@ "id": "882bb492", "metadata": {}, "source": [ - "### Exercise M-2 Upsert the a list of followers and add an edge to `Dave`?" + "### Exercise M-2 Upsert a list of followers and add an edge to `Dave`?" ] }, { diff --git a/src/graph_notebook/notebooks/04-Language-Tutorials/03-SPARQL/01-SPARQL-Basics.ipynb b/src/graph_notebook/notebooks/04-Language-Tutorials/03-SPARQL/01-SPARQL-Basics.ipynb index ef614788..05179e1e 100644 --- a/src/graph_notebook/notebooks/04-Language-Tutorials/03-SPARQL/01-SPARQL-Basics.ipynb +++ b/src/graph_notebook/notebooks/04-Language-Tutorials/03-SPARQL/01-SPARQL-Basics.ipynb @@ -21,9 +21,9 @@ "source": [ "# 01-SPARQL Basics\n", "\n", - "Working with Semantic Web knowledge graphs can be a daunting prospect for a newcomer. Having lots of new acronyms and terms to learn and understand – like RDF, SPARQL and knowledge graph – can been seen as a barrier of entry for these powerful technologies. \n", + "Working with Semantic Web knowledge graphs can be a daunting prospect for a newcomer. Having lots of new acronyms and terms to learn and understand – like RDF, SPARQL and knowledge graph – can be seen as a barrier to entry for these powerful technologies. \n", "\n", - "In reality, Semantic Web knowledge graph databases are a very easy and intuitive way to understand, record and traverse data. This is often easier to do using graph databases, and graph query languages, as they are optimized for these types of operation.\n", + "In reality, Semantic Web knowledge graph databases are a very easy and intuitive way to understand, record and traverse data. This is often easier to do using graph databases, and graph query languages, as they are optimized for these types of operations.\n", "\n", "In this notebook, we describe the basics of the SPARQL1.1 query language. \n", "\n", @@ -136,17 +136,17 @@ "\n", "## NAMESPACES\n", "\n", - "You will notice that throughout this guilde you will see either\n", + "You will notice that throughout this guide you will see either\n", "\n", "`@base .`\n", "\n", - "At the top of an RDF snippit, or\n", + "At the top of an RDF snippet, or\n", "\n", "`PREFIX : `\n", "\n", "At the top of a SPARQL query. \n", "\n", - "This is the BASE `NAMESPACE` or `PREFIX`, and provides a short hand way of writing RDF and SPARQL queries, by substituting the value of the PREFIX with the \":\" charachter in the SPARQL query or RDF snippit.\n", + "This is the BASE `NAMESPACE` or `PREFIX`, and provides a short hand way of writing RDF and SPARQL queries, by substituting the value of the PREFIX with the \":\" character in the SPARQL query or RDF snippit.\n", "\n", "For example:\n", "\n", @@ -938,7 +938,7 @@ "metadata": {}, "source": [ "*The result set:*\n", - "By concatinating the \"?publication\"s, we can get an easily readable result set. \n", + "By concatenating the \"?publication\"s, we can get an easily readable result set. \n", "\n", "\n", "\n", @@ -962,7 +962,7 @@ "id": "693a7fd3", "metadata": {}, "source": [ - "4. There are lots of useful functions you can use in conjuction with grouped results in SPARQL. Here we use the SUM function to find out how much it would cost to buy all of the publications published by each Author:" + "4. There are lots of useful functions you can use in conjunction with grouped results in SPARQL. Here we use the SUM function to find out how much it would cost to buy all of the publications published by each Author:" ] }, { @@ -1336,7 +1336,7 @@ "source": [ "### 1.8 DISTINCT\n", "\n", - "Distinct is another aggregator that behaves in an almost identical way to it’s namesake in many SQL databases. \n", + "Distinct is another aggregator that behaves in an almost identical way to its namesake in many SQL databases. \n", "Let's run a query to list all the people who have authored a publication:\n" ] }, From 528017f95bf894942c50122c06267b3cac7da7bd Mon Sep 17 00:00:00 2001 From: theneelshah Date: Tue, 23 Sep 2025 12:18:34 -0700 Subject: [PATCH 4/4] Show graph visualization for bolt queries (#761) * Add graph visualization support for bolt protocol requests Fixes #557 by converting bolt responses to compatible format for graph tab display. Transforms nodes and relationships to standardized format with ~id, ~entityType, ~labels/~type, and ~properties field. * Add graph visualization support for bolt protocol requests Fixes #557 by converting bolt responses to compatible format for graph tab display. Transforms nodes and relationships to standardized format with ~id, ~entityType, ~labels/~type, and ~properties field. * Update graph_magic.py --------- Co-authored-by: Neel Shah --- src/graph_notebook/magics/graph_magic.py | 28 +++++++++++++++++- src/graph_notebook/neptune/client.py | 29 ++++++++++++++++++- .../network/opencypher/OCNetwork.py | 3 +- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/graph_notebook/magics/graph_magic.py b/src/graph_notebook/magics/graph_magic.py index c1ecf3cf..a3400e01 100644 --- a/src/graph_notebook/magics/graph_magic.py +++ b/src/graph_notebook/magics/graph_magic.py @@ -3764,7 +3764,33 @@ def handle_opencypher_query(self, line, cell, local_ns): results_df, has_results = oc_results_df(res, res_format) if has_results: titles.append('Console') - # Need to eventually add code to parse and display a network for the bolt format here + # Create graph visualization for bolt response + try: + # Wrap bolt response in expected format + # Required because the graph visualizer need the data to be present in a certain format + transformed_res = {"results": res} if isinstance(res, list) else {"results": []} + + gn = OCNetwork(group_by_property=args.group_by, display_property=args.display_property, + group_by_raw=args.group_by_raw, + group_by_depth=args.group_by_depth, + edge_display_property=args.edge_display_property, + tooltip_property=args.tooltip_property, + edge_tooltip_property=args.edge_tooltip_property, + label_max_length=args.label_max_length, + edge_label_max_length=args.rel_label_max_length, + ignore_groups=args.ignore_groups) + gn.add_results(transformed_res) + logger.debug(f'number of nodes is {len(gn.graph.nodes)}') + if len(gn.graph.nodes) > 0: + self.graph_notebook_vis_options['physics']['disablePhysicsAfterInitialSimulation'] \ + = args.stop_physics + self.graph_notebook_vis_options['physics']['simulationDuration'] = args.simulation_duration + force_graph_output = Force(network=gn, options=self.graph_notebook_vis_options) + titles.append('Graph') + children.append(force_graph_output) + except (TypeError, ValueError) as network_creation_error: + logger.debug(f'Unable to create network from bolt result. Skipping from result set: {res}') + logger.debug(f'Error: {network_creation_error}') if not args.silent: if args.mode != 'explain': diff --git a/src/graph_notebook/neptune/client.py b/src/graph_notebook/neptune/client.py index 2a683452..e2e235e7 100644 --- a/src/graph_notebook/neptune/client.py +++ b/src/graph_notebook/neptune/client.py @@ -606,7 +606,34 @@ def opencyper_bolt(self, query: str, **kwargs): with driver.session(database=self.neo4j_database) as session: try: res = session.run(query, kwargs) - data = res.data() + data = [] + for record in res: + record_dict = {} + for key in record.keys(): + value = record[key] + if hasattr(value, 'id') and (hasattr(value, 'labels') or hasattr(value, 'type')): # Node or Relationship + if hasattr(value, 'labels'): # Node + record_dict[key] = { + '~id': str(value.id), + '~entityType': 'node', + '~labels': list(value.labels), + '~properties': dict(value) + } + elif hasattr(value, 'type'): # Relationship + + start_node = value.nodes[0] if value.nodes else None + end_node = value.nodes[1] if len(value.nodes) > 1 else None + record_dict[key] = { + '~id': str(value.id), + '~entityType': 'relationship', + '~start': str(start_node.id) if start_node else '', + '~end': str(end_node.id) if end_node else '', + '~type': value.type, + '~properties': dict(value) + } + else: + record_dict[key] = value + data.append(record_dict) except AuthError: print("Neo4J Bolt request failed with an authentication error. Please ensure that the 'neo4j' section " "of your %graph_notebook_config contains the correct credentials and auth setting.") diff --git a/src/graph_notebook/network/opencypher/OCNetwork.py b/src/graph_notebook/network/opencypher/OCNetwork.py index 3a21bd48..aa08f9d4 100644 --- a/src/graph_notebook/network/opencypher/OCNetwork.py +++ b/src/graph_notebook/network/opencypher/OCNetwork.py @@ -26,8 +26,7 @@ class OCNetwork(EventfulNetwork): """OCNetwork extends the EventfulNetwork class and uses the add_results method to parse any response that returns - nodes/relationships as part (or all) of the response. Currently this only works with HTTPS response format but in - the future, we will work to support Bolt based responses as well. + nodes/relationships as part (or all) of the response. """ def __init__(self, graph: MultiDiGraph = None, callbacks=None, label_max_length=DEFAULT_LABEL_MAX_LENGTH,