Getting to Know NeoAlchemy¶
NeoAlchemy features both a low-level and a high-level API for working with Neo4J. The low-level API aims to be expressive, offering the user flexibility and control. The high-level API is built on top of the low-level API, and trades control for automation.
You don’t have to choose between APIs! The low-level and high-level APIs can be
used in conjunction with one another as well as with manual Cypher querying
with graph.query
.
The Low-Level API¶
NeoAlchemy’s low-level API is called The QueryBuilder API. It is similar in feel and purpose to the SqlAlchemy Expression Language. In SqlAlchemy, defining the schema for a table and writing its metadata to the database looks like this:
from sqlalchemy import Table, Column, Integer, String, MetaData
metadata = MetaData()
users = Table('users', metadata,
Column('id', Integer, primary_key=True),
Column('name', String),
Column('fullname', String)
)
# Emit schema-generating DDL
metadata.create_all(engine)
This defines a simple users
table with three columns. A call to
metadata.create_all()
is required to emit schema-generating DDL,
laying out the empty table.
The same thing in NeoAlchemy looks like this:
import uuid
from neoalchemy import Node, Property, Graph
from neoalchemy.validators import UUID
graph = Graph()
user = Node('User',
uuid=Property(unique=True, type=UUID, default=uuid.uuid4),
name=Property(indexed=True),
full_name=Property(required=True)
)
# Emit schema-generating DDL
graph.schema.create(user)
This creates a simple Node
with three properties similar to the
above table. Each property represents an available constraint in Neo4J. The
proper indexes and constraints are created when graph.schema.create()
is called.
Note
The required
property represents a Property Existence constraint
which is only supported in the Neo4J Enterprise Edition.
Can’t wait to learn more? Dive into The QueryBuilder API.
The High-Level API¶
NeoAlchemy’s high-level API is Schema OGM. Python classes are used to map metadata to the database transparently. It is compared below to SqlAlchemy’s Declarative ORM:
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
fullname = Column(String)
# Emit schema-generating DDL
Base.metadata.create_all(engine)
The same thing in NeoAlchemy looks like this:
import uuid
from neoalchemy import OGMBase, Property, Graph
from neoalchemy.validators import UUID
class User(OGMBase):
graph = Graph()
uuid = Property(unique=True, type=UUID, default=uuid.uuid4)
name = Property(indexed=True)
fullname = Property(required=True)
# Cypher schema generation emitted automatically
# No user action required
Notice that unlike SqlAlchemy, we have far less to import and we do not need
to manually trigger metadata creation. We also don’t have to explicitly
specify a label for our underlying Node
. NeoAlchemy
uses the name of the class if none is specified.
Note
Since every class is connected to a graph explicitly via its .graph
property, users running multiple instances of Neo4J should have no trouble
distinguishing which classes map to which graphs, even if multiple classes
touching different graphs are grouped in the same file.
Wanna learn more? Skip straight to the Schema OGM.