Neo4j Recipes and Tips
Common Python patterns and use cases you may come across when working with Neo4j or other databases using Neontology.
Created and Modified Timestamps
A common requirement can be to record when nodes and relationships were created or modified in Neo4j (or any other graph database).
If you're using Neontology, there are a couple of features which can help with this. The below example uses Pydantic and Neontology functionality to set a created timestamp when nodes are created and then updates the merged timestamp any time the Node is merged/modified.
You can also manually override the values by explicitly setting them when creating/merging the node.
from datetime import datetime
from typing import Optional
from pydantic import Field, field_validator, ValidationInfo
from neontology import BaseNode
class MyBaseNode(BaseNode):
merged: datetime = Field(
default_factory=datetime.now,
)
# created property will only be set 'on create' - when the node is first created
created: Optional[datetime] = Field(
default=None,
validate_default=True,
json_schema_extra={"set_on_create": True})
# Use Pydantic's validator functionality to set created off the merged value
@field_validator("created")
def set_created_to_merged(
cls, value: Optional[datetime], values: ValidationInfo
) -> datetime:
"""When the node is first created, we want the created value to be set equal to merged.
Otherwise they will be a tiny amount of time different.
"""
# set created = merged (which was set to datetime.now())
if value is None:
return values.data["merged"]
# if the created value has been manually set, don't override it
else:
return value
Neontology v0 and v1 included merged and created fields with this functionality by default but it was removed in v2 to provide greater flexibility to users.
Set a property value to a unique ID
Another common pattern is to assign each new node that goes into Neo4j with a unique identifier or key. Again, we can use Pydantic to help with this - combining Python's built-in uuid4 function with Pydantic's support for a default_factory to generate a property value.
from uuid import uuid4
from neontology import BaseNode
class PersonNode(BaseNode):
__primarylabel__: ClassVar[str] = "PersonLabel"
__primaryproperty__: ClassVar[str] = "id"
name: str
age: int
id: str = Field(default_factory=lambda: uuid4().hex)
Depending on what you're trying to achieve, you could also use a custom field_validator to help generate an appropriate unique value.