Browse Source

Switch `Meta.allowDiskUse` from `boolean` to `String`.

Closes: #4667
Original pull request: #5035
pull/5044/head
Christoph Strobl 5 months ago committed by Mark Paluch
parent
commit
1413f4349b
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 48
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOptions.java
  2. 78
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/DiskUse.java
  3. 14
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java
  4. 7
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java
  5. 10
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Meta.java
  6. 7
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/QueryBlocks.java
  7. 6
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java
  8. 12
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributorUnitTests.java
  9. 2
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java

48
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOptions.java

@ -23,6 +23,7 @@ import org.jspecify.annotations.Nullable; @@ -23,6 +23,7 @@ import org.jspecify.annotations.Nullable;
import org.springframework.data.mongodb.core.ReadConcernAware;
import org.springframework.data.mongodb.core.ReadPreferenceAware;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.DiskUse;
import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.lang.Contract;
import org.springframework.util.Assert;
@ -60,7 +61,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware @@ -60,7 +61,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
private static final String MAX_TIME = "maxTimeMS";
private static final String HINT = "hint";
private final Optional<Boolean> allowDiskUse;
private final DiskUse diskUse;
private final boolean explain;
private final Optional<Document> cursor;
private final Optional<Collation> collation;
@ -85,6 +86,15 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware @@ -85,6 +86,15 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
this(allowDiskUse, explain, cursor, null);
}
public AggregationOptions(DiskUse diskUse, boolean explain, @Nullable Document cursor) {
this(diskUse, explain, cursor, null);
}
public AggregationOptions(DiskUse allowDiskUse, boolean explain, @Nullable Document cursor,
@Nullable Collation collation) {
this(allowDiskUse, explain, cursor, collation, null);
}
/**
* Creates a new {@link AggregationOptions}.
*
@ -97,7 +107,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware @@ -97,7 +107,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
*/
public AggregationOptions(boolean allowDiskUse, boolean explain, @Nullable Document cursor,
@Nullable Collation collation) {
this(allowDiskUse, explain, cursor, collation, null, null);
this(DiskUse.of(allowDiskUse), explain, cursor, collation, null, null);
}
/**
@ -113,13 +123,18 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware @@ -113,13 +123,18 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
*/
public AggregationOptions(boolean allowDiskUse, boolean explain, @Nullable Document cursor,
@Nullable Collation collation, @Nullable String comment) {
this(allowDiskUse ? DiskUse.ALLOW : DiskUse.DENY, explain, cursor, collation, comment, null);
}
public AggregationOptions(DiskUse allowDiskUse, boolean explain, @Nullable Document cursor,
@Nullable Collation collation, @Nullable String comment) {
this(allowDiskUse, explain, cursor, collation, comment, null);
}
/**
* Creates a new {@link AggregationOptions}.
*
* @param allowDiskUse whether to off-load intensive sort-operations to disk.
* @param diskUse whether to off-load intensive sort-operations to disk.
* @param explain whether to get the execution plan for the aggregation instead of the actual results.
* @param cursor can be {@literal null}, used to pass additional options (such as {@code batchSize}) to the
* aggregation.
@ -128,10 +143,10 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware @@ -128,10 +143,10 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
* @param hint can be {@literal null}, used to provide an index that would be forcibly used by query optimizer.
* @since 3.1
*/
private AggregationOptions(@Nullable Boolean allowDiskUse, boolean explain, @Nullable Document cursor,
private AggregationOptions(DiskUse diskUse, boolean explain, @Nullable Document cursor,
@Nullable Collation collation, @Nullable String comment, @Nullable Object hint) {
this.allowDiskUse = Optional.ofNullable(allowDiskUse);
this.diskUse = diskUse;
this.explain = explain;
this.cursor = Optional.ofNullable(cursor);
this.collation = Optional.ofNullable(collation);
@ -172,7 +187,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware @@ -172,7 +187,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
String comment = document.getString(COMMENT);
Document hint = document.get(HINT, Document.class);
AggregationOptions options = new AggregationOptions(allowDiskUse, explain, cursor, collation, comment, hint);
AggregationOptions options = new AggregationOptions(DiskUse.of(allowDiskUse) , explain, cursor, collation, comment, hint);
if (document.containsKey(MAX_TIME)) {
options.maxTime = Duration.ofMillis(document.getLong(MAX_TIME));
}
@ -196,7 +211,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware @@ -196,7 +211,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
* @return {@literal true} if enabled; {@literal false} otherwise (or if not set).
*/
public boolean isAllowDiskUse() {
return allowDiskUse.orElse(false);
return diskUse.equals(DiskUse.ALLOW);
}
/**
@ -206,7 +221,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware @@ -206,7 +221,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
* @since 4.2.5
*/
public boolean isAllowDiskUseSet() {
return allowDiskUse.isPresent();
return !diskUse.equals(DiskUse.DEFAULT);
}
/**
@ -427,7 +442,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware @@ -427,7 +442,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
*/
public static class Builder {
private @Nullable Boolean allowDiskUse;
private @Nullable DiskUse diskUse = DiskUse.DEFAULT;
private boolean explain;
private @Nullable Document cursor;
private @Nullable Collation collation;
@ -447,8 +462,19 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware @@ -447,8 +462,19 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
*/
@Contract("_ -> this")
public Builder allowDiskUse(boolean allowDiskUse) {
return diskUse(DiskUse.of(allowDiskUse));
}
/**
* Defines whether to off-load intensive sort-operations to disk.
*
* @param diskUse use {@literal true} to allow disk use during the aggregation.
* @return this.
*/
@Contract("_ -> this")
public Builder diskUse(DiskUse diskUse) {
this.allowDiskUse = allowDiskUse;
this.diskUse = diskUse;
return this;
}
@ -655,7 +681,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware @@ -655,7 +681,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware
@Contract("-> new")
public AggregationOptions build() {
AggregationOptions options = new AggregationOptions(allowDiskUse, explain, cursor, collation, comment, hint);
AggregationOptions options = new AggregationOptions(diskUse, explain, cursor, collation, comment, hint);
if (maxTime != null) {
options.maxTime = maxTime;
}

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

@ -0,0 +1,78 @@ @@ -0,0 +1,78 @@
/*
* Copyright 2025-present 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
*
* https://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.core.query;
import org.jspecify.annotations.Nullable;
import org.springframework.util.StringUtils;
/**
* Disk use indicates if the MongoDB server is allowed to write temporary files to disk during query/aggregation
* execution. MongoDB 6.0 server (and later) default for {@literal allowDiskUseByDefault} is {@literal true} on server
* side.
*
* @author Christoph Strobl
* @since 5.0
*/
public enum DiskUse {
/**
* Go with the server default value and do not specify any override.
*/
DEFAULT,
/**
* Override server default value and explicitly allow disk writes.
*/
ALLOW,
/**
* Override server default value and explicitly deny disk writes.
*/
DENY;
/**
* Obtain the {@link DiskUse} corresponding to the given Boolean flag. {@literal null} is considered {@link #DEFAULT},
* {@literal true} as {@link #ALLOW}, {@literal false} as {@link #DENY}.
*
* @param value can be {@literal null}.
* @return the {@link DiskUse} corresponding to the given value.
*/
public static DiskUse of(@Nullable Boolean value) {
return value != null ? (value ? ALLOW : DENY) : DEFAULT;
}
/**
* Obtain the {@link DiskUse} referred to by the given value. Considers {@literal null} or empty Strings as
* {@link #DEFAULT}, {@literal true} as {@link #ALLOW}, {@literal false} as {@link #DENY} and delegates other values
* to {@link #valueOf(String)}.
*
* @param value can be {@literal null}.
* @return the {@link DiskUse} corresponding to the given value.
* @throws IllegalArgumentException if not matching {@link DiskUse} found.
*/
public static DiskUse of(@Nullable String value) {
if (!StringUtils.hasText(value)) {
return DEFAULT;
}
return switch (value) {
case "true" -> ALLOW;
case "false" -> DENY;
default -> valueOf(value.toUpperCase());
};
}
}

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

@ -51,7 +51,7 @@ public class Meta { @@ -51,7 +51,7 @@ public class Meta {
private Map<String, Object> values = Collections.emptyMap();
private Set<CursorOption> flags = Collections.emptySet();
private @Nullable Integer cursorBatchSize;
private @Nullable Boolean allowDiskUse;
private DiskUse diskUse = DiskUse.DEFAULT;
public Meta() {}
@ -66,7 +66,7 @@ public class Meta { @@ -66,7 +66,7 @@ public class Meta {
this.values = new LinkedHashMap<>(source.values);
this.flags = new LinkedHashSet<>(source.flags);
this.cursorBatchSize = source.cursorBatchSize;
this.allowDiskUse = source.allowDiskUse;
this.diskUse = source.diskUse;
}
/**
@ -230,7 +230,7 @@ public class Meta { @@ -230,7 +230,7 @@ public class Meta {
*/
@Nullable
public Boolean getAllowDiskUse() {
return allowDiskUse;
return diskUse.equals(DiskUse.DEFAULT) ? null : diskUse.equals(DiskUse.ALLOW);
}
/**
@ -244,14 +244,18 @@ public class Meta { @@ -244,14 +244,18 @@ public class Meta {
* @since 3.0
*/
public void setAllowDiskUse(@Nullable Boolean allowDiskUse) {
this.allowDiskUse = allowDiskUse;
setDiskUse(DiskUse.of(allowDiskUse));
}
public void setDiskUse(DiskUse diskUse) {
this.diskUse = diskUse;
}
/**
* @return
*/
public boolean hasValues() {
return !this.values.isEmpty() || !this.flags.isEmpty() || this.cursorBatchSize != null || this.allowDiskUse != null;
return !this.values.isEmpty() || !this.flags.isEmpty() || this.cursorBatchSize != null || !this.diskUse.equals(DiskUse.DEFAULT);
}
/**

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

@ -582,8 +582,13 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { @@ -582,8 +582,13 @@ public class Query implements ReadConcernAware, ReadPreferenceAware {
*/
@Contract("_ -> this")
public Query allowDiskUse(boolean allowDiskUse) {
return diskUse(DiskUse.of(allowDiskUse));
}
@Contract("_ -> this")
public Query diskUse(DiskUse diskUse) {
meta.setAllowDiskUse(allowDiskUse);
meta.setDiskUse(diskUse);
return this;
}

10
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Meta.java

@ -69,11 +69,17 @@ public @interface Meta { @@ -69,11 +69,17 @@ public @interface Meta {
/**
* When set to {@literal true}, aggregation stages can write data to disk.
* Valid arguments are:
* <dl>
* <dt>""</dt><dd>DiskUse#DEFAULT</dd>
* <dt>true|allow</dt><dd>DiskUse#ALLOW</dd>
* <dt>false|deny</dt><dd>DiskUse#DENY</dd>
* </dl>
*
* @return {@literal false} by default.
* @since 3.0
* @see Aggregation
* @see org.springframework.data.mongodb.core.query.DiskUse
*/
boolean allowDiskUse() default false;
String allowDiskUse() default "";
}

7
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/QueryBlocks.java

@ -26,6 +26,7 @@ import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQue @@ -26,6 +26,7 @@ import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQue
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.annotation.Collation;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.DiskUse;
import org.springframework.data.mongodb.repository.Hint;
import org.springframework.data.mongodb.repository.Meta;
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.PagedExecution;
@ -292,6 +293,12 @@ class QueryBlocks { @@ -292,6 +293,12 @@ class QueryBlocks {
if (StringUtils.hasText(comment)) {
builder.addStatement("$L.comment($S)", queryVariableName, comment);
}
String allowDiskUse = metaAnnotation.getString("allowDiskUse");
if (StringUtils.hasText(allowDiskUse)) {
DiskUse diskUse = DiskUse.of(allowDiskUse);
builder.addStatement("$L.diskUse($T.$L)", queryVariableName, DiskUse.class, diskUse.name());
}
}
MergedAnnotation<Collation> collationAnnotation = context.getAnnotation(Collation.class);

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

@ -27,6 +27,7 @@ import org.springframework.data.mapping.context.MappingContext; @@ -27,6 +27,7 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.annotation.Collation;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.DiskUse;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.repository.Aggregation;
import org.springframework.data.mongodb.repository.Hint;
@ -272,8 +273,9 @@ public class MongoQueryMethod extends QueryMethod { @@ -272,8 +273,9 @@ public class MongoQueryMethod extends QueryMethod {
}
}
if (meta.allowDiskUse()) {
metaAttributes.setAllowDiskUse(meta.allowDiskUse());
DiskUse diskUse = DiskUse.of(meta.allowDiskUse());
if (!diskUse.equals(DiskUse.DEFAULT)) {
metaAttributes.setAllowDiskUse(diskUse.equals(DiskUse.ALLOW));
}
return metaAttributes;

12
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributorUnitTests.java

@ -15,8 +15,8 @@ @@ -15,8 +15,8 @@
*/
package org.springframework.data.mongodb.repository.aot;
import static org.mockito.Mockito.*;
import static org.springframework.data.mongodb.test.util.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.springframework.data.mongodb.test.util.Assertions.assertThat;
import example.aot.User;
import example.aot.UserRepository;
@ -25,7 +25,6 @@ import java.io.IOException; @@ -25,7 +25,6 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Test;
import org.springframework.aot.generate.GeneratedFiles;
import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.beans.factory.annotation.Autowired;
@ -42,8 +41,8 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; @@ -42,8 +41,8 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
* Unit tests for the {@link UserRepository} fragment sources via {@link MongoRepositoryContributor}.
*
* @author Mark Paluch
* @author Christoph Strobl
*/
@SpringJUnitConfig(classes = MongoRepositoryContributorUnitTests.MongoRepositoryContributorConfiguration.class)
class MongoRepositoryContributorUnitTests {
@ -63,7 +62,7 @@ class MongoRepositoryContributorUnitTests { @@ -63,7 +62,7 @@ class MongoRepositoryContributorUnitTests {
@Autowired TestGenerationContext generationContext;
@Test // GH-4970
@Test // GH-4970, GH-4667
void shouldConsiderMetaAnnotation() throws IOException {
InputStreamSource aotFragment = generationContext.getGeneratedFiles().getGeneratedFile(GeneratedFiles.Kind.SOURCE,
@ -74,6 +73,7 @@ class MongoRepositoryContributorUnitTests { @@ -74,6 +73,7 @@ class MongoRepositoryContributorUnitTests {
assertThat(content).contains("filterQuery.maxTimeMsec(555)");
assertThat(content).contains("filterQuery.cursorBatchSize(1234)");
assertThat(content).contains("filterQuery.comment(\"foo\")");
assertThat(content).contains("filterQuery.diskUse(DiskUse.DENY)");
}
interface MetaUserRepository extends CrudRepository<User, String> {
@ -81,7 +81,7 @@ class MongoRepositoryContributorUnitTests { @@ -81,7 +81,7 @@ class MongoRepositoryContributorUnitTests {
@Meta
User findAllByLastname(String lastname);
@Meta(cursorBatchSize = 1234, comment = "foo", maxExecutionTimeMs = 555)
@Meta(cursorBatchSize = 1234, comment = "foo", maxExecutionTimeMs = 555, allowDiskUse = "false")
User findWithMetaAllByLastname(String lastname);
}

2
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java

@ -349,7 +349,7 @@ public class StringBasedAggregationUnitTests { @@ -349,7 +349,7 @@ public class StringBasedAggregationUnitTests {
private interface SampleRepository extends Repository<Person, Long> {
@Meta(cursorBatchSize = 42, comment = "expensive-aggregation", allowDiskUse = true, maxExecutionTimeMs = 100)
@Meta(cursorBatchSize = 42, comment = "expensive-aggregation", allowDiskUse = "true", maxExecutionTimeMs = 100)
@Aggregation({ RAW_GROUP_BY_LASTNAME_STRING, RAW_SORT_STRING })
PersonAggregate plainStringAggregation();

Loading…
Cancel
Save