Skip to content

Commit

Permalink
resolves #353: Add support for $arrayElemAt, $ifNull, $dateToString a…
Browse files Browse the repository at this point in the history
…nd typesafe $lookup shortcut
  • Loading branch information
zigzago committed Jun 19, 2022
1 parent 7e9c324 commit 92204cc
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import org.litote.kmongo.AggregateTypedTest.Article
import org.litote.kmongo.MongoOperator.`in`
import org.litote.kmongo.MongoOperator.and
import org.litote.kmongo.MongoOperator.dateToString
import org.litote.kmongo.MongoOperator.eq
import org.litote.kmongo.MongoOperator.gte
import org.litote.kmongo.MongoOperator.`in`
import org.litote.kmongo.model.Friend
import java.time.Instant
import java.time.LocalDate
Expand Down Expand Up @@ -126,7 +126,7 @@ class AggregateTypedTest : AllCategoriesKMongoBaseTest<Article>() {
),
project(
Article::title from Article::title,
Article::ok from cond(Article::ok, 1, 0),
Article::ok from cond(Article::ok, 1, 0),
Result::averageYear from year(Article::date)
),
group(
Expand Down Expand Up @@ -370,4 +370,17 @@ class AggregateTypedTest : AllCategoriesKMongoBaseTest<Article>() {
}

}

@Test
fun `typesafe lookup test`() {
assertEquals(
lookup("evaluationsAnswers", "userId", "questionId", "evaluator"),
lookup(
database.getCollection(),
EvaluationRequest::userId,
EvaluationsAnswers::questionId,
Answer::evaluator
)
)
}
}
38 changes: 38 additions & 0 deletions kmongo-core/src/main/kotlin/org/litote/kmongo/SyncProperties.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2016/2022 Litote
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.litote.kmongo

import com.mongodb.client.MongoCollection
import kotlin.reflect.KProperty1

/**
* Creates a $lookup pipeline stage for the specified filter (Typesafe version)
*
* @param from the collection in the same database to perform the join with.
* @param localField specifies the field from the local collection to match values against.
* @param foreignField specifies the field in the from collection to match values against.
* @param newAs the name of the new array field to add to the input documents.
* @return the $lookup pipeline stage
* @mongodb.driver.manual reference/operator/aggregation/lookup/ $lookup
*/
fun <FROM : Any> lookup(
from: MongoCollection<FROM>,
localField: KProperty1<out Any, Any?>,
foreignField: KProperty1<FROM, Any?>,
newAs: KProperty1<out Any, Any?>
) =
lookup(from.namespace.collectionName, localField.path(), foreignField.path(), newAs.path())
51 changes: 51 additions & 0 deletions kmongo-property/src/main/kotlin/org/litote/kmongo/Aggregates.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@ import com.mongodb.client.model.Updates
import com.mongodb.client.model.Variable
import org.bson.BsonDocument
import org.bson.BsonDocumentWriter
import org.bson.BsonInt32
import org.bson.BsonString
import org.bson.codecs.configuration.CodecRegistry
import org.bson.conversions.Bson
import org.bson.types.ObjectId
import org.litote.kmongo.util.KMongoUtil.encodeValue
import java.time.ZoneId
import java.time.temporal.TemporalAccessor
import kotlin.reflect.KProperty

