1+
2+ import com.mongodb.client.model.*
3+ import com.mongodb.client.model.Sorts.*
4+ import com.mongodb.kotlin.client.coroutine.MongoClient
5+ import io.github.cdimascio.dotenv.dotenv
6+ import kotlinx.coroutines.flow.first
7+ import kotlinx.coroutines.flow.firstOrNull
8+ import kotlinx.coroutines.runBlocking
9+ import org.bson.codecs.pojo.annotations.BsonId
10+ import org.junit.jupiter.api.*
11+ import org.junit.jupiter.api.Assertions.*
12+ import org.junit.jupiter.api.Test
13+ import java.util.*
14+ import java.util.concurrent.TimeUnit
15+ import kotlin.test.*
16+
17+ @TestInstance(TestInstance .Lifecycle .PER_CLASS )
18+ internal class CompoundOperationsTest {
19+ // :snippet-start: compound-data-model
20+ data class FoodOrder (
21+ @BsonId val id : Int ,
22+ val food : String ,
23+ val color : String
24+ )
25+ // :snippet-end:
26+
27+ // :snippet-start: room-data-class
28+ data class HotelRoom (
29+ @BsonId val id : Int ,
30+ val guest : String? = null ,
31+ val room : String ,
32+ val reserved : Boolean = false
33+ )
34+ // :snippet-end:
35+ companion object {
36+ val dotenv = dotenv()
37+ val client = MongoClient .create(dotenv[" MONGODB_CONNECTION_URI" ])
38+ val database = client.getDatabase(" compound_operations" )
39+ val collection = database.getCollection<FoodOrder >(" example" )
40+ val hotelCollection = database.getCollection<HotelRoom >(" rooms" )
41+
42+ @AfterAll
43+ @JvmStatic
44+ fun afterAll () {
45+ runBlocking {
46+ database.drop()
47+ client.close()
48+ }
49+ }
50+ }
51+
52+ @BeforeEach
53+ fun beforeEach () {
54+ runBlocking {
55+ val room = HotelRoom (1 , null , " Blue Room" )
56+ hotelCollection.insertOne(room)
57+ }
58+ }
59+
60+ @AfterEach
61+ fun afterEach () {
62+ runBlocking {
63+ collection.drop()
64+ hotelCollection.drop()
65+ }
66+ }
67+
68+ @Test
69+ fun findOneUpdateTest () = runBlocking {
70+ val foodOrders = FoodOrder (1 , " donut" , " green" )
71+ collection.insertOne(foodOrders)
72+ // :snippet-start: find-one-update
73+
74+ val filter = Filters .eq(FoodOrder ::color.name, " green" )
75+ val update = Updates .set(FoodOrder ::food.name, " pizza" )
76+ val options = FindOneAndUpdateOptions ()
77+ .upsert(true )
78+ .maxTime(5 , TimeUnit .SECONDS )
79+ /* The result variable contains your document in the
80+ state before your update operation is performed
81+ or null if the document was inserted due to upsert
82+ being true */
83+ val result = collection.findOneAndUpdate(filter, update, options)
84+ println (result)
85+ // :snippet-end:
86+ // Junit test for the above code
87+ val expected = FoodOrder (1 , " donut" , " green" )
88+ assertEquals(expected, result)
89+ }
90+
91+ @Test
92+ fun findOneReplaceTest () = runBlocking {
93+ val foodOrders = FoodOrder (1 , " pizza" , " green" )
94+ collection.insertOne(foodOrders)
95+ // :snippet-start: find-one-replace
96+ data class Music (
97+ @BsonId val id : Int ,
98+ val music : String ,
99+ val color : String
100+ )
101+
102+ val filter = Filters .eq(FoodOrder ::color.name, " green" )
103+ val replace = Music (1 , " classical" , " green" )
104+ val options = FindOneAndReplaceOptions ()
105+ .returnDocument(ReturnDocument .AFTER )
106+ val result = collection.withDocumentClass<Music >().findOneAndReplace(filter, replace, options)
107+ println (result)
108+ // :snippet-end:
109+ assertEquals(replace, result)
110+ }
111+
112+ @Test
113+ fun findOneDeleteTest () = runBlocking {
114+ val foodOrders = listOf (
115+ FoodOrder (1 , " pizza" , " green" ),
116+ FoodOrder (2 , " pear" , " yellow" )
117+ )
118+ collection.insertMany(foodOrders)
119+ // :snippet-start: find-one-delete
120+ val sort = Sorts .descending(" _id" )
121+ val filter = Filters .empty()
122+ val options = FindOneAndDeleteOptions ().sort(sort)
123+ val result = collection.findOneAndDelete(filter, options)
124+ // Returns the deleted document
125+ println (result)
126+ // :snippet-end:
127+ // Junit test for the above code
128+ val expectedDeleted = FoodOrder (2 , " pear" , " yellow" )
129+ assertEquals(expectedDeleted, result)
130+ }
131+ @Test
132+ fun bookARoom () = runBlocking {
133+ // :snippet-start: unsafe
134+ suspend fun bookARoomUnsafe (guestName : String ) {
135+ val filter = Filters .eq(" reserved" , false )
136+ val myRoom = hotelCollection.find(filter).firstOrNull()
137+ if (myRoom == null ) {
138+ println (" Sorry, we are booked, $guestName " )
139+ return
140+ }
141+
142+ val myRoomName = myRoom.room
143+ println (" You got the $myRoomName , $guestName " )
144+
145+ val update = Updates .combine(Updates .set(" reserved" , true ), Updates .set(" guest" , guestName))
146+ val roomFilter = Filters .eq(" _id" , myRoom.id)
147+ hotelCollection.updateOne(roomFilter, update)
148+ }
149+ // :snippet-end:
150+ bookARoomUnsafe(" joe" )
151+ val roomAfterUnsafe = hotelCollection.find()
152+ assertEquals(" joe" , roomAfterUnsafe.first().guest)
153+ assertTrue(roomAfterUnsafe.first().reserved)
154+
155+ // :snippet-start: safe
156+ suspend fun bookARoomSafe (guestName : String ) {
157+ val update = Updates .combine(
158+ Updates .set(HotelRoom ::reserved.name, true ),
159+ Updates .set(HotelRoom ::guest.name, guestName)
160+ )
161+ val filter = Filters .eq(" reserved" , false )
162+ val myRoom = hotelCollection.findOneAndUpdate(filter, update)
163+ if (myRoom == null ) {
164+ println (" Sorry, we are booked, $guestName " )
165+ return
166+ }
167+ val myRoomName = myRoom.room
168+ println (" You got the $myRoomName , $guestName " )
169+ }
170+ // :snippet-end:
171+ bookARoomSafe(" joe" )
172+ val roomAfterSafe = hotelCollection.find()
173+ assertEquals(" joe" , roomAfterSafe.first().guest)
174+ assertTrue(roomAfterSafe.first().reserved)
175+ }
176+ }
0 commit comments