1

I have a query in mongodb with aggregation framework, which returns number of topics in my documents. This is done by utilizing the $group operator and the result looks like:

{
postsByTopics: [
        {
            "_id" : "Topic 1",
            "count" : 3.0
        },
        {
            "_id" : "Topic 2",
            "count" : 1.0
        }
    ]
}

I use projection to get a better shaped results

{
"$project": {
        "postsByTopics": {
            "$arrayToObject": {
                "$map": {
                    "input": "$postsByTopics",
                    "as": "el",
                    "in": {
                        "k": "$$el._id",
                        "v": "$$el.count",
                    }
                }
            }
        },

}

which returns much nicer shaped result:

{
postsByTopics: {
    "Topic1" : 3.0,
    "Topic2" : 1.0
    }
}

Now I would like to turn this projection in a java spring boot code. I found project().and(ArrayOperators.ArrayToObject.arrayToObject("postsByTopics"))

and

project().and(VariableOperators.Map.itemsOf("postsByTopics").as("el")

but I am struggling to combine them to achieve the desired result

2 Answers 2

4

Spring-data syntax for "complex" query is not friendly.

Workaround 1: Implement AggregationExpression for .andApply method.

ProjectionOperation project = Aggregation.project().and(
    ArrayOperators.ArrayToObject.arrayValueOfToObject(
      Map
       .itemsOf("postsByTopics")
       .as("el")
       .andApply(agg -> new Document("k", "$$el._id").append("v", "$$el.count"))
    )   
).as("postsByTopics");

Workaround 2: Implement AggregationOperation with MongoDB JSON syntax

Aggregation agg = Aggregation.newAggregation(
    exp -> new Document("$project", 
               new Document("postsByTopics", 
                   new Document("$arrayToObject",
                       new Document("$map", 
                           new Document("input", "$postsByTopics")
                               .append("as", "el")
                               .append("in", 
                                   new Document("k", "$$el._id")
                                        .append("v", "$$el.count")
                           )
                       )
                   )
               )
           )        
);
Sign up to request clarification or add additional context in comments.

3 Comments

Many thanks, it works like a charm and your workaround example did help me solve some further issues with projection of map with posts-by-month, because the month is represented as number and the map operator allows nur strings as keys. For some reason new Document("k",ConvertOperators.ToString.toString("$$el._id.month")) gives me error (required string, found object) but new Document("k", new Document("$toString", "$$el._id.month")) works just fine
@karlitos You are welcome. If you start to implement MongoDB native syntax, you cannot mix it with Spring-Data syntax. Try this: new Document("k",ConvertOperators.ToString.toString("$$el._id.month").toDocument(Aggregation.DEFAULT_CONTEXT))
Many thanks, the .toDocument(Aggregation.DEFAULT_CONTEXT) was exactly what I was missing
0

Code above is NoSQL Query and down is almost similiar with MongoTemplate Cirteria Query. Maybe same propertieies are different, hope it will help someone ! Important is to save List as projection new property, i didnt find solution with arrayOf(List ... always inserted List in list with input: { elemetnAt: [[ .... whci hwas really wrong.

   $project {
     stats: {
         $map: {
             input: [ "2018-09-01", "2022-01-24", "2022-01-22", "2018-09-04", "2018-09-05" ],
             as: "date",
             in: {
             $let: {
                 vars: { dateIndex: { "$indexOfArray": [ "$days.day", "$$date" ] } },
                 in: {
                     $cond: {
                         if: { $ne: [ "$$dateIndex", -1 ] },
                         then: { $arrayElemAt: [ "$days", "$$dateIndex" ] },
                         else: { _id: "$$date", date: "$$date", distance: 0 }
                     }
                 }
                }
             }
         }
     }





project("_id", "days", "category").andArrayOf(timearray).as("daysEmpty"),
                project("category").and(
                        mapItemsOf("daysEmpty")
                                .as("date")
                                .andApply(new AggregationExpression() {
                                    @Override
                                    public Document toDocument(AggregationOperationContext context) {
                                       return new Document("$let",
                                                new Document("vars",
                                                        new Document("dateIndex",
                                                                new Document("$indexOfArray", asList("$days.day", "$$date"))))
                                                        .append("in",
                                                                new Document("$cond",
                                                                        new Document("if",
                                                                                new Document("$ne", asList("$$dateIndex", -1L)))
                                                                                .append("then",
                                                                                        new Document("$arrayElemAt", asList("$days", "$$dateIndex")))
                                                                                .append("else",
                                                                                        new Document("_id", "$$date")
                                                                                                .append("date", "$$date")
                                                                                                .append("distance", 0L)
                                                                                )
                                                                )
                                                        )
                                        );
                                    }
                                })).as("data")

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.