From e4c87f2adca7a494dfddd482d92a06aacb6acaf2 Mon Sep 17 00:00:00 2001 From: Amadou Wolfgang Cisse Date: Tue, 3 Mar 2026 23:22:23 +0100 Subject: [PATCH 1/4] updated docs added the relase flow --- .github/workflows/cicd.yml | 30 +++++++ .gitignore | 3 + README.md | 178 ++++++++++++------------------------- docs/introduction.rst | 2 +- docs/quick_start.rst | 10 ++- 5 files changed, 100 insertions(+), 123 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 63ff51a..707513b 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -4,6 +4,8 @@ on: push: branches: - "*" + release: + types: [published] permissions: contents: read @@ -70,3 +72,31 @@ jobs: - name: Run tests run: python -m pytest -vv -s --timeout=600 --timeout-method=thread ${{ matrix.test_file }} + + publish: + name: Publish to PyPI + needs: tests + runs-on: ubuntu-latest + if: github.event_name == 'release' && github.event.action == 'published' + environment: + name: pypi + url: https://pypi.org/p/py-dockerdb + permissions: + id-token: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Build package + run: | + python -m pip install build + python -m build + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore index d1dc73d..5666ac5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,14 @@ env/ venv/ .venv +.claude ztrash debug.log config.yaml chromedriver.log .skill* +experiments/ + # Created by https://www.gitignore.io/api/python diff --git a/README.md b/README.md index fa0a83c..fabbfa6 100644 --- a/README.md +++ b/README.md @@ -5,19 +5,32 @@ [![Build](https://img.shields.io/github/actions/workflow/status/amadou-6e/docker-db/cicd.yml?branch=main&label=tests)](https://github.com/amadou-6e/docker-db/actions/workflows/cicd.yml) [![PyPI](https://img.shields.io/pypi/v/py-dockerdb)](https://pypi.org/project/py-dockerdb/) [![License](https://img.shields.io/badge/license-MIT-lightgrey)](./LICENSE) +[![Docs](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://amadou-6e.github.io/py-docker/introduction.html) -`py-dockerdb` gives you easy Docker database setup in Python for PostgreSQL, MySQL, MongoDB, Microsoft SQL Server, Redis, and Neo4j. It is built for people who teach, demo, and prototype with notebooks or scripts and need repeatable local databases in minutes. Instead of writing Docker commands and per-engine setup code, you use one API to create, start, connect, and clean up containers. +```bash +pip install py-dockerdb +``` -Switch from PostgreSQL to MongoDB and back without changing a line of connection code. This makes side-by-side database comparison a first-class workflow - useful for MVPs where the right engine isn't decided yet, and for RAG and GraphRAG experiments where you want to test one storage backend, then swap it out without rewriting environment glue. +`py-dockerdb` gives you one Python API to create, connect, and clean up Docker +databases: PostgreSQL, MySQL, MongoDB, MSSQL, Redis, Neo4j, and Ollama. It is built +for people who teach, demo, and prototype with notebooks and need repeatable local +databases in seconds. -If you teach SQL or data workflows, it removes the environment-setup section from your slides entirely: every student runs the same two lines and gets a working database. +Switch from PostgreSQL to MongoDB without changing a line of connection code. Test +a pgvector RAG pipeline, then swap to Neo4j for GraphRAG with one config change. +Or hand every student a pre-seeded database at the start of class without touching +Docker on their machine. ## When to use this -- **Teaching a SQL workshop or notebook tutorial** - every learner starts from the same environment with no per-machine Docker setup required. -- **Comparing databases for an MVP** - run PostgreSQL, MySQL, MongoDB, MSSQL, Redis, and Neo4j under the same Python interface and switch engines without rewriting connection code. -- **Building a local RAG prototype** - spin up a backing store, test your retrieval pipeline, then swap from PostgreSQL to MongoDB in one config change without touching orchestration code. -- **GraphRAG with Neo4j** - provision a local knowledge graph for multi-hop retrieval pipelines. The `Neo4jDB.connection` property returns a `neo4j.Driver` that plugs directly into LlamaIndex's `Neo4jGraphStore` and LangChain's `Neo4jGraph`. +- **Teaching a SQL workshop:** two lines give every learner a working, pre-seeded + database, identical across Windows/Mac/Linux. +- **Comparing databases for an MVP:** run Postgres, MongoDB, Redis, and Neo4j + through the same interface and pick based on behaviour, not setup time. +- **Local RAG prototype:** spin up pgvector, validate retrieval, swap to another + backend in one config change without touching orchestration code. +- **GraphRAG with Neo4j:** `Neo4jDB.connection` returns a `neo4j.Driver` that + plugs directly into LlamaIndex's `Neo4jGraphStore` and LangChain's `Neo4jGraph`. ## Supported Databases @@ -27,162 +40,87 @@ If you teach SQL or data workflows, it removes the environment-setup section fro [![SQL Server](https://img.shields.io/badge/SQL_Server-CC2927?logo=microsoftsqlserver&logoColor=white)](https://learn.microsoft.com/en-us/sql/sql-server/) [![Redis](https://img.shields.io/badge/Redis-DC382D?logo=redis&logoColor=white)](https://redis.io/docs/) [![Neo4j](https://img.shields.io/badge/Neo4j-008CC1?logo=neo4j&logoColor=white)](https://neo4j.com/docs/) +[![Ollama](https://img.shields.io/badge/Ollama-000000?logo=ollama&logoColor=white)](https://ollama.com/library) ## Prerequisites -- Python 3.10+ -- Docker installed and running -- Database drivers (installed automatically with package dependencies): - - `psycopg2-binary` for PostgreSQL - - `mysql-connector-python` for MySQL -- `pymongo` for MongoDB -- `pyodbc` for MSSQL -- `redis` for Redis -- `neo4j` for Neo4j +- Python 3.10+ · Docker running ## Installation ```bash -# Core -pip install py-dockerdb - -# With graph dependencies (Neo4j driver, LlamaIndex / LangChain graph stores) -pip install "py-dockerdb[graph]" +pip install py-dockerdb # core +pip install "py-dockerdb[graph]" # + Neo4j / LlamaIndex / LangChain +pip install "py-dockerdb[rag]" # + pgvector / LlamaIndex ``` ## Usage -The API is consistent across all engines: define a config, call `create_db()`, run your workload, then tear down with `delete_db()`. +Define a config, call `create_db()`, run your workload, tear down with `delete_db()`. -### PostgreSQL example +### PostgreSQL ```python -import uuid -from pathlib import Path from docker_db.dbs.postgres_db import PostgresConfig, PostgresDB -container_name = f"demo-postgres-{uuid.uuid4().hex[:8]}" -temp_dir = Path("tmp") -temp_dir.mkdir(exist_ok=True) - -config = PostgresConfig( - user="demouser", - password="demopass", - database="demodb", - project_name="demo", - container_name=container_name, - workdir=temp_dir.absolute(), - retries=20, - delay=3, -) - -db_manager = PostgresDB(config) -db_manager.create_db() - -conn = db_manager.connection +db = PostgresDB(PostgresConfig(user="u", password="p", database="d", project_name="demo")) +db.create_db() +conn = db.connection # psycopg2 connection cur = conn.cursor() cur.execute("SELECT version();") print(cur.fetchone()) - -cur.close() -conn.close() -db_manager.delete_db(running_ok=True) +db.delete_db(running_ok=True) ``` -### Neo4j / GraphRAG example + +### Neo4j / GraphRAG ```python -import uuid -from pathlib import Path from docker_db.dbs.neo4j_db import Neo4jConfig, Neo4jDB -container_name = f"demo-neo4j-{uuid.uuid4().hex[:8]}" -temp_dir = Path("tmp") -temp_dir.mkdir(exist_ok=True) - -config = Neo4jConfig( - password="demopass", - project_name="demo", - container_name=container_name, - workdir=temp_dir.absolute(), -) - -db_manager = Neo4jDB(config) -db_manager.create_db() - -driver = db_manager.connection -with driver.session() as session: - session.run("CREATE (n:Person {name: 'Alice'})") - result = session.run("MATCH (n:Person) RETURN n.name AS name") - print(result.single()["name"]) # Alice - -driver.close() -db_manager.delete_db(running_ok=True) -``` - -### More examples - -Full runnable notebooks for each engine are in the [`usage/`](./usage/) directory: - -- [PostgreSQL](./usage/postgres_example.ipynb) -- [MySQL](./usage/mysql_example.ipynb) -- [MongoDB](./usage/mongo_example.ipynb) -- [MSSQL](./usage/mssql_example.ipynb) -- [Redis](./usage/redis_example.ipynb) -- [Neo4j / GraphRAG](./usage/neo4j_example.ipynb) -- [Container lifecycle and management](./usage/db_management_example.ipynb) - -### Seeding data for demos and workshops - -Use `init_script` to preload tables or documents before handing the environment to students or running a live demo. - -```python -config = PostgresConfig( - ... - init_script=Path("./configs/postgres/initdb.sh"), -) +db = Neo4jDB(Neo4jConfig(password="p", project_name="demo")) +db.create_db() +driver = db.connection # neo4j.Driver -> hand to Neo4jGraphStore or Neo4jGraph +with driver.session() as s: + s.run("CREATE (n:Person {name: 'Alice'})") + print(s.run("MATCH (n:Person) RETURN n.name").single()[0]) +db.delete_db(running_ok=True) ``` -### SQL magic and client tool integration +### Ollama ```python -conn_string = db_manager.connection_string(sql_magic=True) +from docker_db.dbs.ollama_db import OllamaConfig, OllamaDB + +db = OllamaDB(OllamaConfig(project_name="demo")) +db.create_db() +session = db.connection # requests.Session +db.pull_model("llama3") +resp = session.post(f"{db.base_url}/api/generate", json={"model": "llama3", "prompt": "Hello", "stream": False}) +print(resp.json()["response"]) +db.delete_db(running_ok=True) ``` -## Roadmap - -Docker-friendly vector and graph backends that can be run locally without paid licenses: +### More examples -- [x] PostgreSQL + `pgvector` -- [x] Neo4j (knowledge graphs, GraphRAG) -- [ ] Qdrant -- [ ] Weaviate +Full runnable notebooks are in [`usage/`](./usage/): +[PostgreSQL](./usage/postgres_example.ipynb) · [MySQL](./usage/mysql_example.ipynb) +· [MongoDB](./usage/mongo_example.ipynb) · [MSSQL](./usage/mssql_example.ipynb) +· [Redis](./usage/redis_example.ipynb) · [Neo4j / GraphRAG](./usage/neo4j_example.ipynb) +· [pgvector RAG](./usage/pgvector_rag_example.ipynb) · [Lifecycle](./usage/db_management_example.ipynb) ## Development ```bash git clone https://github.com/amadou-6e/docker-db.git cd docker-db -python -m pip install -e ".[test]" -``` - -## Testing - -```bash -python -m pytest -vv -s tests/test_manager.py -python -m pytest -vv -s tests/test_postgres.py -python -m pytest -vv -s tests/test_mysql.py -python -m pytest -vv -s tests/test_mongodb.py -python -m pytest -vv -s tests/test_mssql.py -python -m pytest -vv -s tests/test_redis.py -python -m pytest -vv -s tests/test_neo4j.py -python -m pytest -vv -s tests/test_notebooks.py +pip install -e ".[test]" ``` ## Contributing -Pull requests are welcome. Please include tests for behavior changes and keep examples runnable in the `usage/` notebooks when applicable. +PRs welcome. Include tests for behaviour changes and keep notebooks runnable. ## License diff --git a/docs/introduction.rst b/docs/introduction.rst index e0c96e3..d3b9a23 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -12,7 +12,7 @@ py-dockerdb .. |License| image:: https://img.shields.io/badge/license-MIT-lightgrey :target: ../LICENSE -``py-dockerdb`` gives you easy Docker database setup in Python for PostgreSQL, MySQL, MongoDB, Microsoft SQL Server, Redis, and Neo4j. It is built for people who teach, demo, and prototype with notebooks or scripts and need repeatable local databases in minutes. Instead of writing Docker commands and per-engine setup code, you use one API to create, start, connect, and clean up containers. +``py-dockerdb`` gives you easy Docker database setup in Python for PostgreSQL, MySQL, MongoDB, Microsoft SQL Server, Redis, Neo4j, and Ollama. It is built for people who teach, demo, and prototype with notebooks or scripts and need repeatable local databases in seconds. Instead of writing Docker commands and per-engine setup code, you use one API to create, start, connect, and clean up containers. Switch from PostgreSQL to MongoDB and back without changing a line of connection code. This makes side-by-side database comparison a first-class workflow, useful for MVPs where the right engine is not decided yet, and for RAG and GraphRAG experiments where you want to test one storage backend, then swap it out without rewriting environment glue. diff --git a/docs/quick_start.rst b/docs/quick_start.rst index bd2a167..6d65757 100644 --- a/docs/quick_start.rst +++ b/docs/quick_start.rst @@ -6,7 +6,7 @@ By the end of this page, you will have a running PostgreSQL container managed fr Prerequisites ------------- -- Python 3.7+ +- Python 3.10+ - Docker installed and running - Database drivers (installed automatically with package dependencies): - ``psycopg2-binary`` for PostgreSQL @@ -19,7 +19,9 @@ Installation .. code-block:: bash - pip install py-dockerdb + pip install py-dockerdb # core + pip install "py-dockerdb[graph]" # + Neo4j / LlamaIndex / LangChain + pip install "py-dockerdb[rag]" # + pgvector / LlamaIndex Your First py-dockerdb Workflow ------------------------------- @@ -83,4 +85,8 @@ Next Steps Redis key-value store and caching examples. - `neo4j_example.ipynb `_: Neo4j knowledge graph setup, node/relationship queries, and GraphRAG with LlamaIndex. +- `pgvector_rag_example.ipynb `_: + pgvector RAG pipeline with LlamaIndex vector store. +- `ollama_example.ipynb `_: + Ollama local LLM container setup and model inference. - API reference: :doc:`source/modules` From 68382d8ad295b99e59e5ac542385481413947fa1 Mon Sep 17 00:00:00 2001 From: Amadou Wolfgang Cisse Date: Tue, 3 Mar 2026 23:39:37 +0100 Subject: [PATCH 2/4] Remove usage/tmp from version control --- .gitignore | 3 ++- usage/tmp/oldata/id_ed25519 | 7 ------- usage/tmp/oldata/id_ed25519.pub | 1 - 3 files changed, 2 insertions(+), 9 deletions(-) delete mode 100644 usage/tmp/oldata/id_ed25519 delete mode 100644 usage/tmp/oldata/id_ed25519.pub diff --git a/.gitignore b/.gitignore index 5666ac5..4c286a8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ config.yaml chromedriver.log .skill* experiments/ +usage/tmp/ @@ -134,4 +135,4 @@ message.txt # End of https://www.gitignore.io/api/python /logs /pgdata -entrypoint.log \ No newline at end of file +entrypoint.log diff --git a/usage/tmp/oldata/id_ed25519 b/usage/tmp/oldata/id_ed25519 deleted file mode 100644 index c511d86..0000000 --- a/usage/tmp/oldata/id_ed25519 +++ /dev/null @@ -1,7 +0,0 @@ ------BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz -c2gtZWQyNTUxOQAAACCiieUSI9C0jNq+JZ66LVqb5/6c55ReUfkLY3rvk97yAgAA -AIjCLMZ7wizGewAAAAtzc2gtZWQyNTUxOQAAACCiieUSI9C0jNq+JZ66LVqb5/6c -55ReUfkLY3rvk97yAgAAAEDxv3oUS3gwauGBZ5eze0qLf/U592RgN4KP4aKjSihK -raKJ5RIj0LSM2r4lnrotWpvn/pznlF5R+Qtjeu+T3vICAAAAAAECAwQF ------END OPENSSH PRIVATE KEY----- diff --git a/usage/tmp/oldata/id_ed25519.pub b/usage/tmp/oldata/id_ed25519.pub deleted file mode 100644 index 6eee742..0000000 --- a/usage/tmp/oldata/id_ed25519.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKKJ5RIj0LSM2r4lnrotWpvn/pznlF5R+Qtjeu+T3vIC From 6f82936c89f1f2859a8b56f50d47a240f6b09c88 Mon Sep 17 00:00:00 2001 From: Amadou Wolfgang Cisse Date: Wed, 4 Mar 2026 09:09:14 +0100 Subject: [PATCH 3/4] Ready for release --- .gitignore | 2 +- usage/mongo_example.ipynb | 12 +- usage/mssql_example.ipynb | 12 +- usage/mysql_example.ipynb | 1173 +++++++++++++++--------------- usage/neo4j_example.ipynb | 36 +- usage/ollama_example.ipynb | 310 ++++---- usage/pgvector_rag_example.ipynb | 50 +- usage/postgres_example.ipynb | 1053 +++++++++++++-------------- usage/redis_example.ipynb | 342 ++++----- 9 files changed, 1413 insertions(+), 1577 deletions(-) diff --git a/.gitignore b/.gitignore index 4c286a8..b852b82 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ config.yaml chromedriver.log .skill* experiments/ -usage/tmp/ +tmp/ diff --git a/usage/mongo_example.ipynb b/usage/mongo_example.ipynb index 73b511d..f883d7a 100644 --- a/usage/mongo_example.ipynb +++ b/usage/mongo_example.ipynb @@ -123,15 +123,7 @@ "id": "cb34a6dc", "metadata": {}, "outputs": [], - "source": [ - "import tempfile\n", - "import os\n", - "temp_dir = Path(\"tmp\")\n", - "temp_dir.mkdir(exist_ok=True)\n", - "container_name = f\"demo-mongodb-{uuid.uuid4().hex[:8]}\"\n", - "init_script_path = Path(\"configs\", \"mongodb\", \"init.js\")\n", - "init_script_path.exists()" - ] + "source": "import tempfile\ntemp_dir = Path(tempfile.mkdtemp())\ncontainer_name = f\"demo-mongodb-{uuid.uuid4().hex[:8]}\"\ninit_script_path = Path(\"configs\", \"mongodb\", \"init.js\")\ninit_script_path.exists()" }, { "cell_type": "markdown", @@ -629,4 +621,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/usage/mssql_example.ipynb b/usage/mssql_example.ipynb index cb9cc31..12aa974 100644 --- a/usage/mssql_example.ipynb +++ b/usage/mssql_example.ipynb @@ -127,15 +127,7 @@ "id": "fbf5b274", "metadata": {}, "outputs": [], - "source": [ - "import tempfile\n", - "import os\n", - "temp_dir = Path(\"tmp\").absolute()\n", - "temp_dir.mkdir(exist_ok=True)\n", - "container_name = f\"demo-mssql-{uuid.uuid4().hex[:8]}\"\n", - "init_script_path = Path(\"configs\", \"mssql\", \"initdb.sql\")\n", - "init_script_path.exists()" - ] + "source": "import tempfile\ntemp_dir = Path(tempfile.mkdtemp())\ncontainer_name = f\"demo-mssql-{uuid.uuid4().hex[:8]}\"\ninit_script_path = Path(\"configs\", \"mssql\", \"initdb.sql\")\ninit_script_path.exists()" }, { "cell_type": "markdown", @@ -543,4 +535,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/usage/mysql_example.ipynb b/usage/mysql_example.ipynb index 1c1e8da..f739912 100644 --- a/usage/mysql_example.ipynb +++ b/usage/mysql_example.ipynb @@ -1,592 +1,583 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "e6e0b51d", - "metadata": {}, - "source": [ - "# MySQLDB Manager Usage Examples\n", - "\n", - "This notebook demonstrates a practical `MySQL` workflow with `py-dockerdb`, focusing on fast local setup, predictable lifecycle control, and reproducible execution from a single Python interface. Instead of manually wiring container commands for each run, you configure once, start the service, validate connectivity, and then execute database operations in a consistent sequence.\n", - "\n", - "If you are comparing engines, this pattern helps you isolate query behavior and schema differences without rewriting orchestration code. Official reference: https://dev.mysql.com/doc/." - ] - }, - { - "cell_type": "markdown", - "id": "e04c2668", - "metadata": {}, - "source": [ - "## Table Of Contents\n", - "\n", - "- [Table Of Contents](#table-of-contents)\n", - "- [Prerequisites](#prerequisites)\n", - "- [1. Setup](#1-setup)\n", - " - [Import Dependencies](#import-dependencies)\n", - "- [2. Creating a MySQL Database Instance](#2-creating-a-mysql-database-instance)\n", - "- [3. Start the Database](#3-start-the-database)\n", - "- [4. Connect and Run SQL Queries](#4-connect-and-run-sql-queries)\n", - " - [Creating a Table](#creating-a-table)\n", - " - [Insert Data](#insert-data)\n", - " - [Query Data](#query-data)\n", - " - [Run More Complex Queries](#run-more-complex-queries)\n", - "- [5. Using Regular Python to Access the Database](#5-using-regular-python-to-access-the-database)\n", - "- [6. Using MySQL-Specific Features](#6-using-mysql-specific-features)\n", - " - [Demonstrating MySQL Full-Text Search](#demonstrating-mysql-full-text-search)\n", - "- [7. Clean Up](#7-clean-up)\n", - "- [8. Conclusion](#8-conclusion)\n", - "- [9. Conclusion](#9-conclusion)" - ] - }, - { - "cell_type": "markdown", - "id": "2612f51d", - "metadata": {}, - "source": [ - "## Prerequisites\n", - "\n", - "No environment variables are required unless you intentionally override the defaults used in this notebook.\n", - "\n", - "Additional prerequisites:\n", - "- Docker must be installed and running before you call `create_db()`.\n", - "- Install project dependencies (for example, `pip install -e \".[test]\"` or `pip install py-dockerdb`).\n", - "- Run cells top-to-bottom so container lifecycle state remains consistent.\n", - "\n", - "Provider documentation: https://dev.mysql.com/doc/\n", - "Typical setup guide: https://dev.mysql.com/doc/mysql-installation-excerpt/8.0/en/\n", - "Docker image setup reference: https://hub.docker.com/_/mysql\n", - "\n", - "Example datasets you can use to populate this database:\n", - "- Sakila sample database: https://dev.mysql.com/doc/sakila/en/\n", - "- World sample database: https://dev.mysql.com/doc/world-setup/en/" - ] - }, - { - "cell_type": "markdown", - "id": "c6c886d3", - "metadata": {}, - "source": [ - "## 1. Setup\n", - "\n", - "### Install Required Packages\n", - "\n", - "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "940e472c", - "metadata": {}, - "outputs": [], - "source": [ - "!pip install ipython-sql==0.5.0 prettytable==3.8.0 py-dockerdb pymysql" - ] - }, - { - "cell_type": "markdown", - "id": "160274b0", - "metadata": {}, - "source": [ - "### Import Dependencies\n", - "\n", - "This subsection details `import dependencies` before the next code cell.\n", - "\n", - "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5102e5cb", - "metadata": {}, - "outputs": [], - "source": [ - "import uuid\n", - "from pathlib import Path\n", - "from docker_db.dbs.mysql_db import MySQLConfig, MySQLDB\n", - "\n", - "# For SQL cell magic\n", - "%load_ext sql" - ] - }, - { - "cell_type": "markdown", - "id": "d20f3a19", - "metadata": {}, - "source": [ - "## 2. Creating a MySQL Database Instance\n", - "\n", - "Let's create a temporary directory for our database files:\n", - "\n", - "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "87d3b984", - "metadata": {}, - "outputs": [], - "source": [ - "import tempfile\n", - "import os\n", - "\n", - "temp_dir = Path(\"tmp\")\n", - "temp_dir.mkdir(exist_ok=True)\n", - "container_name = f\"demo-mysql-{uuid.uuid4().hex[:8]}\"\n", - "init_script_path = Path(\"configs\", \"mysql\", \"initdb.sql\")\n", - "init_script_path.exists()" - ] - }, - { - "cell_type": "markdown", - "id": "20df986b", - "metadata": {}, - "source": [ - "This step executes code block `7` and is required for the next stage of the workflow.\n", - "\n", - "Run this only after the previous step completed successfully, because downstream cells assume the resulting object, container state, or connection is already available." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "344ddec7", - "metadata": {}, - "outputs": [], - "source": [ - "from utils import display_sql_script\n", - "display_sql_script(init_script_path)" - ] - }, - { - "cell_type": "markdown", - "id": "9192abca", - "metadata": {}, - "source": [ - "Now, let's set up the MySQLDB configuration:\n", - "\n", - "This context is intentionally explicit so the next code cell is not a blind execution step and you can quickly diagnose issues if behavior differs from expectations." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "76801533", - "metadata": {}, - "outputs": [], - "source": [ - "# Create a configuration for our database\n", - "config = MySQLConfig(\n", - " user=\"demouser\",\n", - " password=\"demopass\",\n", - " database=\"demodb\",\n", - " root_password=\"rootpass\",\n", - " project_name=\"demo\",\n", - " workdir=temp_dir,\n", - " container_name=container_name,\n", - " retries=20,\n", - " delay=3,\n", - " init_script=init_script_path,\n", - " env_vars={\"YourEnvVar\": \"TestEnvironment\"}, \n", - ")\n", - "\n", - "# Initialize the database manager\n", - "db_manager = MySQLDB(config)" - ] - }, - { - "cell_type": "markdown", - "id": "cd5ddc8a", - "metadata": {}, - "source": [ - "## 3. Start the Database\n", - "\n", - "We'll now create and start the database:\n", - "\n", - "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ae39890b", - "metadata": {}, - "outputs": [], - "source": [ - "# Create and start the database\n", - "db_manager.create_db()\n", - "print(f\"Database started successfully in container '{container_name}'\")\n", - "print(f\"Connection details:\")\n", - "print(f\" Host: {config.host}\")\n", - "print(f\" Port: {config.port}\")\n", - "print(f\" User: {config.user}\")\n", - "print(f\" Database: {config.database}\")" - ] - }, - { - "cell_type": "markdown", - "id": "f3da46ad", - "metadata": {}, - "source": [ - "## 4. Connect and Run SQL Queries\n", - "\n", - "Now that our database is running, let's connect to it using SQL cell magic:\n", - "\n", - "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "05768978", - "metadata": {}, - "outputs": [], - "source": [ - "# Define the connection string for SQL magic\n", - "conn_string = db_manager.connection_string(sql_magic=True)\n", - "%sql $conn_string" - ] - }, - { - "cell_type": "markdown", - "id": "09165b3f", - "metadata": {}, - "source": [ - "This step executes code block `14` and is required for the next stage of the workflow.\n", - "\n", - "Run this only after the previous step completed successfully, because downstream cells assume the resulting object, container state, or connection is already available." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fec3a31a", - "metadata": {}, - "outputs": [], - "source": [ - "%sql\n", - "SELECT * FROM test_table;" - ] - }, - { - "cell_type": "markdown", - "id": "9694ae39", - "metadata": {}, - "source": [ - "### Creating a Table\n", - "\n", - "This subsection details `creating a table` before the next code cell.\n", - "\n", - "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5373fd11", - "metadata": {}, - "outputs": [], - "source": [ - "%%sql\n", - "CREATE TABLE demo_users (\n", - " id INT AUTO_INCREMENT PRIMARY KEY,\n", - " username VARCHAR(50) UNIQUE NOT NULL,\n", - " email VARCHAR(100) UNIQUE,\n", - " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n", - ");" - ] - }, - { - "cell_type": "markdown", - "id": "a119ac95", - "metadata": {}, - "source": [ - "### Insert Data\n", - "\n", - "This subsection details `insert data` before the next code cell.\n", - "\n", - "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dc8af33c", - "metadata": {}, - "outputs": [], - "source": [ - "%%sql\n", - "INSERT INTO demo_users (username, email) VALUES\n", - " ('alice', 'alice@example.com'),\n", - " ('bob', 'bob@example.com'),\n", - " ('charlie', 'charlie@example.com');" - ] - }, - { - "cell_type": "markdown", - "id": "8088c77a", - "metadata": {}, - "source": [ - "### Query Data\n", - "\n", - "This subsection details `query data` before the next code cell.\n", - "\n", - "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5c4c6a2", - "metadata": {}, - "outputs": [], - "source": [ - "%%sql\n", - "SELECT * FROM demo_users;" - ] - }, - { - "cell_type": "markdown", - "id": "522b6d46", - "metadata": {}, - "source": [ - "### Run More Complex Queries\n", - "\n", - "This subsection details `run more complex queries` before the next code cell.\n", - "\n", - "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "35d648db", - "metadata": {}, - "outputs": [], - "source": [ - "%%sql\n", - "-- Create another table for demonstration\n", - "CREATE TABLE demo_posts (\n", - " id INT AUTO_INCREMENT PRIMARY KEY,\n", - " user_id INT,\n", - " title VARCHAR(100) NOT NULL,\n", - " content TEXT,\n", - " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n", - " FOREIGN KEY (user_id) REFERENCES demo_users(id)\n", - ");\n", - "\n", - "-- Insert some posts\n", - "INSERT INTO demo_posts (user_id, title, content) VALUES\n", - " (1, 'Alice First Post', 'Hello world from Alice!'),\n", - " (1, 'Alice Second Post', 'Another post from Alice'),\n", - " (2, 'Bob Introduction', 'Hi, this is Bob!'),\n", - " (3, 'Charlie Notes', 'Some notes from Charlie');\n", - " \n", - "-- Query with JOIN\n", - "SELECT u.username, p.title, p.content\n", - "FROM demo_users u\n", - "JOIN demo_posts p ON u.id = p.user_id\n", - "ORDER BY u.username, p.created_at;" - ] - }, - { - "cell_type": "markdown", - "id": "9b538293", - "metadata": {}, - "source": [ - "## 5. Using Regular Python to Access the Database\n", - "\n", - "You can also interact with the database using Python code and mysql-connector-python:\n", - "\n", - "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a9d9fe90", - "metadata": {}, - "outputs": [], - "source": [ - "import mysql.connector\n", - "\n", - "# Connect directly using our db_manager's connection property\n", - "conn = db_manager.connection\n", - "\n", - "# Create a cursor\n", - "cursor = conn.cursor()\n", - "\n", - "# Execute a query\n", - "cursor.execute(\"SELECT COUNT(*) as post_count FROM demo_posts\")\n", - "result = cursor.fetchone()\n", - "print(f\"Total number of posts: {result[0]}\")\n", - "\n", - "# Group by query\n", - "cursor.execute(\"\"\"\n", - " SELECT u.username, COUNT(p.id) as post_count \n", - " FROM demo_users u\n", - " LEFT JOIN demo_posts p ON u.id = p.user_id\n", - " GROUP BY u.username\n", - " ORDER BY post_count DESC\n", - "\"\"\")\n", - "\n", - "print(\"\\nPost count by user:\")\n", - "for row in cursor.fetchall():\n", - " print(f\" {row[0]}: {row[1]} posts\")\n", - "\n", - "# Close the connection\n", - "cursor.close()\n", - "conn.close()" - ] - }, - { - "cell_type": "markdown", - "id": "e270c134", - "metadata": {}, - "source": [ - "## 6. Using MySQL-Specific Features\n", - "\n", - "Let's try some MySQL-specific features like stored procedures:\n", - "\n", - "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f25f7648", - "metadata": {}, - "outputs": [], - "source": [ - "%%sql\n", - "-- Create a stored procedure to get post count for a specific user\n", - "CREATE PROCEDURE GetUserPostCount(IN username_param VARCHAR(50))\n", - "BEGIN\n", - " SELECT u.username, COUNT(p.id) as post_count\n", - " FROM demo_users u\n", - " LEFT JOIN demo_posts p ON u.id = p.user_id\n", - " WHERE u.username = username_param\n", - " GROUP BY u.username;\n", - "END;\n", - "\n", - "-- Call the stored procedure\n", - "CALL GetUserPostCount('alice');" - ] - }, - { - "cell_type": "markdown", - "id": "198388e8", - "metadata": {}, - "source": [ - "### Demonstrating MySQL Full-Text Search\n", - "\n", - "This subsection details `demonstrating mysql full-text search` before the next code cell.\n", - "\n", - "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d60eb68f", - "metadata": {}, - "outputs": [], - "source": [ - "%%sql\n", - "-- Create a table with full-text search capabilities\n", - "CREATE TABLE articles (\n", - " id INT AUTO_INCREMENT PRIMARY KEY,\n", - " title VARCHAR(100) NOT NULL,\n", - " content TEXT,\n", - " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n", - " FULLTEXT(title, content)\n", - ");\n", - "\n", - "-- Insert some articles\n", - "INSERT INTO articles (title, content) VALUES\n", - " ('MySQL Full-Text Search', 'This article explains how to use MySQL\\'s powerful full-text search capabilities.'),\n", - " ('Database Performance Tips', 'Learn how to optimize your MySQL database for better performance.'),\n", - " ('Web Development Basics', 'A guide to getting started with web development using various technologies.');\n", - "\n", - "-- Perform a full-text search\n", - "SELECT title, content, MATCH(title, content) AGAINST('mysql database') AS relevance\n", - "FROM articles\n", - "WHERE MATCH(title, content) AGAINST('mysql database')\n", - "ORDER BY relevance DESC;" - ] - }, - { - "cell_type": "markdown", - "id": "6312e546", - "metadata": {}, - "source": [ - "## 7. Clean Up\n", - "\n", - "When you're done with the database, you can delete it:\n", - "\n", - "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "66b6e5a1", - "metadata": {}, - "outputs": [], - "source": [ - "# Delete the database container\n", - "db_manager.delete_db(running_ok=True)\n", - "print(f\"Database container '{container_name}' deleted\")\n", - "\n", - "# Clean up the temporary directory\n", - "import shutil\n", - "shutil.rmtree(temp_dir)\n", - "print(f\"Temporary directory '{temp_dir}' removed\")" - ] - }, - { - "cell_type": "markdown", - "id": "7d9e1019", - "metadata": {}, - "source": [ - "## 8. Conclusion\n", - "\n", - "This notebook demonstrated how to:\n", - "\n", - "1. Configure and create a MySQL database with `MySQLDB`\n", - "2. Connect to the database using SQL cell magic\n", - "3. Execute SQL queries and create stored procedures\n", - "4. Use MySQL-specific features like full-text search\n", - "5. Use Python with mysql-connector to interact with the database \n", - "6. Clean up the database when finished\n", - "\n", - "The `MySQLDB` manager provides a convenient way to spin up MySQL instances in Docker containers for development, testing, or demonstration purposes.\n", - "\n", - "For production use, align this tutorial flow with your team standards: credential handling, migration strategy, backup policy, and monitoring. When needed, start from the provider setup docs (https://dev.mysql.com/doc/mysql-installation-excerpt/8.0/en/) and validate against real datasets before benchmarking query performance." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.10" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} + "cells": [ + { + "cell_type": "markdown", + "id": "e6e0b51d", + "metadata": {}, + "source": [ + "# MySQLDB Manager Usage Examples\n", + "\n", + "This notebook demonstrates a practical `MySQL` workflow with `py-dockerdb`, focusing on fast local setup, predictable lifecycle control, and reproducible execution from a single Python interface. Instead of manually wiring container commands for each run, you configure once, start the service, validate connectivity, and then execute database operations in a consistent sequence.\n", + "\n", + "If you are comparing engines, this pattern helps you isolate query behavior and schema differences without rewriting orchestration code. Official reference: https://dev.mysql.com/doc/." + ] + }, + { + "cell_type": "markdown", + "id": "e04c2668", + "metadata": {}, + "source": [ + "## Table Of Contents\n", + "\n", + "- [Table Of Contents](#table-of-contents)\n", + "- [Prerequisites](#prerequisites)\n", + "- [1. Setup](#1-setup)\n", + " - [Import Dependencies](#import-dependencies)\n", + "- [2. Creating a MySQL Database Instance](#2-creating-a-mysql-database-instance)\n", + "- [3. Start the Database](#3-start-the-database)\n", + "- [4. Connect and Run SQL Queries](#4-connect-and-run-sql-queries)\n", + " - [Creating a Table](#creating-a-table)\n", + " - [Insert Data](#insert-data)\n", + " - [Query Data](#query-data)\n", + " - [Run More Complex Queries](#run-more-complex-queries)\n", + "- [5. Using Regular Python to Access the Database](#5-using-regular-python-to-access-the-database)\n", + "- [6. Using MySQL-Specific Features](#6-using-mysql-specific-features)\n", + " - [Demonstrating MySQL Full-Text Search](#demonstrating-mysql-full-text-search)\n", + "- [7. Clean Up](#7-clean-up)\n", + "- [8. Conclusion](#8-conclusion)\n", + "- [9. Conclusion](#9-conclusion)" + ] + }, + { + "cell_type": "markdown", + "id": "2612f51d", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "No environment variables are required unless you intentionally override the defaults used in this notebook.\n", + "\n", + "Additional prerequisites:\n", + "- Docker must be installed and running before you call `create_db()`.\n", + "- Install project dependencies (for example, `pip install -e \".[test]\"` or `pip install py-dockerdb`).\n", + "- Run cells top-to-bottom so container lifecycle state remains consistent.\n", + "\n", + "Provider documentation: https://dev.mysql.com/doc/\n", + "Typical setup guide: https://dev.mysql.com/doc/mysql-installation-excerpt/8.0/en/\n", + "Docker image setup reference: https://hub.docker.com/_/mysql\n", + "\n", + "Example datasets you can use to populate this database:\n", + "- Sakila sample database: https://dev.mysql.com/doc/sakila/en/\n", + "- World sample database: https://dev.mysql.com/doc/world-setup/en/" + ] + }, + { + "cell_type": "markdown", + "id": "c6c886d3", + "metadata": {}, + "source": [ + "## 1. Setup\n", + "\n", + "### Install Required Packages\n", + "\n", + "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "940e472c", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install ipython-sql==0.5.0 prettytable==3.8.0 py-dockerdb pymysql" + ] + }, + { + "cell_type": "markdown", + "id": "160274b0", + "metadata": {}, + "source": [ + "### Import Dependencies\n", + "\n", + "This subsection details `import dependencies` before the next code cell.\n", + "\n", + "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5102e5cb", + "metadata": {}, + "outputs": [], + "source": [ + "import uuid\n", + "from pathlib import Path\n", + "from docker_db.dbs.mysql_db import MySQLConfig, MySQLDB\n", + "\n", + "# For SQL cell magic\n", + "%load_ext sql" + ] + }, + { + "cell_type": "markdown", + "id": "d20f3a19", + "metadata": {}, + "source": [ + "## 2. Creating a MySQL Database Instance\n", + "\n", + "Let's create a temporary directory for our database files:\n", + "\n", + "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87d3b984", + "metadata": {}, + "outputs": [], + "source": "import tempfile\ntemp_dir = Path(tempfile.mkdtemp())\ncontainer_name = f\"demo-mysql-{uuid.uuid4().hex[:8]}\"\ninit_script_path = Path(\"configs\", \"mysql\", \"initdb.sql\")\ninit_script_path.exists()" + }, + { + "cell_type": "markdown", + "id": "20df986b", + "metadata": {}, + "source": [ + "This step executes code block `7` and is required for the next stage of the workflow.\n", + "\n", + "Run this only after the previous step completed successfully, because downstream cells assume the resulting object, container state, or connection is already available." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "344ddec7", + "metadata": {}, + "outputs": [], + "source": [ + "from utils import display_sql_script\n", + "display_sql_script(init_script_path)" + ] + }, + { + "cell_type": "markdown", + "id": "9192abca", + "metadata": {}, + "source": [ + "Now, let's set up the MySQLDB configuration:\n", + "\n", + "This context is intentionally explicit so the next code cell is not a blind execution step and you can quickly diagnose issues if behavior differs from expectations." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76801533", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a configuration for our database\n", + "config = MySQLConfig(\n", + " user=\"demouser\",\n", + " password=\"demopass\",\n", + " database=\"demodb\",\n", + " root_password=\"rootpass\",\n", + " project_name=\"demo\",\n", + " workdir=temp_dir,\n", + " container_name=container_name,\n", + " retries=20,\n", + " delay=3,\n", + " init_script=init_script_path,\n", + " env_vars={\"YourEnvVar\": \"TestEnvironment\"}, \n", + ")\n", + "\n", + "# Initialize the database manager\n", + "db_manager = MySQLDB(config)" + ] + }, + { + "cell_type": "markdown", + "id": "cd5ddc8a", + "metadata": {}, + "source": [ + "## 3. Start the Database\n", + "\n", + "We'll now create and start the database:\n", + "\n", + "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ae39890b", + "metadata": {}, + "outputs": [], + "source": [ + "# Create and start the database\n", + "db_manager.create_db()\n", + "print(f\"Database started successfully in container '{container_name}'\")\n", + "print(f\"Connection details:\")\n", + "print(f\" Host: {config.host}\")\n", + "print(f\" Port: {config.port}\")\n", + "print(f\" User: {config.user}\")\n", + "print(f\" Database: {config.database}\")" + ] + }, + { + "cell_type": "markdown", + "id": "f3da46ad", + "metadata": {}, + "source": [ + "## 4. Connect and Run SQL Queries\n", + "\n", + "Now that our database is running, let's connect to it using SQL cell magic:\n", + "\n", + "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05768978", + "metadata": {}, + "outputs": [], + "source": [ + "# Define the connection string for SQL magic\n", + "conn_string = db_manager.connection_string(sql_magic=True)\n", + "%sql $conn_string" + ] + }, + { + "cell_type": "markdown", + "id": "09165b3f", + "metadata": {}, + "source": [ + "This step executes code block `14` and is required for the next stage of the workflow.\n", + "\n", + "Run this only after the previous step completed successfully, because downstream cells assume the resulting object, container state, or connection is already available." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fec3a31a", + "metadata": {}, + "outputs": [], + "source": [ + "%sql\n", + "SELECT * FROM test_table;" + ] + }, + { + "cell_type": "markdown", + "id": "9694ae39", + "metadata": {}, + "source": [ + "### Creating a Table\n", + "\n", + "This subsection details `creating a table` before the next code cell.\n", + "\n", + "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5373fd11", + "metadata": {}, + "outputs": [], + "source": [ + "%%sql\n", + "CREATE TABLE demo_users (\n", + " id INT AUTO_INCREMENT PRIMARY KEY,\n", + " username VARCHAR(50) UNIQUE NOT NULL,\n", + " email VARCHAR(100) UNIQUE,\n", + " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n", + ");" + ] + }, + { + "cell_type": "markdown", + "id": "a119ac95", + "metadata": {}, + "source": [ + "### Insert Data\n", + "\n", + "This subsection details `insert data` before the next code cell.\n", + "\n", + "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc8af33c", + "metadata": {}, + "outputs": [], + "source": [ + "%%sql\n", + "INSERT INTO demo_users (username, email) VALUES\n", + " ('alice', 'alice@example.com'),\n", + " ('bob', 'bob@example.com'),\n", + " ('charlie', 'charlie@example.com');" + ] + }, + { + "cell_type": "markdown", + "id": "8088c77a", + "metadata": {}, + "source": [ + "### Query Data\n", + "\n", + "This subsection details `query data` before the next code cell.\n", + "\n", + "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5c4c6a2", + "metadata": {}, + "outputs": [], + "source": [ + "%%sql\n", + "SELECT * FROM demo_users;" + ] + }, + { + "cell_type": "markdown", + "id": "522b6d46", + "metadata": {}, + "source": [ + "### Run More Complex Queries\n", + "\n", + "This subsection details `run more complex queries` before the next code cell.\n", + "\n", + "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35d648db", + "metadata": {}, + "outputs": [], + "source": [ + "%%sql\n", + "-- Create another table for demonstration\n", + "CREATE TABLE demo_posts (\n", + " id INT AUTO_INCREMENT PRIMARY KEY,\n", + " user_id INT,\n", + " title VARCHAR(100) NOT NULL,\n", + " content TEXT,\n", + " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n", + " FOREIGN KEY (user_id) REFERENCES demo_users(id)\n", + ");\n", + "\n", + "-- Insert some posts\n", + "INSERT INTO demo_posts (user_id, title, content) VALUES\n", + " (1, 'Alice First Post', 'Hello world from Alice!'),\n", + " (1, 'Alice Second Post', 'Another post from Alice'),\n", + " (2, 'Bob Introduction', 'Hi, this is Bob!'),\n", + " (3, 'Charlie Notes', 'Some notes from Charlie');\n", + " \n", + "-- Query with JOIN\n", + "SELECT u.username, p.title, p.content\n", + "FROM demo_users u\n", + "JOIN demo_posts p ON u.id = p.user_id\n", + "ORDER BY u.username, p.created_at;" + ] + }, + { + "cell_type": "markdown", + "id": "9b538293", + "metadata": {}, + "source": [ + "## 5. Using Regular Python to Access the Database\n", + "\n", + "You can also interact with the database using Python code and mysql-connector-python:\n", + "\n", + "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a9d9fe90", + "metadata": {}, + "outputs": [], + "source": [ + "import mysql.connector\n", + "\n", + "# Connect directly using our db_manager's connection property\n", + "conn = db_manager.connection\n", + "\n", + "# Create a cursor\n", + "cursor = conn.cursor()\n", + "\n", + "# Execute a query\n", + "cursor.execute(\"SELECT COUNT(*) as post_count FROM demo_posts\")\n", + "result = cursor.fetchone()\n", + "print(f\"Total number of posts: {result[0]}\")\n", + "\n", + "# Group by query\n", + "cursor.execute(\"\"\"\n", + " SELECT u.username, COUNT(p.id) as post_count \n", + " FROM demo_users u\n", + " LEFT JOIN demo_posts p ON u.id = p.user_id\n", + " GROUP BY u.username\n", + " ORDER BY post_count DESC\n", + "\"\"\")\n", + "\n", + "print(\"\\nPost count by user:\")\n", + "for row in cursor.fetchall():\n", + " print(f\" {row[0]}: {row[1]} posts\")\n", + "\n", + "# Close the connection\n", + "cursor.close()\n", + "conn.close()" + ] + }, + { + "cell_type": "markdown", + "id": "e270c134", + "metadata": {}, + "source": [ + "## 6. Using MySQL-Specific Features\n", + "\n", + "Let's try some MySQL-specific features like stored procedures:\n", + "\n", + "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f25f7648", + "metadata": {}, + "outputs": [], + "source": [ + "%%sql\n", + "-- Create a stored procedure to get post count for a specific user\n", + "CREATE PROCEDURE GetUserPostCount(IN username_param VARCHAR(50))\n", + "BEGIN\n", + " SELECT u.username, COUNT(p.id) as post_count\n", + " FROM demo_users u\n", + " LEFT JOIN demo_posts p ON u.id = p.user_id\n", + " WHERE u.username = username_param\n", + " GROUP BY u.username;\n", + "END;\n", + "\n", + "-- Call the stored procedure\n", + "CALL GetUserPostCount('alice');" + ] + }, + { + "cell_type": "markdown", + "id": "198388e8", + "metadata": {}, + "source": [ + "### Demonstrating MySQL Full-Text Search\n", + "\n", + "This subsection details `demonstrating mysql full-text search` before the next code cell.\n", + "\n", + "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d60eb68f", + "metadata": {}, + "outputs": [], + "source": [ + "%%sql\n", + "-- Create a table with full-text search capabilities\n", + "CREATE TABLE articles (\n", + " id INT AUTO_INCREMENT PRIMARY KEY,\n", + " title VARCHAR(100) NOT NULL,\n", + " content TEXT,\n", + " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n", + " FULLTEXT(title, content)\n", + ");\n", + "\n", + "-- Insert some articles\n", + "INSERT INTO articles (title, content) VALUES\n", + " ('MySQL Full-Text Search', 'This article explains how to use MySQL\\'s powerful full-text search capabilities.'),\n", + " ('Database Performance Tips', 'Learn how to optimize your MySQL database for better performance.'),\n", + " ('Web Development Basics', 'A guide to getting started with web development using various technologies.');\n", + "\n", + "-- Perform a full-text search\n", + "SELECT title, content, MATCH(title, content) AGAINST('mysql database') AS relevance\n", + "FROM articles\n", + "WHERE MATCH(title, content) AGAINST('mysql database')\n", + "ORDER BY relevance DESC;" + ] + }, + { + "cell_type": "markdown", + "id": "6312e546", + "metadata": {}, + "source": [ + "## 7. Clean Up\n", + "\n", + "When you're done with the database, you can delete it:\n", + "\n", + "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66b6e5a1", + "metadata": {}, + "outputs": [], + "source": [ + "# Delete the database container\n", + "db_manager.delete_db(running_ok=True)\n", + "print(f\"Database container '{container_name}' deleted\")\n", + "\n", + "# Clean up the temporary directory\n", + "import shutil\n", + "shutil.rmtree(temp_dir)\n", + "print(f\"Temporary directory '{temp_dir}' removed\")" + ] + }, + { + "cell_type": "markdown", + "id": "7d9e1019", + "metadata": {}, + "source": [ + "## 8. Conclusion\n", + "\n", + "This notebook demonstrated how to:\n", + "\n", + "1. Configure and create a MySQL database with `MySQLDB`\n", + "2. Connect to the database using SQL cell magic\n", + "3. Execute SQL queries and create stored procedures\n", + "4. Use MySQL-specific features like full-text search\n", + "5. Use Python with mysql-connector to interact with the database \n", + "6. Clean up the database when finished\n", + "\n", + "The `MySQLDB` manager provides a convenient way to spin up MySQL instances in Docker containers for development, testing, or demonstration purposes.\n", + "\n", + "For production use, align this tutorial flow with your team standards: credential handling, migration strategy, backup policy, and monitoring. When needed, start from the provider setup docs (https://dev.mysql.com/doc/mysql-installation-excerpt/8.0/en/) and validate against real datasets before benchmarking query performance." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/usage/neo4j_example.ipynb b/usage/neo4j_example.ipynb index 9525e66..215911f 100644 --- a/usage/neo4j_example.ipynb +++ b/usage/neo4j_example.ipynb @@ -54,44 +54,14 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "import sys\n", - "import uuid\n", - "from pathlib import Path\n", - "\n", - "sys.path.insert(0, str(Path().cwd().parent))\n", - "\n", - "from docker_db.dbs.neo4j_db import Neo4jConfig, Neo4jDB" - ] + "source": "import sys\nimport uuid\nimport tempfile\nfrom pathlib import Path\n\nsys.path.insert(0, str(Path().cwd().parent))\n\nfrom docker_db.dbs.neo4j_db import Neo4jConfig, Neo4jDB" }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "temp_dir = Path(\"tmp\")\n", - "temp_dir.mkdir(exist_ok=True)\n", - "\n", - "container_name = f\"demo-neo4j-{uuid.uuid4().hex[:8]}\"\n", - "\n", - "config = Neo4jConfig(\n", - " password=\"demopassword\",\n", - " project_name=\"demo\",\n", - " container_name=container_name,\n", - " workdir=temp_dir.absolute(),\n", - " retries=40,\n", - " delay=3,\n", - ")\n", - "\n", - "db_manager = Neo4jDB(config)\n", - "db_manager.create_db()\n", - "\n", - "print(f\"Neo4j started: {container_name}\")\n", - "print(f\"Bolt URI: {db_manager.connection_string()}\")\n", - "print(f\"Browser UI: http://localhost:{config.http_port}\")\n", - "print(f\"Credentials: {config.user} / {config.password}\")" - ] + "source": "temp_dir = Path(tempfile.mkdtemp())\n\ncontainer_name = f\"demo-neo4j-{uuid.uuid4().hex[:8]}\"\n\nconfig = Neo4jConfig(\n password=\"demopassword\",\n project_name=\"demo\",\n container_name=container_name,\n workdir=temp_dir,\n retries=40,\n delay=3,\n)\n\ndb_manager = Neo4jDB(config)\ndb_manager.create_db()\n\nprint(f\"Neo4j started: {container_name}\")\nprint(f\"Bolt URI: {db_manager.connection_string()}\")\nprint(f\"Browser UI: http://localhost:{config.http_port}\")\nprint(f\"Credentials: {config.user} / {config.password}\")" }, { "cell_type": "markdown", @@ -296,4 +266,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file diff --git a/usage/ollama_example.ipynb b/usage/ollama_example.ipynb index ac9ef73..609d8ac 100644 --- a/usage/ollama_example.ipynb +++ b/usage/ollama_example.ipynb @@ -1,171 +1,143 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Ollama Manager Usage Examples\n", - "\n", - "This notebook demonstrates how to use the `OllamaDB` manager class to create, manage, and interact with an Ollama runtime in a Docker container." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup\n", - "\n", - "### Import Dependencies" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import sys\n", - "import uuid\n", - "from pathlib import Path\n", - "import requests\n", - "\n", - "sys.path.insert(0, str(Path().cwd().parent))\n", - "\n", - "from docker_db.dbs.ollama_db import OllamaConfig, OllamaDB" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating an Ollama Instance\n", - "\n", - "Let's create a temporary directory for Ollama data and define a container configuration." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "temp_dir = Path(\"tmp\")\n", - "temp_dir.mkdir(exist_ok=True)\n", - "\n", - "container_name = f\"demo-ollama-{uuid.uuid4().hex[:8]}\"\n", - "\n", - "config = OllamaConfig(\n", - " project_name=\"demo\",\n", - " container_name=container_name,\n", - " workdir=temp_dir.absolute(),\n", - " retries=30,\n", - " delay=2,\n", - ")\n", - "\n", - "db_manager = OllamaDB(config)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Start the Runtime\n", - "\n", - "We'll now create and start the Ollama runtime." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "db_manager.create_db()\n", - "print(f\"Ollama started successfully in container '{container_name}'\")\n", - "print(\"Connection details:\")\n", - "print(f\" Host: {config.host}\")\n", - "print(f\" Port: {config.port}\")\n", - "print(f\" Base URL: {db_manager.connection_string()}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Query the Ollama API\n", - "\n", - "Now let's call the API and inspect the available local models." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "response = requests.get(f\"{db_manager.base_url}/api/tags\", timeout=10)\n", - "response.raise_for_status()\n", - "payload = response.json()\n", - "\n", - "models = payload.get(\"models\", [])\n", - "print(f\"Available local models: {len(models)}\")\n", - "for model in models:\n", - " print(\" -\", model.get(\"name\", \"unknown\"))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Clean Up\n", - "\n", - "When you're done with the runtime, remove the container." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "db_manager.delete_db(running_ok=True)\n", - "print(f\"Ollama container '{container_name}' deleted\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Conclusion\n", - "\n", - "This notebook demonstrated how to:\n", - "\n", - "1. Configure and create an Ollama runtime with `OllamaDB`\n", - "2. Verify readiness via the Ollama HTTP API\n", - "3. Inspect available local models\n", - "4. Clean up the container when finished" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.12" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Ollama Manager Usage Examples\n", + "\n", + "This notebook demonstrates how to use the `OllamaDB` manager class to create, manage, and interact with an Ollama runtime in a Docker container." + ] }, - "nbformat": 4, - "nbformat_minor": 4 -} + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "### Import Dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "import sys\nimport uuid\nimport tempfile\nfrom pathlib import Path\nimport requests\n\nsys.path.insert(0, str(Path().cwd().parent))\n\nfrom docker_db.dbs.ollama_db import OllamaConfig, OllamaDB" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## Creating an Ollama Instance\n\nLet's create a temporary directory for Ollama data and define a container configuration.\nThe volume is created in the OS temp directory so no container data lands inside the repo." + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "temp_dir = Path(tempfile.mkdtemp())\n\ncontainer_name = f\"demo-ollama-{uuid.uuid4().hex[:8]}\"\n\nconfig = OllamaConfig(\n project_name=\"demo\",\n container_name=container_name,\n workdir=temp_dir,\n retries=30,\n delay=2,\n)\n\ndb_manager = OllamaDB(config)" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Start the Runtime\n", + "\n", + "We'll now create and start the Ollama runtime." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "db_manager.create_db()\n", + "print(f\"Ollama started successfully in container '{container_name}'\")\n", + "print(\"Connection details:\")\n", + "print(f\" Host: {config.host}\")\n", + "print(f\" Port: {config.port}\")\n", + "print(f\" Base URL: {db_manager.connection_string()}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Query the Ollama API\n", + "\n", + "Now let's call the API and inspect the available local models." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = requests.get(f\"{db_manager.base_url}/api/tags\", timeout=10)\n", + "response.raise_for_status()\n", + "payload = response.json()\n", + "\n", + "models = payload.get(\"models\", [])\n", + "print(f\"Available local models: {len(models)}\")\n", + "for model in models:\n", + " print(\" -\", model.get(\"name\", \"unknown\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Clean Up\n", + "\n", + "When you're done with the runtime, remove the container." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "db_manager.delete_db(running_ok=True)\n", + "print(f\"Ollama container '{container_name}' deleted\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusion\n", + "\n", + "This notebook demonstrated how to:\n", + "\n", + "1. Configure and create an Ollama runtime with `OllamaDB`\n", + "2. Verify readiness via the Ollama HTTP API\n", + "3. Inspect available local models\n", + "4. Clean up the container when finished" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/usage/pgvector_rag_example.ipynb b/usage/pgvector_rag_example.ipynb index 5569a05..ab2b7f8 100644 --- a/usage/pgvector_rag_example.ipynb +++ b/usage/pgvector_rag_example.ipynb @@ -60,18 +60,7 @@ "id": "03a6aaa2", "metadata": {}, "outputs": [], - "source": [ - "import uuid\n", - "from pathlib import Path\n", - "\n", - "import psycopg2\n", - "\n", - "from docker_db.dbs.postgres_db import PostgresConfig, PostgresDB\n", - "from llama_index.core import StorageContext, VectorStoreIndex\n", - "from llama_index.core.embeddings import MockEmbedding\n", - "from llama_index.core.schema import Document\n", - "from llama_index.vector_stores.postgres import PGVectorStore" - ] + "source": "import uuid\nimport tempfile\nfrom pathlib import Path\n\nimport psycopg2\n\nfrom docker_db.dbs.postgres_db import PostgresConfig, PostgresDB\nfrom llama_index.core import StorageContext, VectorStoreIndex\nfrom llama_index.core.embeddings import MockEmbedding\nfrom llama_index.core.schema import Document\nfrom llama_index.vector_stores.postgres import PGVectorStore" }, { "cell_type": "markdown", @@ -98,40 +87,7 @@ "id": "f0ac6c0f", "metadata": {}, "outputs": [], - "source": [ - "temp_dir = Path(\"tmp\")\n", - "temp_dir.mkdir(exist_ok=True)\n", - "\n", - "container_name = f\"demo-pgvector-{uuid.uuid4().hex[:8]}\"\n", - "image_name = f\"demo-pgvector-image-{uuid.uuid4().hex[:8]}:latest\"\n", - "\n", - "usage_root = Path(\".\").resolve()\n", - "if not (usage_root / \"configs\" / \"postgres\" / \"Dockerfile.pgvector\").exists():\n", - " usage_root = usage_root / \"usage\"\n", - "\n", - "docker_cfg_dir = usage_root / \"configs\" / \"postgres\"\n", - "\n", - "config = PostgresConfig(\n", - " user=\"demouser\",\n", - " password=\"demopass\",\n", - " database=\"vector_db\",\n", - " project_name=\"demo\",\n", - " container_name=container_name,\n", - " image_name=image_name,\n", - " port=5432,\n", - " workdir=docker_cfg_dir,\n", - " volume_path=Path(temp_dir, \"pgdata_rag\").resolve(),\n", - " dockerfile_path=docker_cfg_dir / \"Dockerfile.pgvector\",\n", - " init_script=docker_cfg_dir / \"initdb_pgvector.sh\",\n", - " env_vars={\"VECTOR_DB_NAME\": \"vector_db\"},\n", - " retries=20,\n", - " delay=3,\n", - ")\n", - "\n", - "db_manager = PostgresDB(config)\n", - "db_manager.create_db()\n", - "print(db_manager.connection_string())" - ] + "source": "temp_dir = Path(tempfile.mkdtemp())\n\ncontainer_name = f\"demo-pgvector-{uuid.uuid4().hex[:8]}\"\nimage_name = f\"demo-pgvector-image-{uuid.uuid4().hex[:8]}:latest\"\n\nusage_root = Path(\".\").resolve()\nif not (usage_root / \"configs\" / \"postgres\" / \"Dockerfile.pgvector\").exists():\n usage_root = usage_root / \"usage\"\n\ndocker_cfg_dir = usage_root / \"configs\" / \"postgres\"\n\nconfig = PostgresConfig(\n user=\"demouser\",\n password=\"demopass\",\n database=\"vector_db\",\n project_name=\"demo\",\n container_name=container_name,\n image_name=image_name,\n port=5432,\n workdir=docker_cfg_dir,\n volume_path=Path(temp_dir, \"pgdata_rag\").resolve(),\n dockerfile_path=docker_cfg_dir / \"Dockerfile.pgvector\",\n init_script=docker_cfg_dir / \"initdb_pgvector.sh\",\n env_vars={\"VECTOR_DB_NAME\": \"vector_db\"},\n retries=20,\n delay=3,\n)\n\ndb_manager = PostgresDB(config)\ndb_manager.create_db()\nprint(db_manager.connection_string())" }, { "cell_type": "markdown", @@ -339,4 +295,4 @@ "metadata": {}, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/usage/postgres_example.ipynb b/usage/postgres_example.ipynb index fbac53c..ddb6d84 100644 --- a/usage/postgres_example.ipynb +++ b/usage/postgres_example.ipynb @@ -1,534 +1,521 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "7c27d4ae", - "metadata": {}, - "source": [ - "# PostgresDB Manager Usage Examples\n", - "\n", - "This notebook demonstrates a practical `PostgreSQL` workflow with `py-dockerdb`, focusing on fast local setup, predictable lifecycle control, and reproducible execution from a single Python interface. Instead of manually wiring container commands for each run, you configure once, start the service, validate connectivity, and then execute database operations in a consistent sequence.\n", - "\n", - "If you are comparing engines, this pattern helps you isolate query behavior and schema differences without rewriting orchestration code. Official reference: https://www.postgresql.org/docs/." - ] - }, - { - "cell_type": "markdown", - "id": "6b4e1903", - "metadata": {}, - "source": [ - "## Table Of Contents\n", - "\n", - "- [Table Of Contents](#table-of-contents)\n", - "- [Prerequisites](#prerequisites)\n", - "- [1. Setup](#1-setup)\n", - " - [Import Dependencies](#import-dependencies)\n", - "- [2. Creating a PostgreSQL Database Instance](#2-creating-a-postgresql-database-instance)\n", - "- [3. Start the Database](#3-start-the-database)\n", - "- [4. Connect and Run SQL Queries](#4-connect-and-run-sql-queries)\n", - " - [Creating a Table](#creating-a-table)\n", - " - [Insert Data](#insert-data)\n", - " - [Query Data](#query-data)\n", - " - [Run More Complex Queries](#run-more-complex-queries)\n", - "- [5. Using Regular Python to Access the Database](#5-using-regular-python-to-access-the-database)\n", - "- [6. Clean Up](#6-clean-up)\n", - "- [7. Conclusion](#7-conclusion)\n", - "- [8. Conclusion](#8-conclusion)" - ] - }, - { - "cell_type": "markdown", - "id": "45681183", - "metadata": {}, - "source": [ - "## Prerequisites\n", - "\n", - "No environment variables are required unless you intentionally override the defaults used in this notebook.\n", - "\n", - "Additional prerequisites:\n", - "- Docker must be installed and running before you call `create_db()`.\n", - "- Install project dependencies (for example, `pip install -e \".[test]\"` or `pip install py-dockerdb`).\n", - "- Run cells top-to-bottom so container lifecycle state remains consistent.\n", - "\n", - "Provider documentation: https://www.postgresql.org/docs/\n", - "Typical setup guide: https://www.postgresql.org/download/\n", - "Docker image setup reference: https://hub.docker.com/_/postgres\n", - "\n", - "Example datasets you can use to populate this database:\n", - "- DVD Rental sample database: https://www.postgresqltutorial.com/postgresql-getting-started/postgresql-sample-database/\n", - "- Pagila sample dataset: https://github.com/devrimgunduz/pagila" - ] - }, - { - "cell_type": "markdown", - "id": "7f01fb4c", - "metadata": {}, - "source": [ - "## 1. Setup\n", - "\n", - "### Install Required Packages\n", - "\n", - "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "151a25f6", - "metadata": {}, - "outputs": [], - "source": [ - "!pip install ipython-sql==0.5.0 prettytable==3.8.0\n", - "!pip uninstall py-dockerdb -y" - ] - }, - { - "cell_type": "markdown", - "id": "c696fca6", - "metadata": {}, - "source": [ - "### Import Dependencies\n", - "\n", - "This subsection details `import dependencies` before the next code cell.\n", - "\n", - "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a0ba1c30", - "metadata": {}, - "outputs": [], - "source": [ - "import sys, os\n", - "from pathlib import Path\n", - "sys.path.insert(0, str(Path().cwd().parent))\n", - "print(sys.path[0])" - ] - }, - { - "cell_type": "markdown", - "id": "442f207f", - "metadata": {}, - "source": [ - "This step executes code block `5` and is required for the next stage of the workflow.\n", - "\n", - "Run this only after the previous step completed successfully, because downstream cells assume the resulting object, container state, or connection is already available." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b4a8c6d8", - "metadata": {}, - "outputs": [], - "source": [ - "import uuid\n", - "from pathlib import Path\n", - "from docker_db.dbs.postgres_db import PostgresConfig, PostgresDB\n", - "\n", - "# For SQL cell magic\n", - "%load_ext sql" - ] - }, - { - "cell_type": "markdown", - "id": "9e5b63b5", - "metadata": {}, - "source": [ - "## 2. Creating a PostgreSQL Database Instance\n", - "\n", - "Now, let's set up the PostgresDB configuration:\n", - "\n", - "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ecd2e63d", - "metadata": {}, - "outputs": [], - "source": [ - "import tempfile\n", - "import os\n", - "import shutil\n", - "\n", - "temp_dir = Path(\"tmp\")\n", - "temp_dir.mkdir(exist_ok=True)\n", - "\n", - "container_name = f\"demo-postgres-{uuid.uuid4().hex[:8]}\"\n", - "source_init_script_path = Path(\"configs\", \"postgres\", \"initdb.sh\")\n", - "init_script_path = Path(temp_dir, \"initdb.sh\")\n", - "shutil.copy2(source_init_script_path, init_script_path)\n", - "init_script_path.exists()" - ] - }, - { - "cell_type": "markdown", - "id": "d51fe400", - "metadata": {}, - "source": [ - "This step executes code block `8` and is required for the next stage of the workflow.\n", - "\n", - "Run this only after the previous step completed successfully, because downstream cells assume the resulting object, container state, or connection is already available." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5eee9ccc", - "metadata": {}, - "outputs": [], - "source": [ - "from utils import display_bash_script\n", - "display_bash_script(init_script_path)" - ] - }, - { - "cell_type": "markdown", - "id": "8f1b21e4", - "metadata": {}, - "source": [ - "This step executes code block `9` and is required for the next stage of the workflow.\n", - "\n", - "Run this only after the previous step completed successfully, because downstream cells assume the resulting object, container state, or connection is already available." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a5bdca77", - "metadata": {}, - "outputs": [], - "source": [ - "# Create a configuration for our database\n", - "config = PostgresConfig(\n", - " user=\"demouser\",\n", - " password=\"demopass\",\n", - " database=\"demodb\",\n", - " project_name=\"demo\",\n", - " container_name=container_name,\n", - " workdir=temp_dir.absolute(),\n", - " retries=20,\n", - " delay=3,\n", - " init_script=init_script_path,\n", - " env_vars={\"YourEnvVar\": \"TestVar\"},\n", - ")\n", - "\n", - "# Initialize the database manager\n", - "db_manager = PostgresDB(config)" - ] - }, - { - "cell_type": "markdown", - "id": "bdf72045", - "metadata": {}, - "source": [ - "## 3. Start the Database\n", - "\n", - "We'll now create and start the database:\n", - "\n", - "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d49374b2", - "metadata": {}, - "outputs": [], - "source": [ - "# Create and start the database\n", - "db_manager.create_db()\n", - "print(f\"Database started successfully in container '{container_name}'\")\n", - "print(f\"Connection details:\")\n", - "print(f\" Host: {config.host}\")\n", - "print(f\" Port: {config.port}\")\n", - "print(f\" User: {config.user}\")\n", - "print(f\" Database: {config.database}\")" - ] - }, - { - "cell_type": "markdown", - "id": "ca91baa2", - "metadata": {}, - "source": [ - "## 4. Connect and Run SQL Queries\n", - "\n", - "Now that our database is running, let's connect to it using SQL cell magic:\n", - "\n", - "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "acf19208", - "metadata": {}, - "outputs": [], - "source": [ - "# Define the connection string for SQL magic\n", - "conn_string = db_manager.connection_string(sql_magic=True)\n", - "%sql $conn_string" - ] - }, - { - "cell_type": "markdown", - "id": "3a767d19", - "metadata": {}, - "source": [ - "This step executes code block `14` and is required for the next stage of the workflow.\n", - "\n", - "Run this only after the previous step completed successfully, because downstream cells assume the resulting object, container state, or connection is already available." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "77386968", - "metadata": {}, - "outputs": [], - "source": [ - "%%sql\n", - "SELECT * FROM test_table;" - ] - }, - { - "cell_type": "markdown", - "id": "84c63374", - "metadata": {}, - "source": [ - "### Creating a Table\n", - "\n", - "This subsection details `creating a table` before the next code cell.\n", - "\n", - "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3f95d1fe", - "metadata": {}, - "outputs": [], - "source": [ - "%%sql\n", - "CREATE TABLE demo_users (\n", - " id SERIAL PRIMARY KEY,\n", - " username VARCHAR(50) UNIQUE NOT NULL,\n", - " email VARCHAR(100) UNIQUE,\n", - " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n", - ");" - ] - }, - { - "cell_type": "markdown", - "id": "7065be13", - "metadata": {}, - "source": [ - "### Insert Data\n", - "\n", - "This subsection details `insert data` before the next code cell.\n", - "\n", - "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c07c97c4", - "metadata": {}, - "outputs": [], - "source": [ - "%%sql\n", - "INSERT INTO demo_users (username, email) VALUES\n", - " ('alice', 'alice@example.com'),\n", - " ('bob', 'bob@example.com'),\n", - " ('charlie', 'charlie@example.com');" - ] - }, - { - "cell_type": "markdown", - "id": "d04fde28", - "metadata": {}, - "source": [ - "### Query Data\n", - "\n", - "This subsection details `query data` before the next code cell.\n", - "\n", - "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e7db6dc9", - "metadata": {}, - "outputs": [], - "source": [ - "%%sql\n", - "SELECT * FROM demo_users;" - ] - }, - { - "cell_type": "markdown", - "id": "00dc978a", - "metadata": {}, - "source": [ - "### Run More Complex Queries\n", - "\n", - "This subsection details `run more complex queries` before the next code cell.\n", - "\n", - "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "77b4454c", - "metadata": {}, - "outputs": [], - "source": [ - "%%sql\n", - "-- Create another table for demonstration\n", - "CREATE TABLE demo_posts (\n", - " id SERIAL PRIMARY KEY,\n", - " user_id INTEGER REFERENCES demo_users(id),\n", - " title VARCHAR(100) NOT NULL,\n", - " content TEXT,\n", - " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n", - ");\n", - "\n", - "-- Insert some posts\n", - "INSERT INTO demo_posts (user_id, title, content) VALUES\n", - " (1, 'Alice First Post', 'Hello world from Alice!'),\n", - " (1, 'Alice Second Post', 'Another post from Alice'),\n", - " (2, 'Bob Introduction', 'Hi, this is Bob!'),\n", - " (3, 'Charlie Notes', 'Some notes from Charlie');\n", - " \n", - "-- Query with JOIN\n", - "SELECT u.username, p.title, p.content\n", - "FROM demo_users u\n", - "JOIN demo_posts p ON u.id = p.user_id\n", - "ORDER BY u.username, p.created_at;" - ] - }, - { - "cell_type": "markdown", - "id": "faebd525", - "metadata": {}, - "source": [ - "## 5. Using Regular Python to Access the Database\n", - "\n", - "You can also interact with the database using Python code and psycopg2:\n", - "\n", - "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "73789e07", - "metadata": {}, - "outputs": [], - "source": [ - "import psycopg2\n", - "from psycopg2.extras import RealDictCursor\n", - "\n", - "# Connect to the database\n", - "conn = db_manager.connection\n", - "\n", - "# Create a cursor\n", - "cur = conn.cursor()\n", - "\n", - "# Execute a query\n", - "cur.execute(\"SELECT COUNT(*) as post_count FROM demo_posts\")\n", - "result = cur.fetchone()\n", - "print(f\"Total number of posts: {result['post_count']}\")\n", - "\n", - "# Group by query\n", - "cur.execute(\"\"\"\n", - " SELECT u.username, COUNT(p.id) as post_count \n", - " FROM demo_users u\n", - " LEFT JOIN demo_posts p ON u.id = p.user_id\n", - " GROUP BY u.username\n", - " ORDER BY post_count DESC\n", - "\"\"\")\n", - "\n", - "print(\"\\nPost count by user:\")\n", - "for row in cur.fetchall():\n", - " print(f\" {row['username']}: {row['post_count']} posts\")\n", - "\n", - "# Close the connection\n", - "cur.close()\n", - "conn.close()" - ] - }, - { - "cell_type": "markdown", - "id": "84b17342", - "metadata": {}, - "source": [ - "## 6. Clean Up\n", - "\n", - "When you're done with the database, you can delete it:\n", - "\n", - "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ae30ac03", - "metadata": {}, - "outputs": [], - "source": [ - "# Delete the database container\n", - "db_manager.delete_db(running_ok=True)\n", - "print(f\"Database container '{container_name}' deleted\")" - ] - }, - { - "cell_type": "markdown", - "id": "dcaa54fc", - "metadata": {}, - "source": [ - "## 7. Conclusion\n", - "\n", - "This notebook demonstrated how to:\n", - "\n", - "1. Configure and create a PostgreSQL database with `PostgresDB`\n", - "2. Connect to the database using SQL cell magic\n", - "3. Execute SQL queries on the database\n", - "4. Clean up the database when finished\n", - "\n", - "The `PostgresDB` manager provides a convenient way to spin up PostgreSQL instances in Docker containers for development, testing, or demonstration purposes.\n", - "\n", - "For production use, align this tutorial flow with your team standards: credential handling, migration strategy, backup policy, and monitoring. When needed, start from the provider setup docs (https://www.postgresql.org/download/) and validate against real datasets before benchmarking query performance." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} + "cells": [ + { + "cell_type": "markdown", + "id": "7c27d4ae", + "metadata": {}, + "source": [ + "# PostgresDB Manager Usage Examples\n", + "\n", + "This notebook demonstrates a practical `PostgreSQL` workflow with `py-dockerdb`, focusing on fast local setup, predictable lifecycle control, and reproducible execution from a single Python interface. Instead of manually wiring container commands for each run, you configure once, start the service, validate connectivity, and then execute database operations in a consistent sequence.\n", + "\n", + "If you are comparing engines, this pattern helps you isolate query behavior and schema differences without rewriting orchestration code. Official reference: https://www.postgresql.org/docs/." + ] + }, + { + "cell_type": "markdown", + "id": "6b4e1903", + "metadata": {}, + "source": [ + "## Table Of Contents\n", + "\n", + "- [Table Of Contents](#table-of-contents)\n", + "- [Prerequisites](#prerequisites)\n", + "- [1. Setup](#1-setup)\n", + " - [Import Dependencies](#import-dependencies)\n", + "- [2. Creating a PostgreSQL Database Instance](#2-creating-a-postgresql-database-instance)\n", + "- [3. Start the Database](#3-start-the-database)\n", + "- [4. Connect and Run SQL Queries](#4-connect-and-run-sql-queries)\n", + " - [Creating a Table](#creating-a-table)\n", + " - [Insert Data](#insert-data)\n", + " - [Query Data](#query-data)\n", + " - [Run More Complex Queries](#run-more-complex-queries)\n", + "- [5. Using Regular Python to Access the Database](#5-using-regular-python-to-access-the-database)\n", + "- [6. Clean Up](#6-clean-up)\n", + "- [7. Conclusion](#7-conclusion)\n", + "- [8. Conclusion](#8-conclusion)" + ] + }, + { + "cell_type": "markdown", + "id": "45681183", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "No environment variables are required unless you intentionally override the defaults used in this notebook.\n", + "\n", + "Additional prerequisites:\n", + "- Docker must be installed and running before you call `create_db()`.\n", + "- Install project dependencies (for example, `pip install -e \".[test]\"` or `pip install py-dockerdb`).\n", + "- Run cells top-to-bottom so container lifecycle state remains consistent.\n", + "\n", + "Provider documentation: https://www.postgresql.org/docs/\n", + "Typical setup guide: https://www.postgresql.org/download/\n", + "Docker image setup reference: https://hub.docker.com/_/postgres\n", + "\n", + "Example datasets you can use to populate this database:\n", + "- DVD Rental sample database: https://www.postgresqltutorial.com/postgresql-getting-started/postgresql-sample-database/\n", + "- Pagila sample dataset: https://github.com/devrimgunduz/pagila" + ] + }, + { + "cell_type": "markdown", + "id": "7f01fb4c", + "metadata": {}, + "source": [ + "## 1. Setup\n", + "\n", + "### Install Required Packages\n", + "\n", + "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "151a25f6", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install ipython-sql==0.5.0 prettytable==3.8.0\n", + "!pip uninstall py-dockerdb -y" + ] + }, + { + "cell_type": "markdown", + "id": "c696fca6", + "metadata": {}, + "source": [ + "### Import Dependencies\n", + "\n", + "This subsection details `import dependencies` before the next code cell.\n", + "\n", + "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a0ba1c30", + "metadata": {}, + "outputs": [], + "source": [ + "import sys, os\n", + "from pathlib import Path\n", + "sys.path.insert(0, str(Path().cwd().parent))\n", + "print(sys.path[0])" + ] + }, + { + "cell_type": "markdown", + "id": "442f207f", + "metadata": {}, + "source": [ + "This step executes code block `5` and is required for the next stage of the workflow.\n", + "\n", + "Run this only after the previous step completed successfully, because downstream cells assume the resulting object, container state, or connection is already available." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b4a8c6d8", + "metadata": {}, + "outputs": [], + "source": [ + "import uuid\n", + "from pathlib import Path\n", + "from docker_db.dbs.postgres_db import PostgresConfig, PostgresDB\n", + "\n", + "# For SQL cell magic\n", + "%load_ext sql" + ] + }, + { + "cell_type": "markdown", + "id": "9e5b63b5", + "metadata": {}, + "source": [ + "## 2. Creating a PostgreSQL Database Instance\n", + "\n", + "Now, let's set up the PostgresDB configuration:\n", + "\n", + "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd2e63d", + "metadata": {}, + "outputs": [], + "source": "import tempfile\nimport shutil\n\ntemp_dir = Path(tempfile.mkdtemp())\n\ncontainer_name = f\"demo-postgres-{uuid.uuid4().hex[:8]}\"\nsource_init_script_path = Path(\"configs\", \"postgres\", \"initdb.sh\")\ninit_script_path = Path(temp_dir, \"initdb.sh\")\nshutil.copy2(source_init_script_path, init_script_path)\ninit_script_path.exists()" + }, + { + "cell_type": "markdown", + "id": "d51fe400", + "metadata": {}, + "source": [ + "This step executes code block `8` and is required for the next stage of the workflow.\n", + "\n", + "Run this only after the previous step completed successfully, because downstream cells assume the resulting object, container state, or connection is already available." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5eee9ccc", + "metadata": {}, + "outputs": [], + "source": [ + "from utils import display_bash_script\n", + "display_bash_script(init_script_path)" + ] + }, + { + "cell_type": "markdown", + "id": "8f1b21e4", + "metadata": {}, + "source": [ + "This step executes code block `9` and is required for the next stage of the workflow.\n", + "\n", + "Run this only after the previous step completed successfully, because downstream cells assume the resulting object, container state, or connection is already available." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a5bdca77", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a configuration for our database\n", + "config = PostgresConfig(\n", + " user=\"demouser\",\n", + " password=\"demopass\",\n", + " database=\"demodb\",\n", + " project_name=\"demo\",\n", + " container_name=container_name,\n", + " workdir=temp_dir.absolute(),\n", + " retries=20,\n", + " delay=3,\n", + " init_script=init_script_path,\n", + " env_vars={\"YourEnvVar\": \"TestVar\"},\n", + ")\n", + "\n", + "# Initialize the database manager\n", + "db_manager = PostgresDB(config)" + ] + }, + { + "cell_type": "markdown", + "id": "bdf72045", + "metadata": {}, + "source": [ + "## 3. Start the Database\n", + "\n", + "We'll now create and start the database:\n", + "\n", + "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d49374b2", + "metadata": {}, + "outputs": [], + "source": [ + "# Create and start the database\n", + "db_manager.create_db()\n", + "print(f\"Database started successfully in container '{container_name}'\")\n", + "print(f\"Connection details:\")\n", + "print(f\" Host: {config.host}\")\n", + "print(f\" Port: {config.port}\")\n", + "print(f\" User: {config.user}\")\n", + "print(f\" Database: {config.database}\")" + ] + }, + { + "cell_type": "markdown", + "id": "ca91baa2", + "metadata": {}, + "source": [ + "## 4. Connect and Run SQL Queries\n", + "\n", + "Now that our database is running, let's connect to it using SQL cell magic:\n", + "\n", + "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "acf19208", + "metadata": {}, + "outputs": [], + "source": [ + "# Define the connection string for SQL magic\n", + "conn_string = db_manager.connection_string(sql_magic=True)\n", + "%sql $conn_string" + ] + }, + { + "cell_type": "markdown", + "id": "3a767d19", + "metadata": {}, + "source": [ + "This step executes code block `14` and is required for the next stage of the workflow.\n", + "\n", + "Run this only after the previous step completed successfully, because downstream cells assume the resulting object, container state, or connection is already available." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77386968", + "metadata": {}, + "outputs": [], + "source": [ + "%%sql\n", + "SELECT * FROM test_table;" + ] + }, + { + "cell_type": "markdown", + "id": "84c63374", + "metadata": {}, + "source": [ + "### Creating a Table\n", + "\n", + "This subsection details `creating a table` before the next code cell.\n", + "\n", + "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3f95d1fe", + "metadata": {}, + "outputs": [], + "source": [ + "%%sql\n", + "CREATE TABLE demo_users (\n", + " id SERIAL PRIMARY KEY,\n", + " username VARCHAR(50) UNIQUE NOT NULL,\n", + " email VARCHAR(100) UNIQUE,\n", + " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n", + ");" + ] + }, + { + "cell_type": "markdown", + "id": "7065be13", + "metadata": {}, + "source": [ + "### Insert Data\n", + "\n", + "This subsection details `insert data` before the next code cell.\n", + "\n", + "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c07c97c4", + "metadata": {}, + "outputs": [], + "source": [ + "%%sql\n", + "INSERT INTO demo_users (username, email) VALUES\n", + " ('alice', 'alice@example.com'),\n", + " ('bob', 'bob@example.com'),\n", + " ('charlie', 'charlie@example.com');" + ] + }, + { + "cell_type": "markdown", + "id": "d04fde28", + "metadata": {}, + "source": [ + "### Query Data\n", + "\n", + "This subsection details `query data` before the next code cell.\n", + "\n", + "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e7db6dc9", + "metadata": {}, + "outputs": [], + "source": [ + "%%sql\n", + "SELECT * FROM demo_users;" + ] + }, + { + "cell_type": "markdown", + "id": "00dc978a", + "metadata": {}, + "source": [ + "### Run More Complex Queries\n", + "\n", + "This subsection details `run more complex queries` before the next code cell.\n", + "\n", + "This subsection introduces the next concrete operation and clarifies why it matters before you run the following code cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77b4454c", + "metadata": {}, + "outputs": [], + "source": [ + "%%sql\n", + "-- Create another table for demonstration\n", + "CREATE TABLE demo_posts (\n", + " id SERIAL PRIMARY KEY,\n", + " user_id INTEGER REFERENCES demo_users(id),\n", + " title VARCHAR(100) NOT NULL,\n", + " content TEXT,\n", + " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n", + ");\n", + "\n", + "-- Insert some posts\n", + "INSERT INTO demo_posts (user_id, title, content) VALUES\n", + " (1, 'Alice First Post', 'Hello world from Alice!'),\n", + " (1, 'Alice Second Post', 'Another post from Alice'),\n", + " (2, 'Bob Introduction', 'Hi, this is Bob!'),\n", + " (3, 'Charlie Notes', 'Some notes from Charlie');\n", + " \n", + "-- Query with JOIN\n", + "SELECT u.username, p.title, p.content\n", + "FROM demo_users u\n", + "JOIN demo_posts p ON u.id = p.user_id\n", + "ORDER BY u.username, p.created_at;" + ] + }, + { + "cell_type": "markdown", + "id": "faebd525", + "metadata": {}, + "source": [ + "## 5. Using Regular Python to Access the Database\n", + "\n", + "You can also interact with the database using Python code and psycopg2:\n", + "\n", + "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73789e07", + "metadata": {}, + "outputs": [], + "source": [ + "import psycopg2\n", + "from psycopg2.extras import RealDictCursor\n", + "\n", + "# Connect to the database\n", + "conn = db_manager.connection\n", + "\n", + "# Create a cursor\n", + "cur = conn.cursor()\n", + "\n", + "# Execute a query\n", + "cur.execute(\"SELECT COUNT(*) as post_count FROM demo_posts\")\n", + "result = cur.fetchone()\n", + "print(f\"Total number of posts: {result['post_count']}\")\n", + "\n", + "# Group by query\n", + "cur.execute(\"\"\"\n", + " SELECT u.username, COUNT(p.id) as post_count \n", + " FROM demo_users u\n", + " LEFT JOIN demo_posts p ON u.id = p.user_id\n", + " GROUP BY u.username\n", + " ORDER BY post_count DESC\n", + "\"\"\")\n", + "\n", + "print(\"\\nPost count by user:\")\n", + "for row in cur.fetchall():\n", + " print(f\" {row['username']}: {row['post_count']} posts\")\n", + "\n", + "# Close the connection\n", + "cur.close()\n", + "conn.close()" + ] + }, + { + "cell_type": "markdown", + "id": "84b17342", + "metadata": {}, + "source": [ + "## 6. Clean Up\n", + "\n", + "When you're done with the database, you can delete it:\n", + "\n", + "This section provides context for the next executable step so the workflow remains reproducible and easy to debug." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ae30ac03", + "metadata": {}, + "outputs": [], + "source": [ + "# Delete the database container\n", + "db_manager.delete_db(running_ok=True)\n", + "print(f\"Database container '{container_name}' deleted\")" + ] + }, + { + "cell_type": "markdown", + "id": "dcaa54fc", + "metadata": {}, + "source": [ + "## 7. Conclusion\n", + "\n", + "This notebook demonstrated how to:\n", + "\n", + "1. Configure and create a PostgreSQL database with `PostgresDB`\n", + "2. Connect to the database using SQL cell magic\n", + "3. Execute SQL queries on the database\n", + "4. Clean up the database when finished\n", + "\n", + "The `PostgresDB` manager provides a convenient way to spin up PostgreSQL instances in Docker containers for development, testing, or demonstration purposes.\n", + "\n", + "For production use, align this tutorial flow with your team standards: credential handling, migration strategy, backup policy, and monitoring. When needed, start from the provider setup docs (https://www.postgresql.org/download/) and validate against real datasets before benchmarking query performance." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/usage/redis_example.ipynb b/usage/redis_example.ipynb index 4173636..27d26c3 100644 --- a/usage/redis_example.ipynb +++ b/usage/redis_example.ipynb @@ -1,189 +1,165 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Redis Manager Usage Examples\n", - "\n", - "This notebook demonstrates how to use the `RedisDB` manager class to create, manage, and interact with a Redis database in a Docker container." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup\n", - "\n", - "### Import Dependencies" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import sys\n", - "import uuid\n", - "from pathlib import Path\n", - "\n", - "sys.path.insert(0, str(Path().cwd().parent))\n", - "\n", - "from docker_db.dbs.redis_db import RedisConfig, RedisDB" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create Redis Configuration" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "temp_dir = Path(\"tmp\")\n", - "temp_dir.mkdir(exist_ok=True)\n", - "\n", - "container_name = f\"demo-redis-{uuid.uuid4().hex[:8]}\"\n", - "\n", - "config = RedisConfig(\n", - " database=0,\n", - " project_name=\"demo\",\n", - " container_name=container_name,\n", - " workdir=temp_dir.absolute(),\n", - " retries=20,\n", - " delay=2,\n", - ")\n", - "\n", - "db_manager = RedisDB(config)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Start Redis" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Redis started in container: demo-redis-d7f1c3d8\n", - "Connection string: redis://127.0.0.1:6379/0\n" - ] - } - ], - "source": [ - "db_manager.create_db()\n", - "print(f\"Redis started in container: {container_name}\")\n", - "print(f\"Connection string: {db_manager.connection_string()}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Write and Read Data" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "app:status -> running\n", - "user:1 -> {'name': 'alice', 'role': 'admin'}\n", - "events -> ['created', 'updated', 'completed']\n" - ] - } - ], - "source": [ - "client = db_manager.connection\n", - "\n", - "client.set(\"app:status\", \"running\")\n", - "client.hset(\"user:1\", mapping={\"name\": \"alice\", \"role\": \"admin\"})\n", - "client.rpush(\"events\", \"created\", \"updated\", \"completed\")\n", - "\n", - "print(\"app:status ->\", client.get(\"app:status\"))\n", - "print(\"user:1 ->\", client.hgetall(\"user:1\"))\n", - "print(\"events ->\", client.lrange(\"events\", 0, -1))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Clean Up" - ] - }, + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Redis Manager Usage Examples\n", + "\n", + "This notebook demonstrates how to use the `RedisDB` manager class to create, manage, and interact with a Redis database in a Docker container." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "### Import Dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "import sys\nimport uuid\nimport tempfile\nfrom pathlib import Path\n\nsys.path.insert(0, str(Path().cwd().parent))\n\nfrom docker_db.dbs.redis_db import RedisConfig, RedisDB" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create Redis Configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "temp_dir = Path(tempfile.mkdtemp())\n\ncontainer_name = f\"demo-redis-{uuid.uuid4().hex[:8]}\"\n\nconfig = RedisConfig(\n database=0,\n project_name=\"demo\",\n container_name=container_name,\n workdir=temp_dir,\n retries=20,\n delay=2,\n)\n\ndb_manager = RedisDB(config)" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Start Redis" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Container demo-redis-d7f1c3d8 stopped and port 6379 is free\n", - "Redis container 'demo-redis-d7f1c3d8' deleted\n" - ] - } - ], - "source": [ - "db_manager.delete_db(running_ok=True)\n", - "print(f\"Redis container '{container_name}' deleted\")" - ] - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Redis started in container: demo-redis-d7f1c3d8\n", + "Connection string: redis://127.0.0.1:6379/0\n" + ] + } + ], + "source": [ + "db_manager.create_db()\n", + "print(f\"Redis started in container: {container_name}\")\n", + "print(f\"Connection string: {db_manager.connection_string()}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Write and Read Data" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Conclusion\n", - "\n", - "This notebook demonstrated how to:\n", - "\n", - "1. Configure and create a Redis instance with `RedisDB`\n", - "2. Connect using redis-py\n", - "3. Write and read key-value, hash, and list data\n", - "4. Clean up the container when finished" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "app:status -> running\n", + "user:1 -> {'name': 'alice', 'role': 'admin'}\n", + "events -> ['created', 'updated', 'completed']\n" + ] } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.12" + ], + "source": [ + "client = db_manager.connection\n", + "\n", + "client.set(\"app:status\", \"running\")\n", + "client.hset(\"user:1\", mapping={\"name\": \"alice\", \"role\": \"admin\"})\n", + "client.rpush(\"events\", \"created\", \"updated\", \"completed\")\n", + "\n", + "print(\"app:status ->\", client.get(\"app:status\"))\n", + "print(\"user:1 ->\", client.hgetall(\"user:1\"))\n", + "print(\"events ->\", client.lrange(\"events\", 0, -1))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Clean Up" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Container demo-redis-d7f1c3d8 stopped and port 6379 is free\n", + "Redis container 'demo-redis-d7f1c3d8' deleted\n" + ] } + ], + "source": [ + "db_manager.delete_db(running_ok=True)\n", + "print(f\"Redis container '{container_name}' deleted\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusion\n", + "\n", + "This notebook demonstrated how to:\n", + "\n", + "1. Configure and create a Redis instance with `RedisDB`\n", + "2. Connect using redis-py\n", + "3. Write and read key-value, hash, and list data\n", + "4. Clean up the container when finished" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 4 -} + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file From d02aab4f56253307bd71f5e0c79591f1d37b1f89 Mon Sep 17 00:00:00 2001 From: Amadou Wolfgang Cisse Date: Wed, 4 Mar 2026 09:32:28 +0100 Subject: [PATCH 4/4] Increment release --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 62227e5..290c0a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "py-dockerdb" -version = "0.9.0" +version = "1.0.0" description = "Provision Docker databases in Python — Postgres, Neo4j, MongoDB, MySQL, MSSQL with a unified one-call API" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.10"