neo4j-contrib / neomodel

An Object Graph Mapper (OGM) for the Neo4j graph database.
https://neomodel.readthedocs.io
MIT License
936 stars 231 forks source link

How to connect nodes in a SemiStructuredNode without defining the Relationship explicitly #818

Open xalien10 opened 1 month ago

xalien10 commented 1 month ago

I'm trying to connect nodes in a SemiStructuredNode where my node definitions are below:

from neomodel import (
    StructuredNode,
    StringProperty,
    RelationshipTo,
    UniqueIdProperty,
    DateTimeProperty,
    One,
)
from neomodel.contrib import SemiStructuredNode
from enum import Enum

class AddressTypes(Enum):
    BILLING = "BILLING"
    HOME = "HOME"

class ContactTypes(Enum):
    WORK = "WORK"
    PERSONAL = "PERSONAL"

class AdministratorTypes(Enum):
    MANAGER = "MANAGER"
    ADMIN = "ADMIN"

class AbstractUUIDBaseNode(StructuredNode):
    __abstract_node__ = True
    id = UniqueIdProperty()
    created_at = DateTimeProperty(default_now=True)
    created_by = StringProperty()
    updated_at = DateTimeProperty()
    updated_by = StringProperty()

class AbstractUUIDFlexibleBaseNode(SemiStructuredNode):
    __abstract_node__ = True
    id = UniqueIdProperty()
    created_at = DateTimeProperty(default_now=True)
    created_by = StringProperty()
    updated_at = DateTimeProperty()
    updated_by = StringProperty()

class Address(AbstractUUIDBaseNode):
    __label__ = "Address"
    type = StringProperty()
    address_line1 = StringProperty()
    post_code = StringProperty()
    city = StringProperty()
    country_id = StringProperty()

class Contact(AbstractUUIDBaseNode):
    __label__ = "Contact"
    type = StringProperty()
    email = StringProperty()
    phone = StringProperty()
    website = StringProperty()

class CostCenter(AbstractUUIDBaseNode):
    __label__ = "CostCenter"
    name = StringProperty()
    address = RelationshipTo("Address", "costcenter_addresses", cardinality=One)
    contact = RelationshipTo("Contact", "costcenter_contacts", cardinality=One)

class Employee(AbstractUUIDBaseNode):
    __label__ = "Employee"
    email = StringProperty()
    first_name = StringProperty()
    last_name = StringProperty()
    is_verified = StringProperty()
    address = RelationshipTo("Address", "employee_addresses", cardinality=One)
    contact = RelationshipTo("Contact", "employee_contacts", cardinality=One)

class Administrator(AbstractUUIDFlexibleBaseNode):
    __label__ = "Administrator"

Now I'm trying to create Administrator like below-

# Create Address nodes
    address1 = Address(
        type=AddressTypes.BILLING.value,
        address_line1="Industrijska cesta 8",
        post_code="3211",
        city="Slovenske Konjice",
        country_id="199",
    ).save()

    address2 = Address(
        type=AddressTypes.BILLING.value,
        address_line1="Industrijska cesta 9",
        post_code="3212",
        city="Slovenske Konjice",
        country_id="199",
    ).save()

    address3 = Address(
        type=AddressTypes.HOME.value,
        address_line1="Industrijska cesta 10",
        post_code="3213",
        city="Slovenske Konjice",
        country_id="199",
    ).save()

    # Create Contact nodes
    contact1 = Contact(
        type=ContactTypes.WORK.value,
        email="prodaja1@pro-bit.si",
        phone="+38637573921",
        website="www.pro-bit.si",
    ).save()
    contact2 = Contact(
        type=ContactTypes.WORK.value,
        email="prodaja2@pro-bit.si",
        phone="+38637573922",
        website="www.pro-bit.si",
    ).save()
    contact3 = Contact(
        type=ContactTypes.PERSONAL.value,
        email="test.user1@gmail.com",
        phone="+38637573923",
        website="www.pro-bit.si",
    ).save()

    # Create CostCenter nodes
    cost_center1 = CostCenter(name="Bank and Cash", address=address1, contact=contact1).save()
    cost_center2 = CostCenter(name="Operational Cost", address=address1, contact=contact1).save()

    # Create Employee nodes
    employee1 = Employee(
        email="employee1@pro-bit.si",
        first_name="Test",
        last_name="User1",
        is_verified="True",
        address=address3,
        contact=contact3
    ).save()

    # administrators
    administrator1 = Administrator(
        employee=employee1,
        cost_center=cost_center1,
        role=AdministratorTypes.MANAGER.value,
        module_name="HRM"
    ).save()

But it seems like as no relationship is defined in the Administrator node, so it can not create with the above approach.

How can I create Administrator object and connect nodes dynamically as for our use cases we don't not which nodes are going to be connected into the Administrator node.

I've just started to use neomodel recently. Is there any alternative way that I can achieve similar things?

Thanks in advance!

mariusconjeaud commented 1 month ago

Is your question that you want to create a relationship between an Address node and the Administrator node, even though you didn't declare that those two labels could be connected by a relationship in your neomodel classes ? If so, then no, you cannot create relationships in this way using the neomodel Python proxies. The only way for you to do so would be using Cypher (with db.cypher_query). But then, you would also have to retrieve those relationships using Cypher instead of neomodel proxies, since that also relies on the class definition.

Note : You don't have to pass that __label__ property to your node classes if the label is identical to the class name, neomodel will map that automatically for you.