Working of your code

Let's take a look at a simple Hello World application code for Neo4j and understand what goes on under the hood when you try to perform some simple operations on Neo4j through the Java API. Here is the code for a sample app:

import org.neo4j.graphdb.*;
import org.neo4j.kernel.EmbeddedGraphDatabase;

/**
* Example class that constructs a simple graph with
* message attributes and then prints them.
*/

public class NeoOneMinute {
  public enum MyRelationshipTypes implements RelationshipType {
    KNOWS
  }

  public static void main(String[] args) {
    GraphDatabaseService graphDb = new EmbeddedGraphDatabase("var/base");
    Transaction tx = graphDb.beginTx();
    try {
      Node node1 = graphDb.createNode();
      Node node2 = graphDb.createNode();
      Relationship someRel =  node1.createRelationshipTo(node2, MyRelationshipTypes.KNOWS);

      node1.setProperty("message", "Hello, ");
      node2.setProperty("message", "world!");
      someRel.setProperty("message", "brave Neo4j ");
      tx.success();

      System.out.print(node1.getProperty("message"));
      System.out.print(someRel.getProperty("message"));
      System.out.print(node2.getProperty("message"));
      }
    finally {
      tx.finish();
      graphDb.shutdown();
      }
  }
}

The initiating point in the Neo4j program is the database service object defined from org.neo4j.graphdb.GraphDatabaseService and referred to as GraphDatabaseService. All the core functionalities for nodes, IDs, transactions and so on are exposed through this interface. This object is a wrapper over a few other classes and interfaces, GraphDbInstance being one of them, which starts with the config map that the user provides (empty in the preceding case). The org.neo4j.kernel.AutoConfigurator object then receives this, and the memory to be used for memory-mapped buffers is computed from JVM statistics. You can change this behavior by setting a false value for the use_memory_mapped_buffers flag, causing the config map to be passed to an object of the org.neo4j.kernel.Config class.

GraphDbModule, the TxModule for transactions, the manager objects for cache, persistence, and locking (CacheManager, PersistenceModule, LockManager, respectively) are then created and sustained until the application's execution ends. If no errors are thrown, then the embedded database has been initiated.

Node and relationship management

NodeManager (defined in the org.neo4j.kernel.impl.core.NodeManager class) is one of the most crucial and large classes in the Neo4j source that provides an abstraction for the caching and the underlying persistent storage-exposing methods to operate on nodes and relationships. The configuration that is passed is then parsed and the caching for the nodes and relationships is initialized by figuring out their sizes. AdaptiveCacheManager abstracts the sizing issue of the caches. NodeManager handles the locking operations with the help of lock stripping. In order to maintain a balance between the level of concurrency and the performance of memory, an array stores ReentrantLocks and, depending upon the integral ID values of the node or the relationship, locking is performed by hashing over it. When you invoke the getNodeById() or getRelationshipById() method, it roughly follows these steps:

  1. The cache is checked. If the entity already exists in the cache, it is returned from there.
  2. Based on the ID passed as a parameter, a lock is acquired.
  3. The cache is checked again. If it has currently come into existence, then it is returned (since multithreaded operations occur!).
  4. The manager class is requested for persistent storage for the required entity. If unavailable, NotFoundException is thrown.
  5. The retrieved value is returned in the form of an appropriate implementation plan.
  6. The retrieved value is cached.
  7. The lock is then released and the value is returned to the calling method.

This is the gist of the main work of NodeManager. However it is intended to be used to perform many other operations that are beyond the scope of this book, but you can check them out in the source.

Implementation specifics

The Node and Relationship interfaces defined in org.neo4j.graphdb provide implementations for NodeProxy and RelationshipProxy and contain the unique IDs for the nodes or relationships that are represented by them. They are used in the propagation of the method calls of the interface to the NodeManager object that is being used. This integral ID is what is returned from the method calls that are executed every time a node, relationship, or property is added to EmbeddedGraphDatabase.

Nodes and relationships also use the NodeImpl and RelationshipImpl classes defined in the org.neo4j.kernel.impl.core package; this package extends the core Primitive class to provide abstract implementations of Properties and help to delegate the loading and storing of property values to the object of NodeManager. It holds the value of the ID and is extended by the implementation classes (NodeImpl and RelationshipImpl), each of which implements the abstract methods accordingly along with operation-specific functions (for example, the NodeImpl class has a getRelationships() method while the RelationshipImpl class has getStartNode()).

These are some of the types of implementations that NodeManager handles internally.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset