10. Labeled Property Graph Model
• Nodes
• Contain properties (key-value pairs)
• Have one or more labels
• Relationships
• Have names
• Are directed
• Can also contain properties
11. Why Use a Graph Database?
• Performance
• SQL performance gets worse with large data sets
• Especially compared to join-intensive queries in SQL
• Graph databases remain mostly constant
• Queries are localized to a section the graph
12. Why Use a Graph Database?
• Flexibility
• Graph databases are additive
• Can add new kinds of relationships, nodes, labels,
subgraphs, etc. to existing structure
• Won't disturb existing queries!
• Less developer time spent on modeling domains
13. Why Use a Graph Database?
• Agility
• Schema-free
• Change the data model as you develop
14. Elixir Ravelry Project
• Replicating a portion of Ravelry.com
• The manufacturing and sales of yarn
• Applicable to most social and manufacturing
processes
24. 😱☠🐑 RUT-ROH! 🐑☠😱
• Scenario: somebody who purchased a skein of yarn
has tested positive for anthrax!
• We need to find all the people who were involved in
the production of this dye lot to test them too
25. SQL Query
// Spinner
SELECT users.*
FROM users
INNER JOIN spinnings
ON spinnings.spinner_id = users.id
INNER JOIN yarn
ON yarn.material_id = spinnings.id
INNER JOIN users as knitters
ON knitters.id = yarn.knitter_id
WHERE knitters.name = "Bob"
UNION
26. SQL Query
// Dyed Roving
SELECT users.*
FROM users
INNER JOIN dyed_roving
ON dyed_roving.dyer_id = users.id
INNER JOIN spinnings
ON spinnings.material_id = dyed_roving.id
INNER JOIN yarn
ON yarn.material_id = spinnings.id
INNER JOIN users as knitters
ON knitters.id = yarn.knitter_id
WHERE knitters.name = "Bob"
UNION
27. SQL Query
// Roving
SELECT users.*
FROM users
INNER JOIN roving
ON roving.carder_id = users.id
INNER JOIN dyed_roving
ON dyed_roving.material_id = roving.id
INNER JOIN spinnings
ON spinnings.material_id = dyed_roving.id
INNER JOIN yarn
ON yarn.material_id = spinnings.id
INNER JOIN users as knitters
ON knitters.id = yarn.knitter_id
WHERE knitters.name = "Bob"
43. defmacro !__using!__([]) do
quote do
alias ElixirRavelry.Repo
def get(conn, id) do
Repo.get_node(conn, type(), id)
end
def graph(conn, id, direction) do
Repo.graph(conn, type(), id, direction)
end
def list(conn) do
Repo.list_node(conn, type())
end
end
end
49. def graph(conn, type ! nil, id, direction, options) do
n_type = if type do
":!#{type}"
else
""
end
conn
!|> Bolt.Sips.query!(
"""
MATCH (n!#{n_type})
WHERE id(n) = toInteger({id})
!#{graph_optional_match(direction, options)}
WITH !#{graph_with(direction)}
RETURN !#{graph_return(direction)}
""",
cypher_parameters(id, options)
)
!|> graph_return_to_map()
end
50. defp graph_optional_match("forward", options) do
"OPTIONAL MATCH forward = (n)-[forward_relationship*0!..]!->(sink!#{forward_type(options)})"
end
defp graph_optional_match("both", options) do
"!#{graph_optional_match("forward", options)}n!#{graph_optional_match("backwards", options)}"
end
defp graph_optional_match("backwards", options) do
"OPTIONAL MATCH backwards = (source!#{backwards_type(options)})-[backwards_relationship*0!..]!->(n)"
end
53. MATCH (n#{n_type})
WHERE id(n) = toInteger({id})
OPTIONAL MATCH backwards = (source#{backwards_type(options)})-
[backwards_relationship*0..]->(n)
WITH COLLECT(DISTINCT source) as source_nodes,
COLLECT(DISTINCT head(backwards_relationship)) as backwards_rels
RETURN source_nodes, backwards_rels
54. MATCH (n#{n_type})
WHERE id(n) = toInteger({id})
OPTIONAL MATCH backwards = (source#{backwards_type(options)})-
[backwards_relationship*0..]->(n)
WITH COLLECT(DISTINCT source) as source_nodes,
COLLECT(DISTINCT head(backwards_relationship)) as backwards_rels
RETURN source_nodes, backwards_rels
55. Real Time Updates
• The Goal:
• See changes to the graph as it is updated
• The Problem:
• Don’t want to send the whole graph every single time it’s updated
• The Solution:
• Store the previously sent graph on the socket
56. Real Time Updates
• Nodes are created before relationships
• Relationships need a start node and an end node
• Monitor when relationships are created
61. Real Time Updates
1. Create relationship in Repo
2. Graph query on start or end node id of relationship
3. Broadcast for each node in graph to that node’s graph id topic
62. Testing Real Time Updates
conn = post conn, "/api/v1/material-for",
%{start_node_id: yarn.id, end_node_id: project.id}
conn =
conn
!|> recycle()
!|> Plug.Conn.put_private(:bolt_sips_conn, bolt_sips_conn)
!|> post("/api/v1/cards", %{user_id: cards_user.id,
roving_id: roving.id})
64. Future Work
• Ecto Adapter
• Use Ecto Schema to do type conversions
• Remove need to pass Bolt.Sips.Conn everywhere
• Query Library
• Ecto Query won’t support Cypher keywords