>> Nodes :




Introduction

bulb provides a group of classes used to structure and manipulate nodes and their properties.
Only the most important things have been introduced, in order to leave lots of flexibility to the package's user and not to burden the user with useless things.
Furthermore, when you work with a database, personalization of each request is the key of fast and powerful interactions. Therefore, don't hesitate to make your own request, for complexes works.



Node models

Note that all node models in each application, must be written in files named node_models.py.


  • Implement and create node models

Whereas Django proposes models for relational databases, bulb introduces the node-models for graph databases. Same principle : create classes that represent database entities, and it gives the possibility to interact easily with them.
A node-models class is a class that inherits from the Node class, imported from bulb.db.node_models.
Like with Django models, you'll have to instantiate these node-models classes to automatically create a node in the database.

Demonstration :

node_models.py

from bulb.db import node_models


class Person(node_models.Node):
    pass


Person()


Preview on Neo4j Desktop :

empty node preview

Create Node is no harder than this !
Now, let's see how to handle the labels of ours nodes.



  • Work with nodes


  • Retrieve nodes

All the node models classes possess a get() method. This method uses many parameters allowing us to make very complex and customizable requests :

  • uuid (required): The Universal Unique Identifier of a node to get a single instance.

  • order_by (optional, default=None) : Must be the name of the property with which the returned datas will be sorted.
    Examples : "datetime", "first_name", etc...

  • limit (optional, default=None) : Must be an integer. This parameter defines the number of returned elements.

  • skip (optional, default=None) : Must be an integer. This parameter defines the number of skipped elements. For example if self.skip = 3, the first 3 elements returned will be skipped.

  • desc (optional, default=False) : Must be a boolean. If it's False the elements will be returned in an ascending order, but if it's True, they'll be returned in a descending order.

  • only (optional, default=None) : Must be a list of field_names. If this parameter is filled, the return won't be Node instances, but a dict with "only" the mentioned fields.

  • filter (optional, default=None) : Must be Q statement. You must use the Q class stored in bulb.db Example: Q(name__contains="al") | Q(age__year__lte=8)

  • distinct (optional, default=False) : Must be a boolean. If it's True, the returned list will be only composed with unique elements.

  • return_query (optional, default=False) : Must be a boolean. If true, the method will return the cypher query.

    Demonstration:

node_models.py

from bulb.db import node_models


class Person(node_models.Node):
    name = node_models.Property(required=True)

Person.create(name="John")
>>> <Person object(uuid="dcd220ab84b5417f8d8e48dd34237e9d")>


Person.create(name="Jane")
>>> <Person object(uuid="e724d344999342438431271ed39c7f92")>

Person.get()
>>> [<Person object(uuid="dcd220ab84b5417f8d8e48dd34237e9d")>, <Person object(uuid="e724d344999342438431271ed39c7f92")>]



  • Delete nodes

Node models' instances possess a delete() method, allowing us to delete nodes and all other nodes linked to these instances with a "CASCADE" relationship.



  • Make your own getter, setter and deletter

A node possesses native getter, setter and deletter, but these methods could be personalized for each Node class. Handling perfectly every situations isn't possible, and trying to acheive this leads to very heavy and inefficient programs. To make your own node methods, you'll only have to take the native methods and re-implement them with your modifications.
The bulb authentication defines a node model named User(), and a get() method has been developed for this class. This method accepts the uuid and email properties of every instances of the User() class, because they are both required and unique properties. Let's see this as an example and consider that this example is a bit more complex than what you'll face.

Note that the returned value, if it contains nodes, should always be in a JSON style format, or be an instance.
See : Conventions.





Labels


  • Create labels

By default, if no configuration is provided by the package's user, as you can see above, the one and only label applied to the node is the name of the class he's coming from. On the other hand, labels of a node are also easily customizable. To do that, you only have to define a labels attribute in your class (which is inheriting from Node()), and put as the value of this attribute, a list that contains all the labels of the nodes that will be produced by the class.

Demonstration :

node_models.py

from bulb.db import node_models


class Person(node_models.Node):
    labels = ["Person", "Administrator"]


Person()


Preview on Neo4j Desktop :

labels customization preview

The labels' proprety can also be defined with a single string :

node_models.py

from bulb.db import node_models


class Person(node_models.Node):
    labels = "Administrator"


