spring-projects / spring-projects/spring-data-mongodb

Using aggregation parameters with @Aggregation

Open
#4,697 3 comments 0 reactions 1 assignee View on GitHub

@christophstrobl is already working on this.

Since May 13, 2024.

type: documentation
Dominant language
Java
Stars
1.7k
Forks
1.1k
PR merge metrics
No merged PRs in 30d

Description

I am trying to run the following MongoDB aggregation using Spring:

@Repository
public interface StockRepository extends MongoRepository<StockDocument, UUID> {
    @Aggregation(pipeline = """
        [
          {
            $match: {
              timestamp: {
                $gte: $?0,
                $lte: $?1
              }
            }
          },
          {
            $group: {
              _id: {
                $toDate: {
                  $subtract: [
                    { $toLong: '$timestamp' },
                    { $mod: [{ $toLong: '$timestamp' }, $?2] }
                  ]
                }
              },
              avgOpen: { $avg: '$open' },
              avgClose: { $avg: '$close' },
              minLow: {$min: '$low'},
              maxHigh: {$max: '$high'}
            }
          },
          {
            $sort: {
              _id: 1
            }
          }
        ]
    """)
    AggregationResults<Document> queryStockPrices(Instant from, Instant to, long intervalMs);
}

The query should find all documents in a time range and aggregate them using a specific time interval (the ?2 parameter).

When I run this pipeline directly on Mongo using specific dates and mod parameter, everything works as expected. Example:

     [
          {
            $match: {
              timestamp: {
                $gte: ISODate('2023-03-02T11:39:00.000+00:00'),
                $lte: ISODate('2023-03-02T11:55:00.000+00:00')
              }
            }
          },
          {
            $group: {
              _id: {
                $toDate: {
                  $subtract: [
                    { $toLong: '$timestamp' },
                    { $mod: [{ $toLong: '$timestamp' }, 60000] }
                  ]
                }
              },
              avgOpen: { $avg: '$open' },
              avgClose: { $avg: '$close' },
              minLow: {$min: '$low'},
              maxHigh: {$max: '$high'}
            }
          },
          {
            $sort: {
              _id: 1
            }
          }
        ]

But, when I run this using Spring, I get the following exception:

java.util.NoSuchElementException: null
	at java.base/java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:758) ~[na:na]
	at java.base/java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:778) ~[na:na]
	at org.springframework.data.mongodb.core.aggregation.AggregationOperation.getOperator(AggregationOperation.java:66) ~[spring-data-mongodb-4.2.5.jar:4.2.5]
	at org.springframework.data.mongodb.core.aggregation.AggregationPipeline.isOut(AggregationPipeline.java:165) ~[spring-data-mongodb-4.2.5.jar:4.2.5]
	at org.springframework.data.mongodb.core.aggregation.AggregationPipeline.verify(AggregationPipeline.java:107) ~[spring-data-mongodb-4.2.5.jar:4.2.5]
	at org.springframework.data.mongodb.core.aggregation.AggregationPipeline.toDocuments(AggregationPipeline.java:85) ~[spring-data-mongodb-4.2.5.jar:4.2.5]
	at org.springframework.data.mongodb.core.aggregation.Aggregation.toPipeline(Aggregation.java:757) ~[spring-data-mongodb-4.2.5.jar:4.2.5]
	at org.springframework.data.mongodb.core.AggregationUtil.createPipeline(AggregationUtil.java:98) ~[spring-data-mongodb-4.2.5.jar:4.2.5]
	at org.springframework.data.mongodb.core.MongoTemplate.doAggregate(MongoTemplate.java:2173) ~[spring-data-mongodb-4.2.5.jar:4.2.5]
	at org.springframework.data.mongodb.core.MongoTemplate.doAggregate(MongoTemplate.java:2148) ~[spring-data-mongodb-4.2.5.jar:4.2.5]
	at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:2142) ~[spring-data-mongodb-4.2.5.jar:4.2.5]
	at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:2008) ~[spring-data-mongodb-4.2.5.jar:4.2.5]
	at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:2002) ~[spring-data-mongodb-4.2.5.jar:4.2.5]
	at org.springframework.data.mongodb.repository.query.StringBasedAggregation.doExecute(StringBasedAggregation.java:126) ~[spring-data-mongodb-4.2.5.jar:4.2.5]
	at org.springframework.data.mongodb.repository.query.AbstractMongoQuery.execute(AbstractMongoQuery.java:119) ~[spring-data-mongodb-4.2.5.jar:4.2.5]
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:170) ~[spring-data-commons-3.2.5.jar:3.2.5]
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:158) ~[spring-data-commons-3.2.5.jar:3.2.5]
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:164) ~[spring-data-commons-3.2.5.jar:3.2.5]
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143) ~[spring-data-commons-3.2.5.jar:3.2.5]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:70) ~[spring-data-commons-3.2.5.jar:3.2.5]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.data.mongodb.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:129) ~[spring-data-mongodb-4.2.5.jar:4.2.5]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223) ~[spring-aop-6.1.6.jar:6.1.6]
	at jdk.proxy2/jdk.proxy2.$Proxy96.queryStockPrices(Unknown Source) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-6.1.6.jar:6.1.6]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223) ~[spring-aop-6.1.6.jar:6.1.6]
	at jdk.proxy2/jdk.proxy2.$Proxy96.queryStockPrices(Unknown Source) ~[na:na]

I have tried wrapping the parameters, such as '$?0' but that did not work either, even though that is the format mentioned in @Aggregation JavaDoc.

What am I doing wrong? How do I pass these parameters correctly in my aggregation pipeline using Spring @Aggregation?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.