I would like to know if there's a way to associate different labels to a class with NeoModel. If not, what module can allow me to do this?
My understanding is that when using the following class declaration, "Person" is a label.
class Person(StructuredNode):
name = StringProperty(unique_index=True)
age = IntegerProperty(index=True, default=0)
Let's say that I'd like to add a second label, "Employed", "Unemployed", "Student".
With Cypher I could use:
CREATE(p:Person:Student)
Is there anyway I can achieve the same with NeoModel?
Notes: From my research working with labels yields faster queries than with properties (neo4j/cypher), which is why I would like employed/unemployed/student to be labels. Otherwise I would be fine adding "occupation" as a node property.
As of 2020, while using neomodel 4.0.1
answer by All Іѕ Vаиітy doesn't produce the desired outcome. Therefore the answer from user3280193 is the most correct, but not without a small caveat, so let me elaborate.
First let's look, why label hacking is flawed:
class Unemployed(StructuredNode):
__label__ = 'Person:Unemployed'
name = StringProperty(unique_index=True)
Unemployed(name='Carol').save()
If you check, it cannot detect nodes later on properly, even if they are correctly saved in the database:
print(len(Unemployed.nodes)) # prints 0
One could think that if we had another class Person
, then we could retrieve it that way - unfortunately not. See for yourself:
class Unemployed(StructuredNode):
__label__ = 'Person:Unemployed'
name = StringProperty(unique_index=True)
class Person(StructuredNode):
name = StringProperty(unique_index=True)
Unemployed(name='Carol').save()
So far, so good, so let's try to get some nodes. The following outcome looks well.
print(len(Person.nodes)) # prints 1
However, the problem will arise when we try to access that node:
print(Person.nodes[0])
# Results in two exceptions
#
# Traceback (most recent call last):
# ...
# KeyError: frozenset({'Person', 'Unemployed'})
#
# During handling of the above exception, another exception occurred:
# ...
# neomodel.exceptions.ModelDefinitionMismatch: <exception str() failed>
I will not go into details why this happens, but simply put neomodel can't cope with label hacking as it was not designed for it. If anyone wants to understand this behaviour, I suggest looking into neomodel.core
part of the library.
Officially, neomodel promotes inheritance and mixins. Read more at:
https://neomodel.readthedocs.io/en/latest/extending.html#inheritance https://neomodel.readthedocs.io/en/latest/extending.html#mixins
As mixins don't provide additional labels, I will focus on inheritance. Let's assume the following example, where we go 2 levels deep into inheritance.
class Person(StructuredNode):
name = StringProperty(unique_index=True)
class Student(Person):
pass
class Employed(Person):
pass
class EmployedStudent(Student, Employed):
pass
Person(name='Bob').save()
Student(name='Will').save()
Employed(name='John').save()
EmployedStudent(name='Kim').save()
Results:
print(len(Person.nodes)) # 4
print(len(Student.nodes)) # 2
print(len(Employed.nodes)) # 2
print(len(EmployedStudent.nodes)) # 1
This has the correct behaviour but seemingly produces one artefact - the label EmployedStudent
. There is no simple hack to get rid of this additional label as it is crucial for automatic class resolution.
Conclusion: OGM has its cons, but I would anytime opt for the additional redundant label over writing cypher queries by myself for every class I construct.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With