Uploaded image for project: 'Spring Data MongoDB'
  1. Spring Data MongoDB
  2. DATAMONGO-2475

QueryDSL: $and and $or with more than two arguments in final query document

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Minor
    • Resolution: Fixed
    • Affects Version/s: 2.2.4 (Moore SR4)
    • Fix Version/s: 3.0 RC1 (Neumann)
    • Component/s: None
    • Labels:
    • Environment:
      macOS Catalina, Apache Maven 3.6.2, Java 1.8.0_202, Spring Boot 2.2.4.RELEASE, MongoDB Community Server 4.2.1

      Description

      I had to build a big QueryDSL predicate to fetch documents. As result, I get an exception; because the depth of the document used by MongoTemplate was greater than the maximum depth limits.

      Using Spring Data MongoDB's Criteria I achieved this query with no errors. Criteria's API defines two different operations: and(...) and andOperator(...). QueryDSL has nothing similar to that, but I think we don't need a different operation.

      MongodbDocumentSerializer is used to create the final query that eventually will be used by MongoTemplate. This serializer should create documents with the minimum depth as possible.

      I actually coded a solution to this (from branch 2.2.x) and tested it. This change removes nested "$or" in "$or" operations, and nested "$and" in "$and" operations, generating just one logical operator. In case of an "$and", this code keeps merging documents with different properties; same way it does now.

       

      How does it work? What do I expect?

      Using QueryDSL we can build a predicate like this:

      QPerson.person.firstname.eq("Hencxjo")
          .or(QPerson.person.lastname.eq("Leon"))
          .or(QPerson.person.age.loe(30))
          .or(QPerson.person.age.qoe(20))
          .or(QPerson.person.uniqueId.isNull())
      

      The final document that MongoTemplate will use for querying will be:

      {
        "$or": [
          {
            "$or": [
              {
                "$or": [
                  {
                    "$or": [
                      { "firstname": "Hencxjo" },
                      { "lastname": "Leon" }
                    ]
                  },
                  { "age": { "$lte": 30 } }
                ]
              },
              { "age": { "$gte": 20 } }
            ]
          },
          { "uniqueId": { "$exists": false } }
        ]
      }

      What I want to get:

      {
       "$or": [
         { "firstname": "Hencxjo" },
         { "lastname": "Leon" },
         { "age": { "$lte": 30 } },
         { "age": { "$gte": 20 } },
         { "uniqueId": { "$exists": false } }
       ]
      }
      

      In case of chained ANDs, this may be the result:

      {
        "$and": [
          {
            "firstname": "Hencxjo",
            "lastname": "Leon",
            "age": { "$lte": 30 },
            "uniqueId": { "$exists": false }
          },
          { "age": { "$gte": 20 } }
        ]
      }
      

       

        Attachments

          Activity

            People

            Assignee:
            cstrobl Christoph Strobl
            Reporter:
            hencxjo Hencxjo
            Last updater:
            Hencxjo
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved: