Skip to content

Commit 2cbe7bf

Browse files
christophstroblodrotbohm
authored andcommitted
DATAMONGO-952 - Derived queries should consider field specification in @query.
PartTreeMongoQuery now explicitly check the presence of a manually defined field spec on the query method and creates a new Query if so. Original pull request: spring-projects#188.
1 parent 6043f6b commit 2cbe7bf

File tree

2 files changed

+188
-1
lines changed

2 files changed

+188
-1
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,14 @@
1919
import org.springframework.data.mongodb.core.MongoOperations;
2020
import org.springframework.data.mongodb.core.MongoTemplate;
2121
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
22+
import org.springframework.data.mongodb.core.query.BasicQuery;
2223
import org.springframework.data.mongodb.core.query.Query;
2324
import org.springframework.data.repository.query.QueryMethod;
2425
import org.springframework.data.repository.query.RepositoryQuery;
2526
import org.springframework.data.repository.query.parser.PartTree;
27+
import org.springframework.util.StringUtils;
28+
29+
import com.mongodb.util.JSONParseException;
2630

2731
/**
2832
* {@link RepositoryQuery} implementation for Mongo.
@@ -67,7 +71,24 @@ public PartTree getTree() {
6771
protected Query createQuery(ConvertingParameterAccessor accessor) {
6872

6973
MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, isGeoNearQuery);
70-
return creator.createQuery();
74+
Query query = creator.createQuery();
75+
76+
String fieldSpec = this.getQueryMethod().getFieldSpecification();
77+
78+
if (!StringUtils.hasText(fieldSpec)) {
79+
return query;
80+
}
81+
82+
try {
83+
84+
BasicQuery result = new BasicQuery(query.getQueryObject().toString(), fieldSpec);
85+
result.setSortObject(query.getSortObject());
86+
return result;
87+
88+
} catch (JSONParseException o_O) {
89+
throw new IllegalStateException(String.format("Invalid query or field specification in %s!", getQueryMethod(),
90+
o_O));
91+
}
7192
}
7293

7394
/*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Copyright 2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.repository.query;
17+
18+
import static org.hamcrest.CoreMatchers.*;
19+
import static org.junit.Assert.*;
20+
import static org.mockito.Mockito.*;
21+
22+
import java.lang.reflect.Method;
23+
24+
import org.junit.Before;
25+
import org.junit.Rule;
26+
import org.junit.Test;
27+
import org.junit.rules.ExpectedException;
28+
import org.junit.runner.RunWith;
29+
import org.mockito.Matchers;
30+
import org.mockito.Mock;
31+
import org.mockito.runners.MockitoJUnitRunner;
32+
import org.springframework.data.mongodb.MongoDbFactory;
33+
import org.springframework.data.mongodb.core.MongoOperations;
34+
import org.springframework.data.mongodb.core.convert.DbRefResolver;
35+
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
36+
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
37+
import org.springframework.data.mongodb.core.convert.MongoConverter;
38+
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
39+
import org.springframework.data.mongodb.repository.MongoRepository;
40+
import org.springframework.data.mongodb.repository.Person;
41+
import org.springframework.data.mongodb.repository.Query;
42+
import org.springframework.data.repository.core.RepositoryMetadata;
43+
44+
import com.mongodb.BasicDBObjectBuilder;
45+
46+
/**
47+
* Unit tests for {@link PartTreeMongoQuery}.
48+
*
49+
* @author Christoph Strobl
50+
* @author Oliver Gierke
51+
*/
52+
@RunWith(MockitoJUnitRunner.class)
53+
public class PartTreeMongoQueryUnitTests {
54+
55+
@Mock RepositoryMetadata metadataMock;
56+
@Mock MongoOperations mongoOperationsMock;
57+
58+
MongoMappingContext mappingContext;
59+
60+
public @Rule ExpectedException exception = ExpectedException.none();
61+
62+
@Before
63+
@SuppressWarnings({ "unchecked", "rawtypes" })
64+
public void setUp() {
65+
66+
when(metadataMock.getDomainType()).thenReturn((Class) Person.class);
67+
when(metadataMock.getReturnedDomainClass(Matchers.any(Method.class))).thenReturn((Class) Person.class);
68+
mappingContext = new MongoMappingContext();
69+
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mock(MongoDbFactory.class));
70+
MongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext);
71+
72+
when(mongoOperationsMock.getConverter()).thenReturn(converter);
73+
}
74+
75+
/**
76+
* @see DATAMOGO-952
77+
*/
78+
@Test
79+
public void rejectsInvalidFieldSpecification() {
80+
81+
exception.expect(IllegalStateException.class);
82+
exception.expectMessage("findByLastname");
83+
84+
deriveQueryFromMethod("findByLastname", new Object[] { "foo" });
85+
}
86+
87+
/**
88+
* @see DATAMOGO-952
89+
*/
90+
@Test
91+
public void singleFieldJsonIncludeRestrictionShouldBeConsidered() {
92+
93+
org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findByFirstname",
94+
new Object[] { "foo" });
95+
96+
assertThat(query.getFieldsObject(), is(new BasicDBObjectBuilder().add("firstname", 1).get()));
97+
}
98+
99+
/**
100+
* @see DATAMOGO-952
101+
*/
102+
@Test
103+
public void multiFieldJsonIncludeRestrictionShouldBeConsidered() {
104+
105+
org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findByFirstnameAndLastname",
106+
new Object[] { "foo", "bar" });
107+
108+
assertThat(query.getFieldsObject(), is(new BasicDBObjectBuilder().add("firstname", 1).add("lastname", 1).get()));
109+
}
110+
111+
/**
112+
* @see DATAMOGO-952
113+
*/
114+
@Test
115+
public void multiFieldJsonExcludeRestrictionShouldBeConsidered() {
116+
117+
org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findPersonByFirstnameAndLastname",
118+
new Object[] { "foo", "bar" });
119+
120+
assertThat(query.getFieldsObject(), is(new BasicDBObjectBuilder().add("firstname", 0).add("lastname", 0).get()));
121+
}
122+
123+
private org.springframework.data.mongodb.core.query.Query deriveQueryFromMethod(String method, Object[] args) {
124+
125+
Class<?>[] types = new Class<?>[args.length];
126+
127+
for (int i = 0; i < args.length; i++) {
128+
types[i] = args[i].getClass();
129+
}
130+
131+
PartTreeMongoQuery partTreeQuery = createQueryForMethod(method, types);
132+
133+
MongoParameterAccessor accessor = new MongoParametersParameterAccessor(partTreeQuery.getQueryMethod(), args);
134+
return partTreeQuery.createQuery(new ConvertingParameterAccessor(mongoOperationsMock.getConverter(), accessor));
135+
}
136+
137+
private PartTreeMongoQuery createQueryForMethod(String methodName, Class<?>... paramTypes) {
138+
139+
try {
140+
141+
Method method = Repo.class.getMethod(methodName, paramTypes);
142+
MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadataMock, mappingContext);
143+
144+
return new PartTreeMongoQuery(queryMethod, mongoOperationsMock);
145+
} catch (NoSuchMethodException e) {
146+
throw new IllegalArgumentException(e.getMessage(), e);
147+
} catch (SecurityException e) {
148+
throw new IllegalArgumentException(e.getMessage(), e);
149+
}
150+
}
151+
152+
interface Repo extends MongoRepository<Person, Long> {
153+
154+
@Query(fields = "firstname")
155+
Person findByLastname(String lastname);
156+
157+
@Query(fields = "{ 'firstname' : 1 }")
158+
Person findByFirstname(String lastname);
159+
160+
@Query(fields = "{ 'firstname' : 1, 'lastname' : 1 }")
161+
Person findByFirstnameAndLastname(String firstname, String lastname);
162+
163+
@Query(fields = "{ 'firstname' : 0, 'lastname' : 0 }")
164+
Person findPersonByFirstnameAndLastname(String firstname, String lastname);
165+
}
166+
}

0 commit comments

Comments
 (0)