View Source ArangoXEcto (ArangoX Ecto v1.3.1)

Methods for interacting with ArangoDB that aren't through Ecto.

This allows for easy interaction with graph functions of Arango. Using Ecto's relations for edge relations was tried but it was found to be too much of a 'hacky' solution. Using separate functions that still utilise Ecto document queries was found to be the optimal solution.

Link to this section Summary

Functions

Runs an Arangox function using a repo

Same as ArangoXEcto.aql_query/4 but will raise an error.

Runs a raw AQL query on the database.

Creates a collection defined by some schema

Creates an edge between two modules

Creates a view defined by some view schema

Deletes all edges matching matching the query

Generates a edge schema dynamically

Gets an ID from a module and a key

Gets an ID from a schema struct

Returns the name of a database for prefix

Returns if a Schema is a document schema or not

Returns if a Schema is an edge or not

Returns if a Schema is a view schema or not

Loads raw map into ecto structs

Converts raw output of a query into a struct

Same as schema_type/1 but throws an error on none

Returns the type of a module

Checks if a view exists

Link to this section Types

Link to this section Functions

Link to this function

api_query(repo, function, args \\ [])

View Source
@spec api_query(mod(), atom(), list()) ::
  {:ok, Arangox.Response.t()} | {:error, any()}

Runs an Arangox function using a repo

This is simply a helper function that extracts the connection from the repo and runs a regular query.

parameters

Parameters

  • repo - The Ecto repo module used for connection
  • function - An atom of the Arangox function to run
  • args - The options passed to the function (not including the conn argument)

The conn argument is automatically prepended to your supplied args

supported-functions

Supported Functions

  • :abort
  • :cursor
  • :delete
  • :delete!
  • :get
  • :get!
  • :head
  • :head!
  • :options
  • :options!
  • :patch
  • :patch!
  • :post
  • :post!
  • :put
  • :put!
  • :request
  • :request!
  • :run
  • :status
  • :transaction (use built in Ecto.Repo.transaction/2 instead)

examples

Examples

iex> ArangoXEcto.api_query(Repo, :get, ["/_api/collection"])
{:ok, %Arangox.Response{body: ...}}

iex> ArangoXEcto.api_query(Repo, :non_existent, ["/_api/collection"])
** (ArgumentError) Invalid function passed to `Arangox` module
Link to this function

aql_query!(repo, query, vars \\ [], opts \\ [])

View Source
@spec aql_query!(Ecto.Repo.t(), query(), vars(), [DBConnection.option()]) :: [map()]

Same as ArangoXEcto.aql_query/4 but will raise an error.

If there is an error in the query such as a syntax error, an Arangox.Error will be raised.

examples

Examples

iex> ArangoXEcto.aql_query!(
      Repo,
      "FOR var in users FILTER var.first_name == @fname AND var.last_name == @lname RETURN var",
      fname: "John",
      lname: "Smith"
    )
[
  %{
    "_id" => "users/12345",
    "_key" => "12345",
    "_rev" => "_bHZ8PAK---",
    "first_name" => "John",
    "last_name" => "Smith"
  }
]
Link to this function

aql_query(repo, query, vars \\ [], opts \\ [])

View Source
@spec aql_query(Ecto.Repo.t(), query(), vars(), [DBConnection.option()]) ::
  {:ok, [map()]} | {:error, any()}

Runs a raw AQL query on the database.

This will create a transaction and cursor on Arango and run the raw query.

If there is an error in the query such as a syntax error, an Arangox.Error will be raised.

parameters

Parameters

  • repo - The Ecto repo module to use for queries
  • query - The AQL query string to execute
  • vars - A keyword list or a map with the values for variables in the query
  • opts - Options to be passed for the transaction

Accepts any of the options accepted by DBConnection.transaction/3, as well as any of the following:

:read - An array of collection names or a single collection name as a binary. :write - An array of collection names or a single collection name as a binary. :exclusive - An array of collection names or a single collection name as a binary. :properties - A list or map of additional body attributes to append to the request body when beginning a transaction.

If doing a write operation, the :write operation must be passed. This is not explicitly required for read operations.

examples

Examples

iex> ArangoXEcto.aql_query(
      Repo,
      "FOR var in users FILTER var.first_name == @fname AND var.last_name == @lname RETURN var",
      fname: "John",
      lname: "Smith"
    )
{:ok,
[
  %{
    "_id" => "users/12345",
    "_key" => "12345",
    "_rev" => "_bHZ8PAK---",
    "first_name" => "John",
    "last_name" => "Smith"
  }
]}
Link to this function

collection_exists?(repo_or_conn, collection_name, type \\ :document, opts \\ [])

View Source
@spec collection_exists?(
  Ecto.Repo.t() | pid(),
  binary() | atom(),
  atom() | integer(),
  Keyword.t()
) :: boolean()

Checks if a collection exists

This will return true if the collection exists in the database, matches the specified type and is not a system database, otherwise it will be false.

parameters

Parameters

  • repo - The Ecto repo module to use for the query
  • collection_name - Name of the collection to check
  • type - The type of collection to check against, defaults to a regular document
  • opts - Options to be used, available options shown below, defaults to none

options

Options

  • :prefix - The prefix to use for database collection checking

examples

Examples

Checking a document collection exists

iex> ArangoXEcto.collection_exists?(Repo, :users)
true

Checking an edge collection exists

iex> ArangoXEcto.collection_exists?(Repo, "my_edge", :edge)
true

Checking a system document collection exists does not work

iex> ArangoXEcto.collection_exists?(Repo, "_system_test")
false

Checking a collection exists with database prefix

iex> ArangoXEcto.collection_exists?(Repo, "test", "tenant1")
true
Link to this function

create_analyzers(repo, analyzer_module, opts \\ [])

View Source (since 1.3.0)
@spec create_analyzers(Ecto.Repo.t() | pid(), ArangoXEcto.Analyzer.t(), Keyword.t()) ::
  {:ok, Arangox.Response.t()}
  | {:error, [Arangox.Response.t()], [Arangox.Error.t()]}

Creates analyzers in a module

This will first force delete any analyzers with the same name before creating the ones defined.

In production you should be using migrations in static mode. This function will be called when an analyzer module is passed to the ArangoXEcto.Migration.create/2 function.

When operating in dynamic mode the analyzers ae automatically created on view create, see ArangoXEcto.create_view/2 for more info.

parameters

Parameters

  • repo - The Ecto repo module to use for queries
  • analyzer_module - The Analyzer module
  • opts - Additional options to use for analyzer creation

options

Options

  • :prefix - The prefix for the tenant to create analyzers for

examples

Examples

iex> ArangoXEcto.create_analyzers(Repo, MyApp.Analyzers)
{:ok, [%Arangox.Response{}, ...]}

If there is an error in and of the schemas

iex> ArangoXEcto.create_analyzers(Repo, MyApp.Analyzers)
{:error, [%Arangox.Response{}, ...], [%Arangox.Error{}, ...]}
Link to this function

create_collection(repo, schema, opts \\ [])

View Source
@spec create_collection(Ecto.Repo.t(), ArangoXEcto.Schema.t(), Keyword.t()) ::
  :ok | {:error, any()}

Creates a collection defined by some schema

This function is only to be used in dynamic mode and will raise an error if called in static mode. Instead use migrations if in static mode.

parameters

Parameters

  • repo - The Ecto repo module to use for queries
  • schema - The Collection Schema to be created
  • opts - Additional options to pass

options

Options

  • :prefix - The prefix to use for database tenant collection creation

examples

Examples

iex> ArangoXEcto.create_collection(Repo, MyApp.Users)
:ok

If there is an error in the schema

iex> ArangoXEcto.create_collection(Repo, MyApp.Users)
{:error, %Arangox.Error{}}
Link to this function

create_edge(repo, from, to, opts \\ [])

View Source
@spec create_edge(Ecto.Repo.t(), mod(), mod(), keyword()) ::
  {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}

Creates an edge between two modules

This can create an edge collection dynamically if no additional fields are required, otherwise an edge schema needs to be specified.

The collection name can be passed as an option or is obtained from the provided schema, otherwise it is generated dynamically.

Since ArangoDB does not care about the order of the from and two options in anonymous graphs, the order of the from and to attributes used in this function will work either way.

parameters

Parameters

  • repo - The Ecto repo module to use for queries
  • from - The Ecto Schema struct to use for the from vertex
  • to - The Ecto Schema struct to use for the to vertex
  • opts - Options to use

options

Options

Accepts the following options:

  • :edge - A specific edge module to use for the edge. This is required for any additional fields on the edge. Overrides collection_name.
  • :fields - The values of the fields to set on the edge. Requires edge to be set otherwise it is ignored.
  • :collection_name - The name of the collection to use.

examples

Examples

iex> ArangoXEcto.create_edge(Repo, user1, user2)
%UserUser{_from: "users/12345", _to: "users/54321"}

Create an edge with a specific edge collection name

iex> ArangoXEcto.create_edge(Repo, user1, user2, collection_name: "friends")
%Friends{_from: "users/12345", _to: "users/54321"}

Create a edge schema and use it to create an edge relation

defmodule UserPosts do
  use ArangoXEcto.Edge,
      from: User,
      to: Post

  import Ecto.Changeset

  schema "user_posts" do
    edge_fields()

    field(:type, :string)
  end

  def changeset(edge, attrs) do
    edges_changeset(edge, attrs)
    |> cast(attrs, [:type])
    |> validate_required([:type])
  end
