Service Contexts
Learn how to avoid naming conflicts and switch between various graph models seamlessly.
We'll cover the following...
Problem
If we import two graph modules, we’ll run into a conflict in our function names.
iex> import NativeGraphNativeGraphiex> import PropertyGraphPropertyGraphiex> list_graphs
This won't do. If we try the commands above in the following terminal, we get a CompileError:
#---
# Excerpted from "Exploring Graphs with Elixir",
# published by The Pragmatic Bookshelf.
# Copyrights apply to this code. It may not be used to create training material,
# courses, books, articles, and the like. Contact us if you are in doubt.
# We make no guarantees that this code is fit for any purpose.
# Visit https://pragprog.com/titles/thgraphs for more book information.
#---
defmodule PropertyGraph.Service do
@behaviour GraphCommons.Service
@cypher_delete """
MATCH (n) OPTIONAL MATCH (n)-[r]-() DELETE n,r
"""
@cypher_read """
MATCH (n) OPTIONAL MATCH (n)-[r]-() RETURN DISTINCT n, r
"""
@cypher_info """
CALL apoc.meta.stats()
YIELD labels, labelCount, nodeCount, relCount, relTypeCount
"""
## GRAPH
def graph_create(graph) do
graph_delete()
graph_update(graph)
end
def graph_delete(), do: Bolt.Sips.query!(Bolt.Sips.conn(), @cypher_delete)
def graph_read(), do: Bolt.Sips.query!(Bolt.Sips.conn(), @cypher_read)
def graph_update(%GraphCommons.Graph{} = graph),
do: Bolt.Sips.query!(Bolt.Sips.conn(), graph.data)
def graph_info() do
{:ok, [stats]} =
@cypher_info
|> PropertyGraph.new_query()
|> query_graph
%GraphCommons.Service.GraphInfo{
type: :property,
file: "",
num_nodes: stats["nodeCount"],
num_edges: stats["relCount"],
labels: Map.keys(stats["labels"])
}
end
## QUERY
def query_graph(%GraphCommons.Query{} = query), do: query_graph(query, %{})
def query_graph(%GraphCommons.Query{} = query, params) do
:property = query.type
Bolt.Sips.query(Bolt.Sips.conn(), query.data, params)
|> case do
{:ok, response} -> parse_response(response, false)
{:error, error} -> {:error, error}
end
end
def query_graph!(%GraphCommons.Query{} = query), do: query_graph!(query, %{})
def query_graph!(%GraphCommons.Query{} = query, params) do
:property = query.type
Bolt.Sips.query(Bolt.Sips.conn(), query.data, params)
|> case do
{:ok, response} -> parse_response(response, true)
{:error, error} -> raise "! #{inspect error}"
end
end
#
# def query_graph!(%GraphCommons.Query{} = query, param \\ %{}) do
# :property = query.type
#
# Bolt.Sips.query(Bolt.Sips.conn(), query.data, param)
# |> case do
# {:ok, response} -> parse_response(response, false)
# {:error, error} -> {:error, error}
# end
# end
#
defp parse_response(%Bolt.Sips.Response{} = response, bang) do
%Bolt.Sips.Response{type: type} = response
case type do
r when r in ["r", "rw"] ->
%Bolt.Sips.Response{results: results} = response
unless bang, do: {:ok, results}, else: results
s when s in ["s"] ->
%Bolt.Sips.Response{results: results} = response
unless bang, do: {:ok, results}, else: results
w when w in ["w"] ->
%Bolt.Sips.Response{stats: stats} = response
unless bang, do: {:ok, stats}, else: stats
end
end
end
Importing the two modules will result in a conflict
Solution
We need to be able to delete any previous graph module import before attempting a new import.
There is a way. Basically, if we do an import, restricting the ...
Ask