diff --git a/Documentation/Books/Drivers/JS/Reference/Database/README.md b/Documentation/Books/Drivers/JS/Reference/Database/README.md index a46ddc5eed..e1daf51772 100644 --- a/Documentation/Books/Drivers/JS/Reference/Database/README.md +++ b/Documentation/Books/Drivers/JS/Reference/Database/README.md @@ -11,11 +11,11 @@ If _config_ is a string, it will be interpreted as _config.url_. **Arguments** -* **config**: `Object` (optional) +- **config**: `Object` (optional) An object with the following properties: - * **url**: `string | Array` (Default: `http://localhost:8529`) + - **url**: `string | Array` (Default: `http://localhost:8529`) Base URL of the ArangoDB server or list of server URLs. @@ -37,14 +37,14 @@ If _config_ is a string, it will be interpreted as _config.url_. } ``` - * **isAbsolute**: `boolean` (Default: `false`) + - **isAbsolute**: `boolean` (Default: `false`) If this option is explicitly set to `true`, the _url_ will be treated as the absolute database path. This is an escape hatch to allow using arangojs with database APIs exposed with a reverse proxy and makes it impossible to switch databases with _useDatabase_ or using _acquireHostList_. - * **arangoVersion**: `number` (Default: `30000`) + - **arangoVersion**: `number` (Default: `30000`) Value of the `x-arango-version` header. This should match the lowest version of ArangoDB you expect to be using. The format is defined as @@ -58,14 +58,14 @@ If _config_ is a string, it will be interpreted as _config.url_. not available on every major version of ArangoDB as indicated in their descriptions below (e.g. _collection.first_, _collection.bulkUpdate_). - * **headers**: `Object` (optional) + - **headers**: `Object` (optional) An object with additional headers to send with every request. Header names should always be lowercase. If an `"authorization"` header is provided, it will be overridden when using _useBasicAuth_ or _useBearerAuth_. - * **agent**: `Agent` (optional) + - **agent**: `Agent` (optional) An http Agent instance to use for connections. @@ -75,7 +75,7 @@ If _config_ is a string, it will be interpreted as _config.url_. This option has no effect when using the browser version of arangojs. - * **agentOptions**: `Object` (Default: see below) + - **agentOptions**: `Object` (Default: see below) An object with options for the agent. This will be ignored if _agent_ is also provided. @@ -92,15 +92,41 @@ If _config_ is a string, it will be interpreted as _config.url_. additional options to the underlying calls of the [`xhr`](https://www.npmjs.com/package/xhr) module. - * **loadBalancingStrategy**: `string` (Default: `"NONE"`) + - **loadBalancingStrategy**: `string` (Default: `"NONE"`) - Determines the behaviour when multiple URLs are provided: + Determines the behavior when multiple URLs are provided: - * `NONE`: No load balancing. All requests will be handled by the first + - `NONE`: No load balancing. All requests will be handled by the first URL in the list until a network error is encountered. On network error, arangojs will advance to using the next URL in the list. - * `ONE_RANDOM`: Randomly picks one URL from the list initially, then + - `ONE_RANDOM`: Randomly picks one URL from the list initially, then behaves like `NONE`. - * `ROUND_ROBIN`: Every sequential request uses the next URL in the list. + - `ROUND_ROBIN`: Every sequential request uses the next URL in the list. + +## database.close + +`database.close(): void` + +Closes all active connections of the database instance. +Can be used to clean up idling connections during longer periods of inactivity. + +**Note**: This method currently has no effect in the browser version of arangojs. + +**Examples** + +```js +const db = new Database(); +const sessions = db.collection("sessions"); +// Clean up expired sessions once per hour +setInterval(async () => { + await db.query(aql` + FOR session IN ${sessions} + FILTER session.expires < DATE_NOW() + REMOVE session IN ${sessions} + `); + // Make sure to close the connections because they're no longer used + db.close(); +}, 1000 * 60 * 60); +``` diff --git a/Documentation/Books/Drivers/Java/Reference/Cursor.md b/Documentation/Books/Drivers/Java/Reference/Cursor.md index 43d67e9481..ad733a6b8d 100644 --- a/Documentation/Books/Drivers/Java/Reference/Cursor.md +++ b/Documentation/Books/Drivers/Java/Reference/Cursor.md @@ -7,7 +7,7 @@ _ArangoCursor_ instances provide an abstraction over the HTTP API's limitations. ArangoDB arango = new ArangoDB.Builder().build(); ArangoDatabase db = arango.db("myDB"); -ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", null, null, Integer.class); +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class); // query result list: [1, 2, 3, 4, 5] Integer value = cursor.next(); assertThat(value, is(1)); @@ -22,6 +22,16 @@ ArangoCursor.hasNext() : boolean Returns _true_ if the cursor has more elements in its current batch of results or the cursor on the server has more batches. +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +boolean hasNext = cursor.hasNext(); +``` + ## ArangoCursor.next ``` @@ -30,6 +40,213 @@ ArangoCursor.next() : T Returns the next element of the query result. If the current element is the last element of the batch and the cursor on the server provides more batches, the next batch is fetched from the server. +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +Integer value = cursor.next(); +assertThat(value, is(1)); +``` + +## ArangoCursor.first + +``` +ArangoCursor.first() : T +``` + +Returns the first element or {@code null} if no element exists. + +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("RETURN 1", Integer.class) +Integer value = cursor.first(); +assertThat(value, is(1)); +``` + +## ArangoCursor.foreach + +``` +ArangoCursor.foreach(Consumer action) : void +``` + +Performs the given action for each element of the _ArangoIterable_ + +**Arguments** + +- **action**: `Consumer` + + A action to perform on the elements + +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +cursor.foreach(e -> { + // remaining results: [1, 2, 3, 4, 5] +}); +``` + +## ArangoCursor.map + +``` +ArangoCursor.map(Function mapper) : ArangoIterable +``` + +Returns a _ArangoIterable_ consisting of the results of applying the given function to the elements of this _ArangoIterable_. + +**Arguments** + +- **mapper**: `Function` + + A function to apply to each element + +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +cursor.map(e -> e * 10).foreach(e -> { + // remaining results: [10, 20, 30, 40, 50] +}); +``` + +## ArangoCursor.filter + +``` +ArangoCursor.filter(Predicate predicate) : ArangoIterable +``` + +**Arguments** + +- **predicate**: `Predicate` + + A predicate to apply to each element to determine if it should be included + +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +cursor.filter(e -> e < 4).foreach(e -> { + // remaining results: [1, 2, 3] +}); +``` + +## ArangoCursor.anyMatch + +``` +ArangoCursor.anyMatch(Predicate predicate) : boolean +``` + +Returns whether any elements of this _ArangoIterable_ match the provided predicate. + +**Arguments** + +- **predicate**: `Predicate` + + A predicate to apply to elements of this {@code ArangoIterable} + +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +boolean match = cursor.anyMatch(e -> e == 3); +assertThat(match, is(true)); +``` + +## ArangoCursor.allMatch + +``` +ArangoCursor.anyMatch(Predicate predicate) : boolean +``` + +Returns whether all elements of this _ArangoIterable_ match the provided predicate. + +**Arguments** + +- **predicate**: `Predicate` + + A predicate to apply to elements of this {@code ArangoIterable} + +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +boolean match = cursor.allMatch(e -> e <= 5); +assertThat(match, is(true)); +``` + +## ArangoCursor.noneMatch + +``` +ArangoCursor.noneMatch(Predicate predicate) : boolean +``` + +Returns whether no elements of this _ArangoIterable_ match the provided predicate. + +**Arguments** + +- **predicate**: `Predicate` + + A predicate to apply to elements of this {@code ArangoIterable} + +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +boolean match = cursor.noneMatch(e -> e > 5); +assertThat(match, is(true)); +``` + +## ArangoCursor.collectInto + +``` +ArangoCursor.collectInto(R target) : R +``` + +**Arguments** + +Iterates over all elements of this {@code ArangoIterable} and adds each to the given target. + +- **target**: `R >` + + The collection to insert into + +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +Collection list = cursor.collectInto(new ArrayList()); +// -- or -- +Collection set = cursor.collectInto(new HashSet()); +``` + ## ArangoCursor.iterator ``` @@ -38,6 +255,16 @@ ArangoCursor.iterator() : Iterator Returns an iterator over elements of the query result. +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +Iterator iterator = cursor.iterator(); +``` + ## ArangoCursor.asListRemaining ``` @@ -46,6 +273,16 @@ ArangoCursor.asListRemaining() : List Returns the remaining results as a _List_. +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +Collection list = cursor.asListRemaining(); +``` + ## ArangoCursor.getCount ``` @@ -54,6 +291,37 @@ ArangoCursor.getCount() : Integer Returns the total number of result documents available (only available if the query was executed with the _count_ attribute set) +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", new AqlQueryOptions().count(true), Integer.class) +Integer count = cursor.getCount(); +assertThat(count, is(5)); +``` + +## ArangoCursor.count + +``` +ArangoCursor.count() : long +``` + +Returns the count of elements of this _ArangoIterable_. + +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +long count = cursor.filter(e -> e < 4).count(); +// remaining results: [1, 2, 3] +asserThat(count, is(3L)); +``` + ## ArangoCursor.getStats ``` @@ -62,6 +330,16 @@ ArangoCursor.getStats() : Stats Returns extra information about the query result. For data-modification queries, the stats will contain the number of modified documents and the number of documents that could not be modified due to an error (if ignoreErrors query option is specified); +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +Stats stats = cursor.getStats(); +``` + ## ArangoCursor.getWarnings ``` @@ -70,6 +348,16 @@ ArangoCursor.getWarnings() : Collection Returns warnings which the query could have been produced. +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +Collection warnings = cursor.getWarnings(); +``` + ## ArangoCursor.isCached ``` @@ -77,3 +365,13 @@ ArangoCursor.isCached() : boolean ``` Iindicating whether the query result was served from the query cache or not. + +**Examples** + +```Java +ArangoDB arango = new ArangoDB.Builder().build(); +ArangoDatabase db = arango.db("myDB"); + +ArangoCursor cursor = db.query("FOR x IN 1..5 RETURN x", Integer.class) +boolean cached = cursor.isCached(); +``` diff --git a/Documentation/Books/Drivers/SpringData/GettingStarted/README.md b/Documentation/Books/Drivers/SpringData/GettingStarted/README.md index ccca4da0be..0f0e9440e0 100644 --- a/Documentation/Books/Drivers/SpringData/GettingStarted/README.md +++ b/Documentation/Books/Drivers/SpringData/GettingStarted/README.md @@ -1,114 +1,114 @@ -# Spring Data ArangoDB - Getting Started - -## Supported versions - -| Spring Data ArangoDB | Spring Data | ArangoDB | -|----------------------|-------------|----------------| -| 1.0.0 | 1.13.x | 3.0*, 3.1, 3.2 | -| 2.0.0 | 2.0.x | 3.0*, 3.1, 3.2 | - -Spring Data ArangoDB requires ArangoDB 3.0 or higher - which you can download [here](https://www.arangodb.com/download/) - and Java 8 or higher. - -**Note**: ArangoDB 3.0 does not support the default transport protocol [VelocyStream](https://github.com/arangodb/velocystream). A manual switch to HTTP is required. See chapter [configuration](#configuration). Also ArangoDB 3.0 does not support geospatial queries. - -## Maven - -To use Spring Data ArangoDB in your project, your build automation tool needs to be configured to include and use the Spring Data ArangoDB dependency. Example with Maven: - -```xml - - com.arangodb - arangodb-spring-data - {version} - -``` - -There is a [demonstration app](https://github.com/arangodb/spring-data-demo), which contains common use cases and examples of how to use Spring Data ArangoDB's functionality. - -## Configuration - -You can use Java to configure your Spring Data environment as show below. Setting up the underlying driver (`ArangoDB.Builder`) with default configuration automatically loads a properties file `arangodb.properties`, if it exists in the classpath. - -```java -@Configuration -@EnableArangoRepositories(basePackages = { "com.company.mypackage" }) -public class MyConfiguration extends AbstractArangoConfiguration { - - @Override - public ArangoDB.Builder arango() { - return new ArangoDB.Builder(); - } - - @Override - public String database() { - // Name of the database to be used - return "example-database"; - } - -} -``` - -The driver is configured with some default values: - -property-key | description | default value --------------|-------------|-------------- -arangodb.host | ArangoDB host | 127.0.0.1 -arangodb.port | ArangoDB port | 8529 -arangodb.timeout | socket connect timeout(millisecond) | 0 -arangodb.user | Basic Authentication User | -arangodb.password | Basic Authentication Password | -arangodb.useSsl | use SSL connection | false - -To customize the configuration, the parameters can be changed in the Java code. - -```java -@Override -public ArangoDB.Builder arango() { - ArangoDB.Builder arango = new ArangoDB.Builder() - .host("127.0.0.1") - .port(8429) - .user("root"); - return arango; -} -``` - -In addition you can use the *arangodb.properties* or a custom properties file to supply credentials to the driver. - -*Properties file* -``` -arangodb.host=127.0.0.1 -arangodb.port=8529 -# arangodb.hosts=127.0.0.1:8529 could be used instead -arangodb.user=root -arangodb.password= -``` - -*Custom properties file* -```java -@Override -public ArangoDB.Builder arango() { - InputStream in = MyClass.class.getResourceAsStream("my.properties"); - ArangoDB.Builder arango = new ArangoDB.Builder() - .loadProperties(in); - return arango; -} -``` - -**Note**: When using ArangoDB 3.0 it is required to set the transport protocol to HTTP and fetch the dependency `org.apache.httpcomponents:httpclient`. - -```java -@Override -public ArangoDB.Builder arango() { - ArangoDB.Builder arango = new ArangoDB.Builder() - .useProtocol(Protocol.HTTP_JSON); - return arango; -} -``` -```xml - - org.apache.httpcomponents - httpclient - 4.5.1 - -``` +# Spring Data ArangoDB - Getting Started + +## Supported versions + +| Spring Data ArangoDB | Spring Data | ArangoDB | +|----------------------|-------------|----------------| +| 1.0.0 | 1.13.x | 3.0*, 3.1, 3.2 | +| 2.0.0 | 2.0.x | 3.0*, 3.1, 3.2 | + +Spring Data ArangoDB requires ArangoDB 3.0 or higher - which you can download [here](https://www.arangodb.com/download/) - and Java 8 or higher. + +**Note**: ArangoDB 3.0 does not support the default transport protocol [VelocyStream](https://github.com/arangodb/velocystream). A manual switch to HTTP is required. See chapter [configuration](#configuration). Also ArangoDB 3.0 does not support geospatial queries. + +## Maven + +To use Spring Data ArangoDB in your project, your build automation tool needs to be configured to include and use the Spring Data ArangoDB dependency. Example with Maven: + +```xml + + com.arangodb + arangodb-spring-data + {version} + +``` + +There is a [demonstration app](https://github.com/arangodb/spring-data-demo), which contains common use cases and examples of how to use Spring Data ArangoDB's functionality. + +## Configuration + +You can use Java to configure your Spring Data environment as show below. Setting up the underlying driver (`ArangoDB.Builder`) with default configuration automatically loads a properties file `arangodb.properties`, if it exists in the classpath. + +```java +@Configuration +@EnableArangoRepositories(basePackages = { "com.company.mypackage" }) +public class MyConfiguration extends AbstractArangoConfiguration { + + @Override + public ArangoDB.Builder arango() { + return new ArangoDB.Builder(); + } + + @Override + public String database() { + // Name of the database to be used + return "example-database"; + } + +} +``` + +The driver is configured with some default values: + +property-key | description | default value +-------------|-------------|-------------- +arangodb.host | ArangoDB host | 127.0.0.1 +arangodb.port | ArangoDB port | 8529 +arangodb.timeout | socket connect timeout(millisecond) | 0 +arangodb.user | Basic Authentication User | +arangodb.password | Basic Authentication Password | +arangodb.useSsl | use SSL connection | false + +To customize the configuration, the parameters can be changed in the Java code. + +```java +@Override +public ArangoDB.Builder arango() { + ArangoDB.Builder arango = new ArangoDB.Builder() + .host("127.0.0.1") + .port(8429) + .user("root"); + return arango; +} +``` + +In addition you can use the *arangodb.properties* or a custom properties file to supply credentials to the driver. + +*Properties file* +``` +arangodb.host=127.0.0.1 +arangodb.port=8529 +# arangodb.hosts=127.0.0.1:8529 could be used instead +arangodb.user=root +arangodb.password= +``` + +*Custom properties file* +```java +@Override +public ArangoDB.Builder arango() { + InputStream in = MyClass.class.getResourceAsStream("my.properties"); + ArangoDB.Builder arango = new ArangoDB.Builder() + .loadProperties(in); + return arango; +} +``` + +**Note**: When using ArangoDB 3.0 it is required to set the transport protocol to HTTP and fetch the dependency `org.apache.httpcomponents:httpclient`. + +```java +@Override +public ArangoDB.Builder arango() { + ArangoDB.Builder arango = new ArangoDB.Builder() + .useProtocol(Protocol.HTTP_JSON); + return arango; +} +``` +```xml + + org.apache.httpcomponents + httpclient + 4.5.1 + +``` diff --git a/Documentation/Books/Drivers/SpringData/README.md b/Documentation/Books/Drivers/SpringData/README.md index 8ea33ea1e3..a1d68f2bdf 100644 --- a/Documentation/Books/Drivers/SpringData/README.md +++ b/Documentation/Books/Drivers/SpringData/README.md @@ -1,14 +1,14 @@ -# Spring Data ArangoDB - -- [Getting Started](GettingStarted/README.md) -- [Reference](Reference/README.md) - -## Learn more - -- [ArangoDB](https://www.arangodb.com/) -- [Demo](https://github.com/arangodb/spring-data-demo) -- [JavaDoc 1.0.0](http://arangodb.github.io/spring-data/javadoc-1_0/index.html) -- [JavaDoc 2.0.0](http://arangodb.github.io/spring-data/javadoc-2_0/index.html) -- [JavaDoc Java driver](http://arangodb.github.io/arangodb-java-driver/javadoc-4_3/index.html) -- [Changelog](https://github.com/arangodb/spring-data/blob/master/ChangeLog.md#changelog) +# Spring Data ArangoDB + +- [Getting Started](GettingStarted/README.md) +- [Reference](Reference/README.md) + +## Learn more + +- [ArangoDB](https://www.arangodb.com/) +- [Demo](https://github.com/arangodb/spring-data-demo) +- [JavaDoc 1.0.0](http://arangodb.github.io/spring-data/javadoc-1_0/index.html) +- [JavaDoc 2.0.0](http://arangodb.github.io/spring-data/javadoc-2_0/index.html) +- [JavaDoc Java driver](http://arangodb.github.io/arangodb-java-driver/javadoc-4_3/index.html) +- [Changelog](https://github.com/arangodb/spring-data/blob/master/ChangeLog.md#changelog) diff --git a/Documentation/Books/Drivers/SpringData/Reference/README.md b/Documentation/Books/Drivers/SpringData/Reference/README.md index 09b6c1d4ad..86c0b0b0be 100644 --- a/Documentation/Books/Drivers/SpringData/Reference/README.md +++ b/Documentation/Books/Drivers/SpringData/Reference/README.md @@ -1,778 +1,778 @@ -# Spring Data ArangoDB - Reference - -# Template - -With `ArangoTemplate` Spring Data ArangoDB offers a central support for interactions with the database over a rich feature set. It mostly offers the features from the ArangoDB Java driver with additional exception translation from the drivers exceptions to the Spring Data access exceptions inheriting the `DataAccessException` class. -The `ArangoTemplate` class is the default implementation of the operations interface `ArangoOperations` which developers of Spring Data are encouraged to code against. - -# Repositories - -## Introduction - -Spring Data Commons provides a composable repository infrastructure which Spring Data ArangoDB is built on. These allow for interface-based composition of repositories consisting of provided default implementations for certain interfaces (like `CrudRepository`) and custom implementations for other methods. - -## Instantiating - -Instances of a Repository are created in Spring beans through the auto-wired mechanism of Spring. - -```java -public class MySpringBean { - - @Autowired - private MyRepository rep; - -} -``` - -## Return types - -The method return type for single results can be a primitive type, a domain class, `Map`, `BaseDocument`, `BaseEdgeDocument`, `Optional`, `GeoResult`. -The method return type for multiple results can additionally be `ArangoCursor`, `Iterable`, `Collection`, `List`, `Set`, `Page`, `Slice`, `GeoPage`, `GeoResults` where Type can be everything a single result can be. - -## Query methods - -Queries using [ArangoDB Query Language (AQL)](https://docs.arangodb.com/current/AQL/index.html) can be supplied with the `@Query` annotation on methods. `AqlQueryOptions` can also be passed to the driver, as an argument anywhere in the method signature. - -There are three ways of passing bind parameters to the query in the query annotation. - -Using number matching, arguments will be substituted into the query in the order they are passed to the query method. - -```java -public interface MyRepository extends Repository{ - - @Query("FOR c IN customers FILTER c.name == @0 AND c.surname == @2 RETURN c") - ArangoCursor query(String name, AqlQueryOptions options, String surname); - -} -``` - -With the `@Param` annotation, the argument will be placed in the query at the place corresponding to the value passed to the `@Param` annotation. - -```java -public interface MyRepository extends Repository{ - - @Query("FOR c IN customers FILTER c.name == @name AND c.surname == @surname RETURN c") - ArangoCursor query(@Param("name") String name, @Param("surname") String surname); - -} -``` - - In addition you can use a parameter of type `Map` annotated with `@BindVars` as your bind parameters. You can then fill the map with any parameter used in the query. (see [here](https://docs.arangodb.com/3.1/AQL/Fundamentals/BindParameters.html#bind-parameters) for more Information about Bind Parameters). - -```java -public interface MyRepository extends Repository{ - - @Query("FOR c IN customers FILTER c.name == @name AND c.surname = @surname RETURN c") - ArangoCursor query(@BindVars Map bindVars); - -} -``` - -A mixture of any of these methods can be used. Parameters with the same name from an `@Param` annotation will override those in the `bindVars`. - -```java -public interface MyRepository extends Repository{ - - @Query("FOR c IN customers FILTER c.name == @name AND c.surname = @surname RETURN c") - ArangoCursor query(@BindVars Map bindVars, @Param("name") String name); - -} -``` - -## Named queries -An alternative to using the `@Query` annotation on methods is specifying them in a separate `.properties` file. The default path for the file is `META-INF/arango-named-queries.properties` and can be changed with the `EnableArangoRepositories#namedQueriesLocation()` setting. The entries in the properties file must adhere to the following convention: `{simple entity name}.{method name} = {query}`. Let's assume we have the following repository interface: - -```java -package com.arangodb.repository; - -public interface CustomerRepository extends ArangoRepository { - Customer findByUsername(@Param("username") String username); -} -``` - -The corresponding `arango-named-queries.properties` file looks like this: - -```properties -Customer.findByUsername = FOR c IN customers FILTER c.username == @username RETURN c -``` - -The queries specified in the properties file are no different than the queries that can be defined with the `@Query` annotation. The only difference is that the queries are in one place. If there is a `@Query` annotation present and a named query defined, the query in the `@Query` annotation takes precedence. - -## Derived queries - -Spring Data ArangoDB supports queries derived from methods names by splitting it into its semantic parts and converting into AQL. The mechanism strips the prefixes `find..By`, `get..By`, `query..By`, `read..By`, `stream..By`, `count..By`, `exists..By`, `delete..By`, `remove..By` from the method and parses the rest. The By acts as a separator to indicate the start of the criteria for the query to be built. You can define conditions on entity properties and concatenate them with `And` and `Or`. - -The complete list of part types for derived methods is below, where doc is a document in the database - -Keyword | Sample | Predicate -----------|----------------|-------- -IsGreaterThan, GreaterThan, After | findByAgeGreaterThan(int age) | doc.age > age -IsGreaterThanEqual, GreaterThanEqual | findByAgeIsGreaterThanEqual(int age) | doc.age >= age -IsLessThan, LessThan, Before | findByAgeIsLessThan(int age) | doc.age < age -IsLessThanEqualLessThanEqual | findByAgeLessThanEqual(int age) | doc.age <= age -IsBetween, Between | findByAgeBetween(int lower, int upper) | lower < doc.age < upper -IsNotNull, NotNull | findByNameNotNull() | doc.name != null -IsNull, Null | findByNameNull() | doc.name == null -IsLike, Like | findByNameLike(String name) | doc.name LIKE name -IsNotLike, NotLike | findByNameNotLike(String name) | NOT(doc.name LIKE name) -IsStartingWith, StartingWith, StartsWith | findByNameStartsWith(String prefix) | doc.name LIKE prefix -IsEndingWith, EndingWith, EndsWith | findByNameEndingWith(String suffix) | doc.name LIKE suffix -Regex, MatchesRegex, Matches | findByNameRegex(String pattern) | REGEX_TEST(doc.name, name, ignoreCase) -(No Keyword) | findByFirstName(String name) | doc.name == name -IsTrue, True | findByActiveTrue() | doc.active == true -IsFalse, False | findByActiveFalse() | doc.active == false -Is, Equals | findByAgeEquals(int age) | doc.age == age -IsNot, Not | findByAgeNot(int age) | doc.age != age -IsIn, In | findByNameIn(String[] names) | doc.name IN names -IsNotIn, NotIn | findByNameIsNotIn(String[] names) | doc.name NOT IN names -IsContaining, Containing, Contains | findByFriendsContaining(String name) | name IN doc.friends -IsNotContaining, NotContaining, NotContains | findByFriendsNotContains(String name) | name NOT IN doc.friends -Exists | findByFriendNameExists() | HAS(doc.friend, name) - - -```java -public interface MyRepository extends Repository { - - // FOR c IN customers FILTER c.name == @0 RETURN c - ArangoCursor findByName(String name); - ArangoCursor getByName(String name); - - // FOR c IN customers - // FILTER c.name == @0 && c.age == @1 - // RETURN c - ArangoCursor findByNameAndAge(String name, int age); - - // FOR c IN customers - // FILTER c.name == @0 || c.age == @1 - // RETURN c - ArangoCursor findByNameOrAge(String name, int age); -} -``` - -You can apply sorting for one or multiple sort criteria by appending `OrderBy` to the method and `Asc` or `Desc` for the directions. - -```java -public interface MyRepository extends Repository { - - // FOR c IN customers - // FILTER c.name == @0 - // SORT c.age DESC RETURN c - ArangoCursor getByNameOrderByAgeDesc(String name); - - // FOR c IN customers - // FILTER c.name = @0 - // SORT c.name ASC, c.age DESC RETURN c - ArangoCursor findByNameOrderByNameAscAgeDesc(String name); - -} -``` - -### Geospatial queries - -Geospatial queries are a subsection of derived queries. To use a geospatial query on a collection, a geo index must exist on that collection. A geo index can be created on a field which is a two element array, corresponding to latitude and longitude coordinates. - -As a subsection of derived queries, geospatial queries support all the same return types, but also support the three return types `GeoPage, GeoResult and Georesults`. These types must be used in order to get the distance of each document as generated by the query. - -There are two kinds of geospatial query, Near and Within. Near sorts documents by distance from the given point, while within both sorts and filters documents, returning those within the given distance range or shape. - -```java -public interface MyRepository extends Repository { - - GeoResult getByLocationNear(Point point); - - GeoResults findByLocationWithinOrLocationWithin(Box box, Polygon polygon); - - //Equivalent queries - GeoResults findByLocationWithinOrLocationWithin(Point point, int distance); - GeoResults findByLocationWithinOrLocationWithin(Point point, Distance distance); - GeoResults findByLocationWithinOrLocationWithin(Circle circle); - -} -``` - -## Property expression - -Property expressions can refer only to direct and nested properties of the managed domain class. The algorithm checks the domain class for the entire expression as the property. If the check fails, the algorithm splits up the expression at the camel case parts from the right and tries to find the corresponding property. - -```java -@Document("customers") -public class Customer { - private Address address; -} - -public class Address { - private ZipCode zipCode; -} - -public interface MyRepository extends Repository { - - // 1. step: search domain class for a property "addressZipCode" - // 2. step: search domain class for "addressZip.code" - // 3. step: search domain class for "address.zipCode" - ArangoCursor findByAddressZipCode(ZipCode zipCode); -} -``` - -It is possible for the algorithm to select the wrong property if the domain class also has a property which matches the first split of the expression. To resolve this ambiguity you can use _ as a separator inside your method-name to define traversal points. - -```java -@Document("customers") -public class Customer { - private Address address; - private AddressZip addressZip; -} - -public class Address { - private ZipCode zipCode; -} - -public class AddressZip { - private String code; -} - -public interface MyRepository extends Repository { - - // 1. step: search domain class for a property "addressZipCode" - // 2. step: search domain class for "addressZip.code" - // creates query with "x.addressZip.code" - ArangoCursor findByAddressZipCode(ZipCode zipCode); - - // 1. step: search domain class for a property "addressZipCode" - // 2. step: search domain class for "addressZip.code" - // 3. step: search domain class for "address.zipCode" - // creates query with "x.address.zipCode" - ArangoCursor findByAddress_ZipCode(ZipCode zipCode); - -} -``` - -## Special parameter handling - -### Bind parameters - -AQL supports the usage of [bind parameters](https://docs.arangodb.com/3.1/AQL/Fundamentals/BindParameters.html) which you can define with a method parameter annotated with `@BindVars` of type `Map`. - -```java -public interface MyRepository extends Repository { - - @Query("FOR c IN customers FILTER c[@field] == @value RETURN c") - ArangoCursor query(Map bindVars); - -} - -Map bindVars = new HashMap(); -bindVars.put("field", "name"); -bindVars.put("value", "john"; - -// will execute query "FOR c IN customers FILTER c.name == "john" RETURN c" -ArangoCursor cursor = myRepo.query(bindVars); -``` - -### AQL query options - -You can set additional options for the query and the created cursor over the class `AqlQueryOptions` which you can simply define as a method parameter without a specific name. AqlQuery options can also be defined with the `@QueryOptions` annotation, as shown below. AqlQueryOptions from an annotation and those from an argument are merged if both exist, with those in the argument taking precedence. - -The `AqlQueryOptions` allows you to set the cursor time-to-live, batch-size, caching flag and several other settings. This special parameter works with both query-methods and finder-methods. Keep in mind that some options, like time-to-live, are only effective if the method return type is`ArangoCursor` or `Iterable`. - -```java -public interface MyRepository extends Repository { - - - @Query("FOR c IN customers FILTER c.name == @0 RETURN c") - Iterable query(String name, AqlQueryOptions options); - - - Iterable findByName(String name, AqlQueryOptions options); - - - @QueryOptions(maxPlans = 1000, ttl = 128) - ArangoCursor findByAddressZipCode(ZipCode zipCode); - - - @Query("FOR c IN customers FILTER c[@field] == @value RETURN c") - @QueryOptions(cache = true, ttl = 128) - ArangoCursor query(Map bindVars, AqlQueryOptions options); - -} -``` - -### Paging and sorting - -Spring Data ArangoDB supports Spring Data's `Pageable` and `Sort` parameters for repository query methods. If these parameters are used together with a native query, either through `@Query` annotation or named queries, a placeholder must be specified: -- `#pageable` for `Pageable` parameter -- `#sort` for `Sort` parameter - -Sort properties or paths are attributes separated by dots (e.g. `customer.age`). Some rules apply for them: -- they must not begin or end with a dot (e.g. `.customer.age`) -- dots in attributes are supported, but the whole attribute must be enclosed by backticks (e.g. ``customer.`attr.with.dots` `` -- backticks in attributes are supported, but they must be escaped with a backslash (e.g. ``customer.attr_with\` ``) -- any backslashes (that do not escape a backtick) are escaped (e.g. `customer\` => `customer\\`) - -``` -just.`some`.`attributes.that`.`form\``.a path\`.\ is converted to -`just`.`some`.`attributes.that`.`form\``.`a path\``.`\\` -``` - -Native queries example: -```java -public interface CustomerRepository extends ArangoRepository { - - @Query("FOR c IN customer FILTER c.name == @1 #pageable RETURN c") - Page findByNameNative(Pageable pageable, String name); - - @Query("FOR c IN customer FILTER c.name == @1 #sort RETURN c") - List findByNameNative(Sort sort, String name); -} - -// don't forget to specify the var name of the document -final Pageable page = PageRequest.of(1, 10, Sort.by("c.age")); -repository.findByNameNative(page, "Matt"); - -final Sort sort = Sort.by(Direction.DESC, "c.age"); -repository.findByNameNative(sort, "Tony"); -``` - -Derived queries example: -```java -public interface CustomerRepository extends ArangoRepository { - - Page findByName(Pageable pageable, String name); - - List findByName(Sort sort, String name); -} - -// no var name is necessary for derived queries -final Pageable page = PageRequest.of(1, 10, Sort.by("age")); -repository.findByName(page, "Matt"); - -final Sort sort = Sort.by(Direction.DESC, "age"); -repository.findByName(sort, "Tony"); -``` - -# Mapping - -## Introduction - -In this section we will describe the features and conventions for mapping Java objects to documents and how to override those conventions with annotation based mapping metadata. - -## Conventions - -* The Java class name is mapped to the collection name -* The non-static fields of a Java object are used as fields in the stored document -* The Java field name is mapped to the stored document field name -* All nested Java object are stored as nested objects in the stored document -* The Java class needs a constructor which meets the following criteria: - * in case of a single constructor: - * a non-parameterized constructor or - * a parameterized constructor - * in case of multiple constructors: - * a non-parameterized constructor or - * a parameterized constructor annotated with `@PersistenceConstructor` - -## Type conventions - -ArangoDB uses [VelocyPack](https://github.com/arangodb/velocypack) as it's internal storage format which supports a large number of data types. In addition Spring Data ArangoDB offers - with the underlying Java driver - built-in converters to add additional types to the mapping. - -Java type | VelocyPack type -----------|---------------- -java.lang.String | string -java.lang.Boolean | bool -java.lang.Integer | signed int 4 bytes, smallint -java.lang.Long | signed int 8 bytes, smallint -java.lang.Short | signed int 2 bytes, smallint -java.lang.Double | double -java.lang.Float | double -java.math.BigInteger | signed int 8 bytes, unsigned int 8 bytes -java.math.BigDecimal | double -java.lang.Number | double -java.lang.Character | string -java.util.Date | string (date-format ISO 8601) -java.sql.Date | string (date-format ISO 8601) -java.sql.Timestamp | string (date-format ISO 8601) -java.util.UUID | string -java.lang.byte[] | string (Base64) - -## Type mapping -As collections in ArangoDB can contain documents of various types, a mechanism to retrieve the correct Java class is required. The type information of properties declared in a class may not be enough to restore the original class (due to inheritance). If the declared complex type and the actual type do not match, information about the actual type is stored together with the document. This is necessary to restore the correct type when reading from the DB. Consider the following example: - -```java -public class Person { - private String name; - private Address homeAddress; - // ... - - // getters and setters omitted -} - -public class Employee extends Person { - private Address workAddress; - // ... - - // getters and setters omitted -} - -public class Address { - private final String street; - private final String number; - // ... - - public Address(String street, String number) { - this.street = street; - this.number = number; - } - - // getters omitted -} - -@Document -public class Company { - @Key - private String key; - private Person manager; - - // getters and setters omitted -} - -Employee manager = new Employee(); -manager.setName("Jane Roberts"); -manager.setHomeAddress(new Address("Park Avenue", "432/64")); -manager.setWorkAddress(new Address("Main Street", "223")); -Company comp = new Company(); -comp.setManager(manager); -``` - -The serialized document for the DB looks like this: - -```json -{ - "manager": { - "name": "Jane Roberts", - "homeAddress": { - "street": "Park Avenue", - "number": "432/64" - }, - "workAddress": { - "street": "Main Street", - "number": "223" - }, - "_class": "com.arangodb.Employee" - }, - "_class": "com.arangodb.Company" -} -``` - -Type hints are written for top-level documents (as a collection can contain different document types) as well as for every value if it's a complex type and a sub-type of the property type declared. `Map`s and `Collection`s are excluded from type mapping. Without the additional information about the concrete classes used, the document couldn't be restored in Java. The type information of the `manager` property is not enough to determine the `Employee` type. The `homeAddress` and `workAddress` properties have the same actual and defined type, thus no type hint is needed. - -### Customizing type mapping -By default, the fully qualified class name is stored in the documents as a type hint. A custom type hint can be set with the `@TypeAlias("my-alias")` annotation on an entity. Make sure that it is an unique identifier across all entities. If we would add a `TypeAlias("employee")` annotation to the `Employee` class above, it would be persisted as `"_class": "employee"`. - -The default type key is `_class` and can be changed by overriding the `typeKey()` method of the `AbstractArangoConfiguration` class. - -If you need to further customize the type mapping process, the `arangoTypeMapper()` method of the configuration class can be overridden. The included `DefaultArangoTypeMapper` can be customized by providing a list of [`TypeInformationMapper`](https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/convert/TypeInformationMapper.html)s that create aliases from types and vice versa. - -In order to fully customize the type mapping process you can provide a custom type mapper implementation by extending the `DefaultArangoTypeMapper` class. - -### Deactivating type mapping -To deactivate the type mapping process, you can return `null` from the `typeKey()` method of the `AbstractArangoConfiguration` class. No type hints are stored in the documents with this setting. If you make sure that each defined type corresponds to the actual type, you can disable the type mapping, otherwise it can lead to exceptions when reading the entities from the DB. - -## Annotations - -### Annotation overview - -annotation | level | description ------------|-------|------------ -@Document | class | marks this class as a candidate for mapping -@Edge | class | marks this class as a candidate for mapping -@Id | field | stores the field as the system field _id -@Key | field | stores the field as the system field _key -@Rev | field | stores the field as the system field _rev -@Field("alt-name") | field | stores the field with an alternative name -@Ref | field | stores the _id of the referenced document and not the nested document -@From | field | stores the _id of the referenced document as the system field _from -@To | field | stores the _id of the referenced document as the system field _to -@Relations | field | vertices which are connected over edges -@Transient | field, method, annotation | marks a field to be transient for the mapping framework, thus the property will not be persisted and not further inspected by the mapping framework -@PersistenceConstructor | constructor | marks a given constructor - even a package protected one - to use when instantiating the object from the database -@TypeAlias("alias") | class | set a type alias for the class when persisted to the DB -@HashIndex | class | describes a hash index -@HashIndexed | field | describes how to index the field -@SkiplistIndex | class | describes a skiplist index -@SkiplistIndexed | field | describes how to index the field -@PersistentIndex | class | describes a persistent index -@PersistentIndexed | field | describes how to index the field -@GeoIndex | class | describes a geo index -@GeoIndexed | field | describes how to index the field -@FulltextIndex | class | describes a fulltext index -@FulltextIndexed | field | describes how to index the field - -### Document - -The annotations `@Document` applied to a class marks this class as a candidate for mapping to the database. The most relevant parameter is `value` to specify the collection name in the database. The annotation `@Document` specifies the collection type to `DOCUMENT`. - -```java -@Document(value="persons") -public class Person { - ... -} -``` - -### Edge - -The annotations `@Edge` applied to a class marks this class as a candidate for mapping to the database. The most relevant parameter is `value` to specify the collection name in the database. The annotation `@Edge` specifies the collection type to `EDGE`. - -```java -@Edge("relations") -public class Relation { - ... -} -``` - -### Reference - -With the annotation `@Ref` applied on a field the nested object isn’t stored as a nested object in the document. The `_id` field of the nested object is stored in the document and the nested object has to be stored as a separate document in another collection described in the `@Document` annotation of the nested object class. To successfully persist an instance of your object the referencing field has to be null or it's instance has to provide a field with the annotation `@Id` including a valid id. - -```java -@Document(value="persons") -public class Person { - @Ref - private Address address; -} - -@Document("addresses") -public class Address { - @Id - private String id; - private String country; - private String street; -} -``` - -The database representation of `Person` in collection *persons* looks as follow: - -``` -{ - "_key" : "123", - "_id" : "persons/123", - "address" : "addresses/456" -} -``` -and the representation of `Address` in collection *addresses*: -``` -{ - "_key" : "456", - "_id" : "addresses/456", - "country" : "...", - "street" : "..." -} -``` - -Without the annotation `@Ref` at the field `address`, the stored document would look: - -``` -{ - "_key" : "123", - "_id" : "persons/123", - "address" : { - "country" : "...", - "street" : "..." - } -} -``` - -### Relations - -With the annotation `@Relations` applied on a collection or array field in a class annotated with `@Document` the nested objects are fetched from the database over a graph traversal with your current object as the starting point. The most relevant parameter is `edge`. With `edge` you define the edge collection - which should be used in the traversal - using the class type. With the parameter `depth` you can define the maximal depth for the traversal (default 1) and the parameter `direction` defines whether the traversal should follow outgoing or incoming edges (default Direction.ANY). - -```java -@Document(value="persons") -public class Person { - @Relations(edge=Relation.class, depth=1, direction=Direction.ANY) - private List friends; -} - -@Edge(name="relations") -public class Relation { - -} -``` - -### Document with From and To - -With the annotations `@From` and `@To` applied on a collection or array field in a class annotated with `@Document` the nested edge objects are fetched from the database. Each of the nested edge objects has to be stored as separate edge document in the edge collection described in the `@Edge` annotation of the nested object class with the *_id* of the parent document as field *_from* or *_to*. - -```java -@Document("persons") -public class Person { - @From - private List relations; -} - -@Edge(name="relations") -public class Relation { - ... -} -``` - -The database representation of `Person` in collection *persons* looks as follow: -``` -{ - "_key" : "123", - "_id" : "persons/123" -} -``` - -and the representation of `Relation` in collection *relations*: -``` -{ - "_key" : "456", - "_id" : "relations/456", - "_from" : "persons/123" - "_to" : ".../..." -} -{ - "_key" : "789", - "_id" : "relations/456", - "_from" : "persons/123" - "_to" : ".../..." -} -... - -``` - -### Edge with From and To - -With the annotations `@From` and `@To` applied on a field in a class annotated with `@Edge` the nested object is fetched from the database. The nested object has to be stored as a separate document in the collection described in the `@Document` annotation of the nested object class. The *_id* field of this nested object is stored in the fields `_from` or `_to` within the edge document. - -```java -@Edge("relations") -public class Relation { - @From - private Person c1; - @To - private Person c2; -} - -@Document(value="persons") -public class Person { - @Id - private String id; -} -``` - -The database representation of `Relation` in collection *relations* looks as follow: -``` -{ - "_key" : "123", - "_id" : "relations/123", - "_from" : "persons/456", - "_to" : "persons/789" -} -``` - -and the representation of `Person` in collection *persons*: -``` -{ - "_key" : "456", - "_id" : "persons/456", -} -{ - "_key" : "789", - "_id" : "persons/789", -} -``` - -**Note:** If you want to save an instance of `Relation`, both `Person` objects (from & to) already have to be persisted and the class `Person` needs a field with the annotation `@Id` so it can hold the persisted `_id` from the database. - -### Index and Indexed annotations - -With the `@Indexed` annotations user defined indexes can be created at a collection level by annotating single fields of a class. - -Possible `@Indexed` annotations are: -* `@HashIndexed` -* `@SkiplistIndexed` -* `@PersistentIndexed` -* `@GeoIndexed` -* `@FulltextIndexed` - -The following example creates a hash index on the field `name` and a separate hash index on the field `age`: -```java -public class Person { - @HashIndexed - private String name; - - @HashIndexed - private int age; -} -``` - -With the `@Indexed` annotations different indexes can be created on the same field. - -The following example creates a hash index and also a skiplist index on the field `name`: -```java -public class Person { - @HashIndexed - @SkiplistIndexed - private String name; -} -``` - -If the index should include multiple fields the `@Index` annotations can be used on the type instead. - -Possible `@Index` annotations are: -* `@HashIndex` -* `@SkiplistIndex` -* `@PersistentIndex` -* `@GeoIndex` -* `@FulltextIndex` - -The following example creates a single hash index on the fields `name` and `age`, note that if a field is renamed in the database with @Field, the new field name must be used in the index declaration: -```java -@HashIndex(fields = {"fullname", "age"}) -public class Person { - @Field("fullname") - private String name; - - private int age; -} -``` - -The `@Index` annotations can also be used to create an index on a nested field. - -The following example creates a single hash index on the fields `name` and `address.country`: -```java -@HashIndex(fields = {"name", "address.country"}) -public class Person { - private String name; - - private Address address; -} -``` - -The `@Index` annotations and the `@Indexed` annotations can be used at the same time in one class. - -The following example creates a hash index on the fields `name` and `age` and a separate hash index on the field `age`: -```java -@HashIndex(fields = {"name", "age"}) -public class Person { - private String name; - - @HashIndexed - private int age; -} -``` - -The `@Index` annotations can be used multiple times to create more than one index in this way. - -The following example creates a hash index on the fields `name` and `age` and a separate hash index on the fields `name` and `gender`: -```java -@HashIndex(fields = {"name", "age"}) -@HashIndex(fields = {"name", "gender"}) -public class Person { - private String name; - - private int age; - - private Gender gender -} -``` +# Spring Data ArangoDB - Reference + +# Template + +With `ArangoTemplate` Spring Data ArangoDB offers a central support for interactions with the database over a rich feature set. It mostly offers the features from the ArangoDB Java driver with additional exception translation from the drivers exceptions to the Spring Data access exceptions inheriting the `DataAccessException` class. +The `ArangoTemplate` class is the default implementation of the operations interface `ArangoOperations` which developers of Spring Data are encouraged to code against. + +# Repositories + +## Introduction + +Spring Data Commons provides a composable repository infrastructure which Spring Data ArangoDB is built on. These allow for interface-based composition of repositories consisting of provided default implementations for certain interfaces (like `CrudRepository`) and custom implementations for other methods. + +## Instantiating + +Instances of a Repository are created in Spring beans through the auto-wired mechanism of Spring. + +```java +public class MySpringBean { + + @Autowired + private MyRepository rep; + +} +``` + +## Return types + +The method return type for single results can be a primitive type, a domain class, `Map`, `BaseDocument`, `BaseEdgeDocument`, `Optional`, `GeoResult`. +The method return type for multiple results can additionally be `ArangoCursor`, `Iterable`, `Collection`, `List`, `Set`, `Page`, `Slice`, `GeoPage`, `GeoResults` where Type can be everything a single result can be. + +## Query methods + +Queries using [ArangoDB Query Language (AQL)](https://docs.arangodb.com/current/AQL/index.html) can be supplied with the `@Query` annotation on methods. `AqlQueryOptions` can also be passed to the driver, as an argument anywhere in the method signature. + +There are three ways of passing bind parameters to the query in the query annotation. + +Using number matching, arguments will be substituted into the query in the order they are passed to the query method. + +```java +public interface MyRepository extends Repository{ + + @Query("FOR c IN customers FILTER c.name == @0 AND c.surname == @2 RETURN c") + ArangoCursor query(String name, AqlQueryOptions options, String surname); + +} +``` + +With the `@Param` annotation, the argument will be placed in the query at the place corresponding to the value passed to the `@Param` annotation. + +```java +public interface MyRepository extends Repository{ + + @Query("FOR c IN customers FILTER c.name == @name AND c.surname == @surname RETURN c") + ArangoCursor query(@Param("name") String name, @Param("surname") String surname); + +} +``` + + In addition you can use a parameter of type `Map` annotated with `@BindVars` as your bind parameters. You can then fill the map with any parameter used in the query. (see [here](https://docs.arangodb.com/3.1/AQL/Fundamentals/BindParameters.html#bind-parameters) for more Information about Bind Parameters). + +```java +public interface MyRepository extends Repository{ + + @Query("FOR c IN customers FILTER c.name == @name AND c.surname = @surname RETURN c") + ArangoCursor query(@BindVars Map bindVars); + +} +``` + +A mixture of any of these methods can be used. Parameters with the same name from an `@Param` annotation will override those in the `bindVars`. + +```java +public interface MyRepository extends Repository{ + + @Query("FOR c IN customers FILTER c.name == @name AND c.surname = @surname RETURN c") + ArangoCursor query(@BindVars Map bindVars, @Param("name") String name); + +} +``` + +## Named queries +An alternative to using the `@Query` annotation on methods is specifying them in a separate `.properties` file. The default path for the file is `META-INF/arango-named-queries.properties` and can be changed with the `EnableArangoRepositories#namedQueriesLocation()` setting. The entries in the properties file must adhere to the following convention: `{simple entity name}.{method name} = {query}`. Let's assume we have the following repository interface: + +```java +package com.arangodb.repository; + +public interface CustomerRepository extends ArangoRepository { + Customer findByUsername(@Param("username") String username); +} +``` + +The corresponding `arango-named-queries.properties` file looks like this: + +```properties +Customer.findByUsername = FOR c IN customers FILTER c.username == @username RETURN c +``` + +The queries specified in the properties file are no different than the queries that can be defined with the `@Query` annotation. The only difference is that the queries are in one place. If there is a `@Query` annotation present and a named query defined, the query in the `@Query` annotation takes precedence. + +## Derived queries + +Spring Data ArangoDB supports queries derived from methods names by splitting it into its semantic parts and converting into AQL. The mechanism strips the prefixes `find..By`, `get..By`, `query..By`, `read..By`, `stream..By`, `count..By`, `exists..By`, `delete..By`, `remove..By` from the method and parses the rest. The By acts as a separator to indicate the start of the criteria for the query to be built. You can define conditions on entity properties and concatenate them with `And` and `Or`. + +The complete list of part types for derived methods is below, where doc is a document in the database + +Keyword | Sample | Predicate +----------|----------------|-------- +IsGreaterThan, GreaterThan, After | findByAgeGreaterThan(int age) | doc.age > age +IsGreaterThanEqual, GreaterThanEqual | findByAgeIsGreaterThanEqual(int age) | doc.age >= age +IsLessThan, LessThan, Before | findByAgeIsLessThan(int age) | doc.age < age +IsLessThanEqualLessThanEqual | findByAgeLessThanEqual(int age) | doc.age <= age +IsBetween, Between | findByAgeBetween(int lower, int upper) | lower < doc.age < upper +IsNotNull, NotNull | findByNameNotNull() | doc.name != null +IsNull, Null | findByNameNull() | doc.name == null +IsLike, Like | findByNameLike(String name) | doc.name LIKE name +IsNotLike, NotLike | findByNameNotLike(String name) | NOT(doc.name LIKE name) +IsStartingWith, StartingWith, StartsWith | findByNameStartsWith(String prefix) | doc.name LIKE prefix +IsEndingWith, EndingWith, EndsWith | findByNameEndingWith(String suffix) | doc.name LIKE suffix +Regex, MatchesRegex, Matches | findByNameRegex(String pattern) | REGEX_TEST(doc.name, name, ignoreCase) +(No Keyword) | findByFirstName(String name) | doc.name == name +IsTrue, True | findByActiveTrue() | doc.active == true +IsFalse, False | findByActiveFalse() | doc.active == false +Is, Equals | findByAgeEquals(int age) | doc.age == age +IsNot, Not | findByAgeNot(int age) | doc.age != age +IsIn, In | findByNameIn(String[] names) | doc.name IN names +IsNotIn, NotIn | findByNameIsNotIn(String[] names) | doc.name NOT IN names +IsContaining, Containing, Contains | findByFriendsContaining(String name) | name IN doc.friends +IsNotContaining, NotContaining, NotContains | findByFriendsNotContains(String name) | name NOT IN doc.friends +Exists | findByFriendNameExists() | HAS(doc.friend, name) + + +```java +public interface MyRepository extends Repository { + + // FOR c IN customers FILTER c.name == @0 RETURN c + ArangoCursor findByName(String name); + ArangoCursor getByName(String name); + + // FOR c IN customers + // FILTER c.name == @0 && c.age == @1 + // RETURN c + ArangoCursor findByNameAndAge(String name, int age); + + // FOR c IN customers + // FILTER c.name == @0 || c.age == @1 + // RETURN c + ArangoCursor findByNameOrAge(String name, int age); +} +``` + +You can apply sorting for one or multiple sort criteria by appending `OrderBy` to the method and `Asc` or `Desc` for the directions. + +```java +public interface MyRepository extends Repository { + + // FOR c IN customers + // FILTER c.name == @0 + // SORT c.age DESC RETURN c + ArangoCursor getByNameOrderByAgeDesc(String name); + + // FOR c IN customers + // FILTER c.name = @0 + // SORT c.name ASC, c.age DESC RETURN c + ArangoCursor findByNameOrderByNameAscAgeDesc(String name); + +} +``` + +### Geospatial queries + +Geospatial queries are a subsection of derived queries. To use a geospatial query on a collection, a geo index must exist on that collection. A geo index can be created on a field which is a two element array, corresponding to latitude and longitude coordinates. + +As a subsection of derived queries, geospatial queries support all the same return types, but also support the three return types `GeoPage, GeoResult and Georesults`. These types must be used in order to get the distance of each document as generated by the query. + +There are two kinds of geospatial query, Near and Within. Near sorts documents by distance from the given point, while within both sorts and filters documents, returning those within the given distance range or shape. + +```java +public interface MyRepository extends Repository { + + GeoResult getByLocationNear(Point point); + + GeoResults findByLocationWithinOrLocationWithin(Box box, Polygon polygon); + + //Equivalent queries + GeoResults findByLocationWithinOrLocationWithin(Point point, int distance); + GeoResults findByLocationWithinOrLocationWithin(Point point, Distance distance); + GeoResults findByLocationWithinOrLocationWithin(Circle circle); + +} +``` + +## Property expression + +Property expressions can refer only to direct and nested properties of the managed domain class. The algorithm checks the domain class for the entire expression as the property. If the check fails, the algorithm splits up the expression at the camel case parts from the right and tries to find the corresponding property. + +```java +@Document("customers") +public class Customer { + private Address address; +} + +public class Address { + private ZipCode zipCode; +} + +public interface MyRepository extends Repository { + + // 1. step: search domain class for a property "addressZipCode" + // 2. step: search domain class for "addressZip.code" + // 3. step: search domain class for "address.zipCode" + ArangoCursor findByAddressZipCode(ZipCode zipCode); +} +``` + +It is possible for the algorithm to select the wrong property if the domain class also has a property which matches the first split of the expression. To resolve this ambiguity you can use _ as a separator inside your method-name to define traversal points. + +```java +@Document("customers") +public class Customer { + private Address address; + private AddressZip addressZip; +} + +public class Address { + private ZipCode zipCode; +} + +public class AddressZip { + private String code; +} + +public interface MyRepository extends Repository { + + // 1. step: search domain class for a property "addressZipCode" + // 2. step: search domain class for "addressZip.code" + // creates query with "x.addressZip.code" + ArangoCursor findByAddressZipCode(ZipCode zipCode); + + // 1. step: search domain class for a property "addressZipCode" + // 2. step: search domain class for "addressZip.code" + // 3. step: search domain class for "address.zipCode" + // creates query with "x.address.zipCode" + ArangoCursor findByAddress_ZipCode(ZipCode zipCode); + +} +``` + +## Special parameter handling + +### Bind parameters + +AQL supports the usage of [bind parameters](https://docs.arangodb.com/3.1/AQL/Fundamentals/BindParameters.html) which you can define with a method parameter annotated with `@BindVars` of type `Map`. + +```java +public interface MyRepository extends Repository { + + @Query("FOR c IN customers FILTER c[@field] == @value RETURN c") + ArangoCursor query(Map bindVars); + +} + +Map bindVars = new HashMap(); +bindVars.put("field", "name"); +bindVars.put("value", "john"; + +// will execute query "FOR c IN customers FILTER c.name == "john" RETURN c" +ArangoCursor cursor = myRepo.query(bindVars); +``` + +### AQL query options + +You can set additional options for the query and the created cursor over the class `AqlQueryOptions` which you can simply define as a method parameter without a specific name. AqlQuery options can also be defined with the `@QueryOptions` annotation, as shown below. AqlQueryOptions from an annotation and those from an argument are merged if both exist, with those in the argument taking precedence. + +The `AqlQueryOptions` allows you to set the cursor time-to-live, batch-size, caching flag and several other settings. This special parameter works with both query-methods and finder-methods. Keep in mind that some options, like time-to-live, are only effective if the method return type is`ArangoCursor` or `Iterable`. + +```java +public interface MyRepository extends Repository { + + + @Query("FOR c IN customers FILTER c.name == @0 RETURN c") + Iterable query(String name, AqlQueryOptions options); + + + Iterable findByName(String name, AqlQueryOptions options); + + + @QueryOptions(maxPlans = 1000, ttl = 128) + ArangoCursor findByAddressZipCode(ZipCode zipCode); + + + @Query("FOR c IN customers FILTER c[@field] == @value RETURN c") + @QueryOptions(cache = true, ttl = 128) + ArangoCursor query(Map bindVars, AqlQueryOptions options); + +} +``` + +### Paging and sorting + +Spring Data ArangoDB supports Spring Data's `Pageable` and `Sort` parameters for repository query methods. If these parameters are used together with a native query, either through `@Query` annotation or named queries, a placeholder must be specified: +- `#pageable` for `Pageable` parameter +- `#sort` for `Sort` parameter + +Sort properties or paths are attributes separated by dots (e.g. `customer.age`). Some rules apply for them: +- they must not begin or end with a dot (e.g. `.customer.age`) +- dots in attributes are supported, but the whole attribute must be enclosed by backticks (e.g. ``customer.`attr.with.dots` `` +- backticks in attributes are supported, but they must be escaped with a backslash (e.g. ``customer.attr_with\` ``) +- any backslashes (that do not escape a backtick) are escaped (e.g. `customer\` => `customer\\`) + +``` +just.`some`.`attributes.that`.`form\``.a path\`.\ is converted to +`just`.`some`.`attributes.that`.`form\``.`a path\``.`\\` +``` + +Native queries example: +```java +public interface CustomerRepository extends ArangoRepository { + + @Query("FOR c IN customer FILTER c.name == @1 #pageable RETURN c") + Page findByNameNative(Pageable pageable, String name); + + @Query("FOR c IN customer FILTER c.name == @1 #sort RETURN c") + List findByNameNative(Sort sort, String name); +} + +// don't forget to specify the var name of the document +final Pageable page = PageRequest.of(1, 10, Sort.by("c.age")); +repository.findByNameNative(page, "Matt"); + +final Sort sort = Sort.by(Direction.DESC, "c.age"); +repository.findByNameNative(sort, "Tony"); +``` + +Derived queries example: +```java +public interface CustomerRepository extends ArangoRepository { + + Page findByName(Pageable pageable, String name); + + List findByName(Sort sort, String name); +} + +// no var name is necessary for derived queries +final Pageable page = PageRequest.of(1, 10, Sort.by("age")); +repository.findByName(page, "Matt"); + +final Sort sort = Sort.by(Direction.DESC, "age"); +repository.findByName(sort, "Tony"); +``` + +# Mapping + +## Introduction + +In this section we will describe the features and conventions for mapping Java objects to documents and how to override those conventions with annotation based mapping metadata. + +## Conventions + +* The Java class name is mapped to the collection name +* The non-static fields of a Java object are used as fields in the stored document +* The Java field name is mapped to the stored document field name +* All nested Java object are stored as nested objects in the stored document +* The Java class needs a constructor which meets the following criteria: + * in case of a single constructor: + * a non-parameterized constructor or + * a parameterized constructor + * in case of multiple constructors: + * a non-parameterized constructor or + * a parameterized constructor annotated with `@PersistenceConstructor` + +## Type conventions + +ArangoDB uses [VelocyPack](https://github.com/arangodb/velocypack) as it's internal storage format which supports a large number of data types. In addition Spring Data ArangoDB offers - with the underlying Java driver - built-in converters to add additional types to the mapping. + +Java type | VelocyPack type +----------|---------------- +java.lang.String | string +java.lang.Boolean | bool +java.lang.Integer | signed int 4 bytes, smallint +java.lang.Long | signed int 8 bytes, smallint +java.lang.Short | signed int 2 bytes, smallint +java.lang.Double | double +java.lang.Float | double +java.math.BigInteger | signed int 8 bytes, unsigned int 8 bytes +java.math.BigDecimal | double +java.lang.Number | double +java.lang.Character | string +java.util.Date | string (date-format ISO 8601) +java.sql.Date | string (date-format ISO 8601) +java.sql.Timestamp | string (date-format ISO 8601) +java.util.UUID | string +java.lang.byte[] | string (Base64) + +## Type mapping +As collections in ArangoDB can contain documents of various types, a mechanism to retrieve the correct Java class is required. The type information of properties declared in a class may not be enough to restore the original class (due to inheritance). If the declared complex type and the actual type do not match, information about the actual type is stored together with the document. This is necessary to restore the correct type when reading from the DB. Consider the following example: + +```java +public class Person { + private String name; + private Address homeAddress; + // ... + + // getters and setters omitted +} + +public class Employee extends Person { + private Address workAddress; + // ... + + // getters and setters omitted +} + +public class Address { + private final String street; + private final String number; + // ... + + public Address(String street, String number) { + this.street = street; + this.number = number; + } + + // getters omitted +} + +@Document +public class Company { + @Key + private String key; + private Person manager; + + // getters and setters omitted +} + +Employee manager = new Employee(); +manager.setName("Jane Roberts"); +manager.setHomeAddress(new Address("Park Avenue", "432/64")); +manager.setWorkAddress(new Address("Main Street", "223")); +Company comp = new Company(); +comp.setManager(manager); +``` + +The serialized document for the DB looks like this: + +```json +{ + "manager": { + "name": "Jane Roberts", + "homeAddress": { + "street": "Park Avenue", + "number": "432/64" + }, + "workAddress": { + "street": "Main Street", + "number": "223" + }, + "_class": "com.arangodb.Employee" + }, + "_class": "com.arangodb.Company" +} +``` + +Type hints are written for top-level documents (as a collection can contain different document types) as well as for every value if it's a complex type and a sub-type of the property type declared. `Map`s and `Collection`s are excluded from type mapping. Without the additional information about the concrete classes used, the document couldn't be restored in Java. The type information of the `manager` property is not enough to determine the `Employee` type. The `homeAddress` and `workAddress` properties have the same actual and defined type, thus no type hint is needed. + +### Customizing type mapping +By default, the fully qualified class name is stored in the documents as a type hint. A custom type hint can be set with the `@TypeAlias("my-alias")` annotation on an entity. Make sure that it is an unique identifier across all entities. If we would add a `TypeAlias("employee")` annotation to the `Employee` class above, it would be persisted as `"_class": "employee"`. + +The default type key is `_class` and can be changed by overriding the `typeKey()` method of the `AbstractArangoConfiguration` class. + +If you need to further customize the type mapping process, the `arangoTypeMapper()` method of the configuration class can be overridden. The included `DefaultArangoTypeMapper` can be customized by providing a list of [`TypeInformationMapper`](https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/convert/TypeInformationMapper.html)s that create aliases from types and vice versa. + +In order to fully customize the type mapping process you can provide a custom type mapper implementation by extending the `DefaultArangoTypeMapper` class. + +### Deactivating type mapping +To deactivate the type mapping process, you can return `null` from the `typeKey()` method of the `AbstractArangoConfiguration` class. No type hints are stored in the documents with this setting. If you make sure that each defined type corresponds to the actual type, you can disable the type mapping, otherwise it can lead to exceptions when reading the entities from the DB. + +## Annotations + +### Annotation overview + +annotation | level | description +-----------|-------|------------ +@Document | class | marks this class as a candidate for mapping +@Edge | class | marks this class as a candidate for mapping +@Id | field | stores the field as the system field _id +@Key | field | stores the field as the system field _key +@Rev | field | stores the field as the system field _rev +@Field("alt-name") | field | stores the field with an alternative name +@Ref | field | stores the _id of the referenced document and not the nested document +@From | field | stores the _id of the referenced document as the system field _from +@To | field | stores the _id of the referenced document as the system field _to +@Relations | field | vertices which are connected over edges +@Transient | field, method, annotation | marks a field to be transient for the mapping framework, thus the property will not be persisted and not further inspected by the mapping framework +@PersistenceConstructor | constructor | marks a given constructor - even a package protected one - to use when instantiating the object from the database +@TypeAlias("alias") | class | set a type alias for the class when persisted to the DB +@HashIndex | class | describes a hash index +@HashIndexed | field | describes how to index the field +@SkiplistIndex | class | describes a skiplist index +@SkiplistIndexed | field | describes how to index the field +@PersistentIndex | class | describes a persistent index +@PersistentIndexed | field | describes how to index the field +@GeoIndex | class | describes a geo index +@GeoIndexed | field | describes how to index the field +@FulltextIndex | class | describes a fulltext index +@FulltextIndexed | field | describes how to index the field + +### Document + +The annotations `@Document` applied to a class marks this class as a candidate for mapping to the database. The most relevant parameter is `value` to specify the collection name in the database. The annotation `@Document` specifies the collection type to `DOCUMENT`. + +```java +@Document(value="persons") +public class Person { + ... +} +``` + +### Edge + +The annotations `@Edge` applied to a class marks this class as a candidate for mapping to the database. The most relevant parameter is `value` to specify the collection name in the database. The annotation `@Edge` specifies the collection type to `EDGE`. + +```java +@Edge("relations") +public class Relation { + ... +} +``` + +### Reference + +With the annotation `@Ref` applied on a field the nested object isn’t stored as a nested object in the document. The `_id` field of the nested object is stored in the document and the nested object has to be stored as a separate document in another collection described in the `@Document` annotation of the nested object class. To successfully persist an instance of your object the referencing field has to be null or it's instance has to provide a field with the annotation `@Id` including a valid id. + +```java +@Document(value="persons") +public class Person { + @Ref + private Address address; +} + +@Document("addresses") +public class Address { + @Id + private String id; + private String country; + private String street; +} +``` + +The database representation of `Person` in collection *persons* looks as follow: + +``` +{ + "_key" : "123", + "_id" : "persons/123", + "address" : "addresses/456" +} +``` +and the representation of `Address` in collection *addresses*: +``` +{ + "_key" : "456", + "_id" : "addresses/456", + "country" : "...", + "street" : "..." +} +``` + +Without the annotation `@Ref` at the field `address`, the stored document would look: + +``` +{ + "_key" : "123", + "_id" : "persons/123", + "address" : { + "country" : "...", + "street" : "..." + } +} +``` + +### Relations + +With the annotation `@Relations` applied on a collection or array field in a class annotated with `@Document` the nested objects are fetched from the database over a graph traversal with your current object as the starting point. The most relevant parameter is `edge`. With `edge` you define the edge collection - which should be used in the traversal - using the class type. With the parameter `depth` you can define the maximal depth for the traversal (default 1) and the parameter `direction` defines whether the traversal should follow outgoing or incoming edges (default Direction.ANY). + +```java +@Document(value="persons") +public class Person { + @Relations(edge=Relation.class, depth=1, direction=Direction.ANY) + private List friends; +} + +@Edge(name="relations") +public class Relation { + +} +``` + +### Document with From and To + +With the annotations `@From` and `@To` applied on a collection or array field in a class annotated with `@Document` the nested edge objects are fetched from the database. Each of the nested edge objects has to be stored as separate edge document in the edge collection described in the `@Edge` annotation of the nested object class with the *_id* of the parent document as field *_from* or *_to*. + +```java +@Document("persons") +public class Person { + @From + private List relations; +} + +@Edge(name="relations") +public class Relation { + ... +} +``` + +The database representation of `Person` in collection *persons* looks as follow: +``` +{ + "_key" : "123", + "_id" : "persons/123" +} +``` + +and the representation of `Relation` in collection *relations*: +``` +{ + "_key" : "456", + "_id" : "relations/456", + "_from" : "persons/123" + "_to" : ".../..." +} +{ + "_key" : "789", + "_id" : "relations/456", + "_from" : "persons/123" + "_to" : ".../..." +} +... + +``` + +### Edge with From and To + +With the annotations `@From` and `@To` applied on a field in a class annotated with `@Edge` the nested object is fetched from the database. The nested object has to be stored as a separate document in the collection described in the `@Document` annotation of the nested object class. The *_id* field of this nested object is stored in the fields `_from` or `_to` within the edge document. + +```java +@Edge("relations") +public class Relation { + @From + private Person c1; + @To + private Person c2; +} + +@Document(value="persons") +public class Person { + @Id + private String id; +} +``` + +The database representation of `Relation` in collection *relations* looks as follow: +``` +{ + "_key" : "123", + "_id" : "relations/123", + "_from" : "persons/456", + "_to" : "persons/789" +} +``` + +and the representation of `Person` in collection *persons*: +``` +{ + "_key" : "456", + "_id" : "persons/456", +} +{ + "_key" : "789", + "_id" : "persons/789", +} +``` + +**Note:** If you want to save an instance of `Relation`, both `Person` objects (from & to) already have to be persisted and the class `Person` needs a field with the annotation `@Id` so it can hold the persisted `_id` from the database. + +### Index and Indexed annotations + +With the `@Indexed` annotations user defined indexes can be created at a collection level by annotating single fields of a class. + +Possible `@Indexed` annotations are: +* `@HashIndexed` +* `@SkiplistIndexed` +* `@PersistentIndexed` +* `@GeoIndexed` +* `@FulltextIndexed` + +The following example creates a hash index on the field `name` and a separate hash index on the field `age`: +```java +public class Person { + @HashIndexed + private String name; + + @HashIndexed + private int age; +} +``` + +With the `@Indexed` annotations different indexes can be created on the same field. + +The following example creates a hash index and also a skiplist index on the field `name`: +```java +public class Person { + @HashIndexed + @SkiplistIndexed + private String name; +} +``` + +If the index should include multiple fields the `@Index` annotations can be used on the type instead. + +Possible `@Index` annotations are: +* `@HashIndex` +* `@SkiplistIndex` +* `@PersistentIndex` +* `@GeoIndex` +* `@FulltextIndex` + +The following example creates a single hash index on the fields `name` and `age`, note that if a field is renamed in the database with @Field, the new field name must be used in the index declaration: +```java +@HashIndex(fields = {"fullname", "age"}) +public class Person { + @Field("fullname") + private String name; + + private int age; +} +``` + +The `@Index` annotations can also be used to create an index on a nested field. + +The following example creates a single hash index on the fields `name` and `address.country`: +```java +@HashIndex(fields = {"name", "address.country"}) +public class Person { + private String name; + + private Address address; +} +``` + +The `@Index` annotations and the `@Indexed` annotations can be used at the same time in one class. + +The following example creates a hash index on the fields `name` and `age` and a separate hash index on the field `age`: +```java +@HashIndex(fields = {"name", "age"}) +public class Person { + private String name; + + @HashIndexed + private int age; +} +``` + +The `@Index` annotations can be used multiple times to create more than one index in this way. + +The following example creates a hash index on the fields `name` and `age` and a separate hash index on the fields `name` and `gender`: +```java +@HashIndex(fields = {"name", "age"}) +@HashIndex(fields = {"name", "gender"}) +public class Person { + private String name; + + private int age; + + private Gender gender +} +``` diff --git a/Documentation/Books/Manual/Deployment/ActiveFailover/UsingTheStarter.md b/Documentation/Books/Manual/Deployment/ActiveFailover/UsingTheStarter.md index 6875bc6fb2..67ca12e4bf 100644 --- a/Documentation/Books/Manual/Deployment/ActiveFailover/UsingTheStarter.md +++ b/Documentation/Books/Manual/Deployment/ActiveFailover/UsingTheStarter.md @@ -1,3 +1,4 @@ + Using the ArangoDB Starter ========================== @@ -67,4 +68,3 @@ The _Starter_ will decide on which 2 machines to run a single server instance. To override this decision (only valid while bootstrapping), add a `--cluster.start-single=false` to the machine where the single server instance should _not_ be started. - diff --git a/Documentation/Books/Manual/Deployment/ArangoDBStarter/README.md b/Documentation/Books/Manual/Deployment/ArangoDBStarter/README.md index e0835406b2..9109df465b 100644 --- a/Documentation/Books/Manual/Deployment/ArangoDBStarter/README.md +++ b/Documentation/Books/Manual/Deployment/ArangoDBStarter/README.md @@ -1,3 +1,4 @@ + Deploying using the ArangoDB Starter ==================================== diff --git a/Documentation/Books/Manual/Deployment/Cluster/UsingTheStarter.md b/Documentation/Books/Manual/Deployment/Cluster/UsingTheStarter.md index 754b78387e..b006a9d772 100644 --- a/Documentation/Books/Manual/Deployment/Cluster/UsingTheStarter.md +++ b/Documentation/Books/Manual/Deployment/Cluster/UsingTheStarter.md @@ -1,3 +1,4 @@ + Using the ArangoDB Starter ========================== diff --git a/Documentation/Books/Manual/Deployment/SingleInstance/UsingTheStarter.md b/Documentation/Books/Manual/Deployment/SingleInstance/UsingTheStarter.md index 59edd8dbe3..e1fff576e5 100644 --- a/Documentation/Books/Manual/Deployment/SingleInstance/UsingTheStarter.md +++ b/Documentation/Books/Manual/Deployment/SingleInstance/UsingTheStarter.md @@ -1,3 +1,4 @@ + Using the ArangoDB Starter ========================== diff --git a/Documentation/Books/Manual/Programs/Starter/Architecture.md b/Documentation/Books/Manual/Programs/Starter/Architecture.md index a68f091565..5903057786 100644 --- a/Documentation/Books/Manual/Programs/Starter/Architecture.md +++ b/Documentation/Books/Manual/Programs/Starter/Architecture.md @@ -1,4 +1,4 @@ - + # ArangoDB Starter Architecture ## What does the Starter do diff --git a/Documentation/Books/Manual/Programs/Starter/Options.md b/Documentation/Books/Manual/Programs/Starter/Options.md index 71b1d5c2b2..d5f8d76703 100644 --- a/Documentation/Books/Manual/Programs/Starter/Options.md +++ b/Documentation/Books/Manual/Programs/Starter/Options.md @@ -1,4 +1,4 @@ - + # Option reference The ArangoDB Starter provides a lot of options to control various aspects diff --git a/Documentation/Books/Manual/Programs/Starter/README.md b/Documentation/Books/Manual/Programs/Starter/README.md index 3b2ae4a5dc..e1b393af78 100644 --- a/Documentation/Books/Manual/Programs/Starter/README.md +++ b/Documentation/Books/Manual/Programs/Starter/README.md @@ -1,4 +1,4 @@ - + # ArangoDB Starter This chapter documents the _ArangoDB Starter_. diff --git a/Documentation/Books/Manual/Programs/Starter/Security.md b/Documentation/Books/Manual/Programs/Starter/Security.md index 7a9df70839..821f6c45cc 100644 --- a/Documentation/Books/Manual/Programs/Starter/Security.md +++ b/Documentation/Books/Manual/Programs/Starter/Security.md @@ -1,4 +1,4 @@ - + # Security Securing an ArangoDB deployment involves encrypting its connections and diff --git a/Documentation/Books/Manual/Scalability/DC2DC/README.md b/Documentation/Books/Manual/Scalability/DC2DC/README.md index b5d60e3b81..88fc250963 100644 --- a/Documentation/Books/Manual/Scalability/DC2DC/README.md +++ b/Documentation/Books/Manual/Scalability/DC2DC/README.md @@ -17,3 +17,4 @@ For further information about _datacenter to datacenter replication_, please ref - [Troubleshooting](../../Troubleshooting/DC2DC/README.md) - [Monitoring](../../Monitoring/DC2DC/README.md) - [Security](../../Security/DC2DC/README.md) + diff --git a/Documentation/Books/Manual/Security/Starter/README.md b/Documentation/Books/Manual/Security/Starter/README.md index e3928d2ec1..0ff0edf6dd 100644 --- a/Documentation/Books/Manual/Security/Starter/README.md +++ b/Documentation/Books/Manual/Security/Starter/README.md @@ -1,4 +1,4 @@ - + Securing Starter Deployments ============================ diff --git a/Documentation/Books/Manual/Tutorials/Starter/README.md b/Documentation/Books/Manual/Tutorials/Starter/README.md index cf34fa1664..6672297d25 100644 --- a/Documentation/Books/Manual/Tutorials/Starter/README.md +++ b/Documentation/Books/Manual/Tutorials/Starter/README.md @@ -1,4 +1,4 @@ - + # Starting an ArangoDB cluster or database the easy way Starting an ArangoDB cluster is complex. It involves starting various servers with diff --git a/Documentation/Books/Manual/Upgrading/Starter/README.md b/Documentation/Books/Manual/Upgrading/Starter/README.md index d66ffc6e89..b2706164d4 100644 --- a/Documentation/Books/Manual/Upgrading/Starter/README.md +++ b/Documentation/Books/Manual/Upgrading/Starter/README.md @@ -1,3 +1,4 @@ + Upgrading _Starter_ Deployments ===============================