javaspringspring-bootspring-data-mongodbspring-el

@Aggregation using SpEL in Spring Data MongoDB repository problem


I'm working on spring boot application with mongodb database, and I have a collection named DOCUMENT: Here is the Document.java:

@Getter
@Setter
@Document("DOCUMENT")
public class Document {

    @Id
    private String id;
    private List<String> field1;
    private String field2;
    private Date field3;
    etc...
}

I have also a dedicated Repository:

@Repository
public interface DocumentRepository extends MongoRepository<Document, String> {

    @Aggregation(
            pipeline = {
                    "{ '$match': { " +
                        "?#{ [0] == null || [0].isEmpty() ? '' : 'field1 : { $in: [0] },' }" +
                        "?#{ T(org.apache.commons.lang3.StringUtils).isBlank([1]) ? '' :             'field2 : { $regex: '.*' + [1] + '.*', $options: 'i' },' }" +
    "?#{ [2] == null ? '' : 'field3 : { $gte: [2] },' }" +
    "?#{ [2] == null ? '' : 'field3 : { $lte: [2] },' }" +
"} }"
            }
    )
    List<Document> findDocument(
            List<String> field1, String field2, Date field3LowerBound, Date Date field3UpperBound
    );

}

And in my service layer i call this method:

List<Document> documentList = documentRepository.findDocument(field1, field2, field3Lower,   field3Upper);

But i get this:

jakarta.servlet.ServletException: Handler dispatch failed: java.lang.StackOverflowError
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1096) ~[spring-webmvc-6.0.7.jar:6.0.7]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974) ~[spring-webmvc-6.0.7.jar:6.0.7]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011) ~[spring-webmvc-6.0.7.jar:6.0.7]
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[spring-webmvc-6.0.7.jar:6.0.7]
Caused by: java.lang.StackOverflowError: null
    at org.springframework.data.mongodb.util.json.JsonBuffer.read(JsonBuffer.java:50) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.JsonScanner.nextToken(JsonScanner.java:70) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingJsonReader.popToken(ParameterBindingJsonReader.java:753) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingJsonReader.readBsonType(ParameterBindingJsonReader.java:167) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.bson.AbstractBsonReader.verifyBSONType(AbstractBsonReader.java:679) ~[bson-4.8.2.jar:na]
    at org.bson.AbstractBsonReader.checkPreconditions(AbstractBsonReader.java:721) ~[bson-4.8.2.jar:na]
    at org.bson.AbstractBsonReader.readStartDocument(AbstractBsonReader.java:449) ~[bson-4.8.2.jar:na]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.decode(ParameterBindingDocumentCodec.java:235) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.decode(ParameterBindingDocumentCodec.java:67) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.readValue(ParameterBindingDocumentCodec.java:371) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.decode(ParameterBindingDocumentCodec.java:248) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.decode(ParameterBindingDocumentCodec.java:67) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.readValue(ParameterBindingDocumentCodec.java:371) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.decode(ParameterBindingDocumentCodec.java:248) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.decode(ParameterBindingDocumentCodec.java:67) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.readValue(ParameterBindingDocumentCodec.java:371) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.decode(ParameterBindingDocumentCodec.java:248) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.decode(ParameterBindingDocumentCodec.java:67) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.readValue(ParameterBindingDocumentCodec.java:371) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.decode(ParameterBindingDocumentCodec.java:248) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.decode(ParameterBindingDocumentCodec.java:67) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
    at org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec.readValue(ParameterBindingDocumentCodec.java:371) ~[spring-data-mongodb-4.0.4.jar:4.0.4]
etc...

I change @Date lombok annotation to @Getter and @Setter, while debuggin i notice that it loops over hashcode but i'm not sure that is the problem


Solution

  • the problem seems the SpEL itself, I replicated here https://github.com/sbernardo/spring-issues-examples/tree/main/sof-questions-77705053 and modified the SpEL as follow:

    public interface DocumentRepository extends MongoRepository<DocumentEntity, String> {
    
        @Aggregation(
                pipeline = {"""
                        {
                            '$match': {
                                $and: [
                                    ?#{ [0] == null || [0].isEmpty() ? { _id: { $exists: true } } : { field1 : { $in: [0] } } },
                                    ?#{ T(org.apache.commons.lang3.StringUtils).isBlank([1]) ? { _id: { $exists: true } } : { field2 : { $regex: '.*' + [1] + '.*', $options: 'i' } } },
                                    ?#{ [2] == null ? { _id: { $exists: true } } : { field3 : { $gte: [2] } } },
                                    ?#{ [3] == null ? { _id: { $exists: true } } : { field3 : { $lte: [3] } } }
                                ]
                            }
                        }
                        """
                }
        )
        List<DocumentEntity> findDocument(List<String> field1, String field2, Date field3LowerBound, Date field3UpperBound);
    
    }
    

    NOTE: I can't avoid to add parameter as described here, so I suggest to use a mandatory field instead of { _id: { $exists: true } }

    I found this issue in Spring Data Mongo to manage null values and the workaround is similar to my implementation. This the issue: https://github.com/spring-projects/spring-data-mongodb/issues/4050. Note the issue is for @Query but I think is the same.

    I tried some cases and works as expected. Hope this will help. If you find a better solution I will want to know :D