1

I have mongodb facet and aggregate query in spring boot using mongo template and aggregation query. everything works fine except case sensitivity of value. I have below query in mongodb:

db.getCollection('product').aggregate([{"colors":[{$unwind:"$variants"},
{"$group": {
        _id: { $toLower: "$variants.color" },
        count:{$sum:1},
        image : { $first: '$variants.color_image' },
    }}

]

I have equivalent spring data query:

Aggregation.facet(unwind("variants"), group("variants.color").count().as("count").first("variants.color_image").as("image"))
            .as("colors");

But here how can I mention toLower to group field?

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
Mihir Shah
  • 1,799
  • 3
  • 29
  • 49

1 Answers1

2

Spring-boot does not allow complex aggregations as you can do in shell. So, you may apply such workaround.

Let's change your facet query by this (create new field where we set lower case color value):

db.collection.aggregate([
  {
    $facet: {
      "colors": [
        {
          $unwind: "$variants"
        },
        {
          $addFields: {
            "variants.color_lower": {
              $toLower: "$variants.color"
            }
          }
        },
        {
          "$group": {
            _id: "$variants.color_lower",
            count: {
              $sum: 1
            },
            image: {
              $first: "$variants.color_image"
            },

          }
        }
      ]
    }
  }
])

MongoPlayground

Now, Spring-Boot allows to define custom AggregationOperation (Generic solution: here):

public class LowerAggregationOperation implements AggregationOperation() {

    @Override
    public List<Document> toPipelineStages(AggregationOperationContext context) {

        return Arrays.asList(
            new Document("$addFields", 
                new Document("variants.color_lower", 
                    new Document("$toLower", "$variants.color")))
        );
    }

}

Now, you complete your facet aggregation:

Aggregation.facet(
    unwind("variants"), 
    new LowerAggregationOperation(),
    group("variants.color_lower").count().as("count").first("variants.color_image").as("image"))
.as("colors");
Valijon
  • 12,667
  • 4
  • 34
  • 67
  • Is that possible to input all aggregation stages in to one method and get the output? Eg: `unwind`, `lookup`, `group` query as String and get the output – varman May 26 '20 at 14:53
  • @varman What do you mean? You need to build valid aggregation pipeline. If you can do so in MongoDB shell, you can try doing within Spring-data – Valijon May 26 '20 at 15:01
  • Of course, what I do is, after checking the mongo script in shell, i do in spring like `Aggregation aggregation = Aggregation.newAggregation(match(..),lookup(..),unwind(..),group(..))`. Instead of rewriting again in Spring, can't we have a method that gets the already written mongo script as a String parameter which returns the output same as shell? – varman May 26 '20 at 15:12
  • @varman Ah, I see. Ofcourse, you need to call MongoDB aggregate method with list of Document. Example [here](https://stackoverflow.com/questions/31643109/mongodb-aggregation-with-java-driver). Take a look `Document.parse` – Valijon May 26 '20 at 15:16