@Aggregation not work for { $group: { _id: $XXX } }
public interface ResourceRepository
extends ReactiveMongoRepository<Resource, String>, ReactiveQuerydslPredicateExecutor<Resource> {
@Aggregation(pipeline = {
"{ $match: { sha256: ?0, completed: true } }",
"{ $group: { _id: $refId, count: { $sum: 1 }, ids: { $push: $_id } } }",
"{ $project: { _id: 1, count: 1, ids: 1, sortKey: { $cond: { if: { $eq: ['$_id', null] }, then: -Infinity, else: '$count' } } } }",
"{ $sort: { sortKey: -1 } }",
"{ $group: { _id: null, mostFrequent: { $first: '$$ROOT' }, allGroups: { $push: '$$ROOT' } } }",
"{ $project: { refId: '$mostFrequent._id', ids: { $reduce: { input: { $filter: { input: '$allGroups', cond: { $or: [{ $eq: ['$mostFrequent._id', null] }, { $ne: ['$$this._id', '$mostFrequent._id'] }] } } }, initialValue: [], in: { $concatArrays: ['$$value', '$$this.ids'] } } }, _id: 0 } }"
})
Mono<RefIdResult> findMostFrequentRefIdWithIds(String sha256);
}
Here is my method. And when invoking it, an error occured:
org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [org.bson.types.ObjectId] for value [$refId]
After debug, I found the problem here (QueryMapper.class):
Field field = createPropertyField(entity, key, mappingContext); // here it created a field point to Resource.id, becuase key == '_id'
// TODO: move to dedicated method
if (field.getProperty() != null && field.getProperty().isUnwrapped()) {
Object theNestedObject = BsonUtils.get(query, key);
Document mappedValue = (Document) getMappedValue(field, theNestedObject);
if (!StringUtils.hasText(field.getMappedKey())) {
result.putAll(mappedValue);
} else {
result.put(field.getMappedKey(), mappedValue);
}
} else {
Entry<String, Object> entry = getMappedObjectForField(field, BsonUtils.get(query, key)); // BsonUtils.get(query, key) == '$refId', spring-data-mongodb think $refId is an ObjectId, and convert it to ObjectId by new ObjectId("$refId"), and failed.
Thank you for reporting - it would help us if you could provide a complete minimal sample (something that we can unzip or git clone, build, and deploy) that reproduces the problem.
@christophstrobl Here is the sample By the way, if this is confirmed to be a bug, I hope a workaround can be provided. Waiting for an official update is too long.
Here is the output:
%TESTC 1 v2
%TSTTREE2,com.cxj.bugreport.bugreport.mongo.dao.ResourceRepositoryTest,true,1,false,1,ResourceRepositoryTest,,[engine:junit-jupiter]/[class:com.cxj.bugreport.bugreport.mongo.dao.ResourceRepositoryTest]
%TSTTREE3,testFindMostFrequentRefIdWithIds(com.cxj.bugreport.bugreport.mongo.dao.ResourceRepositoryTest),false,1,false,2,testFindMostFrequentRefIdWithIds(),,[engine:junit-jupiter]/[class:com.cxj.bugreport.bugreport.mongo.dao.ResourceRepositoryTest]/[method:testFindMostFrequentRefIdWithIds()]
%TESTS 3,testFindMostFrequentRefIdWithIds(com.cxj.bugreport.bugreport.mongo.dao.ResourceRepositoryTest)
%FAILED 3,testFindMostFrequentRefIdWithIds(com.cxj.bugreport.bugreport.mongo.dao.ResourceRepositoryTest)
%TRACES
java.lang.AssertionError: expectation "assertNext" failed (expected: onNext(); actual: onError(org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [org.bson.types.ObjectId] for value [$refId]))
at reactor.test.MessageFormatter.assertionError(MessageFormatter.java:115)
at reactor.test.MessageFormatter.failPrefix(MessageFormatter.java:104)
at reactor.test.MessageFormatter.fail(MessageFormatter.java:73)
at reactor.test.MessageFormatter.failOptional(MessageFormatter.java:88)
at reactor.test.DefaultStepVerifierBuilder.lambda$consumeNextWith$1(DefaultStepVerifierBuilder.java:276)
at reactor.test.DefaultStepVerifierBuilder$SignalEvent.test(DefaultStepVerifierBuilder.java:2289)
at reactor.test.DefaultStepVerifierBuilder$DefaultVerifySubscriber.onSignal(DefaultStepVerifierBuilder.java:1529)
at reactor.test.DefaultStepVerifierBuilder$DefaultVerifySubscriber.onExpectation(DefaultStepVerifierBuilder.java:1477)
at reactor.test.DefaultStepVerifierBuilder$DefaultVerifySubscriber.onError(DefaultStepVerifierBuilder.java:1129)
at reactor.core.publisher.MonoNext$NextSubscriber.onError(MonoNext.java:93)
at reactor.core.publisher.FluxUsingWhen$UsingWhenSubscriber.deferredError(FluxUsingWhen.java:403)
at reactor.core.publisher.FluxUsingWhen$RollbackInner.onComplete(FluxUsingWhen.java:480)
at reactor.core.publisher.Operators.complete(Operators.java:137)
at reactor.core.publisher.MonoEmpty.subscribe(MonoEmpty.java:46)
at reactor.core.publisher.Mono.subscribe(Mono.java:4576)
at reactor.core.publisher.FluxUsingWhen$UsingWhenSubscriber.onError(FluxUsingWhen.java:368)
at reactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onError(MonoFlatMapMany.java:256)
at reactor.core.publisher.MonoNext$NextSubscriber.onError(MonoNext.java:93)
at reactor.core.publisher.FluxMap$MapSubscriber.onError(FluxMap.java:134)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:106)
at reactor.core.publisher.Operators.error(Operators.java:198)
at reactor.core.publisher.MonoError.subscribe(MonoError.java:53)
at reactor.core.publisher.Mono.subscribe(Mono.java:4576)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103)
at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:168)
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
at reactor.core.publisher.MonoSupplier$MonoSupplierSubscription.request(MonoSupplier.java:145)
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2366)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onSubscribe(FluxOnErrorResume.java:74)
at reactor.core.publisher.MonoSupplier.subscribe(MonoSupplier.java:48)
at reactor.core.publisher.Mono.subscribe(Mono.java:4576)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:180)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onError(FluxFilter.java:157)
at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onError(FluxMap.java:265)
at reactor.core.publisher.Operators.error(Operators.java:198)
at reactor.core.publisher.MonoError.subscribe(MonoError.java:53)
at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55)
at reactor.core.publisher.Mono.subscribe(Mono.java:4576)
at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:196)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:245)
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:305)
at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2096)
at reactor.core.publisher.MonoCollectList$MonoCollectListSubscriber.onComplete(MonoCollectList.java:118)
at reactor.core.publisher.FluxConcatIterable$ConcatIterableSubscriber.onComplete(FluxConcatIterable.java:121)
at reactor.core.publisher.FluxConcatIterable.subscribe(FluxConcatIterable.java:60)
at reactor.core.publisher.MonoFromFluxOperator.subscribe(MonoFromFluxOperator.java:83)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122)
at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:122)
at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2570)
at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribeInner(MonoFlatMapMany.java:150)
at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:189)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
at reactor.core.publisher.MonoSupplier$MonoSupplierSubscription.request(MonoSupplier.java:145)
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2366)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onSubscribe(FluxOnErrorResume.java:74)
at reactor.core.publisher.MonoSupplier.subscribe(MonoSupplier.java:48)
at reactor.core.publisher.Mono.subscribe(Mono.java:4576)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:180)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onError(FluxFilter.java:157)
at reactor.core.publisher.FluxMap$MapConditionalSubscriber.onError(FluxMap.java:265)
at reactor.core.publisher.Operators.error(Operators.java:198)
at reactor.core.publisher.MonoError.subscribe(MonoError.java:53)
at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55)
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53)
at reactor.core.publisher.Flux.subscribe(Flux.java:8891)
at reactor.core.publisher.FluxUsingWhen.subscribe(FluxUsingWhen.java:94)
at reactor.core.publisher.Mono.subscribe(Mono.java:4576)
at reactor.test.DefaultStepVerifierBuilder$DefaultStepVerifier.toVerifierAndSubscribe(DefaultStepVerifierBuilder.java:891)
at reactor.test.DefaultStepVerifierBuilder$DefaultStepVerifier.verify(DefaultStepVerifierBuilder.java:831)
at reactor.test.DefaultStepVerifierBuilder$DefaultStepVerifier.verify(DefaultStepVerifierBuilder.java:823)
at reactor.test.DefaultStepVerifierBuilder.verifyComplete(DefaultStepVerifierBuilder.java:690)
at com.cxj.bugreport.bugreport.mongo.dao.ResourceRepositoryTest.testFindMostFrequentRefIdWithIds(ResourceRepositoryTest.java:74)
at java.base/java.lang.reflect.Method.invoke(Method.java:569)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Suppressed: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [org.bson.types.ObjectId] for value [$refId]
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:182)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:165)
at org.springframework.data.mongodb.core.convert.QueryMapper.applyFieldTargetTypeHintToValue(QueryMapper.java:930)
at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedValue(QueryMapper.java:461)
at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObjectForField(QueryMapper.java:365)
at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObject(QueryMapper.java:184)
at org.springframework.data.mongodb.core.convert.QueryMapper.convertSimpleOrDocument(QueryMapper.java:566)
at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedKeyword(QueryMapper.java:424)
at org.springframework.data.mongodb.core.convert.QueryMapper.getMappedObject(QueryMapper.java:143)
at org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext.getMappedObject(TypeBasedAggregationOperationContext.java:102)
at org.springframework.data.mongodb.repository.query.StringAggregationOperation.toDocument(StringAggregationOperation.java:54)
at org.springframework.data.mongodb.core.aggregation.AggregationOperation.toPipelineStages(AggregationOperation.java:55)
at org.springframework.data.mongodb.core.aggregation.AggregationOperationRenderer.toDocument(AggregationOperationRenderer.java:56)
at org.springframework.data.mongodb.core.aggregation.AggregationPipeline.toDocuments(AggregationPipeline.java:86)
at org.springframework.data.mongodb.core.aggregation.Aggregation.toPipeline(Aggregation.java:771)
at org.springframework.data.mongodb.core.AggregationUtil.createPipeline(AggregationUtil.java:88)
at org.springframework.data.mongodb.core.QueryOperations$AggregationDefinition.lambda$new$3(QueryOperations.java:1008)
at org.springframework.data.util.Lazy.getNullable(Lazy.java:135)
at org.springframework.data.util.Lazy.get(Lazy.java:113)
at org.springframework.data.mongodb.core.QueryOperations$AggregationDefinition.getAggregationPipeline(QueryOperations.java:1017)
at org.springframework.data.mongodb.core.ReactiveMongoTemplate.lambda$doAggregate$26(ReactiveMongoTemplate.java:1032)
at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:163)
... 59 more
Caused by: java.lang.IllegalArgumentException: state should be: hexString has 24 characters
at org.bson.assertions.Assertions.isTrueArgument(Assertions.java:64)
at org.bson.types.ObjectId.parseHexString(ObjectId.java:384)
at org.bson.types.ObjectId.<init>(ObjectId.java:193)
at org.springframework.data.mongodb.core.convert.MongoConverters$StringToObjectIdConverter.convert(MongoConverters.java:146)
at org.springframework.data.mongodb.core.convert.MongoConverters$StringToObjectIdConverter.convert(MongoConverters.java:142)
at org.springframework.core.convert.support.GenericConversionService$ConverterAdapter.convert(GenericConversionService.java:364)
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41)
... 81 more
%TRACEE
%TESTE 3,testFindMostFrequentRefIdWithIds(com.cxj.bugreport.bugreport.mongo.dao.ResourceRepositoryTest)
%RUNTIME5586
thanks for reporting. looks like we need a dedicated aggregation mapper that can deal with field references instead of piping values through the converter as the QueryMapper does.