A high-performance Cypher-capable graph query engine with Python bindings for building scalable, serverless knowledge graphs.
Lance Graph combines a Rust-powered Cypher query engine with Python APIs for:
- Fast graph queries using Cypher query language
- AI-powered knowledge extraction from text (via LLM)
- Lance-backed storage for efficient graph data management
- Natural language Q&A over your knowledge graphs
- FastAPI web service for graph queries
pip install lance-graphimport pyarrow as pa
from lance_graph import CypherQuery, GraphConfig
cfg = (
GraphConfig.builder()
.with_node_label("Person", "id")
.with_node_label("City", "id")
.with_relationship("lives_in", "src", "dst")
.build()
)
datasets = {
"Person": pa.table({"id": [1, 2], "name": ["Alice", "Bob"]}),
"City": pa.table({"id": [10, 20], "name": ["London", "Sydney"]}),
"lives_in": pa.table({"src": [1, 2], "dst": [10, 20]}),
}
query = """
MATCH (p:Person)-[:lives_in]->(c:City)
RETURN p.name, c.name
"""
result = CypherQuery(query).with_config(cfg).execute(datasets)
print(result.to_pylist())
[{'p.name': 'Alice', 'c.name': 'London'}, {'p.name': 'Bob', 'c.name': 'Sydney'}]For executing multiple queries against the same datasets, use CypherEngine to cache the catalog and achieve better performance:
import pyarrow as pa
from lance_graph import CypherEngine, GraphConfig
cfg = (
GraphConfig.builder()
.with_node_label("Person", "id")
.with_node_label("City", "id")
.with_relationship("lives_in", "src", "dst")
.build()
)
datasets = {
"Person": pa.table({"id": [1, 2], "name": ["Alice", "Bob"], "age": [30, 25]}),
"City": pa.table({"id": [10, 20], "name": ["London", "Sydney"]}),
"lives_in": pa.table({"src": [1, 2], "dst": [10, 20]}),
}
# Create engine once - builds catalog
engine = CypherEngine(cfg, datasets)
# Execute multiple queries efficiently - catalog is reused
result1 = engine.execute("MATCH (p:Person) WHERE p.age > 25 RETURN p.name")
result2 = engine.execute("MATCH (p:Person)-[:lives_in]->(c:City) RETURN p.name, c.name")
result3 = engine.execute("MATCH (p:Person) RETURN count(*) as total")
print(result1.to_pylist())
# [{'p.name': 'Alice'}]For data analytics workflows where you prefer standard SQL over Cypher, use SqlQuery or SqlEngine. No GraphConfig is needed — write explicit JOINs against your tables directly:
import pyarrow as pa
from lance_graph import SqlQuery, SqlEngine
person = pa.table({
"id": [1, 2, 3],
"name": ["Alice", "Bob", "Carol"],
"age": [28, 34, 29],
})
knows = pa.table({"src_id": [1, 1, 2], "dst_id": [2, 3, 3]})
datasets = {"person": person, "knows": knows}
# One-off query
result = SqlQuery(
"SELECT p.name, p.age FROM person p WHERE p.age > 30"
).execute(datasets)
print(result.to_pylist())
# [{'name': 'Bob', 'age': 34}]
# Multi-query with cached context
engine = SqlEngine(datasets)
r1 = engine.execute("SELECT COUNT(*) AS cnt FROM person")
r2 = engine.execute(
"SELECT p1.name AS person, p2.name AS friend "
"FROM person p1 "
"JOIN knows k ON p1.id = k.src_id "
"JOIN person p2 ON p2.id = k.dst_id"
)Connect to Unity Catalog (OSS) to discover and query Delta Lake or Parquet tables without manually loading data:
from lance_graph import UnityCatalog
# Connect to Unity Catalog
uc = UnityCatalog("http://localhost:8080/api/2.1/unity-catalog")
# Browse catalog hierarchy
catalogs = uc.list_catalogs()
schemas = uc.list_schemas("unity")
tables = uc.list_tables("unity", "default")
# Inspect table metadata
table = uc.get_table("unity", "default", "marksheet")
print(table.data_source_format) # "Delta"
print(table.columns()) # [{"name": "id", "type_name": "INT", ...}, ...]
# Auto-register all tables and query via SQL
engine = uc.create_sql_engine("unity", "default")
result = engine.execute("SELECT * FROM marksheet WHERE mark > 80")
print(result.to_pandas())For tables on cloud storage (S3, Azure, GCS):
uc = UnityCatalog(
"http://localhost:8080/api/2.1/unity-catalog",
storage_options={
"azure_storage_account_name": "myaccount",
"azure_storage_account_key": "...",
}
)
engine = uc.create_sql_engine("unity", "default")from pathlib import Path
from knowledge_graph import (
KnowledgeGraphConfig,
LanceKnowledgeGraph,
LanceGraphStore,
get_extractor,
)
from knowledge_graph.cli.ingest import extract_and_add
# Initialize knowledge graph
config = KnowledgeGraphConfig.from_root(Path("./my_graph"))
config.ensure_directories()
# Create schema
schema_path = config.resolved_schema_path()
if not schema_path.exists():
schema_content = """
nodes:
Entity:
id_field: entity_id
relationships:
RELATIONSHIP:
source: source_entity_id
target: target_entity_id
"""
schema_path.write_text(schema_content, encoding="utf-8")
store = LanceGraphStore(config)
store.ensure_layout()
graph_config = config.load_graph_config()
kg = LanceKnowledgeGraph(graph_config, storage=store)
kg.ensure_initialized()
# Extract and add entities/relationships from text
# Using heuristic extractor for testing without API key
extractor = get_extractor("heuristic")
# or using LLM extractor (requires API key)
# extractor = get_extractor("llm", llm_model="gpt-4o-mini")
text = """
Albert Einstein developed the theory of relativity at Princeton.
Marie Curie discovered radioactivity in Paris.
"""
extract_and_add(text, kg, extractor, embedding_generator=None)
# Query the graph
result = kg.query("""
MATCH (e:Entity)
RETURN e.name, e.entity_type
LIMIT 10
""")
print(result.to_pylist())from knowledge_graph.llm.qa import ask_question
# Ask questions in natural language
answer = ask_question(
"Who discovered radioactivity?",
kg,
llm_model="gpt-4o-mini"
)
print(answer)
# Output: Marie Curie discovered radioactivity.Lance Graph includes a CLI for building and querying knowledge graphs:
# Initialize and extract
knowledge_graph --root ./my_graph --init
knowledge_graph --root ./my_graph --extract-and-add notes.txt
# Query with Cypher
knowledge_graph --root ./my_graph "MATCH (e:Entity) RETURN e.name LIMIT 10"
# Natural language Q&A
knowledge_graph --root ./my_graph --ask "Who discovered DNA?"For complete CLI documentation and examples, see the main README.
- Python 3.11+
- Optional: OpenAI API key for LLM extraction
Lance Graph is open source! Contributions are welcome.
cd python
uv venv --python 3.11 .venv
source .venv/bin/activate
uv pip install maturin[patchelf]
uv pip install -e '.[tests]'
maturin develop
pytest python/tests/ -vFor linting and type checks:
# Install dev dependencies
uv pip install -e '.[dev]'
# Run linters and type checker
ruff format python/ # format code
ruff check python/ # lint code
pyright # type check
# Run specific tests
pytest python/tests/test_graph.py::test_basic_node_selection -v
# Rebuild extension after Rust changes
maturin developIf another virtual environment is already active, run
deactivate(orunset VIRTUAL_ENV) before invokinguv runso uv binds to.venv.
crates/lance-graph-python/src/– PyO3 bridge that exposes graph APIs to Pythonpython/python/lance_graph/– pure-Python wrapper and__init__python/python/knowledge_graph/– CLI, FastAPI, and extractor utilities built on Lancepython/python/tests/– graph-centric functional tests
For more information on development setup, building from source, running tests, and code quality guidelines, see DEVELOPMENT.md.
Apache 2.0