How to Use ArrayProperty in Neomodel for Storing Lists of Values
ArrayProperty is Neomodel's wrapper for Neo4j's native list type, enabling models to store ordered collections of scalar values or typed objects with optional indexing and validation.
In the neo4j-contrib/neomodel library, ArrayProperty provides the primary mechanism for persisting Python lists as Neo4j array properties. Defined in neomodel/properties.py (lines 336-386), this property class handles automatic serialization between Python lists and Neo4j's native array type while supporting type-safe collections through its base_property parameter.
Core Implementation of ArrayProperty
Class Architecture and Data Translation
The ArrayProperty class inherits from the base Property class and implements custom inflate and deflate methods to manage bidirectional translation between Python lists and Neo4j arrays. When a node is saved, the deflate method converts the Python list to a format Neo4j can store; upon retrieval, inflate reconstructs the Python list from the database array.
The base_property Parameter for Type Safety
The optional base_property parameter enables element-level validation by processing each list item through another Property instance's conversion methods:
from neomodel import StructuredNode, StringProperty, ArrayProperty, IntegerProperty
class Score(StructuredNode):
player = StringProperty()
points = ArrayProperty(IntegerProperty()) # Enforces integer elements only
When base_property is provided, each element passes through that property's inflate and deflate methods, raising DeflateError if type conversion fails (as implemented in lines 340-378 of neomodel/properties.py).
Basic Usage Patterns
Storing Untyped Arrays
For flexible storage of heterogeneous data, instantiate ArrayProperty without a base type:
class Person(StructuredNode):
name = StringProperty(required=True)
tags = ArrayProperty() # Accepts any Neo4j-compatible values
# Create and persist
person = Person(name="Alice", tags=["friend", "colleague", "admin"]).save()
# Retrieve and verify
retrieved = Person.nodes.get(name="Alice")
assert "friend" in retrieved.tags
This pattern corresponds to the test implementation in test/sync_/test_properties.py (lines 84-88).
Validating Typed Arrays
When data integrity is critical, combine ArrayProperty with specific property types:
# Valid operation
Score(player="Bob", points=[10, 20, 30]).save()
# Invalid operation raises DeflateError
try:
Score(player="Bob", points=["ten"]).save()
except DeflateError:
print("Validation failed: non-integer value in array")
The validation logic executes during the save operation, preventing invalid data from reaching the database (verified in test/sync_/test_properties.py, lines 90-100).
Indexing and Constraints
Creating Database Indexes on Arrays
ArrayProperty supports Neo4j indexing at the array level, treating the entire list as a single indexed value:
class UniqueArrayNode(StructuredNode):
values = ArrayProperty(unique_index=True)
When install_labels executes, this creates a unique constraint on the complete array value. Query performance improves for exact array matches:
node = UniqueArrayNode(values=[1, 2, 3]).save()
result = UniqueArrayNode.nodes.get(values=[1, 2, 3]) # Utilizes index
assert node.element_id == result.element_id
This functionality is tested in test/sync_/test_properties.py (lines 18-24).
Validation Constraints and Limitations
The implementation enforces strict constraints to prevent invalid configurations:
- Nested arrays prohibited: Instantiating
ArrayProperty(ArrayProperty())raisesTypeErrorduring class definition (lines 352-356). - Illegal base property attributes: The
base_propertycannot specifydefault,index,unique_index, orrequiredattributes, as these apply to the container rather than individual elements. Violations raiseValueError(lines 357-366). - Default value isolation: The property returns a copy of the default list to prevent shared state mutations across instances (lines 386-388).
Advanced Usage: Vector Indexes and Semantic Search
For AI and machine learning applications, ArrayProperty integrates with Neo4j's vector indexes to enable semantic similarity search:
from neomodel import StructuredNode, ArrayProperty, FloatProperty
from neomodel.indexes import VectorIndex
class Document(StructuredNode):
embedding = ArrayProperty(
base_property=FloatProperty(),
vector_index=VectorIndex(dimensions=512, similarity_function="cosine")
)
This configuration stores 512-dimensional float vectors and creates a vector index for high-performance similarity queries using cosine similarity, as documented in doc/source/semantic_indexes.rst (lines 91-114).
Integration with Cypher Queries
ArrayProperty handles serialization automatically through its inflate and deflate methods (lines 368-386 in neomodel/properties.py). When filtering in Cypher queries, you can use array operators:
# Find nodes where array contains specific value
Person.nodes.filter(tags__contains="admin")
# Exact array match (utilizes index if defined)
Person.nodes.filter(tags=["friend", "colleague"])
The async API provides identical functionality through test/async_/test_properties.py, ensuring consistent behavior across sync and async operations.
Summary
- ArrayProperty stores ordered lists in Neo4j nodes, implemented in
neomodel/properties.py(lines 336-386). - Untyped arrays accept any Neo4j-compatible values, while typed arrays use
base_propertyfor element-level validation. - Indexing supports
unique_indexandindexon the complete array value, not individual elements. - Constraints prohibit nested arrays and illegal attributes (
default,index,unique_index,required) on base properties. - Vector indexes enable semantic search when combining
ArrayPropertywithFloatPropertyandVectorIndex. - Automatic serialization handles Python list ↔ Neo4j array conversion without manual intervention in both sync and async contexts.
Frequently Asked Questions
Can I store nested arrays using ArrayProperty?
No. The ArrayProperty implementation explicitly prohibits wrapping another ArrayProperty. Attempting to define a field as ArrayProperty(ArrayProperty()) raises a TypeError during class definition (lines 352-356 in neomodel/properties.py). For complex nested data structures, model the inner arrays as separate nodes with relationships to the parent node.
How do I create a unique constraint on individual array elements?
You cannot create constraints on individual elements within an array. The unique_index constraint on an ArrayProperty applies to the entire array as a single value. Neo4j treats the complete list as one property value for indexing purposes. To enforce uniqueness on individual items, model each item as a separate node with its own unique_index constraint and create relationships between the array container and the element nodes.
Does ArrayProperty support async operations in neomodel?
Yes. ArrayProperty functions identically in both synchronous and asynchronous neomodel contexts. The property's inflate and deflate methods operate on Python lists regardless of whether you use the sync API (Person.nodes.get()) or the async API (await Person.nodes.async_get()). The test suite validates this behavior in test/async_/test_properties.py, which mirrors the sync tests in test/sync_/test_properties.py.
What happens if I pass an invalid type to a typed ArrayProperty?
The property raises a DeflateError during the save operation. When you specify a base_property (such as IntegerProperty()), each element in the list is processed through that property's deflate method for validation and conversion. If any element fails type conversion—for example, passing a string to an integer-typed array—neomodel raises DeflateError before the data reaches Neo4j, preventing invalid data persistence (as verified in test/sync_/test_properties.py lines 90-100).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →