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.
Checks if a collection exists
Creates analyzers in a module
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
@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 connectionfunction
- An atom of the Arangox function to runargs
- 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 inEcto.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
@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"
}
]
@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 queriesquery
- The AQL query string to executevars
- A keyword list or a map with the values for variables in the queryopts
- 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"
}
]}
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 querycollection_name
- Name of the collection to checktype
- The type of collection to check against, defaults to a regular documentopts
- 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
@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 queriesanalyzer_module
- The Analyzer moduleopts
- 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{}, ...]}
@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 queriesschema
- The Collection Schema to be createdopts
- 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{}}
@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 queriesfrom
- The Ecto Schema struct to use for the from vertexto
- The Ecto Schema struct to use for the to vertexopts
- 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. Overridescollection_name
.:fields
- The values of the fields to set on the edge. Requiresedge
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"}
@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 queriesview
- The View Schema to be createdopts
- 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{}}
@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 queriesfrom
- The Ecto Schema struct to use for the from vertexto
- The Ecto Schema struct to use for the to vertexopts
- 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
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 edgeto_module
- Ecto Schema Module for the to part of the edgeopts
- 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
@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 fromkey
- The_key
to use in the id
example
Example
iex> ArangoXEcto.get_id_from_module(User, "123456")
"users/123456"
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"
@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 queryprefix
- The prefix to get the database name for
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.
Returns if a Schema is an edge or not
Checks for the presence of the __edge__/0
function on the module.
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 structmodule
- 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"
}
]
@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 structmodule
- 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
@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 queryview_name
- Name of the collection to check
examples
Examples
Checking a document collection exists
iex> ArangoXEcto.view_exists?(Repo, :users_search)
true