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.

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 of MyApp.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 by ArangoXEcto.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

after_begin()

(optional)
@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.

before_commit()

(optional)
@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

add(field, type, opts \\ [])

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

add_embed(field, opts \\ [], list)

(macro)
@spec add_embed(atom(), Keyword.t(), [{:do, Macro.t()}]) :: Macro.t()

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

add_embed_many(field, opts \\ [], list)

(macro)
@spec add_embed_many(atom(), Keyword.t(), [{:do, Macro.t()}]) :: Macro.t()

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.

add_if_not_exists(field, type, opts \\ [])

@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

add_link(schema_name, link)

@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]
    }
  }
}

add_sort(field, direction \\ :asc)

@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.

add_store(fields, compression \\ :lz4)

@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.

alter(object, list)

(macro)

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

analyzer(name)

@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.

analyzer(name, type, features, properties \\ %{}, opts \\ [])

Creates an analyzer struct that can be passed to an action function.

Parameters

collection(collection_name, opts \\ [])

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})

create(collection)

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
  })

create(object, list)

(macro)

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

create_if_not_exists(index)

@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
  })

create_if_not_exists(object, list)

(macro)
@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.

direction()

@spec direction() :: :up | :down

Gets the migrator direction.

drop(object)

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")

drop_if_exists(object)

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")

edge(edge_name, opts \\ [])

Creates an edge collection struct

Same as passing :edge as the type to collection/3.

execute(command)

@spec execute(String.t() | function()) :: :ok

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)

execute(up, down)

@spec execute(String.t() | function(), String.t() | function()) :: :ok

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

flush()

(macro)

Executes queued migration commands

index(collection_name, fields, opts \\ [])

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
  • :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 as expired (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}

modify(field, type, opts \\ [])

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.

modify_embed(field, opts \\ [], list)

(macro)
@spec modify_embed(atom(), Keyword.t(), [{:do, Macro.t()}]) :: Macro.t()

Modifies a field when creating or altering a collection with subfields.

See modify/3 for options and more info.

modify_embed_many(field, opts \\ [], list)

(macro)
@spec modify_embed_many(atom(), Keyword.t(), [{:do, Macro.t()}]) :: Macro.t()

Modifies a field when creating or altering many objects with subfields.

See modify_embed/3 for options and more info.

prefix()

Gets the migrator prefix.

remove(field)

@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

remove(field, type, opts \\ [])

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

remove_if_exists(field, type, opts \\ [])

@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

remove_link(schema_name)

@spec remove_link(atom() | String.t()) :: :ok

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

remove_link(schema_name, link)

@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

rename(collection_current, list)

@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

repo()

@spec repo() :: Ecto.Repo.t()

Gets the migrator repo.

timestamps(opts \\ [])

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 to false disables the column.
  • :updated_at - the name of the column for storing last-updated-at times. Setting it to false 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

unique_index(collection, fields, opts \\ [])

Shortcut for creating a unique index.

Same as passing :unique as true to index/3. See index/3 for more information.

view(name, opts \\ [])

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)