View Source ArangoXEcto (ArangoX Ecto v2.0.0)
The ArangoDB adapter for Ecto.
ArangoXEcto is provides the following functionality:
Full Ecto compatability
ArangoDB anonymous graphs
Geographic data
ArangoSearch (views) and Analyzers
The functions in this module are the base functions for interacting with ArangoDB that aren't through Ecto.
All the Ecto functionality hindges on the functions in this module. For example, queries throuh
Ecto will use on the aql_query/4
function in this module.
Ecto compatibility
To use the ArangoDB with Ecto you will need to use the ArangoXEcto.Adapter
module in your repo.
defmodule MyApp.Repo do
use Ecto.Repo,
otp_app: :my_app,
adapter: ArangoXEcto.Adapter
end
You will also have to set the configuration as below. You can set either static or dynamic mode, the default is static mode and the option can be omitted. Find out more in the next section.
config :my_app, MyApp.Repo,
database: "my_db",
endpoints: "http://1.2.3.4:8529",
username: "my_user,
password: "my_password",
static: true
Static or Dynamic Mode
In addition to default config variables, the static
boolean config option controls the use of
migrations. By default the value is true
and will act as other Ecto adapters whereby migrations
are needed to create the collections. Any collections that do not exist on insert or query will
result in an error being raised.
If you set it to false
, then you will operate in dynamic mode, therefore collections that don't
exist will be created on insert and on query no error will be raised.
Whether static (default) or dynamic is chosen depends on the database design of the project.
For a production setup where lots of control is required, it is recommended to have static
set
to true
, hence this is the default. Sometimes in dev it can be easier to work in dynamic mode
for smaller projects, however this can cause database inconsistencies in larger projects.
Schemas
Instead of using the Ecto.Schema
module the ArangoXEcto.Schema
module needs to be used. This
is to enable the use of a binary_id
that ArangoDB requires.
defmodule MyApp.Accounts.User do
use ArangoXEcto.Schema
import Ecto.Changeset
schema "users" do
field :first_name, :string
field :last_name, :string
timestamps()
end
@doc false
def changeset(app, attrs) do
app
|> cast(attrs, [:first_name, :last_name])
|> validate_required([:first_name, :last_name])
end
end
When using dynamic mode you can define options/1
and indexes/1
can be optionally called inside
the schema to set options and indexes to be created in dynamic mode. These options will have no
effect in static mode.
For example, if you wanted to use a uuid as the key type and create an index on the email the following can be used.
defmodule MyApp.Accounts.User do
use ArangoXEcto.Schema
import Ecto.Changeset
options [
keyOptions: %{type: :uuid}
]
indexes [
[fields: [:email]]
]
schema "users" do
field :email, :string
timestamps()
end
@doc false
def changeset(app, attrs) do
app
|> cast(attrs, [:first_name, :last_name])
|> validate_required([:first_name, :last_name])
end
end
Please refer to ArangoXEcto.Schema
for more information on the available options.
Migrations
Migrations in ArangoXEcto operate just like in other Ecto adapters. However, ArangoXEcto uses its own migration commands to allow for interoperability between databases.
For example, you might want to create a users collection with an index and use ArangoSearch with a custom analyzer to make querying more efficient. The below migration could be used to do just that.
defmodule MyProject.Repo.Migrations.CreateUsers do
use ArangoXEcto.Migration
def change do
create analyzer(:norm_en, :norm, [:frequency, :position], %{
locale: "en",
accent: false,
case: :lower
})
create collection(:users) do
add :first_name, :string, comment: "first_name column"
add :last_name, :string
add :gender, :integer
add :age, :integer
add :location, :map
timestamps()
end
create index(:users, [:age])
create unique_index(:users, [:location])
create view(:user_search, commitIntervalMsec: 1, consolidationIntervalMsec: 1) do
add_sort(:created_at, :desc)
add_sort(:first_name)
add_store([:first_name, :last_name], :none)
add_link("users", %Link{
includeAllFields: true,
fields: %{
last_name: %Link{
analyzers: [:identity, :norm_en]
}
}
})
end
end
end
You can find more information about how migrations are implemented in the ArangoXEcto.Migration
module.
Sandbox
Sandbox for concurrent tests is implemented similar to the implementation in ecto_sql
. See
ArangoXEcto.Sandbox
for more info.
Graphs
Ecto traditionally is made for relational databases and hence relations between schemas is represented in such a way. A graph relation (edges) can be thought of as many-to-many relations in the relational database world. When it comes to representing graph relations in Ecto, that is exactly how it works in ArangoXEcto.
To use a graph relation you define an outgoing
and an incoming
relation on the two schemas,
for example the following will create a graph relationship between User -> UserPost -> Post
.
The UserPost
edge module will be created automatically.
defmodule MyApp.User do
schema "users" do
field :first_name, :string
field :last_name, :string
outgoing :posts, MyApp.Post
end
end
defmodule MyApp.Post do
schema "posts" do
field :title, :string
incoming :users, MyApp.User
end
end
Behind the scenes this works very similar to a many_to_many
relationships but has a few key
differences. As you may know, in a graph an edge can have multiple different node types that it
connects to/from. ArangoXEcto uses a special relation type that is based on the principal of
many-to-many but modified to allow such relationships.
For example, you may have a User
schema that you want to connect to Post
and Comment
. You
may not want to have two seperate relationships (e.g. :posts
and :comments
) and want to
combine it all under one (e.g. :content
). This is what the ArangoXEcto adapter enables. You can
read more about this type of relationship below.
Edge Modules
When in dynamic mode the ArangoXEcto adapter dynamically creates and manages edge collections. Each edge collection will be created as an Ecto schema when they are first used. This means that you don't need to create edge collections manually. When in static mode you need to define edges manually.
The edge module will be created under the closest common parent module of the passed modules
plus the Edges
alias. The order of the edge name will always be alphabetical to prevent
duplicate edges. 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
. This assumes that the edge
collection name was generated and not explicitly defined, if it was PostUser
would be replaced
with the camel case of that collection name (i.e. address_people
would be AddressPeople
).
Multiple edge schemas
Creating a graph relation that has multiple schemas through the same edge is possible in ArangoXEcto. For example, take the following.
graph TD
User --> edge(UsersContent)
edge(UsersContent) --> Post
edge(UsersContent) --> Comment
Users can have posts or comments through the same edge. This works because edges can have multiple
from and to schemas. For example if we were to define the UsersContent
edge it would look like
this:
defmodule MyApp.Edges.UsersContent do
use ArangoXEcto.Edge,
from: User,
to: [Post, Comment]
schema "users_content" do
edge_fields()
end
end
We can then define our outgoing definition for the User schema like the following.
defmodule MyApp.User do
schema "users" do
field :first_name, :string
field :last_name, :string
outgoing(
:my_content,
%{
Post => &Post.changeset/2,
Comment => &Comment.changeset/2
},
edge: ArangoXEcto.Edges.UsersContents
)
end
end
The definition of outgoing here is slightly different from above. Instead of specifying a single
schema module we specify a map where the keys are schema modules and the values are a list of
field names to check if it exists in a result to match it with the module. This is an OR
condition, so if you had [:a, :b]
that would mean if a result had :a
it would match or if it
had :b
it would match. If a result had both it would match.
Notice that the edge here was explicitly defined. This is not necessary if you aren't doing anything custom on the edge and not including this would just generate an edge like the one above.
Preloading
Due to how graph relations work the regular Ecto.Repo.preload/3
function will only return maps
instead of structs. If you want to preload the structs you can use the ArangoXEcto.preload/4
function.
iex> Repo.all(User) |> ArangoXEcto.preload(Repo, [:my_content])
[%User{id: 123, my_content: [%Post{...}, %Comment{...}]}]
Geodata
GeoJSON is supported through the Geo
module and a handy wrapper is provided in the
ArangoXEcto.GeoData
module. This module will just verify and create Geo structs that can then be
used in geojson fields. The ArangoXEcto.Types.GeoJSON
module defines the GeoJSON type that can
be used when defining fields, for example below.
schema "users" do
field :location, ArangoXEcto.Types.GeoJSON
end
You can define a geoJson index on the field using either migrations or dynamic index creation.
# Migration
create index(:users, [:location], type: :geo, geoJson: true)
# Dynamic creation
indexes [
[fields: [:location], type: :geo, geoJson: true]
]
You can use Geo
structs to query the database.
ArangoSearch
ArangoDB uses ArangoSearch to index collections to improve the searchability of them. With this you can do things like have multiple collections in one "view". A view is a concept that is essentially take one or more collections and indexes it using whatever "analyzer(s)" you choose to apply.
To query a View you just use it as if you were using any other collection schema. It will return the results the same as if you were querying that schema.
To create a View it is very similar to how you create a collection schema. The following will
create a view that has primary sorts on :created_at and :name, it will store the :email and the
:first_name & :last_name fields. Most importantly it will create a link to the MyApp.Users
schema and set the analyzer for the :name
field to :text_en
. Some options can be set also.
defmodule MyApp.UserSearch do
use ArangoXEcto.View
alias ArangoXEcto.View.Link
view "user_search" do
primary_sort :created_at, :desc
primary_sort :name
store_value [:email], :lz4
store_value [:first_name, :last_name], :none
link MyApp.Users, %Link{
includeAllFields: true,
fields: %{
name: %Link{
analyzers: [:text_en]
}
}
}
options [
primarySortCompression: :lz4
]
end
end
Querying is done exactly the same as a normal schema except the result will not be a struct.
iex> Repo.all(MyApp.UsersView)
[%{first_name: "John", last_name: "Smith"}, _]
You can find out more info in the ArangoXEcto.View
module.
To query using the search function you can use the ArangoXEcto.Query.search/3
and
ArangoXEcto.Query.or_search/3
functions. For example, if we wanted to search using the text
analyzer we could do the following.
import ArangoXEcto.Query
from(UsersView)
|> search([uv], fragment("ANALYZER(? == ?, \"text_en\")", uv.first_name, "John"))
|> Repo.all()
Mix Tasks
ArangoXEcto supplies multiple mix tasks for use with migrations. They are:
mix arango.migrate
- Runs forward through migrationsmix arango.rollback
- Rolls back migrationsmix arango.gen.migration
- Generates a migration file
More information on their usage can be found in their various documentation.
Summary
Functions
Runs an Arangox function using a repo
Runs a raw AQL query on the database.
Same as ArangoXEcto.aql_query/4 but will raise an error.
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
Returns true if a Schema is a document schema
Returns true if a Schema is an edge
Generates an 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
Loads raw map into Ecto structs
Preloads all associations on the given struct or structs.
Returns the type of a module
Same as schema_type/1 but throws an error on none
Returns true if a Schema is a view schema
Checks if a view exists
Types
Functions
@spec api_query( Ecto.Repo.t() | Ecto.Adapter.adapter_meta(), atom(), Arangox.path(), Arangox.body(), Arangox.headers(), [DBConnection.option()] ) :: {: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. It is mainly for internal use but since it can be useful for some custom usecases it is included in the documentation.
Parameters
repo
- The Ecto repo module used for connectionfunction
- An atom of the Arangox function to runpath
- The path of the querybody
- The body of the requestheaders
- The headers of the requestopts
- The last opts arg passed to the function
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
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() | Ecto.Adapter.adapter_meta(), query(), vars(), [ DBConnection.option() ]) :: {:ok, {non_neg_integer(), [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
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
Options
Accepts any of the options accepted by DBConnection.transaction/3, as well as any of the following:
- :prefix - The prefix to use for the database. Defaults to the repo database.
- :read - An array of collection names or a single collection name as a binary to mark as read-only.
- :write - An array of collection names or a single collection name as a binary to mark as writable. If not set then no writes will be stored.
- :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
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, {1,
[
%{
"_id" => "users/12345",
"_key" => "12345",
"_rev" => "_bHZ8PAK---",
"first_name" => "John",
"last_name" => "Smith"
}
]}}
@spec aql_query!(Ecto.Repo.t() | Ecto.Adapter.adapter_meta(), query(), vars(), [ DBConnection.option() ]) :: {non_neg_integer(), [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
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"
)
{1, [
%{
"_id" => "users/12345",
"_key" => "12345",
"_rev" => "_bHZ8PAK---",
"first_name" => "John",
"last_name" => "Smith"
}
]}
@spec collection_exists?( Ecto.Repo.t(), 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
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
:prefix
- The prefix to use for database collection checking
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(), ArangoXEcto.Analyzer.t(), Keyword.t()) :: :ok | {:error, [atom()], [{atom(), Arangox.Error.t()}]}
Creates analyzers in a module
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.
When operating in dynamic mode the analyzers are automatically created on view create,
see ArangoXEcto.create_view/2
for more info.
This will first force delete any analyzers with the same name before creating the ones defined.
Parameters
repo
- The Ecto repo module to use for queriesanalyzer_module
- The Analyzer moduleopts
- Additional options to use for analyzer creation
Options
:prefix
- The prefix for the tenant to create analyzers for
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(), Ecto.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
repo
- The Ecto repo module to use for queriesschema
- The Collection Schema to be createdopts
- Additional options to pass
Options
:prefix
- The prefix to use for database tenant collection creation
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(), struct(), struct(), Keyword.t()) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}
Creates an edge between two modules
If in dynamic mode, the edge collection will be created dynamically if no additional fields are required, otherwise an edge schema needs to be specified.
Either an edge module or a collection name is required to create the edge, otherwise one will be generated automatically using the from and two schemas.
The order of the from
and to
matters. A changeset validation error will be raised if
the from
is not defined in the from
definition of a provided edge. The same goes for the to
.
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
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
.:collection_name
- The name of the collection to use when generating an edge module.:fields
- The values of the fields to set on the edge. Requiresedge
to be set otherwise it is ignored.
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(), ArangoXEcto.View.t(), Keyword.t()) :: :ok | {:error, Arangox.Error.t()}
Creates a view defined by some view 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.
This will automatically create analyzers and linked collections. For the analyzers to be
automatically created the analyzers module needs to be passed as an option into the view
module definition. See ArangoXEcto.View
for more info.
Parameters
repo
- The Ecto repo module to use for queriesview
- The View Schema to be createdopts
- Additional options to use for analyzer creation
Options
:prefix
- The prefix for the tenant to create analyzers for
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(), struct(), struct(), Keyword.t()) :: :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
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
:edge
- A specific edge module to use for 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
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
Returns true if a Schema is a document schema
Checks for the presence of the __schema__/1
function on the module and not an edge.
Parameters
module
- the module to check
Returns true if a Schema is an edge
Checks for the presence of the __edge__/0
function on the module.
Parameters
module
- the module to check
Generates an edge schema dynamically
If a collection name is not provided one will be dynamically generated. The naming convention
is the names of the modules in alphabetical order. E.g. User
and Post
will combine for a collection
name of posts_users
(uses the collection names of the modules) and an edge module name of PostUser
.
This order is used to prevent duplicates if the from and to orders are switched.
You can also use a list of from and to modules, e.g. from: User, to: [Post, Comment], would result in a module name of CommentPostUser and a collection name of comments_posts_users. This may not always be an optimal naming convention so it is generally a good idea to make your own edge collection.
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
from_modules
- Ecto Schema modules for the from part of the edgeto_modules
- Ecto Schema modules for the to part of the edgeopts
- Options passed for module generation
Options
:collection_name
- The name of collection to use instead of generating it
Examples
iex> ArangoXEcto.edge_module(MyProject.User, MyProject.Company, [collection_name: "works_for"])
MyProject.Edges.WorksFor
iex> ArangoXEcto.edge_module(MyProject.User, [MyProject.Company, MyProject.Group])
MyProject.Edges.CompanyGroupUser
@spec get_id_from_module(Ecto.Schema.t(), binary()) :: binary()
Gets an ID from a module and a key
Parameters
module
- Module to get the collection name fromkey
- The_key
to use in the id
Example
iex> ArangoXEcto.get_id_from_module(User, "123456")
"users/123456"
Gets an ID from a schema struct
If the struct has been queried from the database it will have the :__id__
field with the id
which is used. Otherwise it is generated using the schema name and the id (the _key
).
Parameters
struct
- The Ecto struct
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
repo
- The Ecto repo module to use for the queryprefix
- The prefix to get the database name for
@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
maps
- List of maps or singular map to convert to a structmodule
- Module(s) to use for the struct
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 preload( structs_or_struct_or_nil, Ecto.Repo.t(), preloads :: term(), opts :: Keyword.t() ) :: structs_or_struct_or_nil when structs_or_struct_or_nil: [Ecto.Schema.t()] | Ecto.Schema.t() | nil
Preloads all associations on the given struct or structs.
This is similar to Ecto.Repo.preload/3
except it loads graph associations. It functions the same
as Ecto.Repo.preload/3
but has a slight syntax change.
This is needed because graph associations use a fake relation module to simulate the required behaviour. This means that due to a check in Ecto preloads, it won't match the queried structs. Therefore this function applies some logic to preload the data properly.
Parameters
structs_or_struct_or_nil
- A singular or list of structs to preload (if nil will return nil)repo
- the Ecto repo to usepreloads
- the preload fields (seeEcto.Repo.preload/3
for more info)opts
- the options to use (same asEcto.Repo.preload/3
Example
iex> Repo.all(User) |> ArangoXEcto.preload(Repo, [:my_content])
[%User{id: 123, my_content: [%Post{...}, %Comment{...}]}]
@spec schema_type(atom()) :: :document | :edge | nil
Returns the type of a module
This is just a shortcut to using edge?/1
, document?/1
and view?/1
. If it is none of them nil is returned.
Parameters
module
- the module to check
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 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.
Parameters
module
- the module to check
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
Returns true if a Schema is a view schema
Checks for the presence of the __view__/1
function on the module.
Parameters
module
- the module to check
@spec view_exists?(Ecto.Repo.t(), binary() | atom()) :: boolean()
Checks if a view exists
This will return true if the view exists in the database, otherwise false.
Parameters
repo
- The Ecto repo module to use for the queryview_name
- Name of the collection to check
Examples
Checking a document collection exists
iex> ArangoXEcto.view_exists?(Repo, :users_search)
true