View Source ArangoXEcto.Migration behaviour (ArangoX Ecto v2.0.0)
Modify the database through static migrations.
NOTE
ArangoXEcto can dynamically create collections for you and can be enabled by setting static: false
in your app config. Refer to ArangoXEcto
for more info. However, this is primarily
useful for dev applications and migrations should be used for production level applications.
Each migrations typically have an up
and down
function or just a change
function. If using
separate up
and down
functions, these will allow for the database to migrated forward or
rolled back. If using the change
function then the migrations for going forward are defined and
automatically reversed in the case of a rollback.
Migrations that have been executed in a database for ArangoXEcto are stored in a system collection
called _migrations
(this is a hidden system collection). This allows ArangoXEcto to track what
has already been run so that it knows what migrations can be run or rolled back. You can configure
the name of this table with the :migration_source
configuration option and the name of the
repository that manages migrations can be configured with :migration_repo
.
Two (or more) servers, one DB, oh no!!
You might be wondering what happens when there are two or more applications trying to run migrations on the database at once. If two or more instances of the database try to write to the database at the same time this could be a huge problem, causing unpredictable results. A lot of databases use locking as a mechanism to solve this. I.e. one connection will lock writing to the database so others cannot write. ArangoDB doesn't do this, so how can we solve this?
Luckily we don't have to worry about this as ArangoDB handles this for us through dynamic locks. Essentially if two connections try to write on the same part of the database then one of them will just fail as the first has acquired an exclusive lock.
Migration creation
Migration files are stored in the "priv/MY_REPO/migrations" (where MY_REPO is a lowercase
underscored name of your repository. For example, if you repo module was MyApp.MyRepo
then the
path would be "priv/my_repo/migrations" or if your repo module was MyApp.ThisIsTheCoolestRepo
then your migrations would be in "priv/this_is_the_coolest_repo/migrations". The directory can
be overridden when running the mix tasks or in the configuration.
Each migration filename will have a timestamp number followed by a unique name seperated by an underscore. For example if you wanted to add a users collection could put the migration in a file at "priv/repo/migrations/20240928200000_add_users_collection.exs, with contents:
defmodule Repo.Migrations.AddUsersCollection do
use ArangoXEcto.Migration
def up do
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
end
def down do
drop collection("users")
end
end
The up/0
function is responsible for migrating the database forward, while the down/0
function
is responsible for rolling back this migration. The down/0
function must always reverse the
up/0
action. Inside both of these functions we use various API methods defined in this module to
perform actions on the database. We can manage collections, indexes, views, analyzers and run
custom AQL commands.
Now that our migration is setup, all that is left is to execute it. While migrations can be run using code (and there are valid use cases for this, e.g. generating releases for production) we generally will use mix tasks. To do this run the following from the root directory of your project:
$ mix arango.migrate
If we wanted to reverse the migration we could run:
$ mix arango.rollback -n 1
We must always specify how many migrations to rollback. Whereas mix arango.migrate
will always
run all pending migrations
There is one other mix task that is used regularly and it saves us a lot of time. That is the mix arango.gen.migration
task. This task will generate a blank migration with boilerplate contents
and use the proper timestamps of when it was generated.
$ mix arango.gen.migration add_users_collection
You can now generate and run migrations successfully. You may be pretty proud of yourself, and you should be, but there is some more parts to it. You can learn all about migrations in ArangoXEcto in this module documentation.
IMPORTANT
Order matters!! Make sure you create collections before indexes and views, and analyzers before views if they are used. In general this is a good order to follow:
Analyzers > Collections > Indexes > Views
ArangoDB Schemas
ArangoDB can take a schema, but it is not strictly required, hence no fields need to be provided. The only required thing is the collection name. More info below on how to define fields. For example, the example provided in the previous section can be provided without defining the fields.
defmodule Repo.Migrations.AddUsersCollection do
use ArangoXEcto.Migration
def up do
create collection(:users)
end
def down do
drop collection("users")
end
end
This will work perfectly fine, however there is one caveat, documents in the collection will not
be validated. This means that the migration will only create the collection and not define a
JSONSchema for the collection. Schemas a generated using the ArangoXEcto.Migration.JsonSchema
module, however all documentation on how to use it can be found in this module.
Mix tasks
As seen in the example above, ArangoXEcto has a few built in mix tasks that can be used to help
with the development workflow. These tasks are similar to the ones found in ecto_sql
but are
adapted for the ArangoXEcto
context. To allow for using multiple database adapters together they
follow a similar but slightly different pattern, as seen below.
mix arango.gen.migration
- generates a migration that the user can fill in with particular commandsmix arango.migrate
- migrates a repositorymix arango.rollback
- rolls back a particular migration
For additional help on how to use these tasks you can run mix help COMMAND
where COMMAND
is
the command you need assistance with.
As mentioned previously, you can also run migrations using code using this module. If you think
you're pretty cool and know what you're doing you can also use the lower level APIs in
ArangoXEcto.Migrator
.
Change (combining up and down)
I know what you're thinking, "Ugh, do I really have to write an up/0
and down/0
function for
every single migration? Surely you could have just written some code the generate the down/0
version for me". Well yes, yes I did. That's what change/0
is for. You just write your regular
up/0
action and it will do some fancy footwork and figure out what the down/0
version would
be. For example, this would operate the same as seen above:
defmodule Repo.Migrations.AddUsersCollection do
use ArangoXEcto.Migration
def change do
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
end
end
Like a good salesman I only gave you half the story, there is one caveat when using this. Not all
commands are reversible and will raise an Ecto.MigrationError
.
A great example of a command that cannot be reversed is execute/1
but can easily be made
reversible by calling execute/2
instead. The first argument will be run on up/0
and the second
will be run on down/0
.
If you implement up/0
and down/0
they will take precedence over change/0
which won't be
invoked.
Field Types
The field types provided must match one of the types available in
ArangoXEcto.Migration.JsonSchema
. Any other option will raise an error. There is one caveat to
this, the sub_command
option is used through add_embed
and add_embed_many
(essentially a
shortcut for an array of many embeds).
The options that can be passed to each of the types can be found in the
ArangoXEcto.Migration.JsonSchema
docs.
Executing and flushing
Most functions won't be executed until the end of the relevant up
, change
or down
callback.
However, if you call any Ecto.Repo
function they will be executed immediately, unless called
inside an anonymous function passed to execute/1
.
You may want to ensure that all the previous steps have been executed before continuing. To do
this you can call the flush/0
function to execute everything before.
However flush/0
will raise if it would be called from change
function when doing a rollback.
To avoid that we recommend to use execute/2
with anonymous functions instead.
For more information and example usage please take a look at execute/2
function.
Repo configuration
Migrator configuration
The following options are not required but can be used to adjust how your migrations are stored and run.
:migration_source
- The name of the collection to store the migrations that have already been run. Only the version numbers (timestamps) are stored. The default is_migrations
. Whatever is specified it will be set as a system collection in ArangoDB. The convention is to prefix the collection name with an underscore (_). You can configure this as follows:config :my_app, MyApp.Repo, migration_source: "_custom_migrations"
:migration_repo
- This configures which repository the migrations will be stored in. By default this will be the given repository but it can be overridden. A possible use case of this is if you wanted all migrations to be stored in a particular database in ArangoDB.config :my_app, MyApp.Repo, migration_repo: MyApp.MigrationRepo
:priv
- the location where to store important assets, such as migrations. By default this will be "priv/my_reoo" for a repository module ofMyApp.MyRepo
and migrations would be placed in "priv/my_repo/migrations".:start_apps_before_migration
- A list of applications to be started before migrations are run. Used byArangoXEcto.Migrator.with_repo/3
and hence the migration task.config :my_app, MyApp.Repo, start_apps_before_migration: [:ssl, :some_custom_logger]
Migrations configuration
The previous section's options were focused on how migrations are run and stored, whereas the following options are focused on adjusting what values are stored. If you change these after your app goes into production it will cause unexpected behaviour.
:migration_timestamps
- Modifies the type and field names of:inserted_at
and:updated_at
. By default timestamps will be stored as:naive_datetime
but this, and the field names can be configured as seen below:config :my_app, MyApp.Repo, migration_timestamps: [ type: :utc_datetime, inserted_at: :created_at, updated_at: :changed_at ]
:migration_default_prefix
- By default no prefix is used, but you might want to. You can configure it using this option. This will be overriden by a prefix being passed at the command level.config :my_app, MyApp.Repo, migration_default_prefix: "my_migrations_prefix"
Comments
The schema stored in ArangoDB supports storing comments on the fields. You can specify this by
passing the :comment
option when using the add/3
, add_embed/3
, add_embed_many/3
and the
modify equivalents.
def up do
create collection(:users) do
add :name, :string, comment: "full name column"
add_embed :facts, comment: "my facts" do
add :statement, :string, comment: "represents the fact statement"
add :falsehood, :boolean, comment: "is the fact false?"
end
timestamps()
end
end
Prefixes
ArangoXEcto fully supports prefixes, which includes in migrations. Unlike in a database provider like PostgreSQL, ArangoDB doesn't have the notion of prefixes, hence prefixes actually create a separate database to create the separation.
def up do
create collection(:users, prefix: "base_app") 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], prefix: "base_app")
end
Notice here how the same prefix must be specified on both the collection creation and the index
creation. If you don't do this then they simply won't be created in the same database and will
likely result in an error. You can specify a default prefix, as mentioned above, using the
:migration_default_prefix
in your configuration.
Transaction Callbacks
Migrations are run in transactions. You may need to perform some actions after beginning a
transaction or before committing the migration. You can do this with the after_begin/0
and
before_commit/0
callbacks to your migration.
Sometimes you may want to use these callbacks for every migration and doing so can be quite
repetitive. You can solve this by implementing your own migration module that extends the
ArangoXEcto.Migration
module:
defmodule MyApp.Migration do
defmacro __using__(_) do
quote do
use ArangoXEcto.Migration
def after_begin() do
repo().query! "SOME ARANGO QUERY"
end
end
end
end
Then in your migrations you can replace use ArangoXEcto.Migration
with use MyApp.Migration
.
Example
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
Summary
Callbacks
Migration code to run immediately after the transaction is opened.
Migration code to run immediately before the transaction is closed.
Functions
Adds a field when creating or altering a collection.
Adds a field when creating or altering a collection with subfields.
Adds a field that has multiple instances of the object.
Adds a field if it does not exist yet when altering a collection.
Adds a link to a view.
Adds a primary sort to a view.
Adds a stored value to a view.
Alters a collection or view.
Represents an analyzer for deletion.
Creates an analyzer struct that can be passed to an action function.
Creates a collection struct that can be provided to one of the action functions.
Creates one of the following:
Creates a collection or view.
Same as create/1
except it will only create the object if it doesn't exist already.
Creates a collection or view if it doesn't exist.
Gets the migrator direction.
Drops one of the following:
Same as drop/1
except only drops if it exists.
Creates an edge collection struct
Executes arbitrary AQL.
Executes reversible AQL commands.
Executes queued migration commands
Creates an index struct that can be passed to an action function.
Modifies the type of a field when altering a collection.
Modifies a field when creating or altering a collection with subfields.
Modifies a field when creating or altering many objects with subfields.
Gets the migrator prefix.
Removes a field when altering a collection.
Removes a field in a reversible way when altering a collection.
Removes a field only if it exists in the schema
Removes a link when altering a view.
Removes a link in a reversable way when altering a view.
Renames a collection, view or a collection field
Gets the migrator repo.
Adds :inserted_at
and :updated_at
timestamp fields.
Shortcut for creating a unique index.
Creates a view struct that can be passed to an action function.
Callbacks
@callback after_begin() :: term()
Migration code to run immediately after the transaction is opened.
Keep in mind that it is treated like any normal migration code, and should consider both the up and down cases of the migration.
@callback before_commit() :: term()
Migration code to run immediately before the transaction is closed.
Keep in mind that it is treated like any normal migration code, and should consider both the up and down cases of the migration.
Functions
@spec add(atom(), ArangoXEcto.Migration.JsonSchema.field(), Keyword.t()) :: :ok
Adds a field when creating or altering a collection.
This function accepts certain JSON Schema types and depending on the type it will accept certain
options. The available types and options can be found in the
ArangoXEcto.Migration.JsonSchema.convert/2
function.
Examples
create collection("posts") do
add :title, :string, comment: "Some comment"
end
alter collection("posts") do
add :summary, :string
add :object, :map
add :age, :integer, min: 18, max: 99
end
Adds a field when creating or altering a collection with subfields.
This makes the field type an object in the JSON Schema with the sub fields and options. See
add/3
for available options for an object.
Example
add_embed :fields do
add :name, :string
add :type, :string
end
add_embed :likes, comment: "my likes" do
add :type, :string
add :number, :integer
end
Adds a field that has multiple instances of the object.
This represents a list of objects. This essentially wraps add_embed/3
's JSON Schema output with
an array so that multiple instances of the object is represented.
@spec add_if_not_exists(atom(), ArangoXEcto.Migration.JsonSchema.field(), Keyword.t()) :: :ok
Adds a field if it does not exist yet when altering a collection.
This is identical to add/3
except only creates it if it hasn't been already.
This is not reversible as existence can't be known beforehand.
Examples
alter collection("posts") do
add_if_not_exists :title, :string, comment: "Some comment"
end
@spec add_link(atom() | String.t(), ArangoXEcto.View.Link.t()) :: :ok
Adds a link to a view.
Uses a ArangoXEcto.View.Link
struct to define the link. See the module for more information.
Example
link "users", %Link{
includeAllFields: true,
fields: %{
name: %Link{
analyzers: [:text_en]
}
}
}
@spec add_sort(atom(), :asc | :desc) :: :ok
Adds a primary sort to a view.
This adds a sort to the view. TODO: add a description of it.
This accepts a field name and a sort direction (either :asc
or :desc
), defaults to :asc
.
@spec add_store(atom(), :none | :lz4) :: :ok
Adds a stored value to a view.
This adds a stored value to the view. TODO: add a description of it.
This accepts a field name and a compression (either :lz4
or :none
), defaults to :lz4
.
@spec alter(ArangoXEcto.Migration.Collection.t() | ArangoXEcto.Migration.View.t(), [ {:do, Macro.t()} ]) :: Macro.t()
Alters a collection or view.
Collection Examples
alter collection(:users) do
add :middle_name, :string
modify :people, :integer
rename :people, to: :num
remove :last_name
end
View Example
alter view(:user_search) do
add_link("new_test", %Link{
includeAllFields: true,
fields: %{
last_name: %Link{
analyzers: [:text_en]
}
}
})
remove_link("new_test")
end
@spec analyzer(atom() | String.t()) :: ArangoXEcto.Migration.Analyzer.t()
Represents an analyzer for deletion.
Creates an analyzer struct with only a name so that it can be used to delete an analyzer.
@spec analyzer( atom() | String.t(), ArangoXEcto.Migration.Analyzer.type(), [ArangoXEcto.Migration.Analyzer.feature()], map(), Keyword.t() ) :: ArangoXEcto.Migration.Analyzer.t()
Creates an analyzer struct that can be passed to an action function.
Parameters
:name
- the name of the analyzer to be created:type
- the type of the analyzer, available options areArangoXEcto.Migration.Analyzer.type/0
.:features
- a list of enabled features, available options areArangoXEcto.Migration.Analyzer.feature/0
.:properties
- additional options for the analyzer, dependant on the type. Refer to theArangoDB
docs. TODO: add link.
@spec collection(String.t(), [ArangoXEcto.Migration.Collection.collection_option()]) :: ArangoXEcto.Migration.Collection.t()
Creates a collection struct that can be provided to one of the action functions.
Options
Accepts an options parameter as the third argument. For available keys please refer to the ArangoDB API doc. The following options differ slightly:
:type
- accepts either:document
or:edge
. Default is:document
.:prefix
- the prefix for the collection. This will be the prefix database used for the collection.
Examples
iex> collection("users")
%ArangoXEcto.Migration.Collection{name: "users", type: 2)
iex> collection("users", type: :edge)
%ArangoXEcto.Migration.Collection{name: "users", type: 3)
iex> collection("users", keyOptions: %{type: :uuid})
%ArangoXEcto.Migration.Collection{name: "users", type: 2, keyOptions: %{type: :uuid})
@spec create(object) :: object when object: ArangoXEcto.Migration.Collection.t() | ArangoXEcto.Migration.Index.t() | ArangoXEcto.Migration.Analyzer.t()
Creates one of the following:
- an index
- a collection with no schema
- an analyzer
When reversing (in a change/0
running backwards), objects are only dropped
if they exist, and no errors are raised. To enforce dropping an object, use
drop/1
.
Examples
create index("users", [:name])
create collection("posts")
create analyzer(:norm_en, :norm, [:frequency, :position], %{
locale: "en",
accent: false,
case: :lower
})
@spec create(ArangoXEcto.Migration.Collection.t() | ArangoXEcto.Migration.View.t(), [ {:do, Macro.t()} ]) :: Macro.t()
Creates a collection or view.
Collection Example
create collection(:users) do
add :first_name, :string
add :last_name, :string, default: "Smith"
timestamps()
end
View Example
create view(:user_search) 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, :text_en]
}
}
})
end
@spec create_if_not_exists(object) :: object when object: ArangoXEcto.Migration.Collection.t() | ArangoXEcto.Migration.Index.t() | ArangoXEcto.Migration.Analyzer.t()
Same as create/1
except it will only create the object if it doesn't exist already.
Examples
create_if_not_exists index("users", [:name])
create_if_not_exists collection("posts")
create_if_not_exists analyzer(:norm_en, :norm, [:frequency, :position], %{
locale: "en",
accent: false,
case: :lower
})
@spec create_if_not_exists( ArangoXEcto.Migration.Collection.t() | ArangoXEcto.Migration.View.t(), [ {:do, Macro.t()} ] ) :: Macro.t()
Creates a collection or view if it doesn't exist.
Works just the same as create/2
but will raise an error
when the object already exists.
@spec direction() :: :up | :down
Gets the migrator direction.
@spec drop(object) :: object when object: ArangoXEcto.Migration.Collection.t() | ArangoXEcto.Migration.Index.t() | ArangoXEcto.Migration.View.t() | ArangoXEcto.Migration.Analyzer.t()
Drops one of the following:
- an index
- a collection
- a view
- an analyzer
Examples
drop index("users", [:name])
drop collection("posts")
drop view("users_search")
drop analyzer("users_search")
@spec drop_if_exists(object) :: object when object: ArangoXEcto.Migration.Collection.t() | ArangoXEcto.Migration.Index.t() | ArangoXEcto.Migration.View.t() | ArangoXEcto.Migration.Analyzer.t()
Same as drop/1
except only drops if it exists.
Does not raise an error if the specified collection or index does not exist.
Examples
drop_if_exists index("users", [:name])
drop_if_exists collection("posts")
drop_if_exists view("users_search")
drop_if_exists analyzer("users_search")
@spec edge(String.t(), [ArangoXEcto.Migration.Collection.collection_option()]) :: ArangoXEcto.Migration.Collection.t()
Creates an edge collection struct
Same as passing :edge
as the type to collection/3
.
Executes arbitrary AQL.
The argument is typically a string, containing the AQL command to be executed.
You can also run arbitrary code as part of your migrations by supplying an anonymous function. This is advantageous as it defers the execution of the anonymous function until after the migration callback is terminated (see Executing and flushing).
Reversible commands can be defined by calling execute/2
.
Examples
execute "FOR u IN `users` RETURN u.name"
execute(fn -> repo().update_all("posts", set: [published: true]) end)
Executes reversible AQL commands.
This is useful for database-specific functionality that does not
warrant special support in ArangoXEcto. The execute/2
form avoids having
having to define separate up/0
and down/0
blocks that each contain an execute/1
expression.
The allowed parameters are explained in execute/1
.
Examples
defmodule MyApp.MyMigration do
use ArangoXEcto.Migration
def change do
execute "FOR u IN `users` RETURN u", "FOR u IN `users` RETURN u"
execute(&execute_up/0, &execute_down/0)
end
defp execute_up, do: repo().query!("'Up query …';", [], [log: :info])
defp execute_down, do: repo().query!("'Down query …';", [], [log: :info])
end
Executes queued migration commands
@spec index(String.t(), [atom() | String.t()], [ ArangoXEcto.Migration.Index.index_option() ]) :: ArangoXEcto.Migration.Index.t()
Creates an index struct that can be passed to an action function.
Default index type is a hash. To change this pass the :type
option in options.
This will generate a name for the index if not provided. This allows for the dropping of the index.
Options
Options only apply to the creation of indexes and has no effect when using the drop/1
function.
:name
- The name of the index (usefull for phoenix constraints). Defaults to "idx_<collection>_<fields_separated_by_underscore>".:prefix
- the prefix for the collection. This will be the prefix database used for the index collection.:type
- The type of index to create- Accepts:
:fulltext
,:geo
,:hash
,:persistent
,:skiplist
or:ttl
- Accepts:
:unique
- If the index should be unique, defaults to false (hash, persistent & skiplist only):sparse
- If index should be spares, defaults to false (hash, persistent & skiplist only):deduplication
- If duplication of array values should be turned off, defaults to true (hash & skiplist only):minLength
- Minimum character length of words to index (fulltext only):geoJson
- If a geo-spatial index on a location is constructed and geoJson is true, then the order within the array is longitude followed by latitude (geo only):expireAfter
- Time in seconds after a document's creation it should count asexpired
(ttl only)
Examples
Create index on email field
iex> index("users", [:email])
%ArangoXEcto.Migration.Index{collection_name: "users", fields: [:email]}
Create dual index on email and ph_number fields with a specified name
iex> index("users", [:email, :ph_number], name: "my_cool_index")
%ArangoXEcto.Migration.Index{collection_name: "users", fields: [:email, :ph_number], name:
"my_cool_index"}
Create unique email index
iex> index("users", [:email], unique: true)
%ArangoXEcto.Migration.Index{collection_name: "users", fields: [:email], unique: true}
Create a geo index
iex> index("users", [:coordinates], type: :geo)
%ArangoXEcto.Migration.Index{collection_name: "users", fields: [:coordinates], type: :geo}
@spec modify(atom(), ArangoXEcto.Migration.JsonSchema.field(), Keyword.t()) :: :ok
Modifies the type of a field when altering a collection.
This command is not reversible unless the :from
option is provided. You want to specify all the
necessary options and types so that it can be rolled back properly.
See add/3
for more information on supported types.
Examples
alter collection("users") do
modify :name, :string
end
# Self rollback when using the :from option
alter collection("users") do
modify :name, :string, from: :integer
end
# Modify field with rollback options
alter collection("users") do
modify :name, :string, null: true, from: {:integer, null: false}
end
Options
Options are the same as add/3
but with the additional following option.
:from
- specifies the current type and options of the field.
Modifies a field when creating or altering a collection with subfields.
See modify/3
for options and more info.
Modifies a field when creating or altering many objects with subfields.
See modify_embed/3
for options and more info.
Gets the migrator prefix.
@spec remove(atom()) :: :ok
Removes a field when altering a collection.
If it doesn't exist it will simply be ignored.
This command is not reversible as Ecto does not know what type it should add
the field back as. See remove/3
as a reversible alternative.
Examples
alter collection("users") do
remove :name
end
@spec remove(atom(), ArangoXEcto.Migration.JsonSchema.field(), Keyword.t()) :: :ok
Removes a field in a reversible way when altering a collection.
type
and opts
are exactly the same as in add/3
, and
they are used when the command is reversed.
Examples
alter collection("users") do
remove :name, :string, min_length: 4
end
@spec remove_if_exists(atom(), ArangoXEcto.Migration.JsonSchema.field(), Keyword.t()) :: :ok
Removes a field only if it exists in the schema
type
and opts
are exactly the same as in add/3
, and
they are used when the command is reversed.
Examples
alter collection("users") do
remove_if_exists :name, :string, min_length: 4
end
Removes a link when altering a view.
If it doesn't exist it will simply be ignored.
This command is not reversible as Ecto does not know what type it should add
the field back as. See remove_link/2
as a reversible alternative.
Examples
alter view("user_search") do
remove_link "users"
end
@spec remove_link(atom() | String.t(), ArangoXEcto.View.Link.t()) :: :ok
Removes a link in a reversable way when altering a view.
link
is the same as in add_link/2
.
Examples
alter view("user_search") do
remove_link "users", %Link{
includeAllFields: true,
fields: %{
last_name: %Link{
analyzers: [:text_en]
}
}
}
end
@spec rename(object, [{:to, object}]) :: object when object: ArangoXEcto.Migration.Collection.t() | ArangoXEcto.Migration.View.t() | atom()
Renames a collection, view or a collection field
Examples
# rename a collection
rename collection("users"), to: collection("new_users")
# rename a view
rename view("user_search"), to: view("new_user_search")
alter collection("users") do
rename :name, to: :first_name
end
@spec repo() :: Ecto.Repo.t()
Gets the migrator repo.
Adds :inserted_at
and :updated_at
timestamp fields.
Those fields are of :naive_datetime
type and by default cannot be null. A
list of opts
can be given to customize the generated fields.
Following options will override the repo configuration specified by
:migration_timestamps
option.
Options
:inserted_at
- the name of the column for storing insertion times. Setting it tofalse
disables the column.:updated_at
- the name of the column for storing last-updated-at times. Setting it tofalse
disables the column.:type
- the type of the:inserted_at
and:updated_at
columns. Defaults to:naive_datetime
.
The rest of the options are passed to the fields
@spec unique_index(String.t(), [atom() | String.t()], [ ArangoXEcto.Migration.Index.index_option() ]) :: ArangoXEcto.Migration.Index.t()
Shortcut for creating a unique index.
Same as passing :unique as true to index/3
. See index/3
for more information.
@spec view(atom() | String.t(), [ArangoXEcto.Migration.View.view_option()]) :: ArangoXEcto.Migration.View.t()
Creates a view struct that can be passed to an action function.
This is similar to the collection/2
function except its representation is for a view.
Options
TODO: Add note about reading the docs for options.
:prefix
- the prefix for the view. This will be the prefix database used for the Arango search view.
Examples
iex> view("users_search")
%ArangoXEcto.Migration.View{name: "users_search")
iex> view("users_search", primarySortCompression: :none)
%ArangoXEcto.Migration.View{name: "users_search", primarySortCompression: :none)