Skip to content

mkh-user/graphite

Repository files navigation

Graphite

A clean, embedded graph database for Python.


Graphite is a lightweight yet flexible graph database implemented in pure Python. It is designed to model graph-like data inside Python codebases where graph is a core part of project, without introducing the complexity of an external database.

It's optimized for graphs with up to 1M nodes. Blazing fast as a pure Python database, outperforms NetworkX in both memory and speed, with 90x smaller package size. (Repeatable benchmark is available at tests/benchmark.py)

Documentation, with guides about usage or contributing and API reference is available here: https://mkh-user.github.io/graphite

📖 Full Mission & Scope →


Features

Graphite provides an easy and robust way to use any graph-like data in Python projects, it's designed to provide:

  • 🧩 Embedded Database: Database can live inside your project and in same process, so you can modify data and its structure fast, secure, and without any server-interaction headache.
  • ⚙️ Hackable Behavior: Graphite is designed to provide all common features out-of-the-box, but is completely clean-coded to help you hack it easy and fast to shape it for your special needs.
  • 🐍 First-Class Python API: Graphite uses its DSL as optional utility layer, so you can do anything directly with refactor-safe and intelligent Python API. Use DSL just when you like.
  • 🔍 No Query String: Chain well-documented methods to query on data, no learning, parsing, error vanishing, or guessing. Your Python IDE helps you when you write! Just type engine.query and start.
  • 🔄 Runtime Evolution: Customize data structure without shutdown, and deeply control behavior with flexible functions.
  • 🧱 Structure-Oriented Modeling: Define types of nodes and relations with features like inheritance, typed fields, and valid patterns. Model your domain explicitly and safely.
  • 🧬 Node Inheritance: Model real-world data easy and robust. Use subtypes, limited relations, inherited properties, complex validations.
  • ✨ Really useful DSL: Use DSL to create data with more readable and less-duplicated minimal syntax.
  • 💾 Serializable: Persist the entire database into a single JSON file.

Installation

Install from PyPI:

pip install graphitedb

Why Graphite?

Graphite was extracted from a large production codebase where Neo4j introduced more complexity than value.

Neo4j is a powerful tool — but in large projects, adding a separate graph database often increases:

  • infrastructure complexity
  • deployment cost
  • maintenance burden
  • cognitive load on developers

Graphite exists for cases where this cost is not justified.

It provides graph modeling without adding another system to operate.

Comparation

Feature Neo4j Graphite Custom Graph Engine
Bug Safety 🥇Very High:
Mature & tested
🥈High:
Unit tests, monitored
🥉Low-Medium:
You manage testing
Implementation 🥈High:
Setup & Cypher
🥇Low:
Embed easily
🥉Very High:
Build from scratch
Flexibility 🥈High:
Complex queries
🥉Medium:
Limited but extendable
🥇Very High:
Fully customizable
Performance 🥇High:
Optimized large data
🥈Medium:
Good for small/medium
❓Unknown:
Depends on design
Scalability 🥇High:
Cluster & sharding
🥈Medium:
Single-node & Base types
❓Unknown:
Possible but hard
Support / Community 🥇Very High:
Large & active
🥈Medium:
Docstrings only
🥉Low:
Internal only
Customizability 🥉Low:
Limited to API
🥈High:
Open source
🥇Very High:
Full control
Ease of Use 🥈Medium:
Learn Cypher
🥇High:
Quick & simple
🥉Low:
Needs study & test

Example Usage

import graphite
from datetime import date

engine = graphite.engine()

# Use DSL to define types and create data
engine.parse("""
# Node types with 'node '
node Person
    # Indentation is optional
    name: string
    age: int
""")
# Define node types with in-editor hints no parsing cost
engine.define_node("User", ("id", "string"), ("email", "string"), parent="Person")
# parse() can include multiple blocks
engine.parse("""
# You can use node types to control abstractness:
node Object

node Book from Object
    title: string
    n_pages: int

node Car from Object
    model: string
    year: int
""")
engine.define_relation(     # Same with parse():
    "FRIEND",               # relation FRIEND both
    "Person",               #     Person - Person
    "Person",               #     since: date
    ("since", "date"),
    is_bidirectional=True
)
# Relation type blocks are same and node types
engine.parse("""
relation OWNER reverse OWNED_BY
    Person -> Object
    since: date
    purchased_at: date

relation AUTHOR reverse AUTHORED_BY
    Person -> Book
    year: int
""")

# Add data now
# Directly create nodes:
engine.create_node("User", "user_1", "Joe Doe", 32, "joe4030", "joe@email.com")
# Or with parse():
engine.parse("""
User, user_2, "Jane Smith", 28, "jane28", "jane@email.com"
User, user_3, "Bob Wilson", 45, "bob45", "bob@email.com"
User, user_4, "Alice Brown", 22, "alice22", "alice@email.com"

Book, book_1, "The Great Gatsby", 180
Book, book_2, "Python Programming", 450
Book, book_3, "Graph Databases", 320

Car, car_1, "Toyota Camry", 2020
Car, car_2, "Honda Civic", 2018
""")
# And relations:
# Dates can be parsed automatically:
engine.create_relation("user_1", "user_2", "FRIEND", "2020-05-15")
engine.create_relation("user_1", "user_3", "FRIEND", date(2019, 8, 22))
engine.create_relation("user_2", "book_2", "AUTHOR", 2021)
# You can pass parse_fields=True to parse all values from string to correct one:
engine.create_relation("user_1", "book_3", "AUTHOR", "2020", parse_fields=True)
# Is available in DSL too:
engine.parse("""
user_2 -[FRIEND, 2021-01-10]- user_4

user_1 -[OWNER, 2021-03-01, 2021-02-15]-> car_1
user_2 -[OWNER, 2019-06-20, 2019-05-10]-> book_1
user_3 -[OWNER, 2022-11-05, 2022-10-20]-> book_2
""")

print("=== Database Stats ===")
stats = engine.stats()
print(f"Node Types: {stats['node_types']}")
print(f"Relation Types: {stats['relation_types']}")
print(f"Nodes: {stats['nodes']}")
print(f"Relations: {stats['relations']}")

print("\n=== Query Examples ===")

# All users
users = engine.query.User.get()
print(f"All Users ({len(users)}): {[u['name'] for u in users]}")

# Users with more than 30 years age
older_users = engine.query.User.where("age > 30").get()
print(f"\nUsers over 30: {[u['name'] for u in older_users]}")

# Joe Doe books
joe_books = (engine.query.User
              .where(lambda u: u['name'] == "Joe Doe")
              .outgoing("AUTHOR")
              .get())
print(f"\nBooks authored by Joe Doe: {[b['title'] for b in joe_books]}")

# Two steps traverse
friends_of_friends = (engine.query.User
                      .where(lambda u: u['name'] == "Joe Doe")
                      .outgoing("FRIEND")
                      .outgoing("FRIEND")
                      .distinct()
                      .get())
print(f"\nFriends of friends of Joe Doe: {[f['name'] for f in friends_of_friends]}")

More examples are available in examples/ in the GitHub repository.

See https://mkh-user.github.io/graphite for the documentation and API reference.


MIT 2026 Mahan Khalili

About

A medium-scale and easy to use knowledge graph for Python / Embedded, in-memory, pure-Python graph database

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Used by

Contributors

Languages