Schema management

Neo4j allows a flexible schema management, where you can define indexes and constraints on the properties of nodes and relationships. To learn more, please refer to the Neo4j schema documentation.

Defining your model

neomodel allows you to define indexes and constraints in your node and relationship classes, like so:

from neomodel import (StructuredNode, StructuredRel, StringProperty,
    IntegerProperty, RelationshipTo)

class LocatedIn(StructuredRel):
    since = IntegerProperty(index=True)

class Country(StructuredNode):
    code = StringProperty(unique_index=True)

class City(StructuredNode):
    name = StringProperty(index=True)
    country = RelationshipTo(Country, 'FROM_COUNTRY', model=LocatedIn)

Applying constraints and indexes

After creating your model, any constraints or indexes must be applied to Neo4j and neomodel provides a script (neomodel_install_labels) to automate this:

$ neomodel_install_labels yourapp.py someapp.models --db bolt://neo4j_username:neo4j_password@localhost:7687

It is important to execute this after altering the schema and observe the number of classes it reports.

Ommitting the --db argument will default to the NEO4J_BOLT_URL environment variable. This is useful for masking your credentials.

Note

The script will only create indexes and constraints that are defined in your model classes. It will not remove any existing indexes or constraints that are not defined in your model classes.

Indexes

The following indexes are supported:

  • index=True: This will create the default Neo4j index on the property (currently RANGE).

  • fulltext_index=FulltextIndex(): This will create a FULLTEXT index on the property. Only available for Neo4j version 5.16 or higher. With this one, you can define the following options:
    • analyzer: The analyzer to use. The default is standard-no-stop-words.

    • eventually_consistent: Whether the index should be eventually consistent. The default is False.

Please refer to the Neo4j documentation. for more information on fulltext indexes.

  • vector_index=VectorIndex(): This will create a VECTOR index on the property. Only available for Neo4j version 5.15 (node) and 5.18 (relationship) or higher. With this one, you can define the following options:
    • dimensions: The dimension of the vector. The default is 1536.

    • similarity_function: The similarity algorithm to use. The default is cosine.

Those indexes are available for both node- and relationship properties.

Note

Yes, you can create multiple indexes of a different type on the same property. For example, a default index and a fulltext index.

Note

For the semantic indexes (fulltext and vector), this allows you to create indexes, but searching those indexes require using Cypher queries. This is because Cypher only supports querying those indexes through a specific procedure for now.

Full example:

from neomodel import StructuredNode, StringProperty, FulltextIndex, VectorIndex
class VeryIndexedNode(StructuredNode):
    name = StringProperty(
        index=True,
        fulltext_index=FulltextIndex(analyzer='english', eventually_consistent=True)
    )
    name_embedding = ArrayProperty(
        FloatProperty(),
        vector_index=VectorIndex(dimensions=512, similarity_function='euclidean')
    )

Constraints

The following constraints are supported:

  • unique_index=True: This will create a uniqueness constraint on the property. Available for both nodes and relationships (Neo4j version 5.7 or higher).

Note

The uniqueness constraint of Neo4j is not supported as such, but using required=True on a property serves the same purpose.

Extracting the schema from a database

You can extract the schema from an existing database using the neomodel_inspect_database script (Database Inspection - Requires APOC). This script will output the schema in the neomodel format, including indexes and constraints.