Skip to content

Commit ffc9937

Browse files
(DOCSP-29212): CRUD > Project (#42)
# Pull Request Info [PR Reviewing Guidelines](https://github.com/mongodb/docs-java/blob/master/REVIEWING.md) JIRA - https://jira.mongodb.org/browse/DOCSP-29212 Staging - https://docs-mongodbcom-staging.corp.mongodb.com/kotlin/docsworker-xlarge/docsp-29212-project/fundamentals/crud/read-operations/project/ ## Updated Page CRUD Operations > Read Operations > [Specify Which Fields to Return page](https://www.mongodb.com/docs/drivers/java/sync/current/fundamentals/crud/read-operations/project/) ## 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: Ben Perlmutter <[email protected]>
1 parent a765961 commit ffc9937

File tree

8 files changed

+240
-59
lines changed

8 files changed

+240
-59
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
2+
import com.mongodb.*
3+
import com.mongodb.client.model.Filters
4+
import com.mongodb.client.model.Filters.*
5+
import com.mongodb.client.model.Projections
6+
import com.mongodb.client.model.Projections.*
7+
import com.mongodb.kotlin.client.coroutine.MongoClient
8+
import io.github.cdimascio.dotenv.dotenv
9+
import kotlinx.coroutines.flow.toList
10+
import kotlinx.coroutines.runBlocking
11+
import org.bson.codecs.pojo.annotations.BsonId
12+
import org.junit.jupiter.api.AfterAll
13+
import org.junit.jupiter.api.Assertions.*
14+
import org.junit.jupiter.api.BeforeAll
15+
import org.junit.jupiter.api.TestInstance
16+
import java.util.*
17+
import kotlin.test.*
18+
19+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
20+
internal class ProjectTest {
21+
// :snippet-start: fruit-data-class
22+
data class Fruit(
23+
@BsonId val id: Int,
24+
val name: String,
25+
val qty: Int,
26+
val rating: Int
27+
)
28+
// :snippet-end:
29+
30+
companion object {
31+
32+
val dotenv = dotenv()
33+
val client = MongoClient.create(dotenv["MONGODB_CONNECTION_URI"])
34+
val database = client.getDatabase("grocery_store")
35+
val collection = database.getCollection<Fruit>("fruits")
36+
37+
@BeforeAll
38+
@JvmStatic
39+
private fun beforeAll() {
40+
runBlocking {
41+
collection.insertMany(
42+
listOf(
43+
Fruit(1, "apples", 5, 3),
44+
Fruit(2, "bananas", 6, 1),
45+
Fruit(3, "oranges", 7, 2),
46+
Fruit(4, "avocados", 3, 5)
47+
)
48+
)
49+
}
50+
}
51+
52+
@AfterAll
53+
@JvmStatic
54+
private fun afterAll() {
55+
runBlocking {
56+
collection.drop()
57+
client.close()
58+
}
59+
}
60+
}
61+
62+
@Test
63+
fun projectTest() = runBlocking {
64+
// :snippet-start: project-name
65+
data class FruitName(
66+
@BsonId val id: Int? = null,
67+
val name: String
68+
)
69+
70+
// Return all documents with only the name field
71+
val filter = Filters.empty()
72+
val projection = Projections.fields(
73+
Projections.include(FruitName::name.name)
74+
)
75+
val flowResults = collection.find<FruitName>(filter).projection(projection)
76+
flowResults.collect { println(it)}
77+
// :snippet-end:
78+
val resultList = listOf(
79+
FruitName(1, "apples"),
80+
FruitName(2, "bananas"),
81+
FruitName(3, "oranges"),
82+
FruitName(4, "avocados")
83+
)
84+
assertEquals(flowResults.toList(), resultList)
85+
}
86+
87+
@Test
88+
fun excludeIdProjectTest() {
89+
runBlocking {
90+
// :snippet-start: exclude-id
91+
data class FruitName(
92+
@BsonId val id: Int? = null,
93+
val name: String
94+
)
95+
96+
// Return all documents with *only* the name field
97+
// excludes the id
98+
val filter = Filters.empty()
99+
val projection = Projections.fields(
100+
Projections.include(FruitName::name.name),
101+
Projections.excludeId()
102+
)
103+
val flowResults = collection.find<FruitName>(filter).projection(projection)
104+
flowResults.collect { println(it)}
105+
// :snippet-end:
106+
val resultList = listOf(
107+
FruitName(name = "apples"),
108+
FruitName(name = "bananas"),
109+
FruitName(name = "oranges"),
110+
FruitName(name = "avocados")
111+
)
112+
assertEquals(flowResults.toList(), resultList)
113+
}
114+
}
115+
@Test
116+
fun multipleFieldsProjectTest() {
117+
runBlocking {
118+
// :snippet-start: multiple-fields
119+
data class FruitRating(
120+
val name: String,
121+
val rating: Int
122+
)
123+
124+
val filter = Filters.empty()
125+
val projection = Projections.fields(
126+
Projections.include(FruitRating::name.name, FruitRating::rating.name),
127+
Projections.excludeId()
128+
)
129+
val flowResults = collection.find<FruitRating>(filter).projection(projection)
130+
flowResults.collect { println(it)}
131+
// :snippet-end:
132+
val resultList = listOf(
133+
FruitRating(name = "apples", rating = 3),
134+
FruitRating(name = "bananas", rating = 1),
135+
FruitRating(name = "oranges", rating = 2),
136+
FruitRating(name = "avocados", rating = 5)
137+
)
138+
assertEquals(flowResults.toList(), resultList)
139+
}
140+
}
141+
}

examples/src/test/kotlin/SearchTextTest.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@ import org.junit.jupiter.api.BeforeAll
1313
import org.junit.jupiter.api.TestInstance
1414
import kotlin.test.*
1515

16-
// :snippet-start: search-data-model
17-
data class Movies(
18-
@BsonId val id: Int,
19-
val title: String,
20-
val tags: List<String>
21-
)
22-
// :snippet-end:
2316

2417
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
2518
internal class SearchTextTest {
19+
// :snippet-start: search-data-model
20+
data class Movies(
21+
@BsonId val id: Int,
22+
val title: String,
23+
val tags: List<String>
24+
)
25+
// :snippet-end:
2626

2727
companion object {
2828
val dotenv = dotenv()

examples/src/test/kotlin/SkipTest.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ internal class SkipTest {
2424
val color: String
2525
)
2626
// :snippet-end:
27-
2827
companion object {
2928
val dotenv = dotenv()
3029
val client = MongoClient.create(dotenv["MONGODB_CONNECTION_URI"])
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
data class FruitName(
2+
@BsonId val id: Int? = null,
3+
val name: String
4+
)
5+
6+
// return all documents with *only* the name field
7+
// excludes the id
8+
val filter = Filters.empty()
9+
val projection = Projections.fields(
10+
Projections.include(FruitName::name.name),
11+
Projections.excludeId()
12+
)
13+
val flowResults = collection.find<FruitName>(filter).projection(projection)
14+
flowResults.collect { println(it)}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
data class Fruit(
2+
@BsonId val id: Int,
3+
val name: String,
4+
val qty: Int,
5+
val rating: Int
6+
)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
data class FruitRating(
2+
val name: String,
3+
val rating: Int
4+
)
5+
6+
val filter = Filters.empty()
7+
val projection = Projections.fields(
8+
Projections.include(FruitRating::name.name, FruitRating::rating.name),
9+
Projections.excludeId()
10+
)
11+
val flowResults = collection.find<FruitRating>(filter).projection(projection)
12+
flowResults.collect { println(it)}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
data class FruitName(
2+
@BsonId val id: Int? = null,
3+
val name: String
4+
)
5+
6+
// return all documents with only the name field
7+
val filter = Filters.empty()
8+
val projection = Projections.fields(
9+
Projections.include(FruitName::name.name)
10+
)
11+
val flowResults = collection.find<FruitName>(filter).projection(projection)
12+
flowResults.collect { println(it)}

source/fundamentals/crud/read-operations/project.txt

Lines changed: 48 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Overview
1616
--------
1717

1818
In this guide, you can learn how to control which fields appear in
19-
documents returned from read operations with the MongoDB Java driver.
19+
documents returned from read operations with the MongoDB Kotlin driver.
2020

2121
Many read requests require only a subset of fields in a document.
2222
For example, when logging a user in you may only need their username, and
@@ -63,69 +63,67 @@ varieties of fruit:
6363
{ "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
6464
{ "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
6565

66+
67+
This data is modeled using the following Kotlin data class:
68+
69+
.. literalinclude:: /examples/generated/ProjectTest.snippet.fruit-data-class.kt
70+
:language: kotlin
71+
6672
In the following query, pass the projection to return the ``name``
67-
field of each document:
73+
field of each document. The results are modeled using the ``FruitName`` Kotlin data class:
6874

69-
.. code-block:: java
70-
:emphasize-lines: 3
75+
.. io-code-block::
7176

72-
// return all documents with *only* the name field
73-
Bson filter = Filters.empty();
74-
Bson projection = Projections.fields(Projections.include("name"));
75-
collection.find(filter).projection(projection).forEach(doc -> System.out.println(doc));
77+
.. input:: /examples/generated/ProjectTest.snippet.project-name.kt
78+
:language: kotlin
79+
:emphasize-lines: 8-10
7680

77-
The projection document specifies that the read operation result should
81+
.. output::
82+
:language: console
83+
84+
FruitName(id=1, name=apples),
85+
FruitName(id=2, name=bananas),
86+
FruitName(id=3, name=oranges),
87+
FruitName(id=4, name=avocados)
88+
89+
The projection document specifies that the read operation result should
7890
*include* the ``name`` field of each returned document. As a result, this
7991
projection implicitly excludes the ``qty`` and ``rating`` fields. Chaining
8092
this projection to ``find()`` with an empty query filter yields the
81-
following results:
82-
83-
.. code-block:: json
84-
:copyable: false
85-
86-
{ "_id": 1, "name": "apples" }
87-
{ "_id": 2, "name": "bananas" }
88-
{ "_id": 3, "name": "oranges" }
89-
{ "_id": 4, "name": "avocados" }
93+
above results.
9094

9195
Despite the fact that this projection only explicitly included the
92-
``name`` field, the query returned the ``_id`` field as well.
96+
``name`` field, the query also returned the ``_id`` field, represented by ``id`` in the data class.
9397

9498
The ``_id`` field is a special case: it is always included in every query
9599
result unless explicitly excluded. That's because the ``_id`` field is a
96100
unique identifier for each document, a property that can be useful when
97101
constructing queries.
98102

99-
A ``movies`` collection is a good example of why this property is useful:
100-
because remakes and even separate works sometimes reuse movie titles,
101-
you need a unique ``_id`` value to refer to any specific movie.
102-
103103
The ``_id`` is the only exception to the mutually exclusive include-exclude
104104
behavior in projections: you *can* explicitly exclude the ``_id`` field
105105
even when explicitly including other fields if you do not want ``_id``
106106
to be present in returned documents.
107107

108-
.. code-block:: java
109-
:emphasize-lines: 3
108+
.. io-code-block::
109+
110+
.. input:: /examples/generated/ProjectTest.snippet.exclude-id.kt
111+
:language: kotlin
112+
:emphasize-lines: 9-12
110113

111-
// return all documents with only the name field
112-
Bson filter = Filters.empty();
113-
Bson projection = Projections.fields(Projections.include("name"), Projections.excludeId());
114-
collection.find(filter).projection(projection).forEach(doc -> System.out.println(doc));
114+
.. output::
115+
:language: console
116+
117+
FruitName(name=apples),
118+
FruitName(name=bananas),
119+
FruitName(name=oranges),
120+
FruitName(name=avocados)
115121

116122
The projection document specifies that the read operation result should
117123
*include* the ``name`` field of each returned document, and specifies to
118124
*exclude* the ``_id`` field. As a result, this projection implicitly
119125
excludes the ``qty`` and ``rating`` fields. Chaining this projection to
120-
``find()`` with an empty query filter yields the following results:
121-
122-
.. code-block:: javascript
123-
:copyable: false
124-
125-
{ "name": "apples" }
126-
{ "name": "bananas" }
127-
{ "name": "oranges" }
128-
{ "name": "avocados" }
126+
``find()`` with an empty query filter yields the above results.
129127

130128
You can also specify multiple fields to include in your projection.
131129

@@ -134,23 +132,22 @@ You can also specify multiple fields to include in your projection.
134132
The order in which you specify the fields in the projection does not
135133
alter the order in which they are returned.
136134

137-
.. code-block:: java
138-
:emphasize-lines: 2
135+
This example that identifies two fields to include in the projection yields
136+
the following results using the ``FruitRating`` Kotlin data class:
139137

140-
Bson filter = Filters.empty();
141-
Bson projection = Projections.fields(Projections.include("name", "rating"), Projections.excludeId());
142-
collection.find(filter).projection(projection).forEach(doc -> System.out.println(doc));
138+
.. io-code-block::
143139

144-
This example that identifies two fields to include in the projection yields
145-
the following results:
140+
.. input:: /examples/generated/ProjectTest.snippet.multiple-fields.kt
141+
:language: kotlin
142+
:emphasize-lines: 8-9
146143

147-
.. code-block:: json
148-
:copyable: false
144+
.. output::
145+
:language: console
149146

150-
{ "name": "apples", "rating": 3 }
151-
{ "name": "bananas", "rating": 1 }
152-
{ "name": "oranges", "rating": 2 }
153-
{ "name": "avocados", "rating": 5 }
147+
FruitRating(name=apples, rating=3),
148+
FruitRating(name=bananas, rating=1),
149+
FruitRating(name=oranges, rating=2),
150+
FruitRating(name=avocados, rating=5)
154151

155152
For additional projection examples, see the
156153
:manual:`MongoDB Manual page on Project Fields to Return from Query </tutorial/project-fields-from-query-results/>`.

0 commit comments

Comments
 (0)