Sunday 8 June 2014

A look at HypergraphDB (Part 2)

This post is a follow on to A look at HypergraphDB (Part 1) if you are unsure on how to set up HypergraphDB please refer to that post.

There will be one change here, instead of using:
HyperGraph graph = new HyperGraph("/home/mhoward/Hypergraph");

We will use:
HyperGraph graph = HGEnvironment("/home/mhoward/Hypergraph");  

The benefit to using HGEnvironment over creating a new graph manually is HGEnvironment keeps a record of all open graphs and will return an existing graph if it is open.

The first thing we will do is set up two plain Java objects to add to the graph, the first is Product.java and it looks like so:
 package com.markhoward.hypergraphexample;  
 public class Product {  
   private String name;  
   private String specs;  
   private double price;  
   /**  
    * Default constructor is needed by HypergraphDB to serialize.  
    */  
   public Product(){a  
   }  
   public Product(String name, String specs, double price){  
     this.name = name;  
     this.specs = specs;  
     this.price = price;  
   }  
   public String getName() {  
     return name;  
   }  
   public void setName(String name) {  
     this.name = name;  
   }  
   public String getSpecs() {  
     return specs;  
   }  
   public void setSpecs(String specs) {  
     this.specs = specs;  
   }  
   public double getPrice() {  
     return price;  
   }  
   public void setPrice(double price) {  
     this.price = price;  
   }  
   @Override  
   public String toString(){  
     return String.format("Product name: %s, product specs: %s, Product price: %.2f", getName(), getSpecs(), getPrice());  
   }  
 }  

The thing to note when creating the classes that will be persisted is, HypergraphDB follows the Java Bean specification, so your class will need a default (no args) constructor, it is a good idea to override hashcode and equals and also have matching getter and setter methods.

The second Java class is the Category class, it simply holds the name of the category:
 package com.markhoward.hypergraphexample;  
 public class Category {  
   private String name;  
   /**  
    * Default constructor is needed by HypergraphDB to serialize.  
    */  
   public Category(){  
   }  
   public Category(String name){  
     this.name = name;  
   }  
   public String getName() {  
     return name;  
   }  
   public void setName(String name) {  
     this.name = name;  
   }  
   @Override  
   public String toString(){  
     return String.format("Category: %s", getName());  
   }  
 }  

The aim is to add some products to the graph and link them to the relevant category, then query the graph for the product and get the category back.

I created a toString method to allow the results be printed directly to the console to make things easier, if there is no toString method the JVM may just print the memory address of the object.

So create a few Product and Category objects like so, add them to the graph and create a link between the Product and the relevant category.
 private void addSomeProducts(){  
     /*  
      * Create categories add them to the graph and save the handles.  
      */  
     Category digitalCamera = new Category(DIGITAL_CAMERA);  
     Category computer = new Category(COMPUTER);  
     HGHandle digitalCameraCategoryHandle = graph.add(digitalCamera);  
     HGHandle computerCategoryHandle = graph.add(computer);  
     /*  
      * Create Products and add them to the graph (saving the handles).  
      */  
     Product camera1 = new Product("Canon IXUS", "10 Megapixel, 3x Zoom", 100.00);  
     Product camera2 = new Product("Nikon Coolpix", "12 Megapixel, 3x Zoom", 120.00);  
     Product computer1 = new Product("Apple Macbook Pro", "2Ghz, 500GB", 1200.00);  
     Product computer2 = new Product("Dell XPS", "3Ghz, 1TB", 600.00);  
     HGHandle camera1Handle = graph.add(camera1);  
     HGHandle camera2Handle = graph.add(camera2);  
     HGHandle computer1Handle = graph.add(computer1);  
     HGHandle computer2Handle = graph.add(computer2);  
     /*  
      * Make a link from each product to the relevant category.  
      */  
     HGValueLink link1 = new HGValueLink("category", digitalCameraCategoryHandle, camera1Handle);  
     HGValueLink link2 = new HGValueLink("category", digitalCameraCategoryHandle, camera2Handle);  
     HGValueLink link3 = new HGValueLink("category", computerCategoryHandle, computer1Handle);  
     HGValueLink link4 = new HGValueLink("category", computerCategoryHandle, computer2Handle);  
     /*  
      * Add the link to the graph.  
      */  
     graph.add(link1);  
     graph.add(link2);  
     graph.add(link3);  
     graph.add(link4);  
   }  

Once these objects have been added to the graph, create a query like so:
 HGQueryCondition condition = new AtomTypeCondition(Product.class);  
 HGSearchResult<HGHandle> resultSet = graph.find(condition);  

The condition will simply return all products currently saved in the graph. The resultset that is returned is very similar to a ResultSet in JDBC, the results can be iterated over like so
 try  
     {  
       while(resultSet.hasNext()){  
         HGHandle productHandle = resultSet.next();  
         /*  
          * For each product traverse the graph starting with this   
          * product and print the results.  
          */  
         printCategory(productHandle);  
       }  
     }  
     catch(Exception exception){  
       /*  
        * Print any exceptions that occur, I have come across type casting  
        * problems, which would occur at runtime.  
        */  
       exception.printStackTrace();  
     }  
     finally{  
       resultSet.close();  
     }  

Note: be sure to close the resultset as if it is not closed it will cause the referenced objects to be locked, it will stop the graph being closed gracefully and if these objects are queried further the application will throw exceptions.

The resultset contains the handles which reference the objects contained in the graph. To get the product from the resultset you could do the following (instead of printCategory(productHandle)):
 Product product = graph.get(productHandle);  

To traverse the links from the product handle the printCategory method looks like so:
 private void printCategory(HGHandle productHandle){  
     /*  
      * Set up a depth first traversal starting with our product.  
      */  
     HGDepthFirstTraversal traversal = new HGDepthFirstTraversal(productHandle, new SimpleALGenerator(graph));  
     while(traversal.hasNext()){  
       Pair<HGHandle, HGHandle> current = traversal.next();  
       HGHandle categoryHandle = (HGHandle) current.getSecond();  
       System.out.println(graph.get(categoryHandle));  
     }  
   }  

I hope this post helps anyone starting out with HypergraphDB, be sure to check out the JavaDocs as they are very well documented. There is also some great documentation on the HypergraphDB website.

I have uploaded the complete source for this example to Github:
https://github.com/bark4mark/hypergraphexample

No comments:

Post a Comment