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.

3 Answers 3

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

Sign up to request clarification or add additional context in comments.

3 Comments

Hi, seems like you were right, but I'm still getting problems, would you kindly look the edit message? Thank very much.
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?
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. ;)
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);
    }

}

Comments

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);

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.