Advanced Data Modeling Using Pacer

Brought to you by

Traversing Extended Routes

def orders
    self.out_e(:PURCHASED).in_v(type: "Order")
end

Consider our Customer extension.

Instead of the following route extension method

def orders
    self.out_e(:PURCHASED).in_v(NorthWind::Order)
end

We can define

* Assuming we have created a NorthWind::Order extension.

Now, we can traverse the graph by chaining extension methods:

# Get a customer from the graph
c = g.v(NorthWind::Customer).first

# Get their orders
c.orders

# Chain the result (the `Order` extension has a 
# `products` route extension method).
c.orders.products

# If all of our extensions are returning extended routes,
# our traversals start looking like this:
c.orders.products.categories.uniq

Traversing Extended Routes

Exercise

Go over all the extensions you've created, and make sure that extension methods return extended routes.

category = g.v(NorthWind::Category).first

category.products

category.products.orders.customers

# And even a slightly more complex traversal ...
g.v(NorthWind::Customer)
   .lookahead(max: 1) do |c| 
      c.orders.employees.uniq
   end 
.uniq

When you're done, try to run some traversals in the IRB.

For example:

Properties

Instead of getting/setting properties as follows:

jruby-1.7.18 :018 > e = g.v(Employee).first
 => #<V[n202] Michael Suyama, Sales Representative>

jruby-1.7.18 :019 > e[:title] = "Senior Sales Representative"
 => "Senior Sales Representative"

jruby-1.7.18 :020 > e[:title]
 => "Senior Sales Representative"

We can define properties as vertex extensions


  def title
    self[:title]
  end

  def title=(new_title)
    self[:title] = new_title
  end

Properties

And use them

jruby-1.7.18 :021 > e = g.v(Employee).first
 => #<V[n202] Michael Suyama, Senior Sales Representative>

jruby-1.7.18 :022 > e.title = "Sales Representative"
 => "Sales Representative"

jruby-1.7.18 :023 > e.title
 => "Sales Representative"

Properties

# We don't actually store a `name` property.
# Instead, we compute it on demand.

def name
    "#{self[:firstName]} #{self[:lastName]}"
end

Simple technique that leads to slightly cleaner code:

e[:title]
e[:title] = 'Boss'
e.title
e.title = 'Boss'

VS.

Properties that are computed on-demand

Exercise

  • Add a name property to the Product extension.
  • The property methods (name and name=) should get/set the underlying companyName property.

 

After reloading the code, you should be able to do

product = g.v(Product).first

# Test the getter
name = product.name

# Test the setter
product.name = 'foo'

Edge Extensions

Let's look at how orders are stored:

jruby-1.7.18 :205 > o = g.v(Order).first
 => #<V[n819] Order 10853>

jruby-1.7.18 :206 > o.out_e
#<E[e1589]:n819-PRODUCT-n108>

jruby-1.7.18 :207 > o.out_e(:PRODUCT).properties
{"unitPrice"=>62.5, "quantity"=>10.0}

Essentially, the edge from an Order to a Product serves as an order item:

Edge Extensions

Create an edge extension, in a file called order_item.rb

module NorthWind
    module OrderItem
        module Edge  # Containing Edge extension methods

	    def order
		self.out_vertex(NorthWind::Order)
	    end

	    def product
		self.in_vertex(NorthWind::Product)
	    end

	    # ...
	end
    end
end

Edge Extensions

Use the extension

jruby-1.7.18 :207 > load 'order_item.rb'
 => true

jruby-1.7.18 :208 > o = g.v(Order).first
 => #<V[n819] Order 10853>

jruby-1.7.18 :209 > o.out_e(OrderItem)
#<E[e1589]:10.0 units of Carnarvon Tigers at 62.5$/unit>
Total: 1
 => #<outE -> E>

jruby-1.7.18 :210 > o.out_e(OrderItem).first.order
 => #<V[n819] Order 10853>

jruby-1.7.18 :211 > o.out_e(OrderItem).first.product
 => #<V[n108] Carnarvon Tigers, 62.5$>

Exercise

Add the following edge extension methods to OrderItem:

  • display_name
  • quantity property getter and setter
  • price property getter and setter

Edge Extensions

As you might expect, we can define which edges can be extended by OrderItem

module NorthWind
    module OrderItem

        # Extend only edges whose label is PRODUCT
        def self.route_conditions(graph)
            :PRODUCT
    	end

        # ...

Edge Extensions

We can also create route extension methods.

module NorthWind
    module OrderItem

        # ...

        module Route

            def products
        	self.in_v(NorthWind::Product)
            end

            # ...
        end
       
        # ...

Edge Extensions

Use the products extensions method on a route of order items:

# Get (a route containing) a few order items
items = g.e(NorthWind::OrderItem).limit(3)

# And their products
items.products

Exercise

Add the orders route extensions method to OrderItem.

  • Return a route of vertices
  • Extended by the Order extension

Edge Extensions

NOTE: Although Pacer supports edge extensions, we recommend to keep your edges property-free, if possible.

We can turn

Into

Flexibility -  We can have edges to/from the new vertex. 

Indexing

Index properties frequently used for searching.

jruby-1.7.18 :068 > g.v(type: 'foo')
Total: 0
 => #<GraphV -> V-Property(type=="foo")>
jruby-1.7.18 :069 > g.create_key_index :type
jruby-1.7.18 :070 > g.v(type: 'foo')
Total: 0
 => #<V-Index(type: "foo")>

Create an index on the type property:

What's next?

We will see a demo of a full web application, backed by a graph database.

In this demo, we will

  • Use Pacer with Neo4j
  • Expose our traversals as a web API, using Sinatra
Made with Slides.com