2

NOTE: Go down in order to see the edited message.

I'm trying to imitate this query:

db.sentiments.aggregate([
{"$group" : {_id:{theme_id:"$theme",sentiment_id:"$sentiment"}, count:{$sum:1}}},
{"$sort":{"_id.theme_id":1}} ])

This is the code that I had generated in order to imitate it:

    @RepositoryRestResource(collectionResourceRel = "sentiments", path = "sentiments")
public interface SentimentsRepository extends MongoRepository<Sentiments, String> {
    Long countByTheme(@Param("theme") String theme);

@Query(value ="[\n" +
        "    {\"$group\" : {_id:{theme_id:\"$theme\",sentiment_id:\"$sentiment\"}, count:{$sum:1}}},\n" +
        "\t{\"$sort\":{\"_id.theme_id\":1}}\n" +
        "]",count = true)
List<Object> comptarSentiments();

} Well this code is returning me this error:

"exception": "org.springframework.data.mongodb.UncategorizedMongoDbException",


"message": "Can't canonicalize query: BadValue unknown operator: $group; nested exception is com.mongodb.MongoException: Can't canonicalize query: BadValue unknown operator: $group",

Actually I'm a begginer in what refers to the use of Spring so I'm very lost, does any one know what should I do?

Thanks and sorry for my bad english, not my native language.

[EDIT]---------------------------------------- Just as the comment wrote by Shawn Clark It's not possible to do it this way, in order to achieve that you will need to create a customRepository.

What's the difference between Spring Data's MongoTemplate and MongoRepository?

I have been trying to do it this way but something doesn't seem to be correct, here is my new code:

@RepositoryRestResource(collectionResourceRel = "sentiments", path = "sentiments")
public interface SentimentsRepository extends CrudRepository<Sentiments, String>, CustomSentimentsRepository {

//Other methods...

}

public interface CustomSentimentsRepository {

    List<CountResult> yourCustomMethod();

    class CountResult{
        String theme;
        String sentiment;
        int total;
    }
}

public class SentimentsRepositoryImpl implements CustomSentimentsRepository {
    private final MongoOperations operations;


    @Autowired
    public SentimentsRepositoryImpl(MongoOperations operations) {

        Assert.notNull(operations, "MongoOperations must not be null!");
        this.operations = operations;
    }

    @Override
    public List<CountResult> yourCustomMethod(){
        Aggregation agg = Aggregation.newAggregation(
                Aggregation.group("theme","sentiment").count().as("total"),
                Aggregation.project("theme","sentiment").and("total").previousOperation(),
                Aggregation.sort(Sort.Direction.DESC, "theme")
        );

        //Convert the aggregation result into a List
        AggregationResults<CountResult> groupResults
                = operations.aggregate(agg,"sentiments",  CountResult.class);
        //List<CountResult> result = groupResults.getMappedResults();

        return groupResults.getMappedResults();
    }
}

I'm not even able to debbug this code and I'm always getting a 404.

Community
  • 1
  • 1
alam94
  • 21
  • 4

3 Answers3

0

Based on the information I have found you can't do that complex of a @Query on a MongoRepository method. In this case you would want to create a class and implement your comptarSentiments() method using the mongoTemplate to query the data store with your aggregate function. Then create a controller class that exposes a REST endpoint and have it call the repository.

Once you get to doing complex queries in Mongo you lose the ease of @RepositoryRestResource and have to go back to wiring the REST endpoint to the repository yourself.

Spring Data REST : custom query for MongoDB repository

Implementing custom methods of Spring Data repository and exposing them through REST

Community
  • 1
  • 1
Shawn Clark
  • 3,330
  • 2
  • 18
  • 30
  • Hi, seems like you were right, but I'm still getting problems, would you kindly look the edit message? Thank very much. – alam94 Aug 10 '16 at 19:37
  • How are you trying to call the endpoint? What is giving you the 404? Do you have code for your controller class that you can share? – Shawn Clark Aug 10 '16 at 22:15
  • You were right again, a spelling mistake in my controller was causing the problem, I feel a bit ashamed, thank you so much for the interest. ;) – alam94 Aug 11 '16 at 18:02
0

I finally managed to solve the problem, seems like it was related with the controller and the type of the atribute "total" from the innerClass CountResult, it needs to be a String (this is very important, otherwise the Aggregation.project will fail). Here goes the final code:

public interface CustomSentimentsRepository {

    List<CountResult> myCountGroupByThemeAndSentiment();

    class CountResult{
        public String theme;
        public String sentiment;
        public String total;
    }
}

public class SentimentsRepositoryImpl implements CustomSentimentsRepository {
    private final MongoTemplate mongoTemplate;


    @Autowired
    public SentimentsRepositoryImpl(MongoTemplate mongoTemplate) {

        this.mongoTemplate = mongoTemplate;
    }

    @Override
    public List<CountResult> myCountGroupByThemeAndSentiment(){
        Aggregation agg = Aggregation.newAggregation(
                Aggregation.group("theme","sentiment").count().as("total"),
                Aggregation.project("theme","sentiment").andInclude("total"),


        Aggregation.sort(Sort.Direction.ASC,"theme","sentiment")
    );


    AggregationResults<CountResult> groupResults
            = mongoTemplate.aggregate(agg,"sentiments",  CountResult.class);


        return groupResults.getMappedResults();
    }
}

@RepositoryRestResource(collectionResourceRel = "sentiments", path = "sentiments")
public interface SentimentsRepository extends CrudRepository<Sentiments, String>, CustomSentimentsRepository {
    //Other methods

}

@RestController
@RequestMapping(value = "sentiments/search")
public class ChartsController {
    @Autowired
    private SentimentsRepository sentimentsRepository;

    @RequestMapping(value = "myCountGroupByThemeAndSentiment", method = RequestMethod.GET)
    public ResponseEntity<?> yourCustomMethod() {
        List<?> count=sentimentsRepository.myCountGroupByThemeAndSentiment();
        return new ResponseEntity(count, HttpStatus.OK);
    }

}
alam94
  • 21
  • 4
0

You can use @Aggrgation available in spring data mongodb 2.2.X versions:

@Aggregation(pipeline = {"{ '$group': { '_id' : '$lastname', names : { $addToSet : '$?0' } } }", "{ '$sort' : { 'lastname' : -1 } }"}) List<PersonAggregate> groupByLastnameAnd(String property);