Data Modeling using Pacer

Brought to you by
What Is Data Modeling?
Mike buys a book online

Data Modeling is about going back and forth between the real-world (on the left) and the graph (on the right).
VS.
Pacer Extensions
A fundamental building block of Pacer
Without extensions
def comments_to_posts_by(person)
person
.out_e(:POSTED)
.in_v(type: 'post')
.in_e(:IS_ABOUT)
.in_v(type: 'comment')
end
With extensions
def comments_to_posts_by(person)
person.posts.comments
end
Pacer Extensions
What do they look like?
module Person
# ...
module Vertex
def posts
self.out_e(:POSTED).in_v(type: 'post')
end
end
# ...
end
Case Study - NorthWind
- Dataset of a fictional company, NorthWind Traders.
- Created by Microsoft
- Imported into a property graph by Neo4j

Setup
$ cd pacer-northwind
$ irb
jruby-1.7.18 :001 > require 'pacer'
=> true
jruby-1.7.18 :002 > g = Pacer.tg
=> #<PacerGraph tinkergraph[vertices:0 edges:0]
jruby-1.7.18 :003 > Pacer::GraphML.import(g, 'northwind.graphml')
=> true
From the root folder of our repo
And then
NorthWind
# Find out the different types of vertices ...
jruby-1.7.18 :004 > g.v[:type].uniq
"Order" "Category" "Employee"
"Customer" "Product" "Supplier"
Total: 6
Let's explore the dataset ...
# Look at an outgoing edge from a Customer vertex ...
jruby-1.7.18 :005 > e = g.v(type: 'Customer').out_e.limit(1)
#<E[e3026]:n10-PURCHASED-n255>
Total: 1
# What is the type of the in-vertex of that edge?
jruby-1.7.18 :033 > e.in_v.first[:type]
=> "Order"
First Extension - Customer
In a file called customer.rb
module NorthWind
module Customer
end
end
module NorthWind
module Customer
module Vertex
end
end
end
module NorthWind
module Customer
module Vertex
def orders
end
end
end
end
module NorthWind
module Customer
module Vertex
def orders
self.out_e(:PURCHASED).in_v(type: "Order")
end
end
end
end
- Extensions are plain old Ruby modules
- Create a Vertex module
- Define vertex extension methods
- Implement them (self is a vertex object)
Using The Extension
Try it in the IRB ...
jruby-1.7.18 :100 > load 'customer.rb'
=> true
jruby-1.7.18 :101 > c = g.v(NorthWind::Customer,
type: 'Customer').first
=> #<V[n10]>
jruby-1.7.18 :102 > c.orders
#<V[n255]> #<V[n913]> #<V[n450]> #<V[n505]> #<V[n989]>
#<V[n504]> #<V[n909]> #<V[n437]> #<V[n544]> #<V[n565]>
Total: 10
What just happened?
- All vertices
- Whose type is 'Customer'
- Extended by the Customer extension
g.v
The vertex responds to the vertex-extension method.
g.v(NorthWind::Customer, type: 'Customer').first
g.v(NorthWind::Customer, type: 'Customer')
g.v( type: 'Customer')
orders
Filter By Extension
Only vertices whose type is 'Customer' should be extended by the Customer extension.
module NorthWind
module Customer
module Vertex
def orders
self.out_e(:PURCHASED).in_v(type: "Order")
end
end
end
end
def self.route_conditions(graph)
{type: 'Customer'}
end
Filter By Extension
jruby-1.7.18 :103 > load 'customer.rb'
=> true
jruby-1.7.18 :104 > include NorthWind
=> Object
jruby-1.7.18 :105 > g.v(Customer).first.orders
#<V[n255]> #<V[n913]> #<V[n450]> #<V[n505]> #<V[n989]>
#<V[n504]> #<V[n909]> #<V[n437]> #<V[n544]> #<V[n565]>
Total: 10
Filter By Extension
Try to get the orders of multiple customers ...
jruby-1.7.18 :106 > g.v(Customer).orders
NoMethodError
...
Total: 91
undefined method `orders' for
#<GraphV -> V-Property(NorthWind::Customer)>
We want to be able to extend routes, not just vertices.
Vertex vs. Route
module NorthWind
module Customer
def self.route_conditions(graph)
{type: "Customer"}
end
module Vertex # Contains vertex extension methods
# ...
end
module Route # Contains route extension methods
def orders
self.out_e(:PURCHASED).in_v(type: "Order")
end
end
end
end
jruby-1.7.18 :107 > load 'customer.rb'
=> true
jruby-1.7.18 :108 > g.v(Customer).limit(3).orders
#<V[n255]> #<V[n913]> #<V[n450]> #<V[n505]> #<V[n989]>
#<V[n504]> #<V[n909]> #<V[n437]> #<V[n544]> #<V[n565]>
#<V[n225]> #<V[n487]> #<V[n903]> #<V[n1020]> #<V[n748]>
#<V[n847]> #<V[n785]>
Total: 17
=> #<GraphV -> V-Property(NorthWind::Customer) -> V-Range(-1...3)
-> outE(:PURCHASED) -> inV -> V-Property(type=="Order")>
Vertex vs. Route
Reload and try it in the IRB
jruby-1.7.18 :109 > g.v(Customer).first.orders
#<V[n255]> #<V[n913]> #<V[n450]> #<V[n505]> #<V[n989]>
#<V[n504]> #<V[n909]> #<V[n437]> #<V[n544]> #<V[n565]>
Total: 10
=> #<outE(:PURCHASED) -> inV -> V-Property(type=="Order")>
Route-extension methods can be called on a single element:
* Because an element can be converted to a route with one element.
Vertex vs. Route
Exercise
- Create extensions for all 6 types:
- Category
- Customer
- Employee
- Order
- Product
- Supplier
- Conventions:
- Each extension in its own file
- Under the NorthWind namespace.
- Filenames are lower-case
Exercise
- Define route conditions.
- Based on the type property of a vertex.
- Explore the dataset to discover all types of relations.
- Use the IRB and/or write a script.
- Remember: A vertex can have edges of multiple labels.
- Write route-extension methods for all relations.
Display Name
Customers have a few properties.
jruby-1.7.18 :111 > g.v(Customer).first
=> #<V[n10]>
Yet, in the IRB, we only display their internal id.
jruby-1.7.18 :110 > g.v(Customer).first.properties
=> {"phone"=>"(171) 555-1212",
"companyName"=>"B's Beverages",
"customerID"=>"BSBEV",
"type"=>"Customer"}
Can we define some sort of a toString method?
Display Name
Add the following vertex extension method:
def display_name
"#{self[:companyName]} (#{self[:customerID]})"
end
And ...
jruby-1.7.18 :112 > load 'customer.rb'
=> true
jruby-1.7.18 :113 > g.v(Customer).first
=> #<V[n10] B's Beverages (BSBEV)>
Exercise
Add a display_name to every extension you created.
After doing so, you should be able to do the following:
# Get a customer
jruby-1.7.18 :114 > c = g.v(Customer).first
=> #<V[n10] B's Beverages (BSBEV)>
# Get their orders
jruby-1.7.18 :115 > c.orders
#<V[n255] Order 10289> #<V[n913] Order 10947> #<V[n450] Order 10484> ...
Total: 10
# Get the employees who sold something to this customer
jruby-1.7.18 :116 > c.orders.employees.uniq
#<V[n203] Robert King, Sales Representative> ...
Total: 7
# Which other customers did these employees serve?
jruby-1.7.18 :117 > c.orders
jruby-1.7.18 :118 > .employees.uniq
jruby-1.7.18 :119 > .orders.customers
jruby-1.7.18 :120 > .uniq.is_not(c)
#<V[n59] Princesa Isabel Vinhos (PRINI)> #<V[n75] Suprêmes délices (SUPRD)> ...
Total: 89
What's next?
- Common patterns
- Practical tips and tricks
- Considerations when modeling a realistic dataset
We have seen the basic concepts for Data Modeling using Pacer.
Next, we will go through
Graph Training 4
By XN Logic Corporation
Graph Training 4
Data modeling using Pacer - Forget about vertices and edges, traverse the graph with a domain-specific language that makes sense to you.
- 908