6

I have MongoDB Page and Post collections. Each page document has field postIds which is array of post Ids (String objects). I want to use aggregation to count number of posts (=number of elements in array postIds) for each page. I wrote Mongo Shell aggregation function which returns exactly what I want:

db.page.aggregate([
    {$unwind : '$postIds'},
    {$group : {_id: '$_id', 'sum': { $sum: 1}}}
])

and it returns this result:

{ "_id" : "3", "sum" : 3 }
{ "_id" : "2", "sum" : 3 }

This means that page with id 3 has 3 posts, and page with id 2 has 3 posts as well, which is correct.

Now I wanted to write same using Spring MongoDB Aggregation, and wrote this simple JUnit test:

    /**
     * Test page posts count
     */
    @Test
    public void testPagePostsCount() throws Exception{
        MongoTemplate template = repository.getMongoTemplate();
        Page page = new Page();
        page.setPageId("2210");
        page.setUserId("azec");
        List<String> postList = new ArrayList<String>();
        postList.add("53eb1a560efbe048c7ea698d");
        postList.add("53eb1a6b0efbe048c7ea698e");
        page.setPostIds(postList);
        template.save(page);

        Aggregation agg = newAggregation(
            match(Criteria.where("pageId").is("2210")),
            unwind("postIds"),
            group("_id").sum("1").as("sum")
            //project("$sum").and("pageId").previousOperation()
        );

        AggregationResults<PostCount> results = template.aggregate(agg, "page", PostCount.class);
        List<PostCount> postCount = results.getMappedResults();

        System.out.println("Done!");
    }

However, this aggregation query returns this native Mongo query:

2014-08-13 20:06:07,949 DEBUG [org.springframework.data.mongodb.core.MongoTemplate] - Executing aggregation:

{
   "aggregate":"page",
   "pipeline":[
      {
         "$match":{
            "pageId":"2210"
         }
      },
      {
         "$unwind":"$postIds"
      },
      {
         "$group":{
            "_id":"$_id",
            "sum":{
               "$sum":"$1"
            }
         }
      }
   ]
}

Questions: 1. As you can see, the difference is with the $sum value of $1. I need to pass there number 1 instead of $1, but I am not sure how. 2. Do I need project operation here?

Thanks

1 Answer 1

18

I have figured this out finally. The key is to use count() aggregation function in Spring Data for MongoDB, which translates to sum increments by 1 in native Mongo shell. This is my final JUnit Test:

    /**
     * Test page posts count
     */
    @Test
    public void testPagePostsCount() throws Exception{
        MongoTemplate template = repository.getMongoTemplate();
        Page page = new Page();
        page.setPageId("2210");
        page.setUserId("azec");
        List<String> postList = new ArrayList<String>();
        postList.add("53eb1a560efbe048c7ea698d");
        postList.add("53eb1a6b0efbe048c7ea698e");
        page.setPostIds(postList);
        template.save(page);

        Aggregation agg = newAggregation(
            match(Criteria.where("_id").is("2210")),
            unwind("postIds"),
            group("_id").count().as("nPosts"),
            project("nPosts").and("_id").as("pageId")
        );

        AggregationResults<PostCount> results = template.aggregate(agg, "page", PostCount.class);
        List<PostCount> postCount = results.getMappedResults();
        Assert.assertTrue(!postCount.isEmpty());
        Assert.assertTrue(postCount.get(0).nPosts == 2);
        Assert.assertTrue(postCount.get(0).pageId.equals("2210"));
    }

    private class PostCount {
        String pageId;
        int nPosts;
    }

So finally this translates to following native aggregation operation:

{
   "aggregate":"page",
   "pipeline":[
      {
         "$match":{
            "_id":"2210"
         }
      },
      {
         "$unwind":"$postIds"
      },
      {
         "$group":{
            "_id":"$_id",
            "nPosts":{
               "$sum":1
            }
         }
      },
      {
         "$project":{
            "nPosts":1,
            "pageId":"$_id"
         }
      }
   ]
}
Sign up to request clarification or add additional context in comments.

Comments

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.