Skip to content

Commit 56161f2

Browse files
(DOCSP-29204): Kotlin collations (#66)
# Pull Request Info [PR Reviewing Guidelines](https://github.com/mongodb/docs-java/blob/master/REVIEWING.md) JIRA - https://jira.mongodb.org/browse/DOCSP-29204 Staging - https://docs-mongodbcom-staging.corp.mongodb.com/kotlin/docsworker-xlarge/DOCSP-29204/fundamentals/collations/ ## Self-Review Checklist - [ ] Is this free of any warnings or errors in the RST? - [ ] Did you run a spell-check? - [ ] Did you run a grammar-check? - [ ] Are all the links working? --------- Co-authored-by: cbullinger <[email protected]>
1 parent 99c3b2a commit 56161f2

16 files changed

+413
-133
lines changed
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
2+
import com.mongodb.client.model.*
3+
import com.mongodb.kotlin.client.coroutine.MongoClient
4+
import io.github.cdimascio.dotenv.dotenv
5+
import kotlinx.coroutines.flow.first
6+
import kotlinx.coroutines.flow.toList
7+
import kotlinx.coroutines.runBlocking
8+
import org.bson.Document
9+
import org.bson.codecs.pojo.annotations.BsonId
10+
import org.junit.jupiter.api.AfterAll
11+
import org.junit.jupiter.api.AfterEach
12+
import org.junit.jupiter.api.BeforeEach
13+
import org.junit.jupiter.api.Test
14+
import kotlin.test.assertEquals
15+
16+
17+
internal class CollationTest {
18+
19+
// :snippet-start: data-class-first-name
20+
data class FirstName(
21+
@BsonId val id: Int,
22+
val firstName: String,
23+
val verified: Boolean = false
24+
)
25+
// :snippet-end:
26+
// :snippet-start: data-class-collation-example
27+
data class CollationExample(@BsonId val id: Int, val a: String)
28+
// :snippet-end:
29+
30+
companion object {
31+
val dotenv = dotenv()
32+
val client: MongoClient = MongoClient.create(dotenv["MONGODB_CONNECTION_URI"])
33+
val database = client.getDatabase("example_db")
34+
val nameCollection = database.getCollection<FirstName>("names")
35+
val collationExampleCollection = database.getCollection<CollationExample>("collation_examples")
36+
37+
@AfterAll
38+
@JvmStatic
39+
fun afterAll() {
40+
runBlocking {
41+
database.drop()
42+
client.close()
43+
}
44+
}
45+
}
46+
47+
@BeforeEach
48+
fun beforeEach() {
49+
runBlocking {
50+
nameCollection.insertMany(
51+
listOf(
52+
FirstName(1, "Klara"),
53+
FirstName(2, "Gunter"),
54+
FirstName(3, "Günter"),
55+
FirstName(4, "Jürgen"),
56+
FirstName(5, "Hannah")
57+
)
58+
)
59+
collationExampleCollection.insertMany(listOf(
60+
CollationExample(1, "16 apples"),
61+
CollationExample(2, "84 oranges"),
62+
CollationExample(3, "179 bananas")
63+
))
64+
}
65+
}
66+
67+
@AfterEach
68+
fun afterEach() {
69+
runBlocking {
70+
if(nameCollection.countDocuments() > 0){
71+
nameCollection.drop()
72+
}
73+
if(collationExampleCollection.countDocuments() > 0) {
74+
collationExampleCollection.drop()
75+
}
76+
}
77+
}
78+
79+
@Test
80+
fun collationBuilderTest() {
81+
val collation =
82+
// :snippet-start: collation-builder
83+
Collation.builder()
84+
.caseLevel(true)
85+
.collationAlternate(CollationAlternate.SHIFTED)
86+
.collationCaseFirst(CollationCaseFirst.UPPER)
87+
.collationMaxVariable(CollationMaxVariable.SPACE)
88+
.collationStrength(CollationStrength.SECONDARY)
89+
.locale("en_US")
90+
.normalization(false)
91+
.numericOrdering(true)
92+
.build()
93+
// :snippet-end:
94+
assertEquals("en_US", collation.locale)
95+
}
96+
97+
@Test
98+
fun createListCollationTest() = runBlocking {
99+
nameCollection.drop()
100+
// :snippet-start: create-collection-options
101+
database.createCollection(
102+
"names",
103+
CreateCollectionOptions().collation(
104+
Collation.builder().locale("en_US").build()
105+
)
106+
)
107+
// :snippet-end:
108+
// :snippet-start: list-indexes
109+
val collection = database.getCollection<FirstName>("names")
110+
val indexInformation = collection.listIndexes().first()
111+
println(indexInformation.toJson())
112+
// :snippet-end:
113+
assertEquals(2, database.listCollectionNames().toList().size)
114+
assertEquals("en_US", indexInformation.get("collation", Document::class.java).getString("locale"))
115+
}
116+
117+
@Test
118+
fun createCollationWithIndex() = runBlocking {
119+
// :snippet-start: create-collation-with-index
120+
val collection = database.getCollection<FirstName>("names")
121+
collection.dropIndexes() // :remove:
122+
val idxOptions = IndexOptions().collation(Collation.builder().locale("en_US").build())
123+
collection.createIndex(Indexes.ascending(FirstName::firstName.name), idxOptions)
124+
// :snippet-end:
125+
assertEquals(2, collection.listIndexes().toList().size)
126+
assertEquals("en_US", collection.listIndexes().toList()[1].get("collation", Document::class.java).getString("locale"))
127+
}
128+
129+
@Test
130+
fun collationOperationTest() = runBlocking {
131+
val collection = nameCollection
132+
// :snippet-start: collation-operation
133+
val resultsFlow = collection.find()
134+
.collation(Collation.builder().locale("en_US").build())
135+
.sort(Sorts.ascending(FirstName::firstName.name));
136+
// :snippet-end:
137+
val results = resultsFlow.toList()
138+
assertEquals("Gunter", results[0].firstName)
139+
assertEquals("Günter", results[1].firstName)
140+
assertEquals("Hannah", results[2].firstName)
141+
assertEquals(5, results.size)
142+
}
143+
@Test
144+
fun collationCustomOperationTest() = runBlocking {
145+
val collection = nameCollection
146+
// :snippet-start: collation-custom-operation
147+
val findFlow = collection.find()
148+
.collation(Collation.builder().locale("is").build())
149+
.sort(Sorts.ascending(FirstName::firstName.name))
150+
// :snippet-end:
151+
println(findFlow.toList())
152+
assertEquals("Gunter", findFlow.toList()[0].firstName)
153+
assertEquals("Günter", findFlow.toList()[1].firstName)
154+
}
155+
156+
@Test
157+
fun findAndSortExampleTest() = runBlocking {
158+
val collection = nameCollection
159+
// :snippet-start: find-and-sort
160+
val resultsFlow = collection.find()
161+
.collation(Collation.builder().locale("de@collation=phonebook").build())
162+
.sort(Sorts.ascending(FirstName::firstName.name))
163+
164+
resultsFlow.collect { println(it) }
165+
// :snippet-end:
166+
val results = resultsFlow.toList()
167+
assertEquals("Günter", results[0].firstName)
168+
assertEquals("Gunter", results[1].firstName)
169+
}
170+
171+
@Test
172+
fun findOneAndUpdateExampleTest() = runBlocking {
173+
val collection = nameCollection
174+
// :snippet-start: find-one-and-update
175+
val result = collection.findOneAndUpdate(
176+
Filters.lt(FirstName::firstName.name, "Gunter"),
177+
Updates.set("verified", true),
178+
FindOneAndUpdateOptions()
179+
.collation(Collation.builder().locale("de@collation=phonebook").build())
180+
.sort(Sorts.ascending(FirstName::firstName.name))
181+
.returnDocument(ReturnDocument.AFTER)
182+
)
183+
println(result)
184+
// :snippet-end:
185+
assertEquals("Günter", result?.firstName) // returning hannah?
186+
assertEquals(true, result?.verified)
187+
}
188+
189+
@Test
190+
fun findOneAndDeleteExampleTest() = runBlocking {
191+
val collection = collationExampleCollection
192+
// :snippet-start: find-one-and-delete
193+
val result = collection.findOneAndDelete(
194+
Filters.gt(CollationExample::a.name, "100"),
195+
FindOneAndDeleteOptions()
196+
.collation(Collation.builder().locale("en").numericOrdering(true).build())
197+
.sort(Sorts.ascending(CollationExample::a.name))
198+
)
199+
println(result)
200+
// :snippet-end:
201+
val expected = CollationExample(3, "179 bananas")
202+
assertEquals(expected, result)
203+
// Clean up
204+
collection.drop()
205+
}
206+
207+
@Test
208+
fun aggregatesExampleTest() = runBlocking {
209+
val collection = nameCollection
210+
// :snippet-start: aggregates
211+
data class Result(@BsonId val id: String, val nameCount: Int)
212+
val groupStage = Aggregates.group(
213+
"\$${FirstName::firstName.name}",
214+
Accumulators.sum("nameCount", 1)
215+
)
216+
val sortStage = Aggregates.sort(Sorts.ascending("_id"))
217+
val resultsFlow = collection.aggregate<Result>(listOf(groupStage, sortStage))
218+
.collation(
219+
Collation.builder().locale("de")
220+
.collationStrength(CollationStrength.PRIMARY)
221+
.build()
222+
)
223+
resultsFlow.collect { println(it) }
224+
// :snippet-end:
225+
val results = resultsFlow.toList()
226+
println(results)
227+
assertEquals(4, results.size)
228+
assertEquals("Gunter", results[0].id)
229+
assertEquals(2, results[0].nameCount)
230+
}
231+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
data class Result(@BsonId val id: String, val nameCount: Int)
2+
val groupStage = Aggregates.group(
3+
"\$${FirstName::firstName.name}",
4+
Accumulators.sum("nameCount", 1)
5+
)
6+
val sortStage = Aggregates.sort(Sorts.ascending("_id"))
7+
val resultsFlow = collection.aggregate<Result>(listOf(groupStage, sortStage))
8+
.collation(
9+
Collation.builder().locale("de")
10+
.collationStrength(CollationStrength.PRIMARY)
11+
.build()
12+
)
13+
resultsFlow.collect { println(it) }
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Collation.builder()
2+
.caseLevel(true)
3+
.collationAlternate(CollationAlternate.SHIFTED)
4+
.collationCaseFirst(CollationCaseFirst.UPPER)
5+
.collationMaxVariable(CollationMaxVariable.SPACE)
6+
.collationStrength(CollationStrength.SECONDARY)
7+
.locale("en_US")
8+
.normalization(false)
9+
.numericOrdering(true)
10+
.build()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
val findFlow = collection.find()
2+
.collation(Collation.builder().locale("is").build())
3+
.sort(Sorts.ascending(FirstName::firstName.name))
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
val resultsFlow = collection.find()
2+
.collation(Collation.builder().locale("en_US").build())
3+
.sort(Sorts.ascending(FirstName::firstName.name));
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
val collection = database.getCollection<FirstName>("names")
2+
val idxOptions = IndexOptions().collation(Collation.builder().locale("en_US").build())
3+
collection.createIndex(Indexes.ascending(FirstName::firstName.name), idxOptions)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
database.createCollection(
2+
"names",
3+
CreateCollectionOptions().collation(
4+
Collation.builder().locale("en_US").build()
5+
)
6+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
data class CollationExample(@BsonId val id: Int, val a: String)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
data class FirstName(
2+
@BsonId val id: Int,
3+
val firstName: String,
4+
val verified: Boolean = false
5+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
val resultsFlow = collection.find()
2+
.collation(Collation.builder().locale("de@collation=phonebook").build())
3+
.sort(Sorts.ascending(FirstName::firstName.name))
4+
5+
resultsFlow.collect { println(it) }

0 commit comments

Comments
 (0)