end

iex> ArangoXEcto.create_edge(Repo, user1, user2, edge: UserPosts, fields: %{type: "wrote"})
%UserPosts{_from: "users/12345", _to: "users/54321", from: #Ecto.Association.NotLoaded<association :from is not loaded>, to: #Ecto.Association.NotLoaded<association :to is not loaded>, type: "wrote"}
Link to this function

create_view(repo, view, opts \\ [])

View Source (since 1.3.0)
@spec create_view(Ecto.Repo.t() | pid(), ArangoXEcto.View.t(), Keyword.t()) ::
  {:ok, Arangox.Response.t()} | {:error, Arangox.Error.t()}

Creates a view defined by some view schema

When in dynamic mode this will automatically create analyzers and linked collections. If in static mode these need to be done seperately in the migrations.

For the analyzers to be automatically created in dynamic mode the analyzers module needs to be passed as an option into the view module definition. See ArangoXEcto.View for more info.

parameters

Parameters

  • repo - The Ecto repo module to use for queries
  • view - The View Schema to be created
  • opts - Additional options to use for analyzer creation

options

Options

  • :prefix - The prefix for the tenant to create analyzers for

examples

Examples

iex> ArangoXEcto.create_view(Repo, MyApp.Views.UserSearch)
{:ok, %Arangox.Response{}}

If there is an error in the schema

iex> ArangoXEcto.create_view(Repo, MyApp.Views.UserSearch)
{:error, %Arangox.Error{}}
Link to this function

delete_all_edges(repo, from, to, opts \\ [])

View Source
@spec delete_all_edges(Ecto.Repo.t(), mod(), mod(), keyword()) :: :ok

Deletes all edges matching matching the query

If the :conditions option is set then those conditions must be true to delete.

To just delete one edge do so like any other Ecto Schema struct, i.e. using Ecto.Repo methods.

parameters

Parameters

  • repo - The Ecto repo module to use for queries
  • from - The Ecto Schema struct to use for the from vertex
  • to - The Ecto Schema struct to use for the to vertex
  • opts - Options to use

options

Options

Accepts the following options:

  • :edge - A specific edge module to use for the edge. This is required for any additional fields on the edge. Overrides :collection_name.
  • :collection_name - The name of the collection to use.
  • :conditions - A keyword list of conditions to filter for edge deletion

examples

Examples

Deletes all edges from user1 to user2

iex> ArangoXEcto.delete_all_edges(Repo, user1, user2)
:ok

Deletes all edges from user1 to user2 in specific collection

iex> ArangoXEcto.delete_all_edges(Repo, user1, user2, collection_name: "friends")
:ok

Deletes all edges from user1 to user2 that have matching conditions

iex> ArangoXEcto.delete_all_edges(Repo, user1, user2, conditions: [type: "best_friend"])
:ok
Link to this function

edge_module(from_module, to_module, opts \\ [])

View Source
@spec edge_module(mod(), mod(), keyword()) :: atom()

Generates a edge schema dynamically

If a collection name is not provided one will be dynamically generated. The naming convention is the names of the two modules is alphabetical order. E.g. User and Post will combine for a collection name of post_user and an edge module name of PostUser. This order is used to prevent duplicates if the from and to orders are switched.

This will create the Ecto Module in the environment dynamically. It will create it under the closest common parent module of the passed modules plus the Edges alias. For example, if the modules were MyApp.Apple.User and MyApp.Apple.Banana.Post then the edge would be created at MyApp.Apple.Edges.PostUser.

Returns the Edge Module name as an atom.

parameters

Parameters

  • from_module - Ecto Schema Module for the from part of the edge
  • to_module - Ecto Schema Module for the to part of the edge
  • opts - Options passed for module generation

options

Options

  • :collection_name - The name of collection to use instead of generating it

examples

Examples

iex> ArangoXEcto.edge_module(MyProject.User, MyProject.Company, [collection_name: "works_for"])
MyProject.WorksFor

iex> ArangoXEcto.edge_module(MyProject.User, MyProject.Company)
MyProject.UsersCompanies
Link to this function

get_id_from_module(module, key)

View Source
@spec get_id_from_module(Ecto.Schema.t(), binary()) :: binary()

Gets an ID from a module and a key

parameters

Parameters

  • module - Module to get the collection name from
  • key - The _key to use in the id

example

Example

iex> ArangoXEcto.get_id_from_module(User, "123456")
"users/123456"
Link to this function

get_id_from_struct(struct)

View Source
@spec get_id_from_struct(mod()) :: binary()

Gets an ID from a schema struct

parameters

Parameters

  • struct - The Ecto struct

example

Example

If the User schema's collection name is users the following would be:

iex> user = %User{id: "123456"}
%User{id: "123456"}

iex> ArangoXEcto.get_id_from_struct(user)
"users/123456"
Link to this function

get_prefix_database(repo, prefix)

View Source
@spec get_prefix_database(Ecto.Repo.t(), String.t() | atom()) :: String.t()

Returns the name of a database for prefix

This simply prepends the tenant name to the database supplied in the repo config.

parameters

Parameters

  • repo - The Ecto repo module to use for the query
  • prefix - The prefix to get the database name for
@spec is_document?(atom()) :: boolean()

Returns if a Schema is a document schema or not

Checks for the presence of the __schema__/1 function on the module and not an edge.

@spec is_edge?(atom()) :: boolean()

Returns if a Schema is an edge or not

Checks for the presence of the __edge__/0 function on the module.

Link to this function

is_view?(module)

View Source (since 1.3.0)
@spec is_view?(atom()) :: boolean()

Returns if a Schema is a view schema or not

Checks for the presence of the __view__/1 function on the module.

@spec load(map() | [map()], Ecto.Schema.t() | [Ecto.Schema.t()]) :: struct()

Loads raw map into ecto structs

Uses Ecto.Repo.load/2 to load a deep map result into ecto structs

If a list of maps are passed then the maps are enumerated over.

A list of modules can also be passes for possible types. The _id field is used to check against the module schemas. This is especially useful for querying against arango views where there may be multiple schema results returned.

If a module is passed that isn't a Ecto Schema then an error will be raised.

parameters

Parameters

  • maps - List of maps or singular map to convert to a struct
  • module - Module(s) to use for the struct

example

Example

iex> {:ok, users} = ArangoXEcto.aql_query(
      Repo,
      "FOR user IN users RETURN user"
    )
{:ok,
  [
    %{
      "_id" => "users/12345",
      "_key" => "12345",
      "_rev" => "_bHZ8PAK---",
      "first_name" => "John",
      "last_name" => "Smith"
    }
  ]}

iex> ArangoXEcto.load(users, User)
[
  %User{
    id: "12345",
    first_name: "John",
    last_name: "Smith"
  }
]
Link to this function

raw_to_struct(map, module)

View Source
This function is deprecated. Use load/2 instead.
@spec raw_to_struct(map() | [map()], Ecto.Schema.t()) :: struct()

Converts raw output of a query into a struct

Transforms string map arguments into atom key map, adds id key and drops _id, _key and _rev keys. Then it creates a struct from filtered arguments using module.

If a list of maps are passed then the maps are enumerated over.

parameters

Parameters

  • maps - List of maps or singular map to convert to a struct
  • module - Module to use for the struct

example

Example

iex> {:ok, users} = ArangoXEcto.aql_query(
      Repo,
      "FOR user IN users RETURN user"
    )
{:ok,
[
  %{
    "_id" => "users/12345",
    "_key" => "12345",
    "_rev" => "_bHZ8PAK---",
    "first_name" => "John",
    "last_name" => "Smith"
  }
]}

iex> ArangoXEcto.raw_to_struct(users, User)
[
  %User{
    id: "12345",
    first_name: "John",
    last_name: "Smith"
  }
]
@spec schema_type!(atom()) :: :document | :edge

Same as schema_type/1 but throws an error on none

This is just a shortcut to using is_edge/1, is_document/1 and is_view/1. If it is none of them nil is returned.

examples

Examples

A real edge schema

iex> ArangoXEcto.schema_type!(MyApp.RealEdge)
:edge

Some module that is not an Ecto schema

iex> ArangoXEcto.schema_type!(MyApp.RandomModule)
** (ArgumentError) Not an Ecto Schema
@spec schema_type(atom()) :: :document | :edge | nil

Returns the type of a module

This is just a shortcut to using is_edge/1, is_document/1 and is_view/1. If it is none of them nil is returned.

examples

Examples

A real view

iex> ArangoXEcto.schema_type(MyApp.SomeView)
:view

A real edge schema

iex> ArangoXEcto.schema_type(MyApp.RealEdge)
:edge

Some module that is not an Ecto schema

iex> ArangoXEcto.schema_type(MyApp.RandomModule)
nil
Link to this function

view_exists?(repo_or_conn, view_name)

View Source
@spec view_exists?(Ecto.Repo.t() | pid(), binary() | atom()) :: boolean()

Checks if a view exists

This will return true if the view exists in the database, otherwise false.

parameters

Parameters

  • repo - The Ecto repo module to use for the query
  • view_name - Name of the collection to check

examples

Examples

Checking a document collection exists

iex> ArangoXEcto.view_exists?(Repo, :users_search)
true