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