Usage
This guide will help you quickly start using Python and pandas with Neo4j.
Jupyter notebooks are a great way to get started playing with neontology.
We will build a simple model for ingesting data which looks like this:
(p1:Person)-[:FOLLOWS]->(p2:Person)
Defining nodes
Graphs in Neo4j are made up of Nodes and Relationships (or edges). In neontology, both nodes and relationships are defined as Python objects.
To create a new node type to be modeled in your graph, simply create a python type which inherits from neontology's BaseNode
class (which itself inherits from Pydantic's BaseModel
).
from typing import ClassVar, Optional
import pandas as pd
from neontology import BaseNode, BaseRelationship, init_neontology
class PersonNode(BaseNode):
__primarylabel__: ClassVar[str] = "Person"
__primaryproperty__: ClassVar[str] = "name"
name: str
age: Optional[int] = None
Lets see what we've defined here:
__primarylabel__
- this is the label which will be applied to nodes in the graph__primaryproperty__
- this is the property which neontology will use to MATCH these types of node. We are using the 'name' property.name
this is a field on our model which is of typestr
(a string) - it will map to a property when we create a node.age
another property. In this case it is optional and by default gets set toNone
.
Defining relationships
We can define relationships by specifying the types node which are used for the source and target of the relationship. We must also specify the string to use for the relationship type when creating it in Neo4j.
class FollowsRel(BaseRelationship):
__relationshiptype__: ClassVar[str] = "FOLLOWS"
source: PersonNode
target: PersonNode
Here we've created a relationship which takes a PersonNode as source and another as target. The relationship will be stored in the database with the FOLLOWS
relationship type.
Connecting to Neo4j with Python
We use the init_neontology
method to set up our connection.
NEO4J_URI="neo4j+s://<database id>.databases.neo4j.io" # neo4j Aura example
NEO4J_USERNAME="neo4j"
NEO4J_PASSWORD="<your password>"
init_neontology(
neo4j_uri=NEO4J_URI,
neo4j_username=NEO4J_USERNAME,
neo4j_password=NEO4J_PASSWORD
) # initialise the connection to the database
Creating a node
We can now instantiate some node objects and use the merge()
or create()
method to push them to the database.
alice = PersonNode(name="Alice", age=40)
alice.create()
bob = PersonNode(name="Bob", age=40)
bob.merge()
Creating the relationship
Similarly, we can now create a relationship between the two nodes.
rel = FollowsRel(source=bob,target=alice)
rel.merge()
Populating Neo4j with pandas data
Often we might have data in a pandas dataframe which we want to push to Neo4j according to the model which we have defined.
neontology makes this easy!
For nodes, just create a dataframe where each column represents the fields/properties of the nodes.
node_records = [{"name": "Freddy", "age": 42}, {"name": "Philipa", "age":42}]
node_df = pd.DataFrame.from_records(node_records)
PersonNode.merge_df(node_df)
Similarly, if you have the values for the primary properties of sources and targets, you can push relationships to Neo4j using a pandas dataframe. If the relationship has additional properties, these should also be columns on the dataframe.
rel_records = [
{"source": "Freddy", "target": "Philipa"},
{"source": "Alice", "target": "Freddy"}
]
rel_df = pd.DataFrame.from_records(rel_records)
FollowsRel.merge_df(rel_df)
Putting it all together
Running the above code (full example below) should create the following nodes and relationships in Neo4j:
(Bob:Person)-[:FOLLOWS]->(Alice:Person)-[:FOLLOWS]->(Freddy:Person)-[:FOLLOWS]->(Philipa:Person)
Neo4j and Python Sample Code
# demo.py
from typing import ClassVar, Optional
import pandas as pd
from neontology import BaseNode, BaseRelationship, init_neontology
class PersonNode(BaseNode):
__primarylabel__: ClassVar[str] = "Person"
__primaryproperty__: ClassVar[str] = "name"
name: str
age: Optional[int] = None
class FollowsRel(BaseRelationship):
__relationshiptype__: ClassVar[str] = "FOLLOWS"
source: PersonNode
target: PersonNode
init_neontology() # initialise the connection to the database
alice = PersonNode(name="Alice", age=40)
alice.create()
bob = PersonNode(name="Bob", age=40)
bob.merge()
rel = FollowsRel(source=bob, target=alice)
rel.merge()
node_records = [{"name": "Freddy", "age": 42}, {"name": "Philipa", "age": 42}]
node_df = pd.DataFrame.from_records(node_records)
PersonNode.merge_df(node_df)
rel_records = [
{"source": "Freddy", "target": "Philipa"},
{"source": "Alice", "target": "Freddy"},
]
rel_df = pd.DataFrame.from_records(rel_records)
FollowsRel.merge_df(rel_df)