Browse Source

Polishing.

Reformat code. Use existing SimpleTypeHolder instead of static MongoSimpleTypes.

See: #5027
Original pull request: #5038
pull/5040/head
Mark Paluch 4 months ago
parent
commit
46ece81d00
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 80
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
  2. 2
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java
  3. 37
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/AggregationBlocks.java
  4. 3
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/AotPlaceholders.java
  5. 33
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/AotQueryCreator.java
  6. 10
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/AotStringQuery.java
  7. 1
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/MongoAotRepositoryFragmentSupport.java
  8. 9
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/MongoCodeBlocks.java
  9. 31
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributor.java
  10. 1
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/Snippet.java
  11. 2
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java
  12. 42
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java

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

@ -111,11 +111,9 @@ import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
import org.springframework.data.mongodb.core.timeseries.Granularity; import org.springframework.data.mongodb.core.timeseries.Granularity;
import org.springframework.data.mongodb.core.validation.Validator; import org.springframework.data.mongodb.core.validation.Validator;
import org.springframework.data.projection.EntityProjection; import org.springframework.data.projection.EntityProjection;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.util.CloseableIterator; import org.springframework.data.util.CloseableIterator;
import org.springframework.data.util.Lazy; import org.springframework.data.util.Lazy;
import org.springframework.data.util.Optionals; import org.springframework.data.util.Optionals;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Contract; import org.springframework.lang.Contract;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
@ -498,7 +496,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
return doStream(query, entityType, collectionName, returnType, QueryResultConverter.entity()); return doStream(query, entityType, collectionName, returnType, QueryResultConverter.entity());
} }
@SuppressWarnings({"ConstantConditions", "NullAway"}) @SuppressWarnings({ "ConstantConditions", "NullAway" })
<T, R> Stream<R> doStream(Query query, Class<?> entityType, String collectionName, Class<T> returnType, <T, R> Stream<R> doStream(Query query, Class<?> entityType, String collectionName, Class<T> returnType,
QueryResultConverter<? super T, ? extends R> resultConverter) { QueryResultConverter<? super T, ? extends R> resultConverter) {
@ -523,8 +521,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
DocumentCallback<R> resultReader = getResultReader(projection, collectionName, resultConverter); DocumentCallback<R> resultReader = getResultReader(projection, collectionName, resultConverter);
return new CloseableIterableCursorAdapter<>(cursor, exceptionTranslator, return new CloseableIterableCursorAdapter<>(cursor, exceptionTranslator, resultReader).stream();
resultReader).stream();
}); });
} }
@ -1152,7 +1149,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
@Override @Override
public <T> @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, public <T> @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options,
Class<T> entityClass, String collectionName) { Class<T> entityClass, String collectionName) {
return findAndModify(query, update, options, entityClass, collectionName, QueryResultConverter.entity()); return findAndModify(query, update, options, entityClass, collectionName, QueryResultConverter.entity());
} }
@ -1182,12 +1179,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
@Override @Override
public <S, T> @Nullable T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, public <S, T> @Nullable T findAndReplace(Query query, S replacement, FindAndReplaceOptions options,
Class<S> entityType, String collectionName, Class<T> resultType) { Class<S> entityType, String collectionName, Class<T> resultType) {
return findAndReplace(query, replacement, options, entityType, collectionName, resultType, QueryResultConverter.entity()); return findAndReplace(query, replacement, options, entityType, collectionName, resultType,
QueryResultConverter.entity());
} }
<S, T, R> @Nullable R findAndReplace(Query query, S replacement, FindAndReplaceOptions options, <S, T, R> @Nullable R findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType,
Class<S> entityType, String collectionName, Class<T> resultType, QueryResultConverter<? super T, ? extends R> resultConverter) { String collectionName, Class<T> resultType, QueryResultConverter<? super T, ? extends R> resultConverter) {
Assert.notNull(query, "Query must not be null"); Assert.notNull(query, "Query must not be null");
Assert.notNull(replacement, "Replacement must not be null"); Assert.notNull(replacement, "Replacement must not be null");
@ -1215,7 +1213,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
maybeCallBeforeSave(replacement, mappedReplacement, collectionName); maybeCallBeforeSave(replacement, mappedReplacement, collectionName);
R saved = doFindAndReplace(collectionPreparer, collectionName, mappedQuery, mappedFields, mappedSort, R saved = doFindAndReplace(collectionPreparer, collectionName, mappedQuery, mappedFields, mappedSort,
queryContext.getCollation(entityType).orElse(null), entityType, mappedReplacement, options, projection, resultConverter); queryContext.getCollation(entityType).orElse(null), entityType, mappedReplacement, options, projection,
resultConverter);
if (saved != null) { if (saved != null) {
maybeEmitEvent(new AfterSaveEvent<>(saved, mappedReplacement, collectionName)); maybeEmitEvent(new AfterSaveEvent<>(saved, mappedReplacement, collectionName));
@ -1508,7 +1507,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
initialized = maybeCallBeforeSave(initialized, document, collectionName); initialized = maybeCallBeforeSave(initialized, document, collectionName);
MappedDocument mappedDocument = queryOperations.createInsertContext(MappedDocument.of(document)) MappedDocument mappedDocument = queryOperations.createInsertContext(MappedDocument.of(document))
.prepareId(uninitialized.getClass()); .prepareId(uninitialized.getClass());
documentList.add(mappedDocument.getDocument()); documentList.add(mappedDocument.getDocument());
initializedBatchToSave.add(initialized); initializedBatchToSave.add(initialized);
@ -2221,14 +2220,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
} }
}; };
List<T> result = doFind(collectionName, createDelegate(query), query.getQueryObject(), query.getFieldsObject(), entityClass, List<T> result = doFind(collectionName, createDelegate(query), query.getQueryObject(), query.getFieldsObject(),
new QueryCursorPreparer(query, entityClass), callback); entityClass, new QueryCursorPreparer(query, entityClass), callback);
if (!CollectionUtils.isEmpty(result)) { if (!CollectionUtils.isEmpty(result)) {
Criteria[] criterias = ids.stream() // Criteria[] criterias = ids.stream() //
.map(it -> Criteria.where("_id").is(it)) // .map(it -> Criteria.where("_id").is(it)) //
.toArray(Criteria[]::new); .toArray(Criteria[]::new);
Query removeQuery = new Query(criterias.length == 1 ? criterias[0] : new Criteria().orOperator(criterias)); Query removeQuery = new Query(criterias.length == 1 ? criterias[0] : new Criteria().orOperator(criterias));
if (query.hasReadPreference()) { if (query.hasReadPreference()) {
@ -2270,20 +2269,19 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
return doAggregate(aggregation, collectionName, outputType, QueryResultConverter.entity(), context); return doAggregate(aggregation, collectionName, outputType, QueryResultConverter.entity(), context);
} }
@SuppressWarnings({"ConstantConditions", "NullAway"}) @SuppressWarnings({ "ConstantConditions", "NullAway" })
<T, O> AggregationResults<O> doAggregate(Aggregation aggregation, String collectionName, Class<T> outputType, <T, O> AggregationResults<O> doAggregate(Aggregation aggregation, String collectionName, Class<T> outputType,
QueryResultConverter<? super T, ? extends O> resultConverter, AggregationOperationContext context) { QueryResultConverter<? super T, ? extends O> resultConverter, AggregationOperationContext context) {
final DocumentCallback<O> callback; final DocumentCallback<O> callback;
if(aggregation instanceof TypedAggregation<?> ta && outputType.isInterface()) { if (aggregation instanceof TypedAggregation<?> ta && outputType.isInterface()) {
EntityProjection<T, ?> projection = operations.introspectProjection(outputType, ta.getInputType()); EntityProjection<T, ?> projection = operations.introspectProjection(outputType, ta.getInputType());
ProjectingReadCallback cb = new ProjectingReadCallback(mongoConverter, projection, collectionName); ProjectingReadCallback cb = new ProjectingReadCallback(mongoConverter, projection, collectionName);
callback = new QueryResultConverterCallback<>(resultConverter, callback = new QueryResultConverterCallback<>(resultConverter, cb);
cb);
} else { } else {
callback = new QueryResultConverterCallback<>(resultConverter, callback = new QueryResultConverterCallback<>(resultConverter,
new ReadDocumentCallback<>(mongoConverter, outputType, collectionName)); new ReadDocumentCallback<>(mongoConverter, outputType, collectionName));
} }
AggregationOptions options = aggregation.getOptions(); AggregationOptions options = aggregation.getOptions();
@ -2370,8 +2368,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
@SuppressWarnings({ "ConstantConditions", "NullAway" }) @SuppressWarnings({ "ConstantConditions", "NullAway" })
<T, O> Stream<O> doAggregateStream(Aggregation aggregation, String collectionName, Class<T> outputType, <T, O> Stream<O> doAggregateStream(Aggregation aggregation, String collectionName, Class<T> outputType,
QueryResultConverter<? super T, ? extends O> resultConverter, QueryResultConverter<? super T, ? extends O> resultConverter, @Nullable AggregationOperationContext context) {
@Nullable AggregationOperationContext context) {
Assert.notNull(aggregation, "Aggregation pipeline must not be null"); Assert.notNull(aggregation, "Aggregation pipeline must not be null");
Assert.hasText(collectionName, "Collection name must not be null or empty"); Assert.hasText(collectionName, "Collection name must not be null or empty");
@ -2649,9 +2646,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
* @return the converted object or {@literal null} if none exists. * @return the converted object or {@literal null} if none exists.
*/ */
@Nullable @Nullable
protected <T> T doFindOne(String collectionName, protected <T> T doFindOne(String collectionName, CollectionPreparer<MongoCollection<Document>> collectionPreparer,
CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields, Document query, Document fields, Class<T> entityClass) {
Class<T> entityClass) {
return doFindOne(collectionName, collectionPreparer, query, fields, CursorPreparer.NO_OP_PREPARER, entityClass); return doFindOne(collectionName, collectionPreparer, query, fields, CursorPreparer.NO_OP_PREPARER, entityClass);
} }
@ -2670,9 +2666,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
*/ */
@Nullable @Nullable
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
protected <T> T doFindOne(String collectionName, protected <T> T doFindOne(String collectionName, CollectionPreparer<MongoCollection<Document>> collectionPreparer,
CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields, Document query, Document fields, CursorPreparer preparer, Class<T> entityClass) {
CursorPreparer preparer, Class<T> entityClass) {
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass); MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
@ -2870,9 +2865,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
<S, T> @Nullable T doFindAndModify(CollectionPreparer<MongoCollection<Document>> collectionPreparer, <S, T> @Nullable T doFindAndModify(CollectionPreparer<MongoCollection<Document>> collectionPreparer,
String collectionName, String collectionName, Document query, @Nullable Document fields, @Nullable Document sort, Class<S> entityClass,
Document query, @Nullable Document fields, @Nullable Document sort, Class<S> entityClass, UpdateDefinition update, UpdateDefinition update, @Nullable FindAndModifyOptions options,
@Nullable FindAndModifyOptions options, QueryResultConverter<? super S, ? extends T> resultConverter) { QueryResultConverter<? super S, ? extends T> resultConverter) {
if (options == null) { if (options == null) {
options = new FindAndModifyOptions(); options = new FindAndModifyOptions();
@ -2894,7 +2889,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
serializeToJsonSafely(mappedUpdate), collectionName)); serializeToJsonSafely(mappedUpdate), collectionName));
} }
DocumentCallback<T> callback = getResultReader(EntityProjection.nonProjecting(entityClass), collectionName, resultConverter); DocumentCallback<T> callback = getResultReader(EntityProjection.nonProjecting(entityClass), collectionName,
resultConverter);
return executeFindOneInternal( return executeFindOneInternal(
new FindAndModifyCallback(collectionPreparer, mappedQuery, fields, sort, mappedUpdate, new FindAndModifyCallback(collectionPreparer, mappedQuery, fields, sort, mappedUpdate,
@ -2919,8 +2915,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
*/ */
@Nullable @Nullable
protected <S, T> T doFindAndReplace(CollectionPreparer<MongoCollection<Document>> collectionPreparer, protected <S, T> T doFindAndReplace(CollectionPreparer<MongoCollection<Document>> collectionPreparer,
String collectionName, String collectionName, Document mappedQuery, Document mappedFields, Document mappedSort,
Document mappedQuery, Document mappedFields, Document mappedSort,
com.mongodb.client.model.@Nullable Collation collation, Class<S> entityType, Document replacement, com.mongodb.client.model.@Nullable Collation collation, Class<S> entityType, Document replacement,
FindAndReplaceOptions options, Class<T> resultType) { FindAndReplaceOptions options, Class<T> resultType) {
@ -2963,10 +2958,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
*/ */
@Nullable @Nullable
private <S, T, R> R doFindAndReplace(CollectionPreparer<MongoCollection<Document>> collectionPreparer, private <S, T, R> R doFindAndReplace(CollectionPreparer<MongoCollection<Document>> collectionPreparer,
String collectionName, String collectionName, Document mappedQuery, Document mappedFields, Document mappedSort,
Document mappedQuery, Document mappedFields, Document mappedSort,
com.mongodb.client.model.@Nullable Collation collation, Class<T> entityType, Document replacement, com.mongodb.client.model.@Nullable Collation collation, Class<T> entityType, Document replacement,
FindAndReplaceOptions options, EntityProjection<S, T> projection, QueryResultConverter<? super S, ? extends R> resultConverter) { FindAndReplaceOptions options, EntityProjection<S, T> projection,
QueryResultConverter<? super S, ? extends R> resultConverter) {
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER LOGGER
@ -2979,8 +2974,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
DocumentCallback<R> callback = getResultReader(projection, collectionName, resultConverter); DocumentCallback<R> callback = getResultReader(projection, collectionName, resultConverter);
return executeFindOneInternal(new FindAndReplaceCallback(collectionPreparer, mappedQuery, mappedFields, mappedSort, return executeFindOneInternal(new FindAndReplaceCallback(collectionPreparer, mappedQuery, mappedFields, mappedSort,
replacement, collation, options),callback, replacement, collation, options), callback, collectionName);
collectionName);
} }
@SuppressWarnings("NullAway") @SuppressWarnings("NullAway")
@ -3158,8 +3152,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
return getMappedSortObject(sortObject, mappingContext.getPersistentEntity(type)); return getMappedSortObject(sortObject, mappingContext.getPersistentEntity(type));
} }
private @Nullable Document getMappedSortObject(@Nullable Document sortObject,
private @Nullable Document getMappedSortObject(@Nullable Document sortObject, @Nullable MongoPersistentEntity<?> entity) { @Nullable MongoPersistentEntity<?> entity) {
if (ObjectUtils.isEmpty(sortObject)) { if (ObjectUtils.isEmpty(sortObject)) {
return null; return null;
@ -3392,7 +3386,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
private final Document fields; private final Document fields;
private final Document sort; private final Document sort;
private final Document update; private final Document update;
private final com.mongodb.client.model.@Nullable Collation collation; private final com.mongodb.client.model.@Nullable Collation collation;
private final FindAndReplaceOptions options; private final FindAndReplaceOptions options;
FindAndReplaceCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, FindAndReplaceCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,

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

@ -377,7 +377,7 @@ class QueryOperations {
mappedFields = queryMapper.getMappedFields(fields, entity); mappedFields = queryMapper.getMappedFields(fields, entity);
} else { } else {
mappedFields = propertyOperations.computeMappedFieldsForProjection(projection, fields); mappedFields = propertyOperations.computeMappedFieldsForProjection(projection, fields);
if(projection.getMappedType().getType().isInterface()) { if (projection.getMappedType().getType().isInterface()) {
mappedFields = queryMapper.getMappedFields(mappedFields, entity); mappedFields = queryMapper.getMappedFields(mappedFields, entity);
} }
mappedFields = queryMapper.addMetaAttributes(mappedFields, entity); mappedFields = queryMapper.addMetaAttributes(mappedFields, entity);

37
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/AggregationBlocks.java

@ -22,10 +22,12 @@ import java.util.stream.Stream;
import org.bson.Document; import org.bson.Document;
import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.NullUnmarked;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.data.domain.SliceImpl; import org.springframework.data.domain.SliceImpl;
import org.springframework.data.domain.Sort.Order; import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation; import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
@ -33,7 +35,6 @@ import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline; import org.springframework.data.mongodb.core.aggregation.AggregationPipeline;
import org.springframework.data.mongodb.core.aggregation.AggregationResults; import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation; import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.repository.Hint; import org.springframework.data.mongodb.repository.Hint;
import org.springframework.data.mongodb.repository.ReadPreference; import org.springframework.data.mongodb.repository.ReadPreference;
@ -58,12 +59,15 @@ class AggregationBlocks {
static class AggregationExecutionCodeBlockBuilder { static class AggregationExecutionCodeBlockBuilder {
private final AotQueryMethodGenerationContext context; private final AotQueryMethodGenerationContext context;
private final SimpleTypeHolder simpleTypeHolder;
private final MongoQueryMethod queryMethod; private final MongoQueryMethod queryMethod;
private String aggregationVariableName; private String aggregationVariableName;
AggregationExecutionCodeBlockBuilder(AotQueryMethodGenerationContext context, MongoQueryMethod queryMethod) { AggregationExecutionCodeBlockBuilder(AotQueryMethodGenerationContext context, SimpleTypeHolder simpleTypeHolder,
MongoQueryMethod queryMethod) {
this.context = context; this.context = context;
this.simpleTypeHolder = simpleTypeHolder;
this.queryMethod = queryMethod; this.queryMethod = queryMethod;
} }
@ -80,7 +84,7 @@ class AggregationBlocks {
builder.add("\n"); builder.add("\n");
Class<?> outputType = getOutputType(queryMethod); Class<?> outputType = getOutputType(simpleTypeHolder, queryMethod);
if (ReflectionUtils.isVoid(queryMethod.getReturnedObjectType())) { if (ReflectionUtils.isVoid(queryMethod.getReturnedObjectType())) {
builder.addStatement("$L.aggregate($L, $T.class)", mongoOpsRef, aggregationVariableName, outputType); builder.addStatement("$L.aggregate($L, $T.class)", mongoOpsRef, aggregationVariableName, outputType);
@ -152,13 +156,19 @@ class AggregationBlocks {
} }
private static Class<?> getOutputType(MongoQueryMethod queryMethod) { private static Class<?> getOutputType(SimpleTypeHolder simpleTypeHolder, MongoQueryMethod queryMethod) {
Class<?> outputType = queryMethod.getReturnedObjectType(); Class<?> outputType = queryMethod.getReturnedObjectType();
if (MongoSimpleTypes.HOLDER.isSimpleType(outputType)) {
outputType = Document.class; if (simpleTypeHolder.isSimpleType(outputType)) {
} else if (ClassUtils.isAssignable(AggregationResults.class, outputType) && queryMethod.getReturnType().getComponentType() != null) { return Document.class;
outputType = queryMethod.getReturnType().getComponentType().getType();
} }
if (ClassUtils.isAssignable(AggregationResults.class, outputType)
&& queryMethod.getReturnType().getComponentType() != null) {
return queryMethod.getReturnType().getComponentType().getType();
}
return outputType; return outputType;
} }
@ -166,6 +176,7 @@ class AggregationBlocks {
static class AggregationCodeBlockBuilder { static class AggregationCodeBlockBuilder {
private final AotQueryMethodGenerationContext context; private final AotQueryMethodGenerationContext context;
private final SimpleTypeHolder simpleTypeHolder;
private final MongoQueryMethod queryMethod; private final MongoQueryMethod queryMethod;
private final String parameterNames; private final String parameterNames;
@ -174,9 +185,11 @@ class AggregationBlocks {
private String aggregationVariableName; private String aggregationVariableName;
private boolean pipelineOnly; private boolean pipelineOnly;
AggregationCodeBlockBuilder(AotQueryMethodGenerationContext context, MongoQueryMethod queryMethod) { AggregationCodeBlockBuilder(AotQueryMethodGenerationContext context, SimpleTypeHolder simpleTypeHolder,
MongoQueryMethod queryMethod) {
this.context = context; this.context = context;
this.simpleTypeHolder = simpleTypeHolder;
this.queryMethod = queryMethod; this.queryMethod = queryMethod;
this.parameterNames = StringUtils.collectionToDelimitedString(context.getAllParameterNames(), ", "); this.parameterNames = StringUtils.collectionToDelimitedString(context.getAllParameterNames(), ", ");
} }
@ -230,7 +243,7 @@ class AggregationBlocks {
builder.add(aggregationStages(context.localVariable("stages"), source.stages())); builder.add(aggregationStages(context.localVariable("stages"), source.stages()));
if (StringUtils.hasText(sortParameter)) { if (StringUtils.hasText(sortParameter)) {
Class<?> outputType = getOutputType(queryMethod); Class<?> outputType = getOutputType(simpleTypeHolder, queryMethod);
builder.add(sortingStage(sortParameter, outputType)); builder.add(sortingStage(sortParameter, outputType));
} }
@ -324,7 +337,7 @@ class AggregationBlocks {
context.localVariable("sortDocument"), context.localVariable("order")); context.localVariable("sortDocument"), context.localVariable("order"));
builder.endControlFlow(); builder.endControlFlow();
if (outputType == Document.class || MongoSimpleTypes.HOLDER.isSimpleType(outputType) if (outputType == Document.class || simpleTypeHolder.isSimpleType(outputType)
|| ClassUtils.isAssignable(context.getRepositoryInformation().getDomainType(), outputType)) { || ClassUtils.isAssignable(context.getRepositoryInformation().getDomainType(), outputType)) {
builder.addStatement("$L.add(new $T($S, $L))", context.localVariable("stages"), Document.class, "$sort", builder.addStatement("$L.add(new $T($S, $L))", context.localVariable("stages"), Document.class, "$sort",
context.localVariable("sortDocument")); context.localVariable("sortDocument"));
@ -343,7 +356,7 @@ class AggregationBlocks {
Builder builder = CodeBlock.builder(); Builder builder = CodeBlock.builder();
builder.add(sortingStage(pageableProvider + ".getSort()", getOutputType(queryMethod))); builder.add(sortingStage(pageableProvider + ".getSort()", getOutputType(simpleTypeHolder, queryMethod)));
builder.beginControlFlow("if ($L.isPaged())", pageableProvider); builder.beginControlFlow("if ($L.isPaged())", pageableProvider);
builder.beginControlFlow("if ($L.getOffset() > 0)", pageableProvider); builder.beginControlFlow("if ($L.getOffset() > 0)", pageableProvider);

3
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/AotPlaceholders.java

@ -279,7 +279,8 @@ class AotPlaceholders {
this.options = options; this.options = options;
} }
@Nullable String regexOptions() { @Nullable
String regexOptions() {
return options; return options;
} }

33
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/AotQueryCreator.java

@ -68,13 +68,7 @@ import com.mongodb.DBRef;
* @author Christoph Strobl * @author Christoph Strobl
* @since 5.0 * @since 5.0
*/ */
class AotQueryCreator { record AotQueryCreator(MappingContext<?, MongoPersistentProperty> mappingContext) {
private final MappingContext<?, MongoPersistentProperty> mappingContext;
public AotQueryCreator(MappingContext<?, MongoPersistentProperty> mappingContext) {
this.mappingContext = mappingContext;
}
@SuppressWarnings("NullAway") @SuppressWarnings("NullAway")
AotStringQuery createQuery(PartTree partTree, QueryMethod queryMethod, Method source) { AotStringQuery createQuery(PartTree partTree, QueryMethod queryMethod, Method source) {
@ -129,7 +123,7 @@ class AotQueryCreator {
return criteria.is(param); return criteria.is(param);
} }
if(part.getType().equals(Type.NOT_LIKE)) { if (part.getType().equals(Type.NOT_LIKE)) {
return criteria.raw("$not", param); return criteria.raw("$not", param);
} }
@ -179,15 +173,16 @@ class AotQueryCreator {
private final List<Object> placeholders; private final List<Object> placeholders;
@Nullable Part getPartForIndex(PartTree partTree, Parameter parameter) { @Nullable
if(!parameter.isBindable()) { Part getPartForIndex(PartTree partTree, Parameter parameter) {
if (!parameter.isBindable()) {
return null; return null;
} }
List<Part> parts = partTree.getParts().stream().toList(); List<Part> parts = partTree.getParts().stream().toList();
int counter = 0; int counter = 0;
for (Part part : parts) { for (Part part : parts) {
if(counter == parameter.getIndex()) { if (counter == parameter.getIndex()) {
return part; return part;
} }
counter += part.getNumberOfArguments(); counter += part.getNumberOfArguments();
@ -196,17 +191,14 @@ class AotQueryCreator {
} }
public PlaceholderParameterAccessor(PartTree partTree, QueryMethod queryMethod) { public PlaceholderParameterAccessor(PartTree partTree, QueryMethod queryMethod) {
if (queryMethod.getParameters().getNumberOfParameters() == 0) { if (queryMethod.getParameters().getNumberOfParameters() == 0) {
placeholders = List.of(); placeholders = List.of();
} else { } else {
placeholders = new ArrayList<>(); placeholders = new ArrayList<>();
Parameters<?, ?> parameters = queryMethod.getParameters(); Parameters<?, ?> parameters = queryMethod.getParameters();
for (Parameter parameter : parameters.toList()) { for (Parameter parameter : parameters.toList()) {
if (ClassUtils.isAssignable(GeoJson.class, parameter.getType())) { if (ClassUtils.isAssignable(GeoJson.class, parameter.getType())) {
placeholders.add(parameter.getIndex(), AotPlaceholders.geoJson(parameter.getIndex(), "")); placeholders.add(parameter.getIndex(), AotPlaceholders.geoJson(parameter.getIndex(), ""));
@ -224,8 +216,15 @@ class AotQueryCreator {
placeholders.add(parameter.getIndex(), AotPlaceholders.regex(parameter.getIndex(), null)); placeholders.add(parameter.getIndex(), AotPlaceholders.regex(parameter.getIndex(), null));
} else { } else {
Part partForIndex = getPartForIndex(partTree, parameter); Part partForIndex = getPartForIndex(partTree, parameter);
if(partForIndex != null && (partForIndex.getType().equals(Type.LIKE) || partForIndex.getType().equals(Type.NOT_LIKE))) { if (partForIndex != null
placeholders.add(parameter.getIndex(), AotPlaceholders.regex(parameter.getIndex(), partForIndex.shouldIgnoreCase().equals(IgnoreCaseType.ALWAYS) || partForIndex.shouldIgnoreCase().equals(IgnoreCaseType.WHEN_POSSIBLE) ? "i": null)); && (partForIndex.getType().equals(Type.LIKE) || partForIndex.getType().equals(Type.NOT_LIKE))) {
placeholders
.add(parameter.getIndex(),
AotPlaceholders
.regex(parameter.getIndex(),
partForIndex.shouldIgnoreCase().equals(IgnoreCaseType.ALWAYS)
|| partForIndex.shouldIgnoreCase().equals(IgnoreCaseType.WHEN_POSSIBLE) ? "i"
: null));
} else { } else {
placeholders.add(parameter.getIndex(), AotPlaceholders.indexed(parameter.getIndex())); placeholders.add(parameter.getIndex(), AotPlaceholders.indexed(parameter.getIndex()));
} }

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

@ -28,7 +28,6 @@ import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Field; import org.springframework.data.mongodb.core.query.Field;
import org.springframework.data.mongodb.core.query.Meta; import org.springframework.data.mongodb.core.query.Meta;
import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.aot.AotPlaceholders.Placeholder;
import org.springframework.data.mongodb.repository.aot.AotPlaceholders.RegexPlaceholder; import org.springframework.data.mongodb.repository.aot.AotPlaceholders.RegexPlaceholder;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -80,19 +79,20 @@ class AotStringQuery extends Query {
} }
boolean isRegexPlaceholderAt(int index) { boolean isRegexPlaceholderAt(int index) {
if(this.placeholders.isEmpty()) { if (this.placeholders.isEmpty()) {
return false; return false;
} }
return this.placeholders.get(index) instanceof RegexPlaceholder; return this.placeholders.get(index) instanceof RegexPlaceholder;
} }
@Nullable String getRegexOptions(int index) { @Nullable
if(this.placeholders.isEmpty()) { String getRegexOptions(int index) {
if (this.placeholders.isEmpty()) {
return null; return null;
} }
return this.placeholders.get(index) instanceof RegexPlaceholder rgp ? rgp.regexOptions() : null; return this.placeholders.get(index) instanceof RegexPlaceholder rgp ? rgp.regexOptions() : null;
} }
@Override @Override

1
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/MongoAotRepositoryFragmentSupport.java

@ -189,7 +189,6 @@ public class MongoAotRepositoryFragmentSupport {
criteria.lt(value); criteria.lt(value);
} }
} }
}; };
} }

9
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/MongoCodeBlocks.java

@ -20,6 +20,7 @@ import java.util.regex.Pattern;
import org.bson.Document; import org.bson.Document;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.mongodb.repository.ReadPreference; import org.springframework.data.mongodb.repository.ReadPreference;
import org.springframework.data.mongodb.repository.aot.AggregationBlocks.AggregationCodeBlockBuilder; import org.springframework.data.mongodb.repository.aot.AggregationBlocks.AggregationCodeBlockBuilder;
import org.springframework.data.mongodb.repository.aot.AggregationBlocks.AggregationExecutionCodeBlockBuilder; import org.springframework.data.mongodb.repository.aot.AggregationBlocks.AggregationExecutionCodeBlockBuilder;
@ -118,24 +119,28 @@ class MongoCodeBlocks {
* Builder for generating aggregation (pipeline) parsing {@link CodeBlock}. * Builder for generating aggregation (pipeline) parsing {@link CodeBlock}.
* *
* @param context * @param context
* @param simpleTypeHolder
* @param queryMethod * @param queryMethod
* @return * @return
*/ */
static AggregationCodeBlockBuilder aggregationBlockBuilder(AotQueryMethodGenerationContext context, static AggregationCodeBlockBuilder aggregationBlockBuilder(AotQueryMethodGenerationContext context,
SimpleTypeHolder simpleTypeHolder,
MongoQueryMethod queryMethod) { MongoQueryMethod queryMethod) {
return new AggregationCodeBlockBuilder(context, queryMethod); return new AggregationCodeBlockBuilder(context, simpleTypeHolder, queryMethod);
} }
/** /**
* Builder for generating aggregation execution {@link CodeBlock}. * Builder for generating aggregation execution {@link CodeBlock}.
* *
* @param context * @param context
* @param simpleTypeHolder
* @param queryMethod * @param queryMethod
* @return * @return
*/ */
static AggregationExecutionCodeBlockBuilder aggregationExecutionBlockBuilder(AotQueryMethodGenerationContext context, static AggregationExecutionCodeBlockBuilder aggregationExecutionBlockBuilder(AotQueryMethodGenerationContext context,
SimpleTypeHolder simpleTypeHolder,
MongoQueryMethod queryMethod) { MongoQueryMethod queryMethod) {
return new AggregationExecutionCodeBlockBuilder(context, queryMethod); return new AggregationExecutionCodeBlockBuilder(context, simpleTypeHolder, queryMethod);
} }
/** /**

31
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/MongoRepositoryContributor.java

@ -28,6 +28,7 @@ import org.jspecify.annotations.Nullable;
import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.aggregation.AggregationUpdate; import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions; import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
@ -68,6 +69,7 @@ public class MongoRepositoryContributor extends RepositoryContributor {
private static final Log logger = LogFactory.getLog(MongoRepositoryContributor.class); private static final Log logger = LogFactory.getLog(MongoRepositoryContributor.class);
private final AotQueryCreator queryCreator; private final AotQueryCreator queryCreator;
private final SimpleTypeHolder simpleTypeHolder;
private final MongoMappingContext mappingContext; private final MongoMappingContext mappingContext;
private final NamedQueries namedQueries; private final NamedQueries namedQueries;
@ -85,8 +87,10 @@ public class MongoRepositoryContributor extends RepositoryContributor {
MongoCustomConversions mongoCustomConversions = MongoCustomConversions MongoCustomConversions mongoCustomConversions = MongoCustomConversions
.create(MongoCustomConversions.MongoConverterConfigurationAdapter::useNativeDriverJavaTimeCodecs); .create(MongoCustomConversions.MongoConverterConfigurationAdapter::useNativeDriverJavaTimeCodecs);
this.simpleTypeHolder = mongoCustomConversions.getSimpleTypeHolder();
this.mappingContext = new MongoMappingContext(); this.mappingContext = new MongoMappingContext();
this.mappingContext.setSimpleTypeHolder(mongoCustomConversions.getSimpleTypeHolder()); this.mappingContext.setSimpleTypeHolder(this.simpleTypeHolder);
this.mappingContext.setAutoIndexCreation(false); this.mappingContext.setAutoIndexCreation(false);
this.mappingContext.afterPropertiesSet(); this.mappingContext.afterPropertiesSet();
@ -150,7 +154,7 @@ public class MongoRepositoryContributor extends RepositoryContributor {
if (queryMethod.hasAnnotatedAggregation()) { if (queryMethod.hasAnnotatedAggregation()) {
AggregationInteraction aggregation = new AggregationInteraction(queryMethod.getAnnotatedAggregation()); AggregationInteraction aggregation = new AggregationInteraction(queryMethod.getAnnotatedAggregation());
queryMetadata = aggregation; queryMetadata = aggregation;
contribution = aggregationMethodContributor(queryMethod, aggregation); contribution = aggregationMethodContributor(queryMethod, simpleTypeHolder, aggregation);
} else { } else {
QueryInteraction query = createStringQuery(getRepositoryInformation(), queryMethod, QueryInteraction query = createStringQuery(getRepositoryInformation(), queryMethod,
@ -193,7 +197,7 @@ public class MongoRepositoryContributor extends RepositoryContributor {
if (!ObjectUtils.isEmpty(updateSource.pipeline())) { if (!ObjectUtils.isEmpty(updateSource.pipeline())) {
AggregationUpdateInteraction update = new AggregationUpdateInteraction(query, updateSource.pipeline()); AggregationUpdateInteraction update = new AggregationUpdateInteraction(query, updateSource.pipeline());
queryMetadata = update; queryMetadata = update;
contribution = aggregationUpdateMethodContributor(queryMethod, update); contribution = aggregationUpdateMethodContributor(queryMethod, simpleTypeHolder, update);
} }
} }
} else { } else {
@ -210,17 +214,7 @@ public class MongoRepositoryContributor extends RepositoryContributor {
return MethodContributor.forQueryMethod(queryMethod).metadataOnly(queryMetadata); return MethodContributor.forQueryMethod(queryMethod).metadataOnly(queryMetadata);
} }
MethodContributor.RepositoryMethodContribution finalContribution = contribution; return MethodContributor.forQueryMethod(queryMethod).withMetadata(queryMetadata).contribute(contribution);
return MethodContributor.forQueryMethod(queryMethod).withMetadata(queryMetadata).contribute(context -> {
CodeBlock.Builder builder = CodeBlock.builder();
// builder.addStatement("class ExpressionMarker{}");
builder.add(finalContribution.contribute(context));
return builder.build();
});
} }
@SuppressWarnings("NullAway") @SuppressWarnings("NullAway")
@ -289,6 +283,7 @@ public class MongoRepositoryContributor extends RepositoryContributor {
} }
static MethodContributor.RepositoryMethodContribution aggregationMethodContributor(MongoQueryMethod queryMethod, static MethodContributor.RepositoryMethodContribution aggregationMethodContributor(MongoQueryMethod queryMethod,
SimpleTypeHolder simpleTypeHolder,
AggregationInteraction aggregation) { AggregationInteraction aggregation) {
return context -> { return context -> {
@ -297,9 +292,10 @@ public class MongoRepositoryContributor extends RepositoryContributor {
String variableName = context.localVariable("aggregation"); String variableName = context.localVariable("aggregation");
builder.add(aggregationBlockBuilder(context, queryMethod).stages(aggregation) builder.add(aggregationBlockBuilder(context, simpleTypeHolder, queryMethod).stages(aggregation)
.usingAggregationVariableName(variableName).build()); .usingAggregationVariableName(variableName).build());
builder.add(aggregationExecutionBlockBuilder(context, queryMethod).referencing(variableName).build()); builder.add(
aggregationExecutionBlockBuilder(context, simpleTypeHolder, queryMethod).referencing(variableName).build());
return builder.build(); return builder.build();
}; };
@ -351,6 +347,7 @@ public class MongoRepositoryContributor extends RepositoryContributor {
} }
static MethodContributor.RepositoryMethodContribution aggregationUpdateMethodContributor(MongoQueryMethod queryMethod, static MethodContributor.RepositoryMethodContribution aggregationUpdateMethodContributor(MongoQueryMethod queryMethod,
SimpleTypeHolder simpleTypeHolder,
AggregationUpdateInteraction update) { AggregationUpdateInteraction update) {
return context -> { return context -> {
@ -364,7 +361,7 @@ public class MongoRepositoryContributor extends RepositoryContributor {
// update definition // update definition
String updateVariableName = context.localVariable("updateDefinition"); String updateVariableName = context.localVariable("updateDefinition");
builder.add(aggregationBlockBuilder(context, queryMethod).stages(update) builder.add(aggregationBlockBuilder(context, simpleTypeHolder, queryMethod).stages(update)
.usingAggregationVariableName(updateVariableName).pipelineOnly(true).build()); .usingAggregationVariableName(updateVariableName).pipelineOnly(true).build());
builder.addStatement("$T $L = $T.from($L.getOperations())", AggregationUpdate.class, builder.addStatement("$T $L = $T.from($L.getOperations())", AggregationUpdate.class,

1
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/Snippet.java

@ -195,7 +195,6 @@ interface Snippet {
public BuilderStyleBuilder with(Snippet snippet) { public BuilderStyleBuilder with(Snippet snippet) {
Assert.notNull(targetMethodName, "TargetMethodName must be set before calling this method"); Assert.notNull(targetMethodName, "TargetMethodName must be set before calling this method");
new BuilderStyleSnippet(targetVariableName, targetMethodName, snippet).appendTo(targetBuilder); new BuilderStyleSnippet(targetVariableName, targetMethodName, snippet).appendTo(targetBuilder);
return this; return this;
} }

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

@ -375,7 +375,7 @@ public class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
"Argument for creating $regex pattern for property '%s' must not be null", part.getProperty().getSegment())); "Argument for creating $regex pattern for property '%s' must not be null", part.getProperty().getSegment()));
} }
if(value instanceof Pattern pattern) { if (value instanceof Pattern pattern) {
return criteria.regex(pattern); return criteria.regex(pattern);
} }

42
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java

@ -15,31 +15,10 @@
*/ */
package org.springframework.data.mongodb.core.aggregation; package org.springframework.data.mongodb.core.aggregation;
import static org.springframework.data.domain.Sort.Direction.ASC; import static org.springframework.data.domain.Sort.Direction.*;
import static org.springframework.data.domain.Sort.Direction.DESC; import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.DEFAULT_CONTEXT; import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.bind; import static org.springframework.data.mongodb.test.util.Assertions.*;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.bucket;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.bucketAuto;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.count;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.facet;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.group;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.limit;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.lookup;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.match;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregationOptions;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.out;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.previousOperation;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.replaceRoot;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.sample;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.skip;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.sort;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.unwind;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.test.util.Assertions.assertThat;
import static org.springframework.data.mongodb.test.util.Assertions.assertThatIllegalArgumentException;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.text.ParseException; import java.text.ParseException;
@ -67,6 +46,7 @@ import org.bson.Document;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
@ -574,7 +554,7 @@ public class AggregationTests {
/* /*
//complex mongodb aggregation framework example from //complex mongodb aggregation framework example from
https://docs.mongodb.org/manual/tutorial/aggregation-examples/#largest-and-smallest-cities-by-state https://docs.mongodb.org/manual/tutorial/aggregation-examples/#largest-and-smallest-cities-by-state
db.zipcodes.aggregate( db.zipcodes.aggregate(
{ {
$group: { $group: {
@ -3116,8 +3096,8 @@ public class AggregationTests {
} }
} }
@Test @Test // GH-5027
void xxx() { void shouldAggregateAsInterface() {
MyOhMy source = new MyOhMy(); MyOhMy source = new MyOhMy();
source.id = "id-1"; source.id = "id-1";
@ -3127,8 +3107,8 @@ public class AggregationTests {
mongoTemplate.save(source); mongoTemplate.save(source);
TypedAggregation<MyOhMy> agg = newAggregation(MyOhMy.class, project("firstname")); TypedAggregation<MyOhMy> agg = newAggregation(MyOhMy.class, project("firstname"));
AggregationResults<MyMyOh> aggregate = mongoTemplate.aggregate(agg, MyMyOh.class); AggregationResults<MyMyOhInterface> aggregate = mongoTemplate.aggregate(agg, MyMyOhInterface.class);
assertThat(aggregate.getMappedResults()).hasOnlyElementsOfType(MyMyOh.class); assertThat(aggregate.getMappedResults()).hasOnlyElementsOfType(MyMyOhInterface.class);
} }
static class MyOhMy { static class MyOhMy {
@ -3137,7 +3117,7 @@ public class AggregationTests {
String lastname; String lastname;
} }
interface MyMyOh { interface MyMyOhInterface {
String getFirstname(); String getFirstname();
} }
} }

Loading…
Cancel
Save