...

/

Inspect the Structs

Inspect the Structs

Learn how to create a graph using the graph struct and to inspect the struct by defining a user-friendly view.

We'll cover the following...

Let’s try out the %GraphCommons.Graph{} struct by creating a new graph. We’ll use a property graph model example here, which generates a minimal graph with two nodes and one edge between them.

Note: Our query.ex file will be similar to the graph.ex file with the term “graph” replaced with “query.”

Default graph

The following example will serve as our default graph.

Default graph
Default graph

We can create this simple graph with the Cypher string:

iex> CREATE (a)-[:EX]->(b)

Here is the complete command:

iex> "CREATE (a)-[:EX]->(b)" |>
...> GraphCommons.Graph.new("test.cypher", :property)

Run the terminal, and enter the command below:

#---
# 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 http://www.pragmaticprogrammer.com/titles/thgraphs for more book information.
#---
defmodule GraphCommons.Query do

  ## ATTRIBUTES

  @storage_dir GraphCommons.storage_dir()

  ## STRUCT

  @enforce_keys ~w[data file type]a
  defstruct ~w[data file path type uri]a

  ## TYPES

  @type query_data :: String.t()
  @type query_file :: String.t()
  @type query_path :: String.t()
  @type query_type :: GraphCommons.query_type()
  @type query_uri :: String.t()

  @type t :: %__MODULE__{
          # user
          data: query_data,
          file: query_file,
          type: query_type,
          # system
          path: query_path,
          uri: query_uri
        }

  defguard is_query_type(query_type)
           when query_type in [ :dgraph, :native, :property, :rdf, :tinker ]

  ## CONSTRUCTOR

  def new(query_data, query_file, query_type)
      when is_query_type(query_type) do
    query_path = "#{@storage_dir}/#{query_type}/queries/#{query_file}"

    %__MODULE__{
      # user
      data: query_data,
      file: query_file,
      type: query_type,
      # system
      path: query_path,
      uri: "file://" <> query_path
    }
  end

  ## FUNCTIONS

  def queries_dir(query_type), do: "#{@storage_dir}/#{query_type}/queries/"

  @type file_test :: GraphCommons.file_test()

  def list_queries(query_type, file_test \\ :exists?) do
    list_queries_dir("", query_type, file_test)
  end

  def list_queries_dir(query_file, query_type, file_test \\ :exists?) do
    path = "#{@storage_dir}/#{query_type}/queries/"

    (path <> query_file)
    |> File.ls!()
    |> do_filter(path, file_test)
    |> Enum.sort()
    |> Enum.map(fn f ->
      File.dir?(path <> f)
      |> case do
        true -> "#{String.upcase(f)}"
        false -> f
      end
    end)
  end

  defp do_filter(files, path, file_test) do
    files
    |> Enum.filter(fn f ->
      case file_test do
        :dir? -> File.dir?(path <> f)
        :regular? -> File.regular?(path <> f)
        :exists? -> true
      end
    end)
  end

  def read_query(query_file, query_type)
      when query_file != "" and is_query_type(query_type) do
    queries_dir = "#{@storage_dir}/#{query_type}/queries/"
    query_data = File.read!(queries_dir <> query_file)

    new(query_data, query_file, query_type)
  end

  def write_query(query_data, query_file, query_type)
      when query_data != "" and
             query_file != "" and is_query_type(query_type) do
    queries_dir = "#{@storage_dir}/#{query_type}/queries/"
    File.write!(queries_dir <> query_file, query_data)

    new(query_data, query_file, query_type)
  end

  ## MACROS

  defmacro __using__(opts) do
    query_type = Keyword.get(opts, :query_type)
    # query_module = Keyword.get(opts, :query_module)

    quote do

      ## TYPES

      @type query_file_test :: GraphCommons.file_test()

      @type query_data :: GraphCommons.Query.query_data()
      @type query_file :: GraphCommons.Query.query_file()
      @type query_path :: GraphCommons.Query.query_path()
      @type query_type :: GraphCommons.Query.query_type()
      @type query_uri :: GraphCommons.Query.query_uri()

      @type query_t :: GraphCommons.Query.t()

      ## FUNCTIONS

      # 
      # def graph_service(), do: unquote(graph_module)
      # 

      def list_queries(query_file_test \\ :exists?),
        do: GraphCommons.Query.list_queries(
          unquote(query_type), query_file_test)

      def list_queries_dir(dir, query_file_test \\ :exists?),
        do: GraphCommons.Query.list_queries_dir(dir,
          unquote(query_type), query_file_test)

      def new_query(query_data), do: new_query(query_data, "")

      def new_query(query_data, query_file),
        do: GraphCommons.Query.new(query_data, query_file,
          unquote(query_type))

      def read_query(query_file),
        do: GraphCommons.Query.read_query(query_file,
          unquote(query_type))

      def write_query(query_data, query_file),
        do: GraphCommons.Query.write_query(query_data, query_file,
          unquote(query_type))
    end
  end

  ## IMPLEMENTATIONS

end
Create the default graph
...