Skip to content

Commit a9306b9

Browse files
christophstroblThomas Darimont
authored andcommitted
DATAMONGO-957 - Add support for query modifiers.
Using Meta allows to the define $comment, $maxScan, $maxTimeMS and $snapshot on query. When executed we add the meta information to the cursor in use. We’ve introduced the @meta annotation that allows to the define $comment, $maxScan, $maxTimeMS and $snapshot on a repository finder method. Added tests to verify proper invocation of template methods Use DBCursor.copy() for CursorPreparer. Original pull request: #216.
1 parent 3597194 commit a9306b9

File tree

10 files changed

+658
-7
lines changed

10 files changed

+658
-7
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2030,6 +2030,7 @@ public FindCallback(DBObject query, DBObject fields) {
20302030
}
20312031

20322032
public DBCursor doInCollection(DBCollection collection) throws MongoException, DataAccessException {
2033+
20332034
if (fields == null || fields.toMap().isEmpty()) {
20342035
return collection.find(query);
20352036
} else {
@@ -2185,11 +2186,11 @@ public DBCursor prepare(DBCursor cursor) {
21852186
}
21862187

21872188
if (query.getSkip() <= 0 && query.getLimit() <= 0 && query.getSortObject() == null
2188-
&& !StringUtils.hasText(query.getHint())) {
2189+
&& !StringUtils.hasText(query.getHint()) && !query.getMeta().hasValues()) {
21892190
return cursor;
21902191
}
21912192

2192-
DBCursor cursorToUse = cursor;
2193+
DBCursor cursorToUse = cursor.copy();
21932194

21942195
try {
21952196
if (query.getSkip() > 0) {
@@ -2205,6 +2206,12 @@ public DBCursor prepare(DBCursor cursor) {
22052206
if (StringUtils.hasText(query.getHint())) {
22062207
cursorToUse = cursorToUse.hint(query.getHint());
22072208
}
2209+
if (query.getMeta().hasValues()) {
2210+
for (Entry<String, Object> entry : query.getMeta().values()) {
2211+
cursorToUse = cursorToUse.addSpecial(entry.getKey(), entry.getValue());
2212+
}
2213+
}
2214+
22082215
} catch (RuntimeException e) {
22092216
throw potentiallyConvertRuntimeException(e);
22102217
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
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.core.query;
17+
18+
import java.util.Collections;
19+
import java.util.LinkedHashMap;
20+
import java.util.Map;
21+
import java.util.Map.Entry;
22+
import java.util.concurrent.TimeUnit;
23+
24+
import org.springframework.util.Assert;
25+
import org.springframework.util.ObjectUtils;
26+
import org.springframework.util.StringUtils;
27+
28+
/**
29+
* Meta-data for {@link Query} instances.
30+
*
31+
* @author Christoph Strobl
32+
* @author Oliver Gierke
33+
* @since 1.6
34+
*/
35+
public class Meta {
36+
37+
private enum MetaKey {
38+
MAX_TIME_MS("$maxTimeMS"), MAX_SCAN("$maxScan"), COMMENT("$comment"), SNAPSHOT("$snapshot");
39+
40+
private String key;
41+
42+
private MetaKey(String key) {
43+
this.key = key;
44+
}
45+
}
46+
47+
private final Map<String, Object> values = new LinkedHashMap<String, Object>(2);
48+
49+
/**
50+
* @return {@literal null} if not set.
51+
*/
52+
public Long getMaxTimeMsec() {
53+
return getValue(MetaKey.MAX_TIME_MS.key);
54+
}
55+
56+
/**
57+
* Set the maximum time limit in milliseconds for processing operations.
58+
*
59+
* @param maxTimeMsec
60+
*/
61+
public void setMaxTimeMsec(long maxTimeMsec) {
62+
setMaxTime(maxTimeMsec, TimeUnit.MILLISECONDS);
63+
}
64+
65+
/**
66+
* Set the maximum time limit for processing operations.
67+
*
68+
* @param timeout
69+
* @param timeUnit
70+
*/
71+
public void setMaxTime(long timeout, TimeUnit timeUnit) {
72+
setValue(MetaKey.MAX_TIME_MS.key, (timeUnit != null ? timeUnit : TimeUnit.MILLISECONDS).toMillis(timeout));
73+
}
74+
75+
/**
76+
* @return {@literal null} if not set.
77+
*/
78+
public Long getMaxScan() {
79+
return getValue(MetaKey.MAX_SCAN.key);
80+
}
81+
82+
/**
83+
* Only scan the specified number of documents.
84+
*
85+
* @param maxScan
86+
*/
87+
public void setMaxScan(long maxScan) {
88+
setValue(MetaKey.MAX_SCAN.key, maxScan);
89+
}
90+
91+
/**
92+
* Add a comment to the query.
93+
*
94+
* @param comment
95+
*/
96+
public void setComment(String comment) {
97+
setValue(MetaKey.COMMENT.key, comment);
98+
}
99+
100+
/**
101+
* @return {@literal null} if not set.
102+
*/
103+
public String getComment() {
104+
return getValue(MetaKey.COMMENT.key);
105+
}
106+
107+
/**
108+
* Using snapshot prevents the cursor from returning a document more than once.
109+
*
110+
* @param useSnapshot
111+
*/
112+
public void setSnapshot(boolean useSnapshot) {
113+
setValue(MetaKey.SNAPSHOT.key, useSnapshot);
114+
}
115+
116+
/**
117+
* @return {@literal null} if not set.
118+
*/
119+
public boolean getSnapshot() {
120+
return getValue(MetaKey.SNAPSHOT.key, false);
121+
}
122+
123+
/**
124+
* @return
125+
*/
126+
public boolean hasValues() {
127+
return !this.values.isEmpty();
128+
}
129+
130+
/**
131+
* Get {@link Iterable} of set meta values.
132+
*
133+
* @return
134+
*/
135+
public Iterable<Entry<String, Object>> values() {
136+
return Collections.unmodifiableSet(this.values.entrySet());
137+
}
138+
139+
/**
140+
* Sets or removes the value in case of {@literal null} or empty {@link String}.
141+
*
142+
* @param key must not be {@literal null} or empty.
143+
* @param value
144+
*/
145+
private void setValue(String key, Object value) {
146+
147+
Assert.hasText(key, "Meta key must not be 'null' or blank.");
148+
149+
if (value == null || (value instanceof String && !StringUtils.hasText((String) value))) {
150+
this.values.remove(key);
151+
}
152+
this.values.put(key, value);
153+
}
154+
155+
@SuppressWarnings("unchecked")
156+
private <T> T getValue(String key) {
157+
return (T) this.values.get(key);
158+
}
159+
160+
private <T> T getValue(String key, T defaultValue) {
161+
162+
T value = getValue(key);
163+
return value != null ? value : defaultValue;
164+
}
165+
166+
/*
167+
* (non-Javadoc)
168+
* @see java.lang.Object#hashCode()
169+
*/
170+
@Override
171+
public int hashCode() {
172+
return ObjectUtils.nullSafeHashCode(this.values);
173+
}
174+
175+
/*
176+
* (non-Javadoc)
177+
* @see java.lang.Object#equals(java.lang.Object)
178+
*/
179+
@Override
180+
public boolean equals(Object obj) {
181+
182+
if (this == obj) {
183+
return true;
184+
}
185+
186+
if (!(obj instanceof Meta)) {
187+
return false;
188+
}
189+
190+
Meta other = (Meta) obj;
191+
return ObjectUtils.nullSafeEquals(this.values, other.values);
192+
}
193+
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.List;
2626
import java.util.Map;
2727
import java.util.Set;
28+
import java.util.concurrent.TimeUnit;
2829

2930
import org.springframework.data.domain.Pageable;
3031
import org.springframework.data.domain.Sort;
@@ -53,6 +54,8 @@ public class Query {
5354
private int limit;
5455
private String hint;
5556

57+
private Meta meta = new Meta();
58+
5659
/**
5760
* Static factory method to create a {@link Query} using the provided {@link CriteriaDefinition}.
5861
*
@@ -275,6 +278,84 @@ public String getHint() {
275278
return hint;
276279
}
277280

281+
/**
282+
* @param maxTimeMsec
283+
* @return
284+
* @see Meta#setMaxTimeMsec(long)
285+
* @since 1.6
286+
*/
287+
public Query maxTimeMsec(long maxTimeMsec) {
288+
289+
meta.setMaxTimeMsec(maxTimeMsec);
290+
return this;
291+
}
292+
293+
/**
294+
* @param timeout
295+
* @param timeUnit
296+
* @return
297+
* @see Meta#setMaxTime(long, TimeUnit)
298+
* @since 1.6
299+
*/
300+
public Query maxTime(long timeout, TimeUnit timeUnit) {
301+
302+
meta.setMaxTime(timeout, timeUnit);
303+
return this;
304+
}
305+
306+
/**
307+
* @param maxScan
308+
* @return
309+
* @see Meta#setMaxScan(long)
310+
* @since 1.6
311+
*/
312+
public Query maxScan(long maxScan) {
313+
314+
meta.setMaxScan(maxScan);
315+
return this;
316+
}
317+
318+
/**
319+
* @param comment
320+
* @return
321+
* @see Meta#setComment(String)
322+
* @since 1.6
323+
*/
324+
public Query comment(String comment) {
325+
326+
meta.setComment(comment);
327+
return this;
328+
}
329+
330+
/**
331+
* @return
332+
* @see Meta#setSnapshot(boolean)
333+
* @since 1.6
334+
*/
335+
public Query useSnapshot() {
336+
337+
meta.setSnapshot(true);
338+
return this;
339+
}
340+
341+
/**
342+
* @return never {@literal null}.
343+
* @since 1.6
344+
*/
345+
public Meta getMeta() {
346+
return meta;
347+
}
348+
349+
/**
350+
* @param meta must not be {@literal null}.
351+
* @since 1.6
352+
*/
353+
public void setMeta(Meta meta) {
354+
355+
Assert.notNull(meta, "Query meta might be empty but must not be null.");
356+
this.meta = meta;
357+
}
358+
278359
protected List<CriteriaDefinition> getCriteria() {
279360
return new ArrayList<CriteriaDefinition>(this.criteria.values());
280361
}
@@ -312,8 +393,9 @@ public boolean equals(Object obj) {
312393
boolean hintEqual = this.hint == null ? that.hint == null : this.hint.equals(that.hint);
313394
boolean skipEqual = this.skip == that.skip;
314395
boolean limitEqual = this.limit == that.limit;
396+
boolean metaEqual = nullSafeEquals(this.meta, that.meta);
315397

316-
return criteriaEqual && fieldsEqual && sortEqual && hintEqual && skipEqual && limitEqual;
398+
return criteriaEqual && fieldsEqual && sortEqual && hintEqual && skipEqual && limitEqual && metaEqual;
317399
}
318400

319401
/*
@@ -331,6 +413,7 @@ public int hashCode() {
331413
result += 31 * nullSafeHashCode(hint);
332414
result += 31 * skip;
333415
result += 31 * limit;
416+
result += 31 * nullSafeHashCode(meta);
334417

335418
return result;
336419
}

0 commit comments

Comments
 (0)