I have the following document structure in my current project:
[
{
"_id": {"$oid": "5f9a7ed94fea7938c6015e03"},
"ADDRESSES": [
{
"STREET_NUMBER": 99,
"STREET": "Barkey",
"CITY": "Wilmington Island",
"COUNTRY": "Austria"
}
],
"BIRTH_DATE": {"$date": "1967-05-27T00:00:00.000Z"},
"FIRST_NAME": "Wayne",
"ITEM": "employee",
"JOB": "Salesman",
"LAST_NAME": "Bryan",
"WORKDAYS": [
{
"WORK_FROM": {"$date": "2020-10-30T08:31:26.000Z"},
"WORK_TO": {"$date": "2020-10-30T16:31:26.000Z"}
},
{
"WORK_FROM": {"$date": "2021-08-04T11:31:05.000Z"},
"WORK_TO": {"$date": "2021-08-05T00:31:05.000Z"}
},
{
"WORK_FROM": {"$date": "2021-04-13T09:53:34.000Z"},
"WORK_TO": {"$date": "2021-04-13T11:53:34.000Z"}
}
]
}
]
I want to query an aggregation on my mongo database, that's whats my MongoRepository is looking like:
@Repository
public interface MongoRep extends MongoRepository<Person, Long> {
@Meta(allowDiskUse = true)
@Aggregation(pipeline = {" " +
"{$unwind: $WORKDAYS},\n" +
" {$unwind: $ADDRESSES},\n" +
" {\n" +
" $group: {\n" +
" _id: $_id,\n" +
" workingHours: {\n" +
" $sum: {$divide: [{$subtract: [$WORKDAYS.WORK_TO, $WORKDAYS.WORK_FROM]}, 3600000]}\n" +
" },\n" +
" age: {\n" +
" $first: { $subtract: [{$year: $$NOW},{$year: $BIRTH_DATE}]}\n" +
" },\n" +
" firstname: {\n" +
" $first: $FIRST_NAME\n" +
" },\n" +
" lastname: {\n" +
" $first: $LAST_NAME\n" +
" },\n" +
" city: {\n" +
" $first: $ADDRESSES.CITY\n" +
" },\n" +
" job: {\n" +
" $first: $JOB\n" +
" }\n" +
" }\n" +
" },\n" +
" {$project: {\n" +
" _id: 0," +
" age: $age,\n" +
" city: $city,\n" +
" firstname: $firstname,\n" +
" lastname: $lastname,\n" +
" workinghours: $workingHours,\n" +
" job: $job,\n" +
"\n" +
" }}"})
public AggregationResults<PersonDTOImpl> findAllAggregated();
Here's my DTO class:
@Document(collection = "employee")
class PersonDTOImpl(
@Field(name = "age") override val age: Int,
@BsonProperty("city") override val city: String,
@BsonProperty( "firstname") override val firstname: String, @Field(name = "job") override val job: String,
@Field(name = "lastname") override val lastname: String, @Field(name = "workinghours") override val workinghours: Double) : PersonDTOInterface {
}
Unfortunately I get an exception when executing the query:
Failed to instantiate PersonDTOImpl using constructor fun <init>(kotlin.Int, kotlin.String, kotlin.String, kotlin.String, kotlin.String, kotlin.Double): at.fhj.swd.kotlintraditionalpostgres.person.mongo.PersonDTOImpl with arguments null,null,null,null,null,null
I tried to modify the mongo query to run it without the projection, but without luck. The aggregation works perfectly when running it in the mongo console.
Any help is highly appreciated, thanks.
EDIT: Seems like the query is returning the wrong (BSON) result, here's a screenshot. But the same query is returning the correct results when running it in the Mongo Console.
Hello fellow developer,
I hope I am not too late to answer your question. Your @Aggregation annotation has the wrong pipeline. As per: https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/repository/Aggregation.html when you want a pipeline with multiple stages you must follow the following pattern:
@Aggregation(pipeline = {"stage1", "stage2"})
Which for your case would be something along the lines:
@Aggregation(pipeline = {"{$unwind: $WORKDAYS}" , "{$unwind: $ADDRESSES}", "{$group: {...}}", "{$project: {}}"})
If you notice on your @Aggregation you are actually separating the stages by commas and therefore creating an array of strings. You have an array of strings because you have added "" at the top, meaning that the last stage aggregates all the other stages, which is not correct, you need to split them.