/*
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.repository.query;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import static org.springframework.data.mongodb.repository.query.StubParameterAccessor.*;
import java.lang.reflect.Method;
import java.util.List;
import org.bson.types.ObjectId;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.data.domain.Range;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Polygon;
import org.springframework.data.geo.Shape;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.Person;
import org.springframework.data.mongodb.core.Venue;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.geo.GeoJsonLineString;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.query.parser.PartTree;
import org.bson.Document;
/**
* Unit test for {@link MongoQueryCreator}.
*
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
*/
public class MongoQueryCreatorUnitTests {
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context;
MongoConverter converter;
@Rule public ExpectedException expection = ExpectedException.none();
@Before
public void setUp() throws SecurityException, NoSuchMethodException {
context = new MongoMappingContext();
DbRefResolver resolver = new DefaultDbRefResolver(mock(MongoDbFactory.class));
converter = new MappingMongoConverter(resolver, context);
}
@Test
public void createsQueryCorrectly() throws Exception {
PartTree tree = new PartTree("findByFirstName", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Oliver"), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("firstName").is("Oliver"))));
}
@Test // DATAMONGO-469
public void createsAndQueryCorrectly() {
Person person = new Person();
MongoQueryCreator creator = new MongoQueryCreator(new PartTree("findByFirstNameAndFriend", Person.class),
getAccessor(converter, "Oliver", person), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("firstName").is("Oliver").and("friend").is(person))));
}
@Test
public void createsNotNullQueryCorrectly() {
PartTree tree = new PartTree("findByFirstNameNotNull", Person.class);
Query query = new MongoQueryCreator(tree, getAccessor(converter), context).createQuery();
assertThat(query, is(new Query(Criteria.where("firstName").ne(null))));
}
@Test
public void createsIsNullQueryCorrectly() {
PartTree tree = new PartTree("findByFirstNameIsNull", Person.class);
Query query = new MongoQueryCreator(tree, getAccessor(converter), context).createQuery();
assertThat(query, is(new Query(Criteria.where("firstName").is(null))));
}
@Test
public void bindsMetricDistanceParameterToNearSphereCorrectly() throws Exception {
Point point = new Point(10, 20);
Distance distance = new Distance(2.5, Metrics.KILOMETERS);
Query query = query(
where("location").nearSphere(point).maxDistance(distance.getNormalizedValue()).and("firstname").is("Dave"));
assertBindsDistanceToQuery(point, distance, query);
}
@Test
public void bindsDistanceParameterToNearCorrectly() throws Exception {
Point point = new Point(10, 20);
Distance distance = new Distance(2.5);
Query query = query(
where("location").near(point).maxDistance(distance.getNormalizedValue()).and("firstname").is("Dave"));
assertBindsDistanceToQuery(point, distance, query);
}
@Test
public void createsLessThanEqualQueryCorrectly() throws Exception {
PartTree tree = new PartTree("findByAgeLessThanEqual", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, 18), context);
Query reference = query(where("age").lte(18));
assertThat(creator.createQuery(), is(reference));
}
@Test
public void createsGreaterThanEqualQueryCorrectly() throws Exception {
PartTree tree = new PartTree("findByAgeGreaterThanEqual", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, 18), context);
Query reference = query(where("age").gte(18));
assertThat(creator.createQuery(), is(reference));
}
@Test // DATAMONGO-338
public void createsExistsClauseCorrectly() {
PartTree tree = new PartTree("findByAgeExists", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, true), context);
Query query = query(where("age").exists(true));
assertThat(creator.createQuery(), is(query));
}
@Test // DATAMONGO-338
public void createsRegexClauseCorrectly() {
PartTree tree = new PartTree("findByFirstNameRegex", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, ".*"), context);
Query query = query(where("firstName").regex(".*"));
assertThat(creator.createQuery(), is(query));
}
@Test // DATAMONGO-338
public void createsTrueClauseCorrectly() {
PartTree tree = new PartTree("findByActiveTrue", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter), context);
Query query = query(where("active").is(true));
assertThat(creator.createQuery(), is(query));
}
@Test // DATAMONGO-338
public void createsFalseClauseCorrectly() {
PartTree tree = new PartTree("findByActiveFalse", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter), context);
Query query = query(where("active").is(false));
assertThat(creator.createQuery(), is(query));
}
@Test // DATAMONGO-413
public void createsOrQueryCorrectly() {
PartTree tree = new PartTree("findByFirstNameOrAge", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Dave", 42), context);
Query query = creator.createQuery();
assertThat(query, is(query(new Criteria().orOperator(where("firstName").is("Dave"), where("age").is(42)))));
}
@Test // DATAMONGO-347
public void createsQueryReferencingADBRefCorrectly() {
User user = new User();
user.id = new ObjectId();
PartTree tree = new PartTree("findByCreator", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, user), context);
Document queryObject = creator.createQuery().getQueryObject();
assertThat(queryObject.get("creator"), is((Object) user));
}
@Test // DATAMONGO-418
public void createsQueryWithStartingWithPredicateCorrectly() {
PartTree tree = new PartTree("findByUsernameStartingWith", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Matt"), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("username").regex("^Matt"))));
}
@Test // DATAMONGO-418
public void createsQueryWithEndingWithPredicateCorrectly() {
PartTree tree = new PartTree("findByUsernameEndingWith", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "ews"), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("username").regex("ews$"))));
}
@Test // DATAMONGO-418
public void createsQueryWithContainingPredicateCorrectly() {
PartTree tree = new PartTree("findByUsernameContaining", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("username").regex(".*thew.*"))));
}
private void assertBindsDistanceToQuery(Point point, Distance distance, Query reference) throws Exception {
PartTree tree = new PartTree("findByLocationNearAndFirstname",
org.springframework.data.mongodb.repository.Person.class);
Method method = PersonRepository.class.getMethod("findByLocationNearAndFirstname", Point.class, Distance.class,
String.class);
MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(PersonRepository.class),
new SpelAwareProxyProjectionFactory(), new MongoMappingContext());
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(queryMethod,
new Object[] { point, distance, "Dave" });
Query query = new MongoQueryCreator(tree, new ConvertingParameterAccessor(converter, accessor), context)
.createQuery();
assertThat(query, is(query));
}
@Test // DATAMONGO-770
public void createsQueryWithFindByIgnoreCaseCorrectly() {
PartTree tree = new PartTree("findByfirstNameIgnoreCase", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("firstName").regex("^dave$", "i"))));
}
@Test // DATAMONGO-770
public void createsQueryWithFindByNotIgnoreCaseCorrectly() {
PartTree tree = new PartTree("findByFirstNameNotIgnoreCase", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context);
Query query = creator.createQuery();
assertThat(query.toString(), is(query(where("firstName").not().regex("^dave$", "i")).toString()));
}
@Test // DATAMONGO-770
public void createsQueryWithFindByStartingWithIgnoreCaseCorrectly() {
PartTree tree = new PartTree("findByFirstNameStartingWithIgnoreCase", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("firstName").regex("^dave", "i"))));
}
@Test // DATAMONGO-770
public void createsQueryWithFindByEndingWithIgnoreCaseCorrectly() {
PartTree tree = new PartTree("findByFirstNameEndingWithIgnoreCase", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("firstName").regex("dave$", "i"))));
}
@Test // DATAMONGO-770
public void createsQueryWithFindByContainingIgnoreCaseCorrectly() {
PartTree tree = new PartTree("findByFirstNameContainingIgnoreCase", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("firstName").regex(".*dave.*", "i"))));
}
@Test // DATAMONGO-770
public void shouldThrowExceptionForQueryWithFindByIgnoreCaseOnNonStringProperty() {
expection.expect(IllegalArgumentException.class);
expection.expectMessage("must be of type String");
PartTree tree = new PartTree("findByFirstNameAndAgeIgnoreCase", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "foo", 42), context);
creator.createQuery();
}
@Test // DATAMONGO-770
public void shouldOnlyGenerateLikeExpressionsForStringPropertiesIfAllIgnoreCase() {
PartTree tree = new PartTree("findByFirstNameAndAgeAllIgnoreCase", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave", 42), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("firstName").regex("^dave$", "i").and("age").is(42))));
}
@Test // DATAMONGO-566
public void shouldCreateDeleteByQueryCorrectly() {
PartTree tree = new PartTree("deleteByFirstName", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave", 42), context);
Query query = creator.createQuery();
assertThat(tree.isDelete(), is(true));
assertThat(query, is(query(where("firstName").is("dave"))));
}
@Test // DATAMONGO-566
public void shouldCreateDeleteByQueryCorrectlyForMultipleCriteriaAndCaseExpressions() {
PartTree tree = new PartTree("deleteByFirstNameAndAgeAllIgnoreCase", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave", 42), context);
Query query = creator.createQuery();
assertThat(tree.isDelete(), is(true));
assertThat(query, is(query(where("firstName").regex("^dave$", "i").and("age").is(42))));
}
@Test // DATAMONGO-1075
public void shouldCreateInClauseWhenUsingContainsOnCollectionLikeProperty() {
PartTree tree = new PartTree("findByEmailAddressesContaining", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("emailAddresses").in("dave"))));
}
@Test // DATAMONGO-1075
public void shouldCreateInClauseWhenUsingNotContainsOnCollectionLikeProperty() {
PartTree tree = new PartTree("findByEmailAddressesNotContaining", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("emailAddresses").not().in("dave"))));
}
@Test // DATAMONGO-1075, DATAMONGO-1425
public void shouldCreateRegexWhenUsingNotContainsOnStringProperty() {
PartTree tree = new PartTree("findByUsernameNotContaining", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context);
Query query = creator.createQuery();
assertThat(query.getQueryObject().toJson(), is(query(where("username").not().regex(".*thew.*")).getQueryObject().toJson()));
}
@Test // DATAMONGO-1139
public void createsNonSphericalNearForDistanceWithDefaultMetric() {
Point point = new Point(1.0, 1.0);
Distance distance = new Distance(1.0);
PartTree tree = new PartTree("findByLocationNear", Venue.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, distance), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("location").near(point).maxDistance(1.0))));
}
@Test // DATAMONGO-1136
public void shouldCreateWithinQueryCorrectly() {
Point first = new Point(1, 1);
Point second = new Point(2, 2);
Point third = new Point(3, 3);
Shape shape = new Polygon(first, second, third);
PartTree tree = new PartTree("findByAddress_GeoWithin", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, shape), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("address.geo").within(shape))));
}
@Test // DATAMONGO-1110
public void shouldCreateNearSphereQueryForSphericalProperty() {
Point point = new Point(10, 20);
PartTree tree = new PartTree("findByAddress2dSphere_GeoNear", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("address2dSphere.geo").nearSphere(point))));
}
@Test // DATAMONGO-1110
public void shouldCreateNearSphereQueryForSphericalPropertyHavingDistanceWithDefaultMetric() {
Point point = new Point(1.0, 1.0);
Distance distance = new Distance(1.0);
PartTree tree = new PartTree("findByAddress2dSphere_GeoNear", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, distance), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("address2dSphere.geo").nearSphere(point).maxDistance(1.0))));
}
@Test // DATAMONGO-1110
public void shouldCreateNearQueryForMinMaxDistance() {
Point point = new Point(10, 20);
Range<Distance> range = Distance.between(new Distance(10), new Distance(20));
PartTree tree = new PartTree("findByAddress_GeoNear", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, point, range), context);
Query query = creator.createQuery();
assertThat(query, is(query(where("address.geo").near(point).minDistance(10D).maxDistance(20D))));
}
@Test // DATAMONGO-1229
public void appliesIgnoreCaseToLeafProperty() {
PartTree tree = new PartTree("findByAddressStreetIgnoreCase", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "Street");
assertThat(new MongoQueryCreator(tree, accessor, context).createQuery(), is(notNullValue()));
}
@Test // DATAMONGO-1232
public void ignoreCaseShouldEscapeSource() {
PartTree tree = new PartTree("findByUsernameIgnoreCase", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "con.flux+");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query, is(query(where("username").regex("^\\Qcon.flux+\\E$", "i"))));
}
@Test // DATAMONGO-1232
public void ignoreCaseShouldEscapeSourceWhenUsedForStartingWith() {
PartTree tree = new PartTree("findByUsernameStartingWithIgnoreCase", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "dawns.light+");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query, is(query(where("username").regex("^\\Qdawns.light+\\E", "i"))));
}
@Test // DATAMONGO-1232
public void ignoreCaseShouldEscapeSourceWhenUsedForEndingWith() {
PartTree tree = new PartTree("findByUsernameEndingWithIgnoreCase", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "new.ton+");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query, is(query(where("username").regex("\\Qnew.ton+\\E$", "i"))));
}
@Test // DATAMONGO-1232
public void likeShouldEscapeSourceWhenUsedWithLeadingAndTrailingWildcard() {
PartTree tree = new PartTree("findByUsernameLike", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "*fire.fight+*");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query, is(query(where("username").regex(".*\\Qfire.fight+\\E.*"))));
}
@Test // DATAMONGO-1232
public void likeShouldEscapeSourceWhenUsedWithLeadingWildcard() {
PartTree tree = new PartTree("findByUsernameLike", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "*steel.heart+");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query, is(query(where("username").regex(".*\\Qsteel.heart+\\E"))));
}
@Test // DATAMONGO-1232
public void likeShouldEscapeSourceWhenUsedWithTrailingWildcard() {
PartTree tree = new PartTree("findByUsernameLike", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "cala.mity+*");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query, is(query(where("username").regex("\\Qcala.mity+\\E.*"))));
}
@Test // DATAMONGO-1232
public void likeShouldBeTreatedCorrectlyWhenUsedWithWildcardOnly() {
PartTree tree = new PartTree("findByUsernameLike", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "*");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query, is(query(where("username").regex(".*"))));
}
@Test // DATAMONGO-1342
public void bindsNullValueToContainsClause() {
PartTree partTree = new PartTree("emailAddressesContains", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, new Object[] { null });
Query query = new MongoQueryCreator(partTree, accessor, context).createQuery();
assertThat(query, is(query(where("emailAddresses").in((Object) null))));
}
@Test // DATAMONGO-1424
public void notLikeShouldEscapeSourceWhenUsedWithLeadingAndTrailingWildcard() {
PartTree tree = new PartTree("findByUsernameNotLike", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "*fire.fight+*");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query.getQueryObject().toJson(),
is(query(where("username").not().regex(".*\\Qfire.fight+\\E.*")).getQueryObject().toJson()));
}
@Test // DATAMONGO-1424
public void notLikeShouldEscapeSourceWhenUsedWithLeadingWildcard() {
PartTree tree = new PartTree("findByUsernameNotLike", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "*steel.heart+");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query.getQueryObject().toJson(),
is(query(where("username").not().regex(".*\\Qsteel.heart+\\E")).getQueryObject().toJson()));
}
@Test // DATAMONGO-1424
public void notLikeShouldEscapeSourceWhenUsedWithTrailingWildcard() {
PartTree tree = new PartTree("findByUsernameNotLike", User.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "cala.mity+*"), context);
Query query = creator.createQuery();
assertThat(query.getQueryObject().toJson(), is(query(where("username").not().regex("\\Qcala.mity+\\E.*")).getQueryObject().toJson()));
}
@Test // DATAMONGO-1424
public void notLikeShouldBeTreatedCorrectlyWhenUsedWithWildcardOnly() {
PartTree tree = new PartTree("findByUsernameNotLike", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, "*");
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query.getQueryObject().toJson(), is(query(where("username").not().regex(".*")).getQueryObject().toJson()));
}
@Test // DATAMONGO-1588
public void queryShouldAcceptSubclassOfDeclaredArgument() {
PartTree tree = new PartTree("findByLocationNear", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter, new GeoJsonPoint(-74.044502D, 40.689247D));
Query query = new MongoQueryCreator(tree, accessor, context).createQuery();
assertThat(query.getQueryObject().containsKey("location"), is(true));
}
@Test // DATAMONGO-1588
public void queryShouldThrowExceptionWhenArgumentDoesNotMatchDeclaration() {
expection.expect(IllegalArgumentException.class);
expection.expectMessage("Expected parameter type of " + Point.class);
PartTree tree = new PartTree("findByLocationNear", User.class);
ConvertingParameterAccessor accessor = getAccessor(converter,
new GeoJsonLineString(new Point(-74.044502D, 40.689247D), new Point(-73.997330D, 40.730824D)));
new MongoQueryCreator(tree, accessor, context).createQuery();
}
interface PersonRepository extends Repository<Person, Long> {
List<Person> findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname);
}
class User {
ObjectId id;
@Field("foo") String username;
@DBRef User creator;
List<String> emailAddresses;
Address address;
Address2dSphere address2dSphere;
Point location;
}
static class Address {
String street;
Point geo;
}
static class Address2dSphere {
String street;
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) Point geo;
}
}