View Source ArangoXEcto.Query (ArangoX Ecto v2.0.0)

ArangoDB specific Ecto.Query functions.

The all/1, delete_all/1 and update_all/1 functions in this module convert Ecto.Query structs into AQL syntax.

The Arango specific functions are available to support ArangoSearch questions and perform graph queries.

ArangoSearch queries

ArangoSearch queries can be performed through the search/3 and or_search/3 macros. For example, if you wanted to do a wildcard search for the title of your favorite movie you can do the following.

from(MoviesView)
|> search([mv], fragment("LIKE(?, ?)", mv.title, "%War%"))
|> Repo.all()

You can see that a fragment is used to incluse custom AQL. If you wanted to use a specific analyzer, for example for case-insensitive searches, you could so using a fragment also like below.

from(MoviesView)
|> search([mv], fragment("ANALYZER(LIKE(?, ?), \"norm_en\")", mv.title, "%war%"))
|> Repo.all()

Graph searches

You can do graph queries inside Ecto queries. To do this you just use the graph/5 macro provided. This will generate the required fragment to do graph queries.

For example, take the following AQL query.

FOR u IN 1..3 OUTBOUND "user/bob" friends
  RETURN u

You can represent that using the following Ecto query. Note that a select is required since Ecto needs to know what fields to get when querying on a fragment.

from(
  u in graph(1..3, :outbound, "user/bob", "friends"), 
    select: map(u, [:first_name, :last_name])
)
|> Repo.all()

Credit

This initial code for this module was used from https://github.com/ArangoDB-Community/arangodb_ecto/blob/master/lib/arangodb_ecto/query.ex. Credit for the initial code goes to mpoeter. Updates were made since to enable additional functionality and to work with the latest Ecto version.

Summary

Functions

Creates an AQL query to fetch all entries from the data store matching the given Ecto query.

Creates an AQL query to delete all entries from the data store matching the given Ecto query.

A OR search query expression.

An AND search query expression.

Creates an AQL query to update all entries from the data store matching the given Ecto query.

Functions

all(query)

@spec all(Ecto.Query.t()) :: binary()

Creates an AQL query to fetch all entries from the data store matching the given Ecto query.

delete_all(query)

@spec delete_all(Ecto.Query.t()) :: binary()

Creates an AQL query to delete all entries from the data store matching the given Ecto query.

graph(range, direction, source, edge_graph, opts \\ [])

(since 2.0.0) (macro)
@spec graph(Range.t(), :inbound | :outbound | :any, Macro.t(), Macro.t(), Keyword.t()) ::
  Macro.t()

Query an ArangoDB graph

This is a helper around searching a graph. This essentially generates a fragment that is used in the from or join part of the Ecto query.

You MUST provide a select in the query, otherwise an error be raised since Ecto can't work out what fields to use when a fragment is used.

There is a limitiation that you can't use the Ecto.Query.API.struct/2 function since the query is a fragment. If you need to load your results into a struct then you can use the ArangoXEcto.load/2 function.

Example

For example, lets take the following query.

FOR u IN 1..3 OUTBOUND "user/bob" friends
  RETURN u

You can represent that using the following Ecto query. Note that like you would in any other Ecto query, you need to pin any variables used.

from(
  u in graph(1..3, :outbound, "user/bob", "friends"), 
    select: map(u, [:first_name, :last_name])
)
|> Repo.all()

Providing options

You can also provide a keyword list as options as the last parameter to the macro.

FOR u IN 1..3 OUTBOUND "user/bob" friends OPTIONS { vertexCollections: ["users"] }
  RETURN u

This can be done using like the following.

from(u in graph(1..3, :outbound, "user/bob", "friends", vertexCollections: ["users"]))
|> select([u], map(u, [:first_name, :last_name]))
|> Repo.all()

Dynamically selecting fields

If you want to select all of the fields on a struct you can use the following code and pin the select_fields variable. Replace the User schema with the schema in question.

select_fields = Enum.map(User.__schema__(:fields), &User.__schema__(:field_source, &1))

or_search(query, binding \\ [], expr)

(since 1.3.0) (macro)

A OR search query expression.

Extention to the Ecto.Query api for arango searches.

This function is the same as ArangoXEcto.Query.search/3 except implements as an or clause. This also follows the same syntax as the Ecto.Query.or_where/3 function.

search(query, binding \\ [], expr)

(since 1.3.0) (macro)

An AND search query expression.

Extention to the Ecto.Query api for arango searches.

The expression syntax is exactly the same as the regular Ecto.Query.where/3 clause. Refer to that for more info on syntax or for advanced AQL queries see the section below.

You will need to import this function like you do for Ecto.Query.

Implementation

This will store the search in the Ecto query where clause with a custom search operation. This is to prevent having to create a seperate query type. When converting the Ecto query to an AQL query, this is caught and changed into an AQL SEARCH expression.

Using Analyzers and advanced AQL

To save having to learn some new kind of query format for so many different possible search scenarios, you can just use AQL directly in. This is powered by the Ecto.Query.API.fragment/1 function.

Below is an example of how you can implement using a custom analyzer:

from(UsersView)
|> search([uv], fragment("ANALYZER(? == ?, \"identity\")", uv.first_name, "John"))
|> Repo.all()

update_all(query)

@spec update_all(Ecto.Query.t()) :: binary()

Creates an AQL query to update all entries from the data store matching the given Ecto query.