4 changed files with 477 additions and 2 deletions
@ -0,0 +1,208 @@
@@ -0,0 +1,208 @@
|
||||
/* |
||||
* Copyright 2018-2024 the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.data.mongodb.core.query |
||||
|
||||
import org.springframework.data.mapping.toDotPath |
||||
import org.springframework.data.mongodb.core.query.Update.Position |
||||
import kotlin.reflect.KProperty |
||||
|
||||
/** |
||||
* Static factory method to create an Update using the provided key |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.update |
||||
*/ |
||||
fun <T> update(key: KProperty<T>, value: T?) = |
||||
Update.update(key.toDotPath(), value) |
||||
|
||||
/** |
||||
* Update using the {@literal $set} update modifier |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.set |
||||
*/ |
||||
fun <T> Update.set(key: KProperty<T>, value: T?) = |
||||
set(key.toDotPath(), value) |
||||
|
||||
/** |
||||
* Update using the {@literal $setOnInsert} update modifier |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.setOnInsert |
||||
*/ |
||||
fun <T> Update.setOnInsert(key: KProperty<T>, value: T?) = |
||||
setOnInsert(key.toDotPath(), value) |
||||
|
||||
/** |
||||
* Update using the {@literal $unset} update modifier |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.unset |
||||
*/ |
||||
fun <T> Update.unset(key: KProperty<T>) = |
||||
unset(key.toDotPath()) |
||||
|
||||
/** |
||||
* Update using the {@literal $inc} update modifier |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.inc |
||||
*/ |
||||
fun <T> Update.inc(key: KProperty<T>, inc: Number) = |
||||
inc(key.toDotPath(), inc) |
||||
|
||||
fun <T> Update.inc(key: KProperty<T>) = |
||||
inc(key.toDotPath()) |
||||
|
||||
/** |
||||
* Update using the {@literal $push} update modifier |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.push |
||||
*/ |
||||
fun <T> Update.push(key: KProperty<Collection<T>>, value: T?) = |
||||
push(key.toDotPath(), value) |
||||
|
||||
/** |
||||
* Update using {@code $push} modifier. <br/> |
||||
* Allows creation of {@code $push} command for single or multiple (using {@code $each}) values as well as using |
||||
* |
||||
* {@code $position}. |
||||
* @author Pawel Matysek |
||||
* @see Update.push |
||||
*/ |
||||
fun <T> Update.push(key: KProperty<T>) = |
||||
push(key.toDotPath()) |
||||
|
||||
/** |
||||
* Update using {@code $addToSet} modifier. <br/> |
||||
* Allows creation of {@code $push} command for single or multiple (using {@code $each}) values * {@code $position}. |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.addToSet |
||||
*/ |
||||
fun <T> Update.addToSet(key: KProperty<T>) = |
||||
addToSet(key.toDotPath()) |
||||
|
||||
/** |
||||
* Update using the {@literal $addToSet} update modifier |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.addToSet |
||||
*/ |
||||
fun <T> Update.addToSet(key: KProperty<Collection<T>>, value: T?) = |
||||
addToSet(key.toDotPath(), value) |
||||
|
||||
/** |
||||
* Update using the {@literal $pop} update modifier |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.pop |
||||
*/ |
||||
fun <T> Update.pop(key: KProperty<T>, pos: Position) = |
||||
pop(key.toDotPath(), pos) |
||||
|
||||
/** |
||||
* Update using the {@literal $pull} update modifier |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.pull |
||||
*/ |
||||
fun <T> Update.pull(key: KProperty<T>, value: Any) = |
||||
pull(key.toDotPath(), value) |
||||
|
||||
/** |
||||
* Update using the {@literal $pullAll} update modifier |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.pullAll |
||||
*/ |
||||
fun <T> Update.pullAll(key: KProperty<Collection<T>>, values: Array<T>) = |
||||
pullAll(key.toDotPath(), values) |
||||
|
||||
/** |
||||
* Update given key to current date using {@literal $currentDate} modifier. |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.currentDate |
||||
*/ |
||||
fun <T> Update.currentDate(key: KProperty<T>) = |
||||
currentDate(key.toDotPath()) |
||||
|
||||
/** |
||||
* Update given key to current date using {@literal $currentDate : { $type : "timestamp" }} modifier. |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.currentTimestamp |
||||
*/ |
||||
fun <T> Update.currentTimestamp(key: KProperty<T>) = |
||||
currentTimestamp(key.toDotPath()) |
||||
|
||||
/** |
||||
* Multiply the value of given key by the given number. |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.multiply |
||||
*/ |
||||
fun <T> Update.multiply(key: KProperty<T>, multiplier: Number) = |
||||
multiply(key.toDotPath(), multiplier) |
||||
|
||||
/** |
||||
* Update given key to the {@code value} if the {@code value} is greater than the current value of the field. |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.max |
||||
*/ |
||||
fun <T : Any> Update.max(key: KProperty<T>, value: T) = |
||||
max(key.toDotPath(), value) |
||||
|
||||
/** |
||||
* Update given key to the {@code value} if the {@code value} is less than the current value of the field. |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.min |
||||
*/ |
||||
fun <T : Any> Update.min(key: KProperty<T>, value: T) = |
||||
min(key.toDotPath(), value) |
||||
|
||||
/** |
||||
* The operator supports bitwise {@code and}, bitwise {@code or}, and bitwise {@code xor} operations. |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.bitwise |
||||
*/ |
||||
fun <T> Update.bitwise(key: KProperty<T>) = |
||||
bitwise(key.toDotPath()) |
||||
|
||||
/** |
||||
* Filter elements in an array that match the given criteria for update. {@code expression} is used directly with the |
||||
* driver without further type or field mapping. |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.filterArray |
||||
*/ |
||||
fun <T> Update.filterArray(identifier: KProperty<T>, expression: Any) = |
||||
filterArray(identifier.toDotPath(), expression) |
||||
|
||||
/** |
||||
* Determine if a given {@code key} will be touched on execution. |
||||
* |
||||
* @author Pawel Matysek |
||||
* @see Update.modifies |
||||
*/ |
||||
fun <T> Update.modifies(key: KProperty<T>) = |
||||
modifies(key.toDotPath()) |
||||
|
||||
@ -0,0 +1,251 @@
@@ -0,0 +1,251 @@
|
||||
/* |
||||
* Copyright 2018-2024 the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.data.mongodb.core.query |
||||
|
||||
import org.assertj.core.api.Assertions.assertThat |
||||
import org.junit.Test |
||||
import org.springframework.data.mapping.div |
||||
import java.time.Instant |
||||
|
||||
/** |
||||
* Unit tests for [Update] extensions. |
||||
* |
||||
* @author Pawel Matysek |
||||
*/ |
||||
class TypedUpdateExtensionsTests { |
||||
|
||||
@Test |
||||
fun `update() should equal expected Update`() { |
||||
|
||||
val typed = update(Book::title, "Moby-Dick") |
||||
val expected = Update.update("title", "Moby-Dick") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `set() should equal expected Update`() { |
||||
|
||||
val typed = Update().set(Book::title, "Moby-Dick") |
||||
val expected = Update().set("title", "Moby-Dick") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `setOnInsert() should equal expected Update`() { |
||||
|
||||
val typed = Update().setOnInsert(Book::title, "Moby-Dick") |
||||
val expected = Update().setOnInsert("title", "Moby-Dick") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `unset() should equal expected Update`() { |
||||
|
||||
val typed = Update().unset(Book::title) |
||||
val expected = Update().unset("title") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `inc(key, inc) should equal expected Update`() { |
||||
|
||||
val typed = Update().inc(Book::price, 5) |
||||
val expected = Update().inc("price", 5) |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `inc(key) should equal expected Update`() { |
||||
|
||||
val typed = Update().inc(Book::price) |
||||
val expected = Update().inc("price") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `push(key, value) should equal expected Update`() { |
||||
|
||||
val typed = Update().push(Book::categories, "someCategory") |
||||
val expected = Update().push("categories", "someCategory") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `push(key) should equal expected Update`() { |
||||
|
||||
val typed = Update().push(Book::categories) |
||||
val expected = Update().push("categories") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `addToSet(key) should equal expected Update`() { |
||||
|
||||
val typed = Update().addToSet(Book::categories).each("category", "category2") |
||||
val expected = Update().addToSet("categories").each("category", "category2") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `addToSet(key, value) should equal expected Update`() { |
||||
|
||||
val typed = Update().addToSet(Book::categories, "someCategory") |
||||
val expected = Update().addToSet("categories", "someCategory") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `pop() should equal expected Update`() { |
||||
|
||||
val typed = Update().pop(Book::categories, Update.Position.FIRST) |
||||
val expected = Update().pop("categories", Update.Position.FIRST) |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `pull() should equal expected Update`() { |
||||
|
||||
val typed = Update().pull(Book::categories, "someCategory") |
||||
val expected = Update().pull("categories", "someCategory") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `pullAll() should equal expected Update`() { |
||||
|
||||
val typed = Update().pullAll(Book::categories, arrayOf("someCategory", "someCategory2")) |
||||
val expected = Update().pullAll("categories", arrayOf("someCategory", "someCategory2")) |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `currentDate() should equal expected Update`() { |
||||
|
||||
val typed = Update().currentDate(Book::releaseDate) |
||||
val expected = Update().currentDate("releaseDate") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `currentTimestamp() should equal expected Update`() { |
||||
|
||||
val typed = Update().currentTimestamp(Book::releaseDate) |
||||
val expected = Update().currentTimestamp("releaseDate") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `multiply() should equal expected Update`() { |
||||
|
||||
val typed = Update().multiply(Book::price, 2) |
||||
val expected = Update().multiply("price", 2) |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `max() should equal expected Update`() { |
||||
|
||||
val typed = Update().max(Book::price, 200) |
||||
val expected = Update().max("price", 200) |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `min() should equal expected Update`() { |
||||
|
||||
val typed = Update().min(Book::price, 100) |
||||
val expected = Update().min("price", 100) |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `bitwise() should equal expected Update`() { |
||||
|
||||
val typed = Update().bitwise(Book::price).and(2) |
||||
val expected = Update().bitwise("price").and(2) |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `filterArray() should equal expected Update`() { |
||||
|
||||
val typed = Update().filterArray(Book::categories, "someCategory") |
||||
val expected = Update().filterArray("categories", "someCategory") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `typed modifies() should equal expected modifies()`() { |
||||
|
||||
val typed = update(Book::title, "Moby-Dick") |
||||
|
||||
assertThat(typed.modifies(Book::title)).isEqualTo(typed.modifies("title")) |
||||
assertThat(typed.modifies(Book::price)).isEqualTo(typed.modifies("price")) |
||||
} |
||||
|
||||
@Test |
||||
fun `One level nested should equal expected Update`() { |
||||
|
||||
val typed = update(Book::author / Author::name, "Herman Melville") |
||||
val expected = Update.update("author.name", "Herman Melville") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
@Test |
||||
fun `Two levels nested should equal expected Update`() { |
||||
|
||||
data class Entity(val book: Book) |
||||
|
||||
val typed = update(Entity::book / Book::author / Author::name, "Herman Melville") |
||||
val expected = Update.update("book.author.name", "Herman Melville") |
||||
|
||||
assertThat(typed).isEqualTo(expected) |
||||
} |
||||
|
||||
data class Book( |
||||
val title: String = "Moby-Dick", |
||||
val price: Int = 123, |
||||
val available: Boolean = true, |
||||
val categories: List<String> = emptyList(), |
||||
val author: Author = Author(), |
||||
val releaseDate: Instant, |
||||
) |
||||
|
||||
data class Author( |
||||
val name: String = "Herman Melville", |
||||
) |
||||
} |
||||
Loading…
Reference in new issue