Expand Down Expand Up @@ -514,6 +517,7 @@ fun second(property: KProperty<ObjectId?>): Bson = MongoOperator.second.from(pro
*/
@JvmName("weekObjectId")
fun week(property: KProperty<ObjectId?>): Bson = MongoOperator.week.from(property)

/**
* Creates a $lookup pipeline stage, joining the current collection with the one specified in from using the given pipeline
*
Expand Down Expand Up @@ -551,3 +555,50 @@ val KProperty<*>.variable: String get() = path().variable
*/
val String.variable: String get() = "\$\$$this"

/**
* $arrayElemAt aggregator operator support.
*/
fun KProperty<*>.arrayElemAt(index: Int = 0): Bson =
arrayElemAt(projection, index)

/**
* $arrayElemAt aggregator operator support.
*/
fun arrayElemAt(path: String, index: Int = 0): Bson =
document(MongoOperator.arrayElemAt from listOf(BsonString(path), BsonInt32(index)))

/**
* $ifNull aggregator operator support.
*/
fun KProperty<*>.ifNull(defaultValue: Any): Bson =
ifNull(projection, defaultValue)

/**
* $ifNull aggregator operator support.
*/
fun ifNull(expression: Any, defaultValue: Any): Bson =
document(MongoOperator.ifNull from listOf(expression, defaultValue))

/**
* $dateToString aggregator operator support.
*/
fun KProperty<TemporalAccessor?>.dateToString(
format: String? = "%Y-%m-%d",
zoneId: ZoneId? = null,
onNull: String? = null
): Bson =
BsonDocument(
MongoOperator.dateToString.toString(),
BsonDocument().apply {
set("date", BsonString(projection))
if (format != null) {
set("format", BsonString(format))
}
if (zoneId != null) {
set("timezone", BsonString(zoneId.id))
}
if (onNull != null) {
set("onNull", BsonString(onNull))
}
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.mongodb.client.model.Aggregates
import com.mongodb.client.model.Projections
import org.bson.conversions.Bson
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1

/**
* The projection of the property.
Expand All @@ -31,6 +32,11 @@ val <T> KProperty<T>.projection: String get() = path().projection
*/
val String.projection: String get() = "\$$this"

/**
* In order to write `$p.p2`
*/
infix fun <T0, T1> KProperty1<T0, T1?>.projectionWith(p2: String): String = "$projection.$p2"

/**
* Creates a projection of a property whose value is computed from the given expression.
*
Expand All @@ -55,7 +61,7 @@ infix fun String.from(expression: Any): Bson =
Projections.computed(this, (expression as? KProperty<Any>)?.projection ?: expression)

/**
* Creates a projection that includes all of the given properties.
* Creates a projection that includes all given properties.
*
* @param properties the field names
* @return the projection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ val <T> KProperty1<out Any?, Iterable<T>?>.colProperty: KCollectionSimplePropert
get() = KCollectionSimplePropertyPath(null, this)

/**
* In order to write array indexed expressions (like `accesses.0.timestamp`).
*/
fun <T> KProperty1<out Any?, Iterable<T>?>.pos(position: Int) : KPropertyPath<out Any?, T?> =
* In order to write array indexed expressions (like `accesses.0.timestamp`).
*/
fun <T> KProperty1<out Any?, Iterable<T>?>.pos(position: Int): KPropertyPath<out Any?, T?> =
colProperty.pos(position)

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ package org.litote.kmongo

import org.bson.BsonDocument
import org.bson.types.ObjectId
import org.litote.kmongo.MongoOperator.arrayElemAt
import org.litote.kmongo.MongoOperator.dateToString
import org.litote.kmongo.MongoOperator.ifNull
import org.litote.kmongo.MongoOperator.lookup
import java.time.LocalDate
import java.time.ZoneId
import kotlin.test.Test
import kotlin.test.assertEquals

Expand Down Expand Up @@ -48,6 +52,33 @@ class AggregatesTest {
)
}

@Test
fun testDateToString() {
val bson = AggregateData::date.dateToString(zoneId = ZoneId.of("Europe/Paris"), onNull = "2012-12-02")
assertEquals(
BsonDocument.parse("""{"$dateToString":{"date": "${'$'}date", "format": "%Y-%m-%d", "timezone": "Europe/Paris", "onNull": "2012-12-02"}}"""),
bson.document
)
}

@Test
fun testIfNull() {
val bson = AggregateData::s.ifNull("a")
assertEquals(
BsonDocument.parse("""{"$ifNull":["${'$'}s", "a"]}"""),
bson.document
)
}

@Test
fun testArrayElemAt() {
val bson = AggregateData::s.arrayElemAt(0)
assertEquals(
BsonDocument.parse("""{"$arrayElemAt":["${'$'}s", 0]}"""),
bson.document
)
}

@Test
fun testVariable() {
assertEquals("\$\$s", AggregateData::s.variable)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2016/2022 Litote
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.litote.kmongo

import org.junit.Test
import kotlin.test.assertEquals

/**
*
*/
class ProjectionTest {

class ProjectionData(val s: String)

@Test
fun testProjectionWith() {
assertEquals("\$s.a", ProjectionData::s.projectionWith("a"))
}
}

0 comments on commit 92204cc

Please sign in to comment.