diff --git a/persistence-modules/spring-boot-persistence-mongodb-3/pom.xml b/persistence-modules/spring-boot-persistence-mongodb-3/pom.xml
index c48525673a..3f144bae54 100644
--- a/persistence-modules/spring-boot-persistence-mongodb-3/pom.xml
+++ b/persistence-modules/spring-boot-persistence-mongodb-3/pom.xml
@@ -9,27 +9,12 @@
com.baeldung
- parent-boot-2
+ parent-boot-3
0.0.1-SNAPSHOT
- ../../parent-boot-2
+ ../../parent-boot-3
-
- org.mongodb
- mongodb-driver-sync
- ${mongodb-driver.version}
-
-
- org.mongodb
- mongodb-driver-core
- ${mongodb-driver.version}
-
-
- org.mongodb
- bson
- ${mongodb-driver.version}
-
org.springframework.boot
spring-boot-starter-web
@@ -37,16 +22,6 @@
org.springframework.boot
spring-boot-starter-data-mongodb
-
-
- org.mongodb
- mongodb-driver-sync
-
-
- org.mongodb
- mongodb-driver-core
-
-
org.mongodb
@@ -57,12 +32,13 @@
de.flapdoodle.embed
de.flapdoodle.embed.mongo
test
+ ${de.flapdoodle.embed.mongo.version}
- 1.7.3
- 4.9.1
+ com.baeldung.boot.atlassearch.MongoDbAtlasSearchApplication
+ 1.8.0
+ 4.9.2
-
diff --git a/persistence-modules/spring-boot-persistence-mongodb-3/src/main/java/com/baeldung/boot/atlassearch/MongoDbAtlasSearchApplication.java b/persistence-modules/spring-boot-persistence-mongodb-3/src/main/java/com/baeldung/boot/atlassearch/MongoDbAtlasSearchApplication.java
new file mode 100644
index 0000000000..afaf524829
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-mongodb-3/src/main/java/com/baeldung/boot/atlassearch/MongoDbAtlasSearchApplication.java
@@ -0,0 +1,12 @@
+package com.baeldung.boot.atlassearch;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class MongoDbAtlasSearchApplication {
+
+ public static void main(String... args) {
+ SpringApplication.run(MongoDbAtlasSearchApplication.class, args);
+ }
+}
diff --git a/persistence-modules/spring-boot-persistence-mongodb-3/src/main/java/com/baeldung/boot/atlassearch/config/IndexConfig.java b/persistence-modules/spring-boot-persistence-mongodb-3/src/main/java/com/baeldung/boot/atlassearch/config/IndexConfig.java
new file mode 100644
index 0000000000..cb4fae89e4
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-mongodb-3/src/main/java/com/baeldung/boot/atlassearch/config/IndexConfig.java
@@ -0,0 +1,22 @@
+package com.baeldung.boot.atlassearch.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class IndexConfig {
+
+ @Value("${com.baeldung.atlas-search.index.query}")
+ private String queryIndex;
+
+ @Value("${com.baeldung.atlas-search.index.facet}")
+ private String facetIndex;
+
+ public String getFacetIndex() {
+ return facetIndex;
+ }
+
+ public String getQueryIndex() {
+ return queryIndex;
+ }
+}
diff --git a/persistence-modules/spring-boot-persistence-mongodb-3/src/main/java/com/baeldung/boot/atlassearch/service/MovieAtlasSearchService.java b/persistence-modules/spring-boot-persistence-mongodb-3/src/main/java/com/baeldung/boot/atlassearch/service/MovieAtlasSearchService.java
new file mode 100644
index 0000000000..55d47759d5
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-mongodb-3/src/main/java/com/baeldung/boot/atlassearch/service/MovieAtlasSearchService.java
@@ -0,0 +1,183 @@
+package com.baeldung.boot.atlassearch.service;
+
+import static com.mongodb.client.model.Aggregates.facet;
+import static com.mongodb.client.model.Aggregates.limit;
+import static com.mongodb.client.model.Aggregates.project;
+import static com.mongodb.client.model.Aggregates.replaceWith;
+import static com.mongodb.client.model.Aggregates.search;
+import static com.mongodb.client.model.Aggregates.searchMeta;
+import static com.mongodb.client.model.Aggregates.skip;
+import static com.mongodb.client.model.Projections.excludeId;
+import static com.mongodb.client.model.Projections.fields;
+import static com.mongodb.client.model.Projections.include;
+import static com.mongodb.client.model.Projections.metaSearchScore;
+import static com.mongodb.client.model.search.SearchCount.total;
+import static com.mongodb.client.model.search.SearchFacet.combineToBson;
+import static com.mongodb.client.model.search.SearchFacet.numberFacet;
+import static com.mongodb.client.model.search.SearchFacet.stringFacet;
+import static com.mongodb.client.model.search.SearchOperator.compound;
+import static com.mongodb.client.model.search.SearchOperator.numberRange;
+import static com.mongodb.client.model.search.SearchOperator.of;
+import static com.mongodb.client.model.search.SearchOperator.text;
+import static com.mongodb.client.model.search.SearchOptions.searchOptions;
+import static com.mongodb.client.model.search.SearchPath.fieldPath;
+import static java.util.Arrays.asList;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.logging.log4j.LogManager;
+import org.bson.Document;
+import org.bson.conversions.Bson;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.stereotype.Service;
+
+import com.baeldung.boot.atlassearch.config.IndexConfig;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+import com.mongodb.client.model.Facet;
+import com.mongodb.client.model.search.SearchScore;
+
+@Service
+public class MovieAtlasSearchService {
+
+ @Autowired
+ private IndexConfig config;
+
+ private final MongoCollection collection;
+
+ public MovieAtlasSearchService(MongoTemplate mongoTemplate) {
+ MongoDatabase database = mongoTemplate.getDb();
+ this.collection = database.getCollection("movies");
+ }
+
+ public static void debug(List pipeline) {
+ StringBuilder builder = new StringBuilder("[");
+ final AtomicBoolean first = new AtomicBoolean(true);
+ pipeline.forEach(stage -> {
+ builder.append((first.get() ? "" : ",")
+ + stage.toBsonDocument()
+ );
+
+ first.set(false);
+ });
+ builder.append("]");
+
+ LogManager.getLogger(MovieAtlasSearchService.class)
+ .debug(builder.toString());
+ }
+
+ public Document late90sMovies(int skip, int limit, String keywords, SearchScore modifier) {
+ List pipeline = asList(
+ search(
+ compound()
+ .must(asList(
+ numberRange(
+ fieldPath("year"))
+ .gteLt(1995, 2000)
+ ))
+ .should(asList(
+ text(
+ fieldPath("fullplot"), keywords
+ )
+ .score(modifier)
+ )),
+ searchOptions()
+ .index(config.getQueryIndex())
+ ),
+ project(fields(
+ excludeId(),
+ include("title", "year", "fullplot", "imdb.rating"),
+ metaSearchScore("score")
+ )),
+ facet(
+ new Facet("rows",
+ skip(skip),
+ limit(limit)
+ ),
+ new Facet("totalRows",
+ replaceWith("$$SEARCH_META"),
+ limit(1)
+ )
+ )
+ );
+
+ debug(pipeline);
+ return collection.aggregate(pipeline)
+ .first();
+ }
+
+ public Document countLate90sMovies(String keywords) {
+ List pipeline = asList(
+ searchMeta(
+ compound()
+ .must(asList(
+ numberRange(
+ fieldPath("year"))
+ .gteLt(1995, 2000),
+ text(
+ fieldPath("fullplot"), keywords
+ )
+ )),
+ searchOptions()
+ .index(config.getQueryIndex())
+ .count(total())
+ )
+ );
+
+ debug(pipeline);
+ return collection.aggregate(pipeline)
+ .first();
+ }
+
+ public Collection moviesByKeywords(String keywords) {
+ List pipeline = asList(
+ search(
+ text(
+ fieldPath("fullplot"), keywords
+ ),
+ searchOptions()
+ .index(config.getQueryIndex())
+ ),
+ project(fields(
+ excludeId(),
+ include("title", "year", "fullplot", "imdb.rating")
+ ))
+ );
+
+ debug(pipeline);
+ return collection.aggregate(pipeline)
+ .into(new ArrayList());
+ }
+
+ public Document genresThroughTheDecades(String genre) {
+ List pipeline = asList(
+ searchMeta(of(
+ new Document("facet",
+ new Document("operator",
+ text(
+ fieldPath("genres"), genre
+ )
+ ).append("facets", combineToBson(asList(
+ stringFacet("genresFacet",
+ fieldPath("genres")
+ ).numBuckets(5),
+ numberFacet("yearFacet",
+ fieldPath("year"),
+ asList(1900, 1930, 1960, 1990, 2020)
+ )
+ )))
+ )),
+ searchOptions()
+ .index(config.getFacetIndex())
+ )
+ );
+
+ debug(pipeline);
+ return collection.aggregate(pipeline)
+ .first();
+ }
+}
diff --git a/persistence-modules/spring-boot-persistence-mongodb-3/src/main/java/com/baeldung/boot/atlassearch/web/MovieAtlasSearchController.java b/persistence-modules/spring-boot-persistence-mongodb-3/src/main/java/com/baeldung/boot/atlassearch/web/MovieAtlasSearchController.java
new file mode 100644
index 0000000000..4c41915347
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-mongodb-3/src/main/java/com/baeldung/boot/atlassearch/web/MovieAtlasSearchController.java
@@ -0,0 +1,48 @@
+package com.baeldung.boot.atlassearch.web;
+
+import java.util.Collection;
+
+import org.bson.Document;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.boot.atlassearch.service.MovieAtlasSearchService;
+import com.mongodb.client.model.search.SearchScore;
+
+@RestController
+@RequestMapping("/movies")
+public class MovieAtlasSearchController {
+
+ @Autowired
+ private MovieAtlasSearchService service;
+
+ @GetMapping("with/{keywords}")
+ Collection getMoviesWithKeywords(@PathVariable String keywords) {
+ return service.moviesByKeywords(keywords);
+ }
+
+ @GetMapping("90s/with/{keywords}/count")
+ Document getCount90sMoviesWithKeywords(@PathVariable String keywords) {
+ return service.countLate90sMovies(keywords);
+ }
+
+ @GetMapping("90s/{skip}/{limit}/with/{keywords}")
+ Document getMoviesUsingScoreBoost(@PathVariable int skip, @PathVariable int limit, @PathVariable String keywords) {
+ return service.late90sMovies(skip, limit, keywords, SearchScore.boost(2));
+ }
+
+ @PostMapping("90s/{skip}/{limit}/with/{keywords}")
+ Document getMoviesUsingScoringFunction(@RequestBody String jsonFunction, @PathVariable int skip, @PathVariable int limit, @PathVariable String keywords) {
+ return service.late90sMovies(skip, limit, keywords, SearchScore.of(new Document("function", Document.parse(jsonFunction))));
+ }
+
+ @GetMapping("by-genre/{genre}")
+ Document getMoviesWithFacets(@PathVariable String genre) {
+ return service.genresThroughTheDecades(genre);
+ }
+}
diff --git a/persistence-modules/spring-boot-persistence-mongodb-3/src/main/resources/application.properties b/persistence-modules/spring-boot-persistence-mongodb-3/src/main/resources/application.properties
index 8309c4461f..02ba79e336 100644
--- a/persistence-modules/spring-boot-persistence-mongodb-3/src/main/resources/application.properties
+++ b/persistence-modules/spring-boot-persistence-mongodb-3/src/main/resources/application.properties
@@ -1 +1,4 @@
spring.application.name=spring-boot-persistence-mongodb-3
+
+com.baeldung.atlas-search.index.query=idx-queries
+com.baeldung.atlas-search.index.facet=idx-facets
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence-mongodb-3/src/test/java/com/baeldung/boot/atlassearch/MovieAtlasSearchServiceLiveTest.java b/persistence-modules/spring-boot-persistence-mongodb-3/src/test/java/com/baeldung/boot/atlassearch/MovieAtlasSearchServiceLiveTest.java
new file mode 100644
index 0000000000..190ebc19d9
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-mongodb-3/src/test/java/com/baeldung/boot/atlassearch/MovieAtlasSearchServiceLiveTest.java
@@ -0,0 +1,67 @@
+package com.baeldung.boot.atlassearch;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.bson.Document;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import com.baeldung.boot.atlassearch.service.MovieAtlasSearchService;
+import com.mongodb.client.model.search.SearchScore;
+
+@DirtiesContext
+@RunWith(SpringRunner.class)
+@TestPropertySource("/embedded.properties")
+@SpringBootTest(classes = MongoDbAtlasSearchApplication.class)
+public class MovieAtlasSearchServiceLiveTest {
+
+ @Autowired
+ private MovieAtlasSearchService service;
+
+ @Test
+ public void givenScoreBoost_thenFirstItemContainsPlot() {
+ final String plot = "space";
+
+ Document movies = service.late90sMovies(0, 1, plot, SearchScore.boost(2));
+
+ assertTrue(movies.getList("rows", Document.class)
+ .iterator()
+ .next()
+ .getString("fullplot")
+ .contains(plot));
+ }
+
+ @Test
+ public void givenFacetOperator_thenCorrespondingBucketsReturned() {
+ final String genre = "Sci-Fi";
+
+ Document meta = service.genresThroughTheDecades(genre);
+
+ Long lowerBound = meta
+ .get("count", Document.class)
+ .getLong("lowerBound");
+
+ Document genresFacetFirstBucket = meta.get("facet", Document.class)
+ .get("genresFacet", Document.class)
+ .getList("buckets", Document.class)
+ .iterator()
+ .next();
+
+ Document yearFacetFirstBucket = meta.get("facet", Document.class)
+ .get("yearFacet", Document.class)
+ .getList("buckets", Document.class)
+ .iterator()
+ .next();
+
+ assertEquals(lowerBound, genresFacetFirstBucket.getLong("count"));
+ assertEquals(genre, genresFacetFirstBucket.getString("_id"));
+ assertNotNull(yearFacetFirstBucket);
+ }
+}
diff --git a/persistence-modules/spring-boot-persistence-mongodb-3/src/test/resources/embedded.properties b/persistence-modules/spring-boot-persistence-mongodb-3/src/test/resources/embedded.properties
index cd1c1d43c7..f47eca5023 100644
--- a/persistence-modules/spring-boot-persistence-mongodb-3/src/test/resources/embedded.properties
+++ b/persistence-modules/spring-boot-persistence-mongodb-3/src/test/resources/embedded.properties
@@ -1,4 +1,4 @@
-spring.mongodb.embedded.version=4.4.9
+spring.mongodb.embedded.version=4.9.2
#spring.data.mongodb.uri=changeit
#spring.data.mongodb.database=changeit