{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "# 11. Graph Databases\n\nStore a tiny TGraph as RDF triples, ask a SPARQL relationship query, and save the dataset locally. This is a gateway to RDF graph databases without a server or account.\n\n**Goal:** Complete one small workflow. Run the cells from top to bottom in a fresh kernel.\n\n**Extra dependencies:** `rdflib==7.2.1`. See the setup instructions before running.\n\nAll examples use metres unless stated otherwise.\n",
   "id": "tutorial-11-00"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": "import os\nos.environ[\"TOPOLOGICPY_CORE_BACKEND\"] = \"pythonocc\"\n\nimport topologicpy\nfrom topologicpy.Core import Core\n\nassert topologicpy.__version__ == \"0.9.86\", \"Use TopologicPy 0.9.86 for these tutorials.\"\nassert type(Core.Backend()).__name__ == \"PythonOCCBackend\", \"Restart with the pythonocc backend.\"\nprint(\"TopologicPy\", topologicpy.__version__, \"—\", type(Core.Backend()).__name__)\n",
   "outputs": [],
   "execution_count": null,
   "id": "tutorial-11-01"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Build the graph\n",
   "id": "tutorial-11-02"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": "from topologicpy.Vertex import Vertex\nfrom topologicpy.TGraph import TGraph\n\nentrance = Vertex.ByCoordinates(0, 0, 0)\nhall = Vertex.ByCoordinates(2, 0, 0)\nstudio = Vertex.ByCoordinates(4, 0, 0)\noffice = Vertex.ByCoordinates(2, 2, 0)\nvertices = [entrance, hall, studio, office]\nedges = [{\"src\": 0, \"dst\": 1},\n         {\"src\": 1, \"dst\": 2},\n         {\"src\": 1, \"dst\": 3}]\ngraph = TGraph.ByVerticesEdges(vertices, edges, directed=False)\nassert isinstance(graph, TGraph)\nassert len(TGraph.Vertices(graph)) == 4\nassert len(TGraph.Edges(graph)) == 3\n",
   "outputs": [],
   "execution_count": null,
   "id": "tutorial-11-03"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Map TGraph data into an RDF dataset\n\nAn RDF triple has a subject, predicate and object. A vertex becomes an identified resource; an edge becomes a `connects` relationship. `Dataset` is a storage/query container here: the spatial graph itself is created only with `TGraph`. This example uses a local in-memory store, rather than a production database service.\n",
   "id": "tutorial-11-04"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": "from rdflib import Dataset, Namespace, Literal, RDF\n\nstore = Dataset(default_union=True)\nspace = Namespace(\"https://example.org/tutorial/\")\nnames = [\"Entrance\", \"Hall\", \"Studio\", \"Office\"]\nmesh = TGraph.MeshData(graph)\nfor index in range(len(mesh[\"vertices\"])):\n    node = space[str(index)]\n    store.add((node, RDF.type, space.Space))\n    store.add((node, space.name, Literal(names[index])))\nfor start, end in mesh[\"edges\"]:\n    store.add((space[str(start)], space.connects, space[str(end)]))\n    store.add((space[str(end)], space.connects, space[str(start)]))\n",
   "outputs": [],
   "execution_count": null,
   "id": "tutorial-11-05"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Query and save\n\nSPARQL matches triple patterns. The hall is vertex 1; its neighbours should be the entrance, office and studio. TriG preserves the RDF dataset on disk and can be imported into a larger RDF database.\n",
   "id": "tutorial-11-06"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": "from pathlib import Path\n\nquery = \"\"\"PREFIX s: <https://example.org/tutorial/>\nSELECT ?name WHERE { <https://example.org/tutorial/1> s:connects ?other .\n                     ?other s:name ?name . } ORDER BY ?name\"\"\"\nneighbours = []\nfor row in store.query(query):\n    neighbours.append(str(row[0]))\nprint(\"Connected to Hall:\", neighbours)\nassert neighbours == [\"Entrance\", \"Office\", \"Studio\"]\noutput = Path(\"results\")\noutput.mkdir(exist_ok=True)\nstore.serialize(destination=str(output / \"11_spaces.trig\"), format=\"trig\")\nrestored = Dataset(default_union=True)\nrestored.parse(str(output / \"11_spaces.trig\"), format=\"trig\")\nassert len(list(restored.query(query))) == 3\n",
   "outputs": [],
   "execution_count": null,
   "id": "tutorial-11-07"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Try it yourself\n\nAdd coordinates as RDF properties, or create a second TGraph with one more connection and compare query results.\n",
   "id": "tutorial-11-08"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (TopologicPy tutorials)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
