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

Outdated documentation on relation search gives AttributeError: 'ZeroOrMore' #814

Open ziqizhang opened 2 months ago

ziqizhang commented 2 months ago

Expected Behavior (Mandatory)

As per https://neomodel.readthedocs.io/en/latest/getting_started.html#retrieving-additional-relations, you can search on relations using, e.g.,

germany.inhabitant.search(name='Jim')

Actual Behavior (Mandatory)

This no longer works with the library as it gives error:

AttributeError: 'ZeroOrMore' object has no attribute 'search'

How to Reproduce the Problem

Run the code below.

Simple Example

from neomodel import (
    IntegerProperty,
    RelationshipFrom,
    RelationshipTo,
    StringProperty,
    StructuredNode,
    UniqueIdProperty,StructuredRel,
    config,
)

config.DATABASE_URL = "bolt://xxx"

class MovedTo(StructuredRel):
    year=IntegerProperty(index=True)

class Country(StructuredNode):
    code = StringProperty(unique_index=True, required=True)
    inhabitant = RelationshipFrom("Person", "IS_FROM")

class City(StructuredNode):
    name = StringProperty(required=True)
    country = RelationshipTo(Country, "FROM_COUNTRY")

class Person(StructuredNode):
    uid = UniqueIdProperty()
    name = StringProperty(unique_index=True)
    age = IntegerProperty(index=True, default=0)

    # traverse outgoing IS_FROM relations, inflate to Country objects
    country = RelationshipTo(Country, "IS_FROM")

    # traverse outgoing LIVES_IN relations, inflate to City objects
    city = RelationshipTo(City, "LIVES_IN")
    moved_to = RelationshipTo('Country', 'MOVED_TO', model=MovedTo)

jim = Person(name="Jim", age=3).save()  # Create
jim.age = 4
jim.save()  # Update, (with validation)

germany = Country(code="DE").save()
r1=jim.country.connect(germany)
berlin = City(name="Berlin").save()
r2=berlin.country.connect(germany)
jim.city.connect(berlin)
r3=jim.moved_to.connect(germany, {'year':2005})

print(germany.inhabitant)
print(jim.moved_to)
#print(jim.moved_to.year) # LINE A
germany.inhabitant.search(name='Jim')  #LINE B

Screenshots (where it's possibile)

Specifications (Mandatory)

Run the above code, line B will generate the error reported above.

Also, there is no documentation on how to get properties of a custom relationship. Line A would not work as

jim.moved_to

Returns a ZeroOrMove object, not a relation object. Calling all() on that object will traverse the relationship to get 'Country' objects, not the relation itself.

Versions

mariusconjeaud commented 2 months ago

I will look into the "no search" error.

For accessing relationship properties, you need the all_relationships() method, like so:

async def test_retrieve_all_rels():
    tom = await Badger(name="tom").save()
    ian = await Stoat(name="ian").save()

    rel_a = await tom.hates.connect(ian, {"reason": "a"})
    rel_b = await tom.hates.connect(ian, {"reason": "b"})

    rels = await tom.hates.all_relationships(ian)
    print(rels[0].reason)