Derived queries
Semantic parts
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) |
Examples
public interface MyRepository extends ArangoRepository<Customer, String> {
// FOR c IN customers FILTER c.name == @0 RETURN c
ArangoCursor<Customer> findByName(String name);
ArangoCursor<Customer> getByName(String name);
// FOR c IN customers
// FILTER c.name == @0 && c.age == @1
// RETURN c
ArangoCursor<Customer> findByNameAndAge(String name, int age);
// FOR c IN customers
// FILTER c.name == @0 || c.age == @1
// RETURN c
ArangoCursor<Customer> 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.
public interface MyRepository extends ArangoRepository<Customer, String> {
// FOR c IN customers
// FILTER c.name == @0
// SORT c.age DESC RETURN c
ArangoCursor<Customer> getByNameOrderByAgeDesc(String name);
// FOR c IN customers
// FILTER c.name = @0
// SORT c.name ASC, c.age DESC RETURN c
ArangoCursor<Customer> findByNameOrderByNameAscAgeDesc(String name);
}
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.
Examples
@Document("customers")
public class Customer {
private Address address;
}
public class Address {
private ZipCode zipCode;
}
public interface MyRepository extends ArangoRepository<Customer, String> {
// 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<Customer> 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.
Examples
@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 ArangoRepository<Customer, String> {
// 1. step: search domain class for a property "addressZipCode"
// 2. step: search domain class for "addressZip.code"
// creates query with "x.addressZip.code"
ArangoCursor<Customer> 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<Customer> findByAddress_ZipCode(ZipCode zipCode);
}
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.
Alternatively, it can be created on a field of one of the following types:
org.springframework.data.geo.Point
org.springframework.data.geo.Polygon
com.arangodb.springframework.core.geo.GeoJsonPoint
com.arangodb.springframework.core.geo.GeoJsonMultiPoint
com.arangodb.springframework.core.geo.GeoJsonLineString
com.arangodb.springframework.core.geo.GeoJsonMultiLineString
com.arangodb.springframework.core.geo.GeoJsonPolygon
com.arangodb.springframework.core.geo.GeoJsonMultiPolygon
These types will be serialized and deserialized to and from GeoJSON geometry
objects. Following the GeoJSON specification, the first coordinate in the point
coordinates list is the longitude and the second one is the latitude.
Note that to create a geospatial index on GeoJSON data field, the geoJson
option should be set to true
, e.g.
class MyEntity {
// ...
@GeoIndexed(geoJson = true)
private Point location;
// ...
}
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.
Examples
public interface MyRepository extends ArangoRepository<City, String> {
GeoResult<City> getByLocationNear(Point point);
GeoResults<City> findByLocationWithinOrLocationWithin(Box box, Polygon polygon);
//Equivalent queries
GeoResults<City> findByLocationWithinOrLocationWithin(Point point, int distance);
GeoResults<City> findByLocationWithinOrLocationWithin(Point point, Distance distance);
GeoResults<City> findByLocationWithinOrLocationWithin(Circle circle);
}