Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using multiple labels with Neomodel

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.

like image 506
bsuire Avatar asked Apr 10 '15 12:04

bsuire


Video Answer


1 Answers

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.


Label Hacking (not recommended!)

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

Result 1

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.


Inheritance

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

Result 2

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.

like image 70
Peter Majko Avatar answered Sep 18 '22 01:09

Peter Majko