From 3997ae91f30066a42e785f73a80bdcc737400d07 Mon Sep 17 00:00:00 2001 From: Rachel Mackintosh Date: Thu, 27 Mar 2025 15:58:51 -0400 Subject: [PATCH 01/10] atlas searchoperation helper methods --- source/fundamentals/aggregation.txt | 39 +++++++++++++++++++++++++++++ source/whats-new.txt | 5 ++++ 2 files changed, 44 insertions(+) diff --git a/source/fundamentals/aggregation.txt b/source/fundamentals/aggregation.txt index c3d5b45f..4e8687ce 100644 --- a/source/fundamentals/aggregation.txt +++ b/source/fundamentals/aggregation.txt @@ -215,6 +215,44 @@ first element in the ``categories`` field. Results(name=456 Cookies Shop, firstCategory=Bakery) Results(name=XYZ Steak Buffet, firstCategory=Steak) +.. _kotlin-cr-atlas-search-stage: + +Pipelines Stages for Atlas Search +--------------------------------- + +:atlas:`Atlas Search ` queries take the form of an aggregation pipeline stage. Atlas +Search provides ``$search`` and ``$searchMeta`` stages, both of which must be the first +stage in any query pipeline. For more information about Atlas pipeline stages, +see the :atlas:`Choose the Aggregation Pipeline Stage +` page in the Atlas +manual. + +.. sharedinclude:: dbx/jvm/atlas-search-operator-helpers.rst + + .. replacement:: atlas-query-operators-example + + .. code-block:: kotlin + + val searchStage: Bson = Aggregates.search( + SearchOperator.compound() + .filter( + listOf( + SearchOperator.text(fieldPath("genres"), "Drama"), + SearchOperator.phrase(fieldPath("cast"), "sylvester stallone"), + SearchOperator.numberRange(fieldPath("year")).gtLt(1980, 1989), + SearchOperator.wildcard(fieldPath("title"), "Rocky *") + ) + ) + ) + + .. replacement:: searchoperator-interface-api-docs + + the `SearchOperator Interface API documentation + <{+core-api+}/com/mongodb/client/model/search/SearchOperator.html>`__ + +API Documentation +----------------- + For more information about the methods and classes mentioned in this section, see the following API Documentation: @@ -222,3 +260,4 @@ see the following API Documentation: - `$group <{+core-api+}/client/model/Aggregates.html#group(TExpression,java.util.List)>`__ - `$project <{+core-api+}/client/model/Aggregates.html#project(org.bson.conversions.Bson)>`__ - `Projections <{+core-api+}/client/model/Projections.html>`__ +- `SearchOperator Interface <{+core-api+}/com/mongodb/client/model/search/SearchOperator.html>`__ diff --git a/source/whats-new.txt b/source/whats-new.txt index 79ac2ec7..99dbc01e 100644 --- a/source/whats-new.txt +++ b/source/whats-new.txt @@ -49,6 +49,11 @@ and features: the `SearchOperator <{+core-api+}/client/model/search/SearchOperator.html>`__ interface API documentation + .. replacement:: atlas-query-operators + + the :ref:`Pipelines Stages for Atlas Search + ` section + .. _kotlin-coroutine-version-5.3: What's New in 5.3 From 314b9cbc08d41c7ee780a58447d70539f1208fc1 Mon Sep 17 00:00:00 2001 From: Rachel Mackintosh Date: Thu, 27 Mar 2025 16:10:03 -0400 Subject: [PATCH 02/10] fix link --- source/fundamentals/aggregation.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/fundamentals/aggregation.txt b/source/fundamentals/aggregation.txt index 4e8687ce..554fb5e1 100644 --- a/source/fundamentals/aggregation.txt +++ b/source/fundamentals/aggregation.txt @@ -248,7 +248,7 @@ manual. .. replacement:: searchoperator-interface-api-docs the `SearchOperator Interface API documentation - <{+core-api+}/com/mongodb/client/model/search/SearchOperator.html>`__ + <{+core-api+}/client/model/search/SearchOperator.html>`__ API Documentation ----------------- From 7966b0cda0b409d29eed6e3d9c890c6885a70fab Mon Sep 17 00:00:00 2001 From: Rachel Mackintosh Date: Fri, 28 Mar 2025 23:24:38 -0400 Subject: [PATCH 03/10] eg --- .../atlas-examples/AtlasSearchHelpers.kt | 54 +++++++++++++++++++ source/fundamentals/aggregation.txt | 30 +++++------ 2 files changed, 69 insertions(+), 15 deletions(-) create mode 100644 source/examples/atlas-examples/AtlasSearchHelpers.kt diff --git a/source/examples/atlas-examples/AtlasSearchHelpers.kt b/source/examples/atlas-examples/AtlasSearchHelpers.kt new file mode 100644 index 00000000..acb30382 --- /dev/null +++ b/source/examples/atlas-examples/AtlasSearchHelpers.kt @@ -0,0 +1,54 @@ +import com.mongodb.client.model.Aggregates +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 kotlinx.coroutines.runBlocking +import org.bson.Document +import org.bson.conversions.Bson +import java.io.IO.println +import kotlin.collections.List + +const val URI = "" + +// Create data class to represent a MongoDB document +data class Movie(val title: String, val year: Int, val cast: List) + +fun main() { + + // Replace the placeholder with your MongoDB deployment's connection string + val uri = URI + + val mongoClient = MongoClient.create(uri) + val database = mongoClient.getDatabase("sample_mflix") + // Get a collection of documents of type Movie + val collection = database.getCollection("movies") + + // start atlasHelperMethods + runBlocking { + val searchStage: Bson = Aggregates.search( + SearchOperator.compound() + .filter( + listOf( + SearchOperator.text(fieldPath("genres"), "Drama"), + SearchOperator.phrase(fieldPath("cast"), "sylvester stallone"), + SearchOperator.numberRange(fieldPath("year")).gtLt(1980, 1989), + SearchOperator.wildcard(fieldPath("title"), "Rocky *") + ) + ) + ) + + val projection = Projections.fields( + Projections.include("title", "year", "genres", "cast") + ) + + val aggregatePipelineStages: List = listOf(searchStage, Aggregates.project(projection)) + val results = collection.aggregate(aggregatePipelineStages) + + results.collect { println(it) } + } + // end atlasHelperMethods + + mongoClient.close() +} + diff --git a/source/fundamentals/aggregation.txt b/source/fundamentals/aggregation.txt index 554fb5e1..a6990fdc 100644 --- a/source/fundamentals/aggregation.txt +++ b/source/fundamentals/aggregation.txt @@ -231,24 +231,24 @@ manual. .. replacement:: atlas-query-operators-example - .. code-block:: kotlin - - val searchStage: Bson = Aggregates.search( - SearchOperator.compound() - .filter( - listOf( - SearchOperator.text(fieldPath("genres"), "Drama"), - SearchOperator.phrase(fieldPath("cast"), "sylvester stallone"), - SearchOperator.numberRange(fieldPath("year")).gtLt(1980, 1989), - SearchOperator.wildcard(fieldPath("title"), "Rocky *") - ) - ) - ) + .. io-code-block:: + + .. input:: /examples/atlas-examples/AtlasSearchHelpers.kt + :language: kotlin + :start-after: // start atlasHelperMethods + :end-before: // end atlasHelperMethods + :dedent: + + .. output:: + :language: console + :visible: false + + Document{{_id=573a1397f29313caabce86db, genres=[Drama, Sport], cast=[Sylvester Stallone, Talia Shire, Burt Young, Carl Weathers], title=Rocky III, year=1982}} + Document{{_id=573a1398f29313caabce9af0, genres=[Drama, Sport], cast=[Sylvester Stallone, Talia Shire, Burt Young, Carl Weathers], title=Rocky IV, year=1985}} .. replacement:: searchoperator-interface-api-docs - the `SearchOperator Interface API documentation - <{+core-api+}/client/model/search/SearchOperator.html>`__ + the `SearchOperator Interface API documentation <{+core-api+}/client/model/search/SearchOperator.html>`__ API Documentation ----------------- From 6e8b7ec6e50a2d785f7851f751b2af68fc4a131a Mon Sep 17 00:00:00 2001 From: Rachel Mackintosh Date: Sat, 29 Mar 2025 01:24:13 -0400 Subject: [PATCH 04/10] ref link --- source/whats-new.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/whats-new.txt b/source/whats-new.txt index 99dbc01e..455c339b 100644 --- a/source/whats-new.txt +++ b/source/whats-new.txt @@ -52,7 +52,7 @@ and features: .. replacement:: atlas-query-operators the :ref:`Pipelines Stages for Atlas Search - ` section + ` section of the Aggregation page .. _kotlin-coroutine-version-5.3: From e99726eb585fd764639d611eaa32bc02e1f470ea Mon Sep 17 00:00:00 2001 From: rustagir Date: Tue, 1 Apr 2025 14:53:43 -0400 Subject: [PATCH 05/10] WIP --- examples/src/test/kotlin/AggregationTest.kt | 157 ++++++++++++++++-- ...ationTest.snippet.atlas-search-pipeline.kt | 19 +++ ...gregationTest.snippet.basic-aggregation.kt | 6 +- ...egationTest.snippet.build-documents-tip.kt | 4 +- ...egationTest.snippet.explain-aggregation.kt | 2 +- source/fundamentals/aggregation.txt | 26 +-- 6 files changed, 180 insertions(+), 34 deletions(-) create mode 100644 source/examples/generated/AggregationTest.snippet.atlas-search-pipeline.kt diff --git a/examples/src/test/kotlin/AggregationTest.kt b/examples/src/test/kotlin/AggregationTest.kt index 3b7a54d9..3da7876e 100644 --- a/examples/src/test/kotlin/AggregationTest.kt +++ b/examples/src/test/kotlin/AggregationTest.kt @@ -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 { @@ -44,16 +48,102 @@ class AggregationTest { fun beforeAll() { runBlocking { val restaurants = listOf( - Restaurant("Sun Bakery Trattoria", Restaurant.Contact("386-555-0189", "SunBakeryTrattoria@example.org", listOf(-74.0056649, 40.7452371)), 4, listOf("Pizza", "Pasta", "Italian", "Coffee", "Sandwiches")), - Restaurant("Blue Bagels Grill", Restaurant.Contact("786-555-0102", "BlueBagelsGrill@example.com", listOf(-73.92506, 40.8275556)), 3, listOf("Bagels", "Cookies", "Sandwiches")), - Restaurant("XYZ Bagels Restaurant", Restaurant.Contact("435-555-0190", "XYZBagelsRestaurant@example.net", listOf(-74.0707363, 40.59321569999999)), 4, listOf("Bagels", "Sandwiches", "Coffee")), - Restaurant("Hot Bakery Cafe", Restaurant.Contact("264-555-0171", "HotBakeryCafe@example.net", listOf(-73.96485799999999, 40.761899)), 4, listOf("Bakery", "Cafe", "Coffee", "Dessert")), - Restaurant("Green Feast Pizzeria", Restaurant.Contact("840-555-0102", "GreenFeastPizzeria@example.com", listOf(-74.1220973, 40.6129407)), 2, listOf("Pizza", "Italian")), - Restaurant("ZZZ Pasta Buffet", Restaurant.Contact("769-555-0152", "ZZZPastaBuffet@example.com", listOf(-73.9446421, 40.7253944)), 0, listOf("Pasta", "Italian", "Buffet", "Cafeteria")), - Restaurant("XYZ Coffee Bar", Restaurant.Contact("644-555-0193", "XYZCoffeeBar@example.net", listOf(-74.0166091, 40.6284767)), 5, listOf("Coffee", "Cafe", "Bakery", "Chocolates")), - Restaurant("456 Steak Restaurant", Restaurant.Contact("990-555-0165", "456SteakRestaurant@example.com", listOf(-73.9365108, 40.8497077)), 0, listOf("Steak", "Seafood")), - Restaurant("456 Cookies Shop", Restaurant.Contact("604-555-0149", "456CookiesShop@example.org", listOf(-73.8850023, 40.7494272)), 4, listOf("Bakery", "Cookies", "Cake", "Coffee")), - Restaurant("XYZ Steak Buffet", Restaurant.Contact("229-555-0197", "XYZSteakBuffet@example.org", listOf(-73.9799932, 40.7660886)), 3, listOf("Steak", "Salad", "Chinese")) + Restaurant( + "Sun Bakery Trattoria", + Restaurant.Contact( + "386-555-0189", + "SunBakeryTrattoria@example.org", + listOf(-74.0056649, 40.7452371) + ), + 4, + listOf("Pizza", "Pasta", "Italian", "Coffee", "Sandwiches") + ), + Restaurant( + "Blue Bagels Grill", + Restaurant.Contact( + "786-555-0102", + "BlueBagelsGrill@example.com", + listOf(-73.92506, 40.8275556) + ), + 3, + listOf("Bagels", "Cookies", "Sandwiches") + ), + Restaurant( + "XYZ Bagels Restaurant", + Restaurant.Contact( + "435-555-0190", + "XYZBagelsRestaurant@example.net", + listOf(-74.0707363, 40.59321569999999) + ), + 4, + listOf("Bagels", "Sandwiches", "Coffee") + ), + Restaurant( + "Hot Bakery Cafe", + Restaurant.Contact( + "264-555-0171", + "HotBakeryCafe@example.net", + listOf(-73.96485799999999, 40.761899) + ), + 4, + listOf("Bakery", "Cafe", "Coffee", "Dessert") + ), + Restaurant( + "Green Feast Pizzeria", + Restaurant.Contact( + "840-555-0102", + "GreenFeastPizzeria@example.com", + listOf(-74.1220973, 40.6129407) + ), + 2, + listOf("Pizza", "Italian") + ), + Restaurant( + "ZZZ Pasta Buffet", + Restaurant.Contact( + "769-555-0152", + "ZZZPastaBuffet@example.com", + listOf(-73.9446421, 40.7253944) + ), + 0, + listOf("Pasta", "Italian", "Buffet", "Cafeteria") + ), + Restaurant( + "XYZ Coffee Bar", + Restaurant.Contact("644-555-0193", "XYZCoffeeBar@example.net", listOf(-74.0166091, 40.6284767)), + 5, + listOf("Coffee", "Cafe", "Bakery", "Chocolates") + ), + Restaurant( + "456 Steak Restaurant", + Restaurant.Contact( + "990-555-0165", + "456SteakRestaurant@example.com", + listOf(-73.9365108, 40.8497077) + ), + 0, + listOf("Steak", "Seafood") + ), + Restaurant( + "456 Cookies Shop", + Restaurant.Contact( + "604-555-0149", + "456CookiesShop@example.org", + listOf(-73.8850023, 40.7494272) + ), + 4, + listOf("Bakery", "Cookies", "Cake", "Coffee") + ), + Restaurant( + "XYZ Steak Buffet", + Restaurant.Contact( + "229-555-0197", + "XYZSteakBuffet@example.org", + listOf(-73.9799932, 40.7660886) + ), + 3, + listOf("Steak", "Salad", "Chinese") + ) ) collection.insertMany(restaurants) } @@ -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( 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) + ) ) ) @@ -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( listOf( @@ -143,14 +235,45 @@ 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) } + + /* NOTE: Test is not run by default. FTS requires the creation of a text index on the collection before running. + */ + @Ignore + fun atlasSearchOperatorTest() = runBlocking { + val collection = mongoClient.getDatabase("sample_mflix").getCollection("movies") + // :snippet-start: atlas-search-pipeline + val searchStage = Aggregates.search( + SearchOperator.compound() + .filter( + listOf( + SearchOperator.`in`(fieldPath("genres"), listOf("Comedy")), + SearchOperator.phrase(fieldPath("fullplot"), "new york"), + SearchOperator.numberRange(fieldPath("year")).gtLt(1950, 2000), + SearchOperator.wildcard(fieldPath("title"), "Love *") + ) + ) + ) + + val projectStage = Aggregates.project( + Projections.include("title", "year", "genres", "cast")) + + val pipeline = listOf(searchStage, projectStage) + val resultsFlow = collection.aggregate(pipeline) + + resultsFlow.collect { println(it) } + // :snippet-end: + + val result = resultsFlow.toList() + assertEquals(2, result.size) + } } diff --git a/source/examples/generated/AggregationTest.snippet.atlas-search-pipeline.kt b/source/examples/generated/AggregationTest.snippet.atlas-search-pipeline.kt new file mode 100644 index 00000000..2bfc23e6 --- /dev/null +++ b/source/examples/generated/AggregationTest.snippet.atlas-search-pipeline.kt @@ -0,0 +1,19 @@ +val searchStage = Aggregates.search( + SearchOperator.compound() + .filter( + listOf( + SearchOperator.`in`(fieldPath("genres"), listOf("Comedy")), + SearchOperator.phrase(fieldPath("fullplot"), "new york"), + SearchOperator.numberRange(fieldPath("year")).gtLt(1950, 2000), + SearchOperator.wildcard(fieldPath("title"), "Love *") + ) + ) +) + +val projectStage = Aggregates.project( + Projections.include("title", "year", "genres", "cast")) + +val pipeline = listOf(searchStage, projectStage) +val resultsFlow = collection.aggregate(pipeline) + +resultsFlow.collect { println(it) } diff --git a/source/examples/generated/AggregationTest.snippet.basic-aggregation.kt b/source/examples/generated/AggregationTest.snippet.basic-aggregation.kt index d3b7a52f..51f726de 100644 --- a/source/examples/generated/AggregationTest.snippet.basic-aggregation.kt +++ b/source/examples/generated/AggregationTest.snippet.basic-aggregation.kt @@ -3,8 +3,10 @@ data class Results(@BsonId val id: Int, val count: Int) val resultsFlow = collection.aggregate( 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) + ) ) ) diff --git a/source/examples/generated/AggregationTest.snippet.build-documents-tip.kt b/source/examples/generated/AggregationTest.snippet.build-documents-tip.kt index bb27fa8a..21c72e26 100644 --- a/source/examples/generated/AggregationTest.snippet.build-documents-tip.kt +++ b/source/examples/generated/AggregationTest.snippet.build-documents-tip.kt @@ -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] }") diff --git a/source/examples/generated/AggregationTest.snippet.explain-aggregation.kt b/source/examples/generated/AggregationTest.snippet.explain-aggregation.kt index 7efe45ee..6aaebceb 100644 --- a/source/examples/generated/AggregationTest.snippet.explain-aggregation.kt +++ b/source/examples/generated/AggregationTest.snippet.explain-aggregation.kt @@ -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( listOf( diff --git a/source/fundamentals/aggregation.txt b/source/fundamentals/aggregation.txt index a6990fdc..ea5a9f2e 100644 --- a/source/fundamentals/aggregation.txt +++ b/source/fundamentals/aggregation.txt @@ -225,30 +225,32 @@ Search provides ``$search`` and ``$searchMeta`` stages, both of which must be th stage in any query pipeline. For more information about Atlas pipeline stages, see the :atlas:`Choose the Aggregation Pipeline Stage ` page in the Atlas -manual. +manual. + +.. tip:: Aggregates Builders + + This example in this section uses helper methods from the + :ref:`Aggregates ` builder class. .. 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/atlas-examples/AtlasSearchHelpers.kt + .. input:: /examples/generated/AggregationTest.snippet.atlas-search-pipeline.kt :language: kotlin - :start-after: // start atlasHelperMethods - :end-before: // end atlasHelperMethods - :dedent: .. output:: :language: console :visible: false - Document{{_id=573a1397f29313caabce86db, genres=[Drama, Sport], cast=[Sylvester Stallone, Talia Shire, Burt Young, Carl Weathers], title=Rocky III, year=1982}} - Document{{_id=573a1398f29313caabce9af0, genres=[Drama, Sport], cast=[Sylvester Stallone, Talia Shire, Burt Young, Carl Weathers], title=Rocky IV, year=1985}} - - .. replacement:: searchoperator-interface-api-docs - - the `SearchOperator Interface API documentation <{+core-api+}/client/model/search/SearchOperator.html>`__ + Document{{_id=573a1397f29313caabce734c, genres=[Comedy, Romance], cast=[George Hamilton, Susan Saint James, Richard Benjamin, Dick Shawn], title=Love at First Bite, year=1979}} + Document{{_id=573a1399f29313caabcee81e, genres=[Comedy, Drama], cast=[Warren Beatty, Annette Bening, Katharine Hepburn, Garry Shandling], title=Love Affair, year=1994}} API Documentation ----------------- @@ -260,4 +262,4 @@ see the following API Documentation: - `$group <{+core-api+}/client/model/Aggregates.html#group(TExpression,java.util.List)>`__ - `$project <{+core-api+}/client/model/Aggregates.html#project(org.bson.conversions.Bson)>`__ - `Projections <{+core-api+}/client/model/Projections.html>`__ -- `SearchOperator Interface <{+core-api+}/com/mongodb/client/model/search/SearchOperator.html>`__ +- `SearchOperator <{+core-api+}/com/mongodb/client/model/search/SearchOperator.html>`__ From 3c1c81460ee5f617e5dafad84c518bf323fdb598 Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:55:48 -0400 Subject: [PATCH 06/10] moved the file to examples dir --- .../atlas-examples/AtlasSearchHelpers.kt | 54 ------------------- 1 file changed, 54 deletions(-) delete mode 100644 source/examples/atlas-examples/AtlasSearchHelpers.kt diff --git a/source/examples/atlas-examples/AtlasSearchHelpers.kt b/source/examples/atlas-examples/AtlasSearchHelpers.kt deleted file mode 100644 index acb30382..00000000 --- a/source/examples/atlas-examples/AtlasSearchHelpers.kt +++ /dev/null @@ -1,54 +0,0 @@ -import com.mongodb.client.model.Aggregates -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 kotlinx.coroutines.runBlocking -import org.bson.Document -import org.bson.conversions.Bson -import java.io.IO.println -import kotlin.collections.List - -const val URI = "" - -// Create data class to represent a MongoDB document -data class Movie(val title: String, val year: Int, val cast: List) - -fun main() { - - // Replace the placeholder with your MongoDB deployment's connection string - val uri = URI - - val mongoClient = MongoClient.create(uri) - val database = mongoClient.getDatabase("sample_mflix") - // Get a collection of documents of type Movie - val collection = database.getCollection("movies") - - // start atlasHelperMethods - runBlocking { - val searchStage: Bson = Aggregates.search( - SearchOperator.compound() - .filter( - listOf( - SearchOperator.text(fieldPath("genres"), "Drama"), - SearchOperator.phrase(fieldPath("cast"), "sylvester stallone"), - SearchOperator.numberRange(fieldPath("year")).gtLt(1980, 1989), - SearchOperator.wildcard(fieldPath("title"), "Rocky *") - ) - ) - ) - - val projection = Projections.fields( - Projections.include("title", "year", "genres", "cast") - ) - - val aggregatePipelineStages: List = listOf(searchStage, Aggregates.project(projection)) - val results = collection.aggregate(aggregatePipelineStages) - - results.collect { println(it) } - } - // end atlasHelperMethods - - mongoClient.close() -} - From 4896615ceb7fcb93b8d9074bb55d9502c6b07ac3 Mon Sep 17 00:00:00 2001 From: rustagir Date: Tue, 1 Apr 2025 15:20:46 -0400 Subject: [PATCH 07/10] WIP --- .../src/test/kotlin/AggregatesBuilderTest.kt | 32 ++++++++++++++++ examples/src/test/kotlin/AggregationTest.kt | 31 --------------- ...ilderTest.snippet.atlas-search-pipeline.kt | 19 ++++++++++ ...tesBuilderTest.snippet.movie-data-class.kt | 1 + ...ationTest.snippet.atlas-search-pipeline.kt | 19 ---------- source/fundamentals/aggregation.txt | 38 ------------------- source/fundamentals/builders/aggregates.txt | 25 ++++++++++++ 7 files changed, 77 insertions(+), 88 deletions(-) create mode 100644 source/examples/generated/AggregatesBuilderTest.snippet.atlas-search-pipeline.kt delete mode 100644 source/examples/generated/AggregationTest.snippet.atlas-search-pipeline.kt diff --git a/examples/src/test/kotlin/AggregatesBuilderTest.kt b/examples/src/test/kotlin/AggregatesBuilderTest.kt index b07258b2..8c238676 100644 --- a/examples/src/test/kotlin/AggregatesBuilderTest.kt +++ b/examples/src/test/kotlin/AggregatesBuilderTest.kt @@ -56,6 +56,7 @@ class AggregatesBuilderTest { val genres: List, val rated: String, val plot: String, + val fullplot: String, val runtime: Int, val imdb: IMDB ){ @@ -951,6 +952,37 @@ 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 + 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("title", "year", "genres", "cast")) + + val pipeline = listOf(searchStage, projectStage) + val resultsFlow = ftsCollection.aggregate(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). */ diff --git a/examples/src/test/kotlin/AggregationTest.kt b/examples/src/test/kotlin/AggregationTest.kt index 3da7876e..7d366b26 100644 --- a/examples/src/test/kotlin/AggregationTest.kt +++ b/examples/src/test/kotlin/AggregationTest.kt @@ -245,35 +245,4 @@ class AggregationTest { assertEquals(method1, method2) } - - /* NOTE: Test is not run by default. FTS requires the creation of a text index on the collection before running. - */ - @Ignore - fun atlasSearchOperatorTest() = runBlocking { - val collection = mongoClient.getDatabase("sample_mflix").getCollection("movies") - // :snippet-start: atlas-search-pipeline - val searchStage = Aggregates.search( - SearchOperator.compound() - .filter( - listOf( - SearchOperator.`in`(fieldPath("genres"), listOf("Comedy")), - SearchOperator.phrase(fieldPath("fullplot"), "new york"), - SearchOperator.numberRange(fieldPath("year")).gtLt(1950, 2000), - SearchOperator.wildcard(fieldPath("title"), "Love *") - ) - ) - ) - - val projectStage = Aggregates.project( - Projections.include("title", "year", "genres", "cast")) - - val pipeline = listOf(searchStage, projectStage) - val resultsFlow = collection.aggregate(pipeline) - - resultsFlow.collect { println(it) } - // :snippet-end: - - val result = resultsFlow.toList() - assertEquals(2, result.size) - } } diff --git a/source/examples/generated/AggregatesBuilderTest.snippet.atlas-search-pipeline.kt b/source/examples/generated/AggregatesBuilderTest.snippet.atlas-search-pipeline.kt new file mode 100644 index 00000000..c9c604dc --- /dev/null +++ b/source/examples/generated/AggregatesBuilderTest.snippet.atlas-search-pipeline.kt @@ -0,0 +1,19 @@ +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("title", "year", "genres", "cast")) + +val pipeline = listOf(searchStage, projectStage) +val resultsFlow = ftsCollection.aggregate(pipeline) + +resultsFlow.collect { println(it) } diff --git a/source/examples/generated/AggregatesBuilderTest.snippet.movie-data-class.kt b/source/examples/generated/AggregatesBuilderTest.snippet.movie-data-class.kt index dc131d0e..55d73f52 100644 --- a/source/examples/generated/AggregatesBuilderTest.snippet.movie-data-class.kt +++ b/source/examples/generated/AggregatesBuilderTest.snippet.movie-data-class.kt @@ -4,6 +4,7 @@ data class Movie( val genres: List, val rated: String, val plot: String, + val fullplot: String, val runtime: Int, val imdb: IMDB ){ diff --git a/source/examples/generated/AggregationTest.snippet.atlas-search-pipeline.kt b/source/examples/generated/AggregationTest.snippet.atlas-search-pipeline.kt deleted file mode 100644 index 2bfc23e6..00000000 --- a/source/examples/generated/AggregationTest.snippet.atlas-search-pipeline.kt +++ /dev/null @@ -1,19 +0,0 @@ -val searchStage = Aggregates.search( - SearchOperator.compound() - .filter( - listOf( - SearchOperator.`in`(fieldPath("genres"), listOf("Comedy")), - SearchOperator.phrase(fieldPath("fullplot"), "new york"), - SearchOperator.numberRange(fieldPath("year")).gtLt(1950, 2000), - SearchOperator.wildcard(fieldPath("title"), "Love *") - ) - ) -) - -val projectStage = Aggregates.project( - Projections.include("title", "year", "genres", "cast")) - -val pipeline = listOf(searchStage, projectStage) -val resultsFlow = collection.aggregate(pipeline) - -resultsFlow.collect { println(it) } diff --git a/source/fundamentals/aggregation.txt b/source/fundamentals/aggregation.txt index ea5a9f2e..d3913e9a 100644 --- a/source/fundamentals/aggregation.txt +++ b/source/fundamentals/aggregation.txt @@ -215,43 +215,6 @@ first element in the ``categories`` field. Results(name=456 Cookies Shop, firstCategory=Bakery) Results(name=XYZ Steak Buffet, firstCategory=Steak) -.. _kotlin-cr-atlas-search-stage: - -Pipelines Stages for Atlas Search ---------------------------------- - -:atlas:`Atlas Search ` queries take the form of an aggregation pipeline stage. Atlas -Search provides ``$search`` and ``$searchMeta`` stages, both of which must be the first -stage in any query pipeline. For more information about Atlas pipeline stages, -see the :atlas:`Choose the Aggregation Pipeline Stage -` page in the Atlas -manual. - -.. tip:: Aggregates Builders - - This example in this section uses helper methods from the - :ref:`Aggregates ` builder class. - -.. 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/AggregationTest.snippet.atlas-search-pipeline.kt - :language: kotlin - - .. output:: - :language: console - :visible: false - - Document{{_id=573a1397f29313caabce734c, genres=[Comedy, Romance], cast=[George Hamilton, Susan Saint James, Richard Benjamin, Dick Shawn], title=Love at First Bite, year=1979}} - Document{{_id=573a1399f29313caabcee81e, genres=[Comedy, Drama], cast=[Warren Beatty, Annette Bening, Katharine Hepburn, Garry Shandling], title=Love Affair, year=1994}} - API Documentation ----------------- @@ -262,4 +225,3 @@ see the following API Documentation: - `$group <{+core-api+}/client/model/Aggregates.html#group(TExpression,java.util.List)>`__ - `$project <{+core-api+}/client/model/Aggregates.html#project(org.bson.conversions.Bson)>`__ - `Projections <{+core-api+}/client/model/Projections.html>`__ -- `SearchOperator <{+core-api+}/com/mongodb/client/model/search/SearchOperator.html>`__ diff --git a/source/fundamentals/builders/aggregates.txt b/source/fundamentals/builders/aggregates.txt index ca5d8913..416f741e 100644 --- a/source/fundamentals/builders/aggregates.txt +++ b/source/fundamentals/builders/aggregates.txt @@ -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 + + Document{{_id=573a1397f29313caabce734c, genres=[Comedy, Romance], cast=[George Hamilton, Susan Saint James, Richard Benjamin, Dick Shawn], title=Love at First Bite, year=1979}} + Document{{_id=573a1399f29313caabcee81e, genres=[Comedy, Drama], cast=[Warren Beatty, Annette Bening, Katharine Hepburn, Garry Shandling], title=Love Affair, year=1994}} + Atlas Search Metadata --------------------- From c9ffbd1dadc3b3d1c6f759d6b12df1794fe5af15 Mon Sep 17 00:00:00 2001 From: rustagir Date: Tue, 1 Apr 2025 15:22:19 -0400 Subject: [PATCH 08/10] WIP --- source/whats-new.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/whats-new.txt b/source/whats-new.txt index a1aa6127..12874633 100644 --- a/source/whats-new.txt +++ b/source/whats-new.txt @@ -55,8 +55,8 @@ and features: .. replacement:: atlas-query-operators - the :ref:`Pipelines Stages for Atlas Search - ` section of the Aggregation page + the :ref:`kotlin-cr-atlas-search-stage` section of the Aggregates + Builders guide .. _kotlin-coroutine-version-5.3: From aaef8f3f7d761733cb8217bb2edcba83de867c6a Mon Sep 17 00:00:00 2001 From: rustagir Date: Tue, 1 Apr 2025 15:27:23 -0400 Subject: [PATCH 09/10] WIP --- examples/src/test/kotlin/AggregatesBuilderTest.kt | 4 ++-- .../AggregatesBuilderTest.snippet.movie-data-class.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/src/test/kotlin/AggregatesBuilderTest.kt b/examples/src/test/kotlin/AggregatesBuilderTest.kt index 8c238676..412447fe 100644 --- a/examples/src/test/kotlin/AggregatesBuilderTest.kt +++ b/examples/src/test/kotlin/AggregatesBuilderTest.kt @@ -56,9 +56,9 @@ class AggregatesBuilderTest { val genres: List, val rated: String, val plot: String, - val fullplot: String, val runtime: Int, - val imdb: IMDB + val imdb: IMDB, + val fullplot: String? = "No full plot", ){ data class IMDB( val rating: Double diff --git a/source/examples/generated/AggregatesBuilderTest.snippet.movie-data-class.kt b/source/examples/generated/AggregatesBuilderTest.snippet.movie-data-class.kt index 55d73f52..7bfc0f44 100644 --- a/source/examples/generated/AggregatesBuilderTest.snippet.movie-data-class.kt +++ b/source/examples/generated/AggregatesBuilderTest.snippet.movie-data-class.kt @@ -4,9 +4,9 @@ data class Movie( val genres: List, val rated: String, val plot: String, - val fullplot: String, val runtime: Int, - val imdb: IMDB + val imdb: IMDB, + val fullplot: String? = "No full plot", ){ data class IMDB( val rating: Double From e7ce79d650f17867d5020ea4aa50a17abbfae095 Mon Sep 17 00:00:00 2001 From: rustagir Date: Tue, 1 Apr 2025 15:50:45 -0400 Subject: [PATCH 10/10] WIP --- examples/src/test/kotlin/AggregatesBuilderTest.kt | 7 +++++-- .../AggregatesBuilderTest.snippet.atlas-search-pipeline.kt | 6 ++++-- source/fundamentals/builders/aggregates.txt | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/examples/src/test/kotlin/AggregatesBuilderTest.kt b/examples/src/test/kotlin/AggregatesBuilderTest.kt index 412447fe..94cabb6f 100644 --- a/examples/src/test/kotlin/AggregatesBuilderTest.kt +++ b/examples/src/test/kotlin/AggregatesBuilderTest.kt @@ -957,7 +957,10 @@ class AggregatesBuilderTest { */ @Ignore fun atlasSearchOperatorTest() = runBlocking { + // :snippet-start: atlas-search-pipeline + data class Results(val title: String, val year: Int, val genres: List) + val searchStage = Aggregates.search( SearchOperator.compound() .filter( @@ -971,10 +974,10 @@ class AggregatesBuilderTest { ) val projectStage = Aggregates.project( - Projections.include("title", "year", "genres", "cast")) + Projections.include(Movie::title.name, Movie::year.name, Movie::genres.name)) val pipeline = listOf(searchStage, projectStage) - val resultsFlow = ftsCollection.aggregate(pipeline) + val resultsFlow = ftsCollection.aggregate(pipeline) resultsFlow.collect { println(it) } // :snippet-end: diff --git a/source/examples/generated/AggregatesBuilderTest.snippet.atlas-search-pipeline.kt b/source/examples/generated/AggregatesBuilderTest.snippet.atlas-search-pipeline.kt index c9c604dc..9a9352f2 100644 --- a/source/examples/generated/AggregatesBuilderTest.snippet.atlas-search-pipeline.kt +++ b/source/examples/generated/AggregatesBuilderTest.snippet.atlas-search-pipeline.kt @@ -1,3 +1,5 @@ +data class Results(val title: String, val year: Int, val genres: List) + val searchStage = Aggregates.search( SearchOperator.compound() .filter( @@ -11,9 +13,9 @@ val searchStage = Aggregates.search( ) val projectStage = Aggregates.project( - Projections.include("title", "year", "genres", "cast")) + Projections.include(Movie::title.name, Movie::year.name, Movie::genres.name)) val pipeline = listOf(searchStage, projectStage) -val resultsFlow = ftsCollection.aggregate(pipeline) +val resultsFlow = ftsCollection.aggregate(pipeline) resultsFlow.collect { println(it) } diff --git a/source/fundamentals/builders/aggregates.txt b/source/fundamentals/builders/aggregates.txt index 416f741e..1413b0bf 100644 --- a/source/fundamentals/builders/aggregates.txt +++ b/source/fundamentals/builders/aggregates.txt @@ -909,8 +909,8 @@ Search Operator Methods :language: console :visible: false - Document{{_id=573a1397f29313caabce734c, genres=[Comedy, Romance], cast=[George Hamilton, Susan Saint James, Richard Benjamin, Dick Shawn], title=Love at First Bite, year=1979}} - Document{{_id=573a1399f29313caabcee81e, genres=[Comedy, Drama], cast=[Warren Beatty, Annette Bening, Katharine Hepburn, Garry Shandling], title=Love Affair, year=1994}} + Results(title=Love at First Bite, year=1979, genres=[Comedy, Romance]) + Results(title=Love Affair, year=1994, genres=[Comedy, Drama]) Atlas Search Metadata ---------------------