Person()



Note that the class' name is always added as a label, so for the last example, the node created will have both "Person" and "Administrator" labels (even if we fill the labels attribute only with "Administrator").
On the other hand, it's very recommanded to always add the labels attribute, even if the only desired label is the name of the class. This practice will lead to a more explicit and readable code.



  • Work with labels


  • Delete and update labels :

The deletion and the modification of labels is prohibited in order to maintain coherence.
But if it's absolutely needed to do what you want to do, you could easily create a method that deletes and updates labels.


  • Retrieve labels :

You can easily retrieve labels from a node and then use them as you please.
Two solutions exist :

You can simply access to the labels of a node named ABC, by doing ABC.labels.
But you can also use the get_labels() method of node objects like that: ABC.get_labels()

The two ways will return you a list of labels if many exist, and just a string if you have filled the labels attribute only with a string.




Properties


  • Create properties

To end customization of our nodes, add them some properties.
With the bulb package, a property is an instance of the Property() class imported from bulb.db.node_models.

The property can take 5 parameters :

  • content (do not fill) : The content is the value of a property, this parameter will be filled in during the instantiation of the future nodes. Do not fill the content parameter to set a default value, you must fill the default parameter to do so.

  • required (optional) : If set on True, and if the content of the property is empty, an error will be raised.

  • unique (optional) : If set on True, and if the content of the property combination key+content is already in the database, an error will be raised.

  • default (optional) : A default value that will fill the content if it is empty.

  • sftp (default = False) : If set on True, and if the value is a file object it'll be stored on the SFTP server.

Note that :

A property cannot have both required and default parameters in order to maintain coherence.

Demonstration :

node_models.py

from bulb.db import gdbh, node_models
from bulb.db.utils import make_uuid
import datetime


class Article(node_models.Node):
    labels = "Article"

    uuid = node_models.Property(key="uuid",
                                 default=make_uuid,
                                 unique=True)

    author = node_models.Property(key='author',
                                   default="An anonymous author.")

    title = node_models.Property(key='title',
                                  required=True,
                                  unique=True)

    content = node_models.Property(key='content',
                                    required=True)

    publication_datetime = node_models.Property(key="publication_datetime",
                                                default=datetime.datetime.now)


a_new_article = Article(author="Mary C",
                        title="The earth cry",
                        content="Lorem ipsum dolor sit amet...")


response = gdbh.r_transaction("MATCH (a:Article {author: 'Mary C'}) ")
print(response)


>>> [{'a': <Node id=177 labels={'Article'} properties={'title': 'The earth cry', 'uuid': 'c6b99f2ca0b34a
    7eaec930ac1173d673', 'publication_datetime': '2019-04-09 04:36:09.962512', 'author': 'Mary C', 'cont
    ent': 'Lorem ipsum dolor sit amet...'}>}]                 


Preview on Neo4j Desktop :

properties on node preview



  • Apply properties' restrictions


For improved performances, every properties' restrictions of your nodes should be applied in the Neo4j database. To apply all the restrictions contained in your project or update them if there are news, you can simply use the python manage.py bulb-apply command.




  • Work with properties


  • Retrieve properties' values

As for the labels, you can easily access to the properties of a node with the syntax (object).(property key) :

node_models.py

a_new_article.content

>>> 'Lorem ipsum dolor sit amet...'

You could also retrieve all the properties of a node with the get_properties() method of the Node's instances.

node_models.py

a_new_article.get_properties()

>>> {'title': 'The earth cry', 'uuid': 'c6b99f2ca0b34a7eaec930ac1173d673',
 'publication_datetime': '2019-04-09 04:36:09.962512', 'author': 'Mary C',
  'content': 'Lorem ipsum dolor sit amet...'}


  • Update properties' values

All node_models' instances possess an update() method, which takes as first argument the name of the property to update and as second argument, the new value of this property.

node_models.py

from bulb.contrib.auth import User

john = User.get(email="john@mail.")

john.first_name
>>> 'John'

john.update("first_name", "Jo")

john.first_name
>>> 'Jo'





Methods

bulb provides methods (stored in bulb.db.utils) that perform the basic and most frequent actions.

  • The make_uuid() method returns a Universal Unique Identifier. It's very useful to provide a unique property to identify efficiently node instances.