Skip to content
Merged
37 changes: 36 additions & 1 deletion examples/src/test/kotlin/AggregatesBuilderTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ class AggregatesBuilderTest {
val rated: String,
val plot: String,
val runtime: Int,
val imdb: IMDB
val imdb: IMDB,
val fullplot: String? = "No full plot",
){
data class IMDB(
val rating: Double
Expand Down Expand Up @@ -951,6 +952,40 @@ class AggregatesBuilderTest {
assertEquals("Back to the Future", results.first().title)
}

/* NOTE: Test is not run by default. FTS requires the creation of a text index on the collection before running
(see note at top of file for additional setup requirements for FTS).
*/
@Ignore
fun atlasSearchOperatorTest() = runBlocking {

// :snippet-start: atlas-search-pipeline
data class Results(val title: String, val year: Int, val genres: List<String>)

val searchStage = Aggregates.search(
SearchOperator.compound()
.filter(
listOf(
SearchOperator.`in`(SearchPath.fieldPath(Movie::genres.name), listOf("Comedy")),
SearchOperator.phrase(SearchPath.fieldPath(Movie::fullplot.name), "new york"),
SearchOperator.numberRange(SearchPath.fieldPath(Movie::year.name)).gtLt(1950, 2000),
SearchOperator.wildcard(SearchPath.fieldPath(Movie::title.name), "Love *")
)
)
)

val projectStage = Aggregates.project(
Projections.include(Movie::title.name, Movie::year.name, Movie::genres.name))

val pipeline = listOf(searchStage, projectStage)
val resultsFlow = ftsCollection.aggregate<Results>(pipeline)

resultsFlow.collect { println(it) }
// :snippet-end:

val result = resultsFlow.toList()
assertEquals(2, result.size)
}

/* NOTE: Test is not run by default. FTS requires the creation of a text index on the collection before running
(see note at top of file for additional setup requirements for FTS).
*/
Expand Down
126 changes: 109 additions & 17 deletions examples/src/test/kotlin/AggregationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@ import com.mongodb.client.model.Accumulators
import com.mongodb.client.model.Aggregates
import com.mongodb.client.model.Filters
import com.mongodb.client.model.Projections
import com.mongodb.client.model.search.SearchOperator
import com.mongodb.client.model.search.SearchPath.fieldPath
import com.mongodb.kotlin.client.coroutine.MongoClient
import config.getConfig
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import org.bson.Document
import org.bson.codecs.pojo.annotations.BsonId
import org.bson.conversions.Bson
import org.bson.json.JsonWriterSettings
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.Ignore


class AggregationTest {
Expand Down Expand Up @@ -44,16 +48,102 @@ class AggregationTest {
fun beforeAll() {
runBlocking {
val restaurants = listOf(
Restaurant("Sun Bakery Trattoria", Restaurant.Contact("386-555-0189", "[email protected]", listOf(-74.0056649, 40.7452371)), 4, listOf("Pizza", "Pasta", "Italian", "Coffee", "Sandwiches")),
Restaurant("Blue Bagels Grill", Restaurant.Contact("786-555-0102", "[email protected]", listOf(-73.92506, 40.8275556)), 3, listOf("Bagels", "Cookies", "Sandwiches")),
Restaurant("XYZ Bagels Restaurant", Restaurant.Contact("435-555-0190", "[email protected]", listOf(-74.0707363, 40.59321569999999)), 4, listOf("Bagels", "Sandwiches", "Coffee")),
Restaurant("Hot Bakery Cafe", Restaurant.Contact("264-555-0171", "[email protected]", listOf(-73.96485799999999, 40.761899)), 4, listOf("Bakery", "Cafe", "Coffee", "Dessert")),
Restaurant("Green Feast Pizzeria", Restaurant.Contact("840-555-0102", "[email protected]", listOf(-74.1220973, 40.6129407)), 2, listOf("Pizza", "Italian")),
Restaurant("ZZZ Pasta Buffet", Restaurant.Contact("769-555-0152", "[email protected]", listOf(-73.9446421, 40.7253944)), 0, listOf("Pasta", "Italian", "Buffet", "Cafeteria")),
Restaurant("XYZ Coffee Bar", Restaurant.Contact("644-555-0193", "[email protected]", listOf(-74.0166091, 40.6284767)), 5, listOf("Coffee", "Cafe", "Bakery", "Chocolates")),
Restaurant("456 Steak Restaurant", Restaurant.Contact("990-555-0165", "[email protected]", listOf(-73.9365108, 40.8497077)), 0, listOf("Steak", "Seafood")),
Restaurant("456 Cookies Shop", Restaurant.Contact("604-555-0149", "[email protected]", listOf(-73.8850023, 40.7494272)), 4, listOf("Bakery", "Cookies", "Cake", "Coffee")),
Restaurant("XYZ Steak Buffet", Restaurant.Contact("229-555-0197", "[email protected]", listOf(-73.9799932, 40.7660886)), 3, listOf("Steak", "Salad", "Chinese"))
Restaurant(
"Sun Bakery Trattoria",
Restaurant.Contact(
"386-555-0189",
"[email protected]",
listOf(-74.0056649, 40.7452371)
),
4,
listOf("Pizza", "Pasta", "Italian", "Coffee", "Sandwiches")
),
Restaurant(
"Blue Bagels Grill",
Restaurant.Contact(
"786-555-0102",
"[email protected]",
listOf(-73.92506, 40.8275556)
),
3,
listOf("Bagels", "Cookies", "Sandwiches")
),
Restaurant(
"XYZ Bagels Restaurant",
Restaurant.Contact(
"435-555-0190",
"[email protected]",
listOf(-74.0707363, 40.59321569999999)
),
4,
listOf("Bagels", "Sandwiches", "Coffee")
),
Restaurant(
"Hot Bakery Cafe",
Restaurant.Contact(
"264-555-0171",
"[email protected]",
listOf(-73.96485799999999, 40.761899)
),
4,
listOf("Bakery", "Cafe", "Coffee", "Dessert")
),
Restaurant(
"Green Feast Pizzeria",
Restaurant.Contact(
"840-555-0102",
"[email protected]",
listOf(-74.1220973, 40.6129407)
),
2,
listOf("Pizza", "Italian")
),
Restaurant(
"ZZZ Pasta Buffet",
Restaurant.Contact(
"769-555-0152",
"[email protected]",
listOf(-73.9446421, 40.7253944)
),
0,
listOf("Pasta", "Italian", "Buffet", "Cafeteria")
),
Restaurant(
"XYZ Coffee Bar",
Restaurant.Contact("644-555-0193", "[email protected]", listOf(-74.0166091, 40.6284767)),
5,
listOf("Coffee", "Cafe", "Bakery", "Chocolates")
),
Restaurant(
"456 Steak Restaurant",
Restaurant.Contact(
"990-555-0165",
"[email protected]",
listOf(-73.9365108, 40.8497077)
),
0,
listOf("Steak", "Seafood")
),
Restaurant(
"456 Cookies Shop",
Restaurant.Contact(
"604-555-0149",
"[email protected]",
listOf(-73.8850023, 40.7494272)
),
4,
listOf("Bakery", "Cookies", "Cake", "Coffee")
),
Restaurant(
"XYZ Steak Buffet",
Restaurant.Contact(
"229-555-0197",
"[email protected]",
listOf(-73.9799932, 40.7660886)
),
3,
listOf("Steak", "Salad", "Chinese")
)
)
collection.insertMany(restaurants)
}
Expand All @@ -71,15 +161,17 @@ class AggregationTest {
}

@Test
fun basicAggregationTest() = runBlocking {
fun basicAggregationTest() = runBlocking {
// :snippet-start: basic-aggregation
data class Results(@BsonId val id: Int, val count: Int)

val resultsFlow = collection.aggregate<Results>(
listOf(
Aggregates.match(Filters.eq(Restaurant::categories.name, "Bakery")),
Aggregates.group("\$${Restaurant::stars.name}",
Accumulators.sum("count", 1))
Aggregates.group(
"\$${Restaurant::stars.name}",
Accumulators.sum("count", 1)
)
)
)

Expand Down Expand Up @@ -124,7 +216,7 @@ class AggregationTest {
@Test
fun explainAggregationTest() = runBlocking {
// :snippet-start: explain-aggregation
data class Results (val name: String, val count: Int)
data class Results(val name: String, val count: Int)

val explanation = collection.aggregate<Results>(
listOf(
Expand All @@ -143,11 +235,11 @@ class AggregationTest {
@Test
fun buildDocumentsTipTest() {
val method1 =
// :snippet-start: build-documents-tip
Document("\$arrayElemAt", listOf("\$categories", 0))
// :snippet-start: build-documents-tip
Document("\$arrayElemAt", listOf("\$categories", 0))
// is equivalent to
val method2 = // :remove:
Document.parse("{ \$arrayElemAt: ['\$categories', 0] }")
Document.parse("{ \$arrayElemAt: ['\$categories', 0] }")
// :snippet-end:
// assert to test equivalency
assertEquals(method1, method2)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
data class Results(val title: String, val year: Int, val genres: List<String>)

val searchStage = Aggregates.search(
SearchOperator.compound()
.filter(
listOf(
SearchOperator.`in`(SearchPath.fieldPath(Movie::genres.name), listOf("Comedy")),
SearchOperator.phrase(SearchPath.fieldPath(Movie::fullplot.name), "new york"),
SearchOperator.numberRange(SearchPath.fieldPath(Movie::year.name)).gtLt(1950, 2000),
SearchOperator.wildcard(SearchPath.fieldPath(Movie::title.name), "Love *")
)
)
)

val projectStage = Aggregates.project(
Projections.include(Movie::title.name, Movie::year.name, Movie::genres.name))

val pipeline = listOf(searchStage, projectStage)
val resultsFlow = ftsCollection.aggregate<Results>(pipeline)

resultsFlow.collect { println(it) }
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ data class Movie(
val rated: String,
val plot: String,
val runtime: Int,
val imdb: IMDB
val imdb: IMDB,
val fullplot: String? = "No full plot",
){
data class IMDB(
val rating: Double
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ data class Results(@BsonId val id: Int, val count: Int)
val resultsFlow = collection.aggregate<Results>(
listOf(
Aggregates.match(Filters.eq(Restaurant::categories.name, "Bakery")),
Aggregates.group("\$${Restaurant::stars.name}",
Accumulators.sum("count", 1))
Aggregates.group(
"\$${Restaurant::stars.name}",
Accumulators.sum("count", 1)
)
)
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Document("\$arrayElemAt", listOf("\$categories", 0))
Document("\$arrayElemAt", listOf("\$categories", 0))
// is equivalent to
Document.parse("{ \$arrayElemAt: ['\$categories', 0] }")
Document.parse("{ \$arrayElemAt: ['\$categories', 0] }")
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
data class Results (val name: String, val count: Int)
data class Results(val name: String, val count: Int)

val explanation = collection.aggregate<Results>(
listOf(
Expand Down
3 changes: 3 additions & 0 deletions source/fundamentals/aggregation.txt
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ first element in the ``categories`` field.
Results(name=456 Cookies Shop, firstCategory=Bakery)
Results(name=XYZ Steak Buffet, firstCategory=Steak)

API Documentation
-----------------

For more information about the methods and classes mentioned in this section,
see the following API Documentation:

Expand Down
25 changes: 25 additions & 0 deletions source/fundamentals/builders/aggregates.txt
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,31 @@ field in the ``movies`` collection for text that contains the word "Future":
Learn more about the builders from the
`search package API documentation <{+core-api+}/client/model/search/package-summary.html>`__.

.. _kotlin-cr-atlas-search-stage:

Search Operator Methods
~~~~~~~~~~~~~~~~~~~~~~~

.. sharedinclude:: dbx/jvm/atlas-search-operator-helpers.rst

.. replacement:: as-idx-link

the :ref:`kotlin-search-indexes` section of the Indexes guide

.. replacement:: atlas-query-operators-example

.. io-code-block::

.. input:: /examples/generated/AggregatesBuilderTest.snippet.atlas-search-pipeline.kt
:language: kotlin

.. output::
:language: console
:visible: false

Results(title=Love at First Bite, year=1979, genres=[Comedy, Romance])
Results(title=Love Affair, year=1994, genres=[Comedy, Drama])

Atlas Search Metadata
---------------------

Expand Down
5 changes: 5 additions & 0 deletions source/whats-new.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ and features:
the `SearchOperator <{+core-api+}/client/model/search/SearchOperator.html>`__
interface API documentation

.. replacement:: atlas-query-operators

the :ref:`kotlin-cr-atlas-search-stage` section of the Aggregates
Builders guide

.. _kotlin-coroutine-version-5.3:

What's New in 5.3
Expand Down
Loading