diff --git a/core/src/main/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/AggregationDsl.kt b/core/src/main/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/AggregationDsl.kt
index f31da098..cd7fe7bb 100644
--- a/core/src/main/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/AggregationDsl.kt
+++ b/core/src/main/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/AggregationDsl.kt
@@ -277,6 +277,17 @@ class AggregationDsl {
operations += Aggregation.limit(maxElements)
}
+ /**
+ * Configures a stage that separates documents into groups according to a "group key".
+ * The output is one document for each unique group key.
+ *
+ * @param configuration The configuration block for the [GroupStageDsl].
+ * @see $group (aggregation)
+ */
+ fun group(configuration: GroupStageDsl.() -> Unit) {
+ operations += GroupStageDsl().apply(configuration).build()
+ }
+
/**
* Builds the [Aggregation] using the configured [AggregationOperation]s.
*
diff --git a/core/src/main/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/GroupStageDsl.kt b/core/src/main/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/GroupStageDsl.kt
new file mode 100644
index 00000000..bcebca04
--- /dev/null
+++ b/core/src/main/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/GroupStageDsl.kt
@@ -0,0 +1,77 @@
+package com.github.inflab.spring.data.mongodb.core.aggregation
+
+import com.github.inflab.spring.data.mongodb.core.aggregation.expression.AggregationExpressionDsl
+import com.github.inflab.spring.data.mongodb.core.annotation.AggregationMarker
+import com.github.inflab.spring.data.mongodb.core.extension.toDotPath
+import org.bson.Document
+import org.springframework.data.mongodb.core.aggregation.AggregationExpression
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation
+import kotlin.reflect.KProperty
+
+/**
+ * A Kotlin DSL to configure $group stage using idiomatic Kotlin code.
+ *
+ * @author Jake Son
+ * @since 1.0
+ * @see $group (aggregation)
+ */
+@AggregationMarker
+class GroupStageDsl {
+ private var document = Document()
+ private val accumulators: MutableMap = mutableMapOf()
+
+ /**
+ * Specifies the group key to `null`.
+ * If you specify an _id value of null, or any other constant value, the $group stage returns a single document that aggregates values across all of the input documents.
+ */
+ fun idNull() {
+ document["_id"] = null
+ }
+
+ /**
+ * Specifies the group key to a field path.
+ *
+ * @param path The field name.
+ */
+ fun id(path: String) {
+ document["_id"] = "$$path"
+ }
+
+ /**
+ * Specifies the group key to a field path.
+ *
+ * @param property The property reference.
+ */
+ fun id(property: KProperty<*>) {
+ id(property.toDotPath())
+ }
+
+ /**
+ * Specifies the group key to an [AggregationExpression].
+ *
+ * @param configuration The configuration block for the [AggregationExpression].
+ */
+ fun id(configuration: AggregationExpressionDsl.() -> AggregationExpression) {
+ document["_id"] = AggregationExpressionDsl().configuration().toDocument()
+ }
+
+ /**
+ * Computed using the accumulator operators.
+ *
+ * @see accumulator operators
+ */
+ infix fun String.accumulator(configuration: AggregationExpressionDsl.() -> AggregationExpression) {
+ accumulators[this] = AggregationExpressionDsl().configuration()
+ }
+
+ internal fun build() = AggregationOperation { context ->
+ (document["_id"] as? AggregationExpression)?.let {
+ document["_id"] = it.toDocument(context)
+ }
+ accumulators.forEach { (key, value) ->
+ document[key] = value.toDocument(context)
+ }
+
+ Document("\$group", document)
+ }
+}
diff --git a/core/src/main/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/expression/AggregationExpressionDsl.kt b/core/src/main/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/expression/AggregationExpressionDsl.kt
index d59bd902..e3a73366 100644
--- a/core/src/main/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/expression/AggregationExpressionDsl.kt
+++ b/core/src/main/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/expression/AggregationExpressionDsl.kt
@@ -5,6 +5,7 @@ import com.github.inflab.spring.data.mongodb.core.aggregation.expression.arithme
import com.github.inflab.spring.data.mongodb.core.aggregation.expression.arithmetic.SubtractExpressionDsl
import com.github.inflab.spring.data.mongodb.core.annotation.AggregationMarker
import com.github.inflab.spring.data.mongodb.core.extension.toDotPath
+import org.bson.Document
import org.springframework.data.mongodb.core.aggregation.AggregationExpression
import org.springframework.data.mongodb.core.aggregation.ArithmeticOperators
import kotlin.reflect.KProperty
@@ -155,4 +156,11 @@ class AggregationExpressionDsl {
*/
fun subtract(configuration: SubtractExpressionDsl.() -> AggregationExpression): AggregationExpression =
SubtractExpressionDsl().configuration()
+
+ /**
+ * Returns the number of documents in a group.
+ */
+ fun count() = AggregationExpression {
+ Document("\$count", Document())
+ }
}
diff --git a/core/src/test/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/AggregationDslTest.kt b/core/src/test/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/AggregationDslTest.kt
index fc85efa4..4dd23b69 100644
--- a/core/src/test/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/AggregationDslTest.kt
+++ b/core/src/test/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/AggregationDslTest.kt
@@ -313,4 +313,20 @@ internal class AggregationDslTest : FreeSpec({
).toString()
}
}
+
+ "group" - {
+ "should create group stage" {
+ // when
+ val aggregation = aggregation {
+ group {
+ idNull()
+ }
+ }
+
+ // then
+ aggregation.toString() shouldBe Aggregation.newAggregation(
+ Aggregation.group(),
+ ).toString()
+ }
+ }
})
diff --git a/core/src/test/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/GroupStageDslTest.kt b/core/src/test/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/GroupStageDslTest.kt
new file mode 100644
index 00000000..c3134d89
--- /dev/null
+++ b/core/src/test/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/GroupStageDslTest.kt
@@ -0,0 +1,128 @@
+package com.github.inflab.spring.data.mongodb.core.aggregation
+
+import com.github.inflab.spring.data.mongodb.core.util.shouldBeJson
+import io.kotest.core.spec.style.FreeSpec
+
+internal class GroupStageDslTest : FreeSpec({
+ fun group(block: GroupStageDsl.() -> Unit) =
+ GroupStageDsl().apply(block)
+
+ "_id" - {
+ "should build by null" {
+ // when
+ val stage = group {
+ idNull()
+ }
+
+ // then
+ val result = stage.build()
+
+ // then
+ result.shouldBeJson(
+ """
+ {
+ "${'$'}group": {
+ "_id": null
+ }
+ }
+ """.trimIndent(),
+ )
+ }
+
+ "should build by string" {
+ // given
+ val path = "path"
+ val stage = group {
+ id(path)
+ }
+
+ // when
+ val result = stage.build()
+
+ // then
+ result.shouldBeJson(
+ """
+ {
+ "${'$'}group": {
+ "_id": "${'$'}path"
+ }
+ }
+ """.trimIndent(),
+ )
+ }
+
+ "should build by property" {
+ // given
+ data class Test(val property: String)
+ val stage = group {
+ id(Test::property)
+ }
+
+ // when
+ val result = stage.build()
+
+ // then
+ result.shouldBeJson(
+ """
+ {
+ "${'$'}group": {
+ "_id": "${'$'}property"
+ }
+ }
+ """.trimIndent(),
+ )
+ }
+
+ "should build by expression" {
+ // given
+ val stage = group {
+ id {
+ abs("field")
+ }
+ }
+
+ // when
+ val result = stage.build()
+
+ // then
+ result.shouldBeJson(
+ """
+ {
+ "${'$'}group": {
+ "_id": {
+ "${'$'}abs": "${'$'}field"
+ }
+ }
+ }
+ """.trimIndent(),
+ )
+ }
+ }
+
+ "accumulator" - {
+ "should build by expression" {
+ // given
+ val stage = group {
+ "amount" accumulator {
+ abs("field")
+ }
+ }
+
+ // when
+ val result = stage.build()
+
+ // then
+ result.shouldBeJson(
+ """
+ {
+ "${'$'}group": {
+ "amount": {
+ "${'$'}abs": "${'$'}field"
+ }
+ }
+ }
+ """.trimIndent(),
+ )
+ }
+ }
+})
diff --git a/core/src/test/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/expression/AggregationExpressionDslTest.kt b/core/src/test/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/expression/AggregationExpressionDslTest.kt
index 3c7146f7..cb2de448 100644
--- a/core/src/test/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/expression/AggregationExpressionDslTest.kt
+++ b/core/src/test/kotlin/com/github/inflab/spring/data/mongodb/core/aggregation/expression/AggregationExpressionDslTest.kt
@@ -303,4 +303,20 @@ internal class AggregationExpressionDslTest : FreeSpec({
)
}
}
+
+ "count" - {
+ "should build an expression" {
+ // when
+ val result = expression { count() }
+
+ // then
+ result.shouldBeJson(
+ """
+ {
+ "${'$'}count": {}
+ }
+ """.trimIndent(),
+ )
+ }
+ }
})
diff --git a/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/entity/sample/FruitQuantities.kt b/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/entity/sample/FruitQuantities.kt
deleted file mode 100644
index c55fd158..00000000
--- a/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/entity/sample/FruitQuantities.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.github.inflab.example.spring.data.mongodb.entity.sample
-
-data class FruitQuantities(
- val lemons: Int,
- val oranges: Int,
- val grapefruit: Int,
-)
diff --git a/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/entity/sample/Fruits.kt b/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/entity/sample/Fruits.kt
deleted file mode 100644
index a29abb4d..00000000
--- a/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/entity/sample/Fruits.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.github.inflab.example.spring.data.mongodb.entity.sample
-
-import org.springframework.data.annotation.Id
-import org.springframework.data.mongodb.core.mapping.Document
-
-@Document("fruits")
-data class Fruits(
- @Id
- val id: String,
- val type: String?,
- val description: String,
- val quantities: FruitQuantities?,
-)
diff --git a/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/entity/sample/UserAccount.kt b/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/entity/sample/UserAccount.kt
deleted file mode 100644
index b6ffa600..00000000
--- a/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/entity/sample/UserAccount.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.github.inflab.example.spring.data.mongodb.entity.sample
-
-import org.springframework.data.mongodb.core.mapping.Field
-
-data class UserAccount(
- @Field("new_user")
- val newUser: Boolean,
- @Field("active_user")
- val activeUser: Boolean,
-)
diff --git a/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/entity/sample/Users.kt b/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/entity/sample/Users.kt
deleted file mode 100644
index a0601194..00000000
--- a/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/entity/sample/Users.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.github.inflab.example.spring.data.mongodb.entity.sample
-
-import org.springframework.data.annotation.Id
-import org.springframework.data.mongodb.core.mapping.Document
-import org.springframework.data.mongodb.core.mapping.Field
-import java.time.LocalDateTime
-
-@Document("users")
-data class Users(
- @Id
- val id: String,
- val name: String,
- @Field("verified_user")
- val verifiedUser: Boolean,
- val account: UserAccount,
- val teammates: List,
- val region: String,
- @Field("account_created")
- val accountCreated: LocalDateTime,
- @Field("employee_number")
- val employeeNumber: Int,
-)
diff --git a/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/repository/GroupRepository.kt b/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/repository/GroupRepository.kt
new file mode 100644
index 00000000..c391176c
--- /dev/null
+++ b/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/repository/GroupRepository.kt
@@ -0,0 +1,111 @@
+package com.github.inflab.example.spring.data.mongodb.repository
+
+import com.github.inflab.spring.data.mongodb.core.aggregation.aggregation
+import org.springframework.data.mongodb.core.MongoTemplate
+import org.springframework.data.mongodb.core.aggregate
+import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators
+import org.springframework.data.mongodb.core.aggregation.AggregationExpression
+import org.springframework.data.mongodb.core.aggregation.AggregationResults
+import org.springframework.data.mongodb.core.aggregation.ArithmeticOperators
+import org.springframework.data.mongodb.core.aggregation.DateOperators
+import org.springframework.data.mongodb.core.mapping.Document
+import org.springframework.data.mongodb.core.query.Criteria
+import org.springframework.stereotype.Repository
+import java.time.LocalDateTime
+
+@Repository
+class GroupRepository(
+ private val mongoTemplate: MongoTemplate,
+) {
+
+ @Document("sales")
+ data class Sales(val id: Long, val item: String, val price: Double, val quantity: Int, val date: LocalDateTime)
+
+ data class CountDto(val id: Long?, val count: Int)
+
+ /**
+ * @see Count the Number of Documents in a Collection
+ */
+ fun count(): AggregationResults {
+ val aggregation = aggregation {
+ group {
+ idNull()
+ "count" accumulator {
+ count()
+ }
+ }
+ }
+
+ return mongoTemplate.aggregate(aggregation)
+ }
+
+ data class GroupByItemHavingDto(val id: String, val totalSaleAmount: Double)
+
+ /**
+ * @see Group by Item Having
+ */
+ fun groupByItemHaving(): AggregationResults {
+ val aggregation = aggregation {
+ group {
+ id(Sales::item)
+ "totalSaleAmount" accumulator {
+ // TODO: add $sum operator
+ AccumulatorOperators.Sum.sumOf(
+ ArithmeticOperators.Multiply.valueOf(Sales::price.name).multiplyBy(Sales::quantity.name),
+ )
+ }
+ }
+ // TODO: add $match operator
+ match(Criteria.where("totalSaleAmount").gt(100))
+ }
+
+ return mongoTemplate.aggregate(aggregation)
+ }
+
+ data class GroupByDayAndYearDto(
+ val id: String,
+ val totalSaleAmount: Double,
+ val averageQuantity: Double,
+ val count: Int,
+ )
+
+ /**
+ * @see Group by Day and Year
+ */
+ fun groupByDayAndYear(): AggregationResults {
+ val aggregation = aggregation {
+ // TODO: add $match operator
+ match(
+ Criteria.where(Sales::date.name).gte(LocalDateTime.of(2014, 1, 1, 0, 0, 0))
+ .lt(LocalDateTime.of(2015, 1, 1, 0, 0, 0)),
+ )
+
+ group {
+ id {
+ // TODO: add $dayOfYear operator
+ DateOperators.DateToString.dateOf(Sales::date.name).toString("%Y-%m-%d")
+ }
+ "totalSaleAmount" accumulator {
+ // TODO: add $sum operator
+ AccumulatorOperators.Sum.sumOf(
+ ArithmeticOperators.Multiply.valueOf(Sales::price.name).multiplyBy(Sales::quantity.name),
+ )
+ }
+ "averageQuantity" accumulator {
+ // TODO: add $avg operator
+ AccumulatorOperators.Avg.avgOf(Sales::quantity.name)
+ }
+ "count" accumulator {
+ // TODO: add $sum operator
+ AggregationExpression { org.bson.Document("\$sum", 1) }
+ }
+ }
+
+ sort {
+ "totalSaleAmount" by desc
+ }
+ }
+
+ return mongoTemplate.aggregate(aggregation)
+ }
+}
diff --git a/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/repository/atlas/EqualsSearchRepository.kt b/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/repository/atlas/EqualsSearchRepository.kt
index 1ca7782b..e4c4fb05 100644
--- a/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/repository/atlas/EqualsSearchRepository.kt
+++ b/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/repository/atlas/EqualsSearchRepository.kt
@@ -1,11 +1,13 @@
package com.github.inflab.example.spring.data.mongodb.repository.atlas
-import com.github.inflab.example.spring.data.mongodb.entity.sample.Users
import com.github.inflab.spring.data.mongodb.core.aggregation.aggregation
import org.bson.types.ObjectId
+import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.aggregate
import org.springframework.data.mongodb.core.aggregation.AggregationResults
+import org.springframework.data.mongodb.core.mapping.Document
+import org.springframework.data.mongodb.core.mapping.Field
import org.springframework.stereotype.Repository
import java.time.LocalDateTime
@@ -14,6 +16,29 @@ class EqualsSearchRepository(
private val mongoTemplate: MongoTemplate,
) {
+ data class UserAccount(
+ @Field("new_user")
+ val newUser: Boolean,
+ @Field("active_user")
+ val activeUser: Boolean,
+ )
+
+ @Document("users")
+ data class Users(
+ @Id
+ val id: String,
+ val name: String,
+ @Field("verified_user")
+ val verifiedUser: Boolean,
+ val account: UserAccount,
+ val teammates: List,
+ val region: String,
+ @Field("account_created")
+ val accountCreated: LocalDateTime,
+ @Field("employee_number")
+ val employeeNumber: Int,
+ )
+
data class NameAndScoreDto(val name: String, val score: Double)
/**
diff --git a/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/repository/atlas/ExistsSearchRepository.kt b/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/repository/atlas/ExistsSearchRepository.kt
index d12616ca..d7c675e2 100644
--- a/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/repository/atlas/ExistsSearchRepository.kt
+++ b/example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/repository/atlas/ExistsSearchRepository.kt
@@ -1,10 +1,11 @@
package com.github.inflab.example.spring.data.mongodb.repository.atlas
-import com.github.inflab.example.spring.data.mongodb.entity.sample.Fruits
import com.github.inflab.spring.data.mongodb.core.aggregation.aggregation
+import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.aggregate
import org.springframework.data.mongodb.core.aggregation.AggregationResults
+import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.stereotype.Repository
@Repository
@@ -12,6 +13,21 @@ class ExistsSearchRepository(
private val mongoTemplate: MongoTemplate,
) {
+ data class FruitQuantities(
+ val lemons: Int,
+ val oranges: Int,
+ val grapefruit: Int,
+ )
+
+ @Document("fruits")
+ data class Fruits(
+ @Id
+ val id: String,
+ val type: String?,
+ val description: String,
+ val quantities: FruitQuantities?,
+ )
+
/**
* @see Compound Example
*/
diff --git a/example/spring-data-mongodb/src/test/kotlin/com/github/inflab/example/spring/data/mongodb/repository/GroupRepositoryTest.kt b/example/spring-data-mongodb/src/test/kotlin/com/github/inflab/example/spring/data/mongodb/repository/GroupRepositoryTest.kt
new file mode 100644
index 00000000..5b1a97df
--- /dev/null
+++ b/example/spring-data-mongodb/src/test/kotlin/com/github/inflab/example/spring/data/mongodb/repository/GroupRepositoryTest.kt
@@ -0,0 +1,114 @@
+package com.github.inflab.example.spring.data.mongodb.repository
+
+import com.github.inflab.example.spring.data.mongodb.extension.makeMongoTemplate
+import io.kotest.core.spec.style.FreeSpec
+import io.kotest.matchers.shouldBe
+import java.time.LocalDateTime
+
+internal class GroupRepositoryTest : FreeSpec({
+ val mongoTemplate = makeMongoTemplate()
+ val groupRepository = GroupRepository(mongoTemplate)
+
+ beforeSpec {
+ val sales = listOf(
+ GroupRepository.Sales(
+ id = 1,
+ item = "abc",
+ price = 10.0,
+ quantity = 2,
+ date = LocalDateTime.of(2014, 3, 1, 8, 0, 0),
+ ),
+ GroupRepository.Sales(
+ id = 2,
+ item = "jkl",
+ price = 20.0,
+ quantity = 1,
+ date = LocalDateTime.of(2014, 3, 1, 9, 0, 0),
+ ),
+ GroupRepository.Sales(
+ id = 3,
+ item = "xyz",
+ price = 5.0,
+ quantity = 10,
+ date = LocalDateTime.of(2014, 3, 15, 9, 0, 0),
+ ),
+ GroupRepository.Sales(
+ id = 4,
+ item = "xyz",
+ price = 5.0,
+ quantity = 20,
+ date = LocalDateTime.of(2014, 4, 4, 11, 21, 39, 736000000),
+ ),
+ GroupRepository.Sales(
+ id = 5,
+ item = "abc",
+ price = 10.0,
+ quantity = 10,
+ date = LocalDateTime.of(2014, 4, 4, 21, 23, 13, 331000000),
+ ),
+ GroupRepository.Sales(
+ id = 6,
+ item = "def",
+ price = 7.5,
+ quantity = 5,
+ date = LocalDateTime.of(2015, 6, 4, 5, 8, 13),
+ ),
+ GroupRepository.Sales(
+ id = 7,
+ item = "def",
+ price = 7.5,
+ quantity = 10,
+ date = LocalDateTime.of(2015, 9, 10, 8, 43, 0),
+ ),
+ GroupRepository.Sales(
+ id = 8,
+ item = "abc",
+ price = 10.0,
+ quantity = 5,
+ date = LocalDateTime.of(2016, 2, 6, 20, 20, 13),
+ ),
+ )
+ mongoTemplate.insertAll(sales)
+ }
+
+ "count" {
+ // when
+ val result = groupRepository.count()
+
+ // then
+ result.mappedResults.map { it.count } shouldBe listOf(8)
+ }
+
+ "groupByItemHaving" {
+ // when
+ val result = groupRepository.groupByItemHaving()
+
+ // then
+ result.mappedResults.toSet() shouldBe setOf(
+ GroupRepository.GroupByItemHavingDto(id = "abc", totalSaleAmount = 170.0),
+ GroupRepository.GroupByItemHavingDto(id = "xyz", totalSaleAmount = 150.0),
+ GroupRepository.GroupByItemHavingDto(id = "def", totalSaleAmount = 112.5),
+ )
+ }
+
+ "groupByDayAndYear" {
+ // when
+ val result = groupRepository.groupByDayAndYear()
+
+ // then
+ result.mappedResults.take(2) shouldBe listOf(
+ GroupRepository.GroupByDayAndYearDto(
+ id = "2014-04-04",
+ totalSaleAmount = 200.0,
+ averageQuantity = 15.0,
+ count = 2,
+ ),
+ GroupRepository.GroupByDayAndYearDto(
+ id = "2014-03-15",
+ totalSaleAmount = 50.0,
+ averageQuantity = 10.0,
+ count = 1,
+ ),
+ )
+ }
+})