Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement $set stage dsl #104

Merged
merged 6 commits into from
Oct 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,16 @@ class AggregationDsl {
operations += AddFieldsStageDsl().apply(configuration).get()
}

/**
* Configures a stage that set in documents.
*
* @param configuration The configuration block for the [SetStageDsl].
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/set">$set (aggregation)</a>
*/
fun set(configuration: SetStageDsl.() -> Unit) {
operations += SetStageDsl().apply(configuration).get()
}

/**
* Builds the [Aggregation] using the configured [AggregationOperation]s.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
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.springframework.data.mongodb.core.aggregation.AggregationExpression
import org.springframework.data.mongodb.core.aggregation.SetOperation
import kotlin.reflect.KProperty

/**
* A Kotlin DSL to configure $set stage using idiomatic Kotlin code.
*
* @author username1103
* @since 1.0
* @see <a href="https://www.mongodb.com/docs/manual/reference/operator/aggregation/set">$set (aggregation)</a>
*/
@AggregationMarker
class SetStageDsl {
private val builder = SetOperation.builder()

private var operation: SetOperation? = null

/**
* Adds new fields to documents with aggregation expression.
*
* @param configuration The configuration block where you can use DSL to define aggregation expression.
*/
infix fun String.set(configuration: AggregationExpressionDsl.() -> AggregationExpression) {
operation = builder.set(this).toValue(AggregationExpressionDsl().configuration())
}

/**
* Adds new fields to documents from a field.
*
* @param path The path of the field to contain value to be added.
*/
infix fun String.set(path: KProperty<Any?>) {
operation = builder.set(this).toValue("${'$'}${path.toDotPath()}")
}

/**
* Adds new fields to documents with a value.
*
* @param value The value of the field to add.
*/
infix fun String.set(value: Any?) {
operation = builder.set(this).toValue(value)
}

/**
* Adds new fields to documents from a field.
*
* @param fieldPath The path of the field to contain value to be added.
*/
infix fun String.setByField(fieldPath: String) {
operation = builder.set(this).toValue("$$fieldPath")
}

/**
* Adds new fields to documents with aggregation expression.
*
* @param configuration The configuration block where you can use DSL to define aggregation expression.
*/
infix fun KProperty<Any?>.set(configuration: AggregationExpressionDsl.() -> AggregationExpression) {
operation = builder.set(this.toDotPath()).toValue(AggregationExpressionDsl().configuration())
}

/**
* Adds new fields to documents from a field.
*
* @param path The path of the field to contain value to be added.
*/
infix fun KProperty<Any?>.set(path: KProperty<Any?>) {
operation = builder.set(this.toDotPath()).toValue("${'$'}${path.toDotPath()}")
}

/**
* Adds new fields to documents with a value.
*
* @param value The value of the field to add.
*/
infix fun KProperty<Any?>.set(value: Any?) {
operation = builder.set(this.toDotPath()).toValue(value)
}

/**
* Adds new fields to documents from a field.
*
* @param fieldPath The path of the field to contain value to be added.
*/
infix fun KProperty<Any?>.setByField(fieldPath: String) {
operation = builder.set(this.toDotPath()).toValue("$$fieldPath")
}

internal fun get() = checkNotNull(operation) { "Set operation must not be null!" }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
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

class SetStageDslTest : FreeSpec({
fun set(configuration: SetStageDsl.() -> Unit): SetStageDsl =
SetStageDsl().apply(configuration)

"set" - {
"should set field with path" {
// given
data class Test(val targetPath: Int)
val stage = set {
"a" set Test::targetPath
}

// when
val result = stage.get()

// then
result.shouldBeJson(
"""
{
"${'$'}set": {
"a": "${'$'}targetPath"
}
}
""".trimIndent(),
)
}

"should set field path with path" {
// given
data class Test(val sourcePath: Int, val targetPath: Int)
val stage = set {
Test::targetPath set Test::sourcePath
}

// when
val result = stage.get()

// then
result.shouldBeJson(
"""
{
"${'$'}set": {
"targetPath": "${'$'}sourcePath"
}
}
""".trimIndent(),
)
}

"should set field with aggregation expression" {
// given
data class Test(val targetPath: Int)
val stage = set {
"a" set {
add { of(1) and 2 and Test::targetPath }
}
}

// when
val result = stage.get()

// then
result.shouldBeJson(
"""
{
"${'$'}set": {
"a": {
"${'$'}add": [
1,
2,
"${'$'}targetPath"
]
}
}
}
""".trimIndent(),
)
}

"should set field path with aggregation expression" {
// given
data class Test(val sourcePath: Int, val targetPath: Int)
val stage = set {
Test::targetPath set {
add { of(1) and 2 and Test::sourcePath }
}
}

// when
val result = stage.get()

// then
result.shouldBeJson(
"""
{
"${'$'}set": {
"targetPath": {
"${'$'}add": [
1,
2,
"${'$'}sourcePath"
]
}
}
}
""".trimIndent(),
)
}

"should set field with value" {
// given
val stage = set {
"a" set 1
}

// when
val result = stage.get()

// then
result.shouldBeJson(
"""
{
"${'$'}set": {
"a": 1
}
}
""".trimIndent(),
)
}

"should set field path with value" {
// given
data class Test(val targetPath: Int)
val stage = set {
Test::targetPath set 1
}

// when
val result = stage.get()

// then
result.shouldBeJson(
"""
{
"${'$'}set": {
"targetPath": 1
}
}
""".trimIndent(),
)
}
}

"setByField" - {
"should set field with value of other field" {
// given
val stage = set {
"a" setByField "b"
}

// when
val result = stage.get()

// then
result.shouldBeJson(
"""
{
"${'$'}set": {
"a": "${'$'}b"
}
}
""".trimIndent(),
)
}

"should set field path with value of other field" {
// given
data class Test(val targetPath: Int)
val stage = set {
Test::targetPath setByField "b"
}

// when
val result = stage.get()

// then
result.shouldBeJson(
"""
{
"${'$'}set": {
"targetPath": "${'$'}b"
}
}
""".trimIndent(),
)
}
}
})
Loading