BAEL 6701 - MongoDB - Atlas Search using the Java Driver and Spring Data (#14777)
* research 1 * new search methods * first draft * removing old code * updated to parent-boot-3
This commit is contained in:
parent
cafabdfe55
commit
a93751923d
@ -9,27 +9,12 @@
|
|||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-boot-2</artifactId>
|
<artifactId>parent-boot-3</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
<relativePath>../../parent-boot-2</relativePath>
|
<relativePath>../../parent-boot-3</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>org.mongodb</groupId>
|
|
||||||
<artifactId>mongodb-driver-sync</artifactId>
|
|
||||||
<version>${mongodb-driver.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mongodb</groupId>
|
|
||||||
<artifactId>mongodb-driver-core</artifactId>
|
|
||||||
<version>${mongodb-driver.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mongodb</groupId>
|
|
||||||
<artifactId>bson</artifactId>
|
|
||||||
<version>${mongodb-driver.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
@ -37,16 +22,6 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.mongodb</groupId>
|
|
||||||
<artifactId>mongodb-driver-sync</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.mongodb</groupId>
|
|
||||||
<artifactId>mongodb-driver-core</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mongodb</groupId>
|
<groupId>org.mongodb</groupId>
|
||||||
@ -57,12 +32,13 @@
|
|||||||
<groupId>de.flapdoodle.embed</groupId>
|
<groupId>de.flapdoodle.embed</groupId>
|
||||||
<artifactId>de.flapdoodle.embed.mongo</artifactId>
|
<artifactId>de.flapdoodle.embed.mongo</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
|
<version>${de.flapdoodle.embed.mongo.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<mongodb-crypt.version>1.7.3</mongodb-crypt.version>
|
<start-class>com.baeldung.boot.atlassearch.MongoDbAtlasSearchApplication</start-class>
|
||||||
<mongodb-driver.version>4.9.1</mongodb-driver.version>
|
<mongodb-crypt.version>1.8.0</mongodb-crypt.version>
|
||||||
|
<de.flapdoodle.embed.mongo.version>4.9.2</de.flapdoodle.embed.mongo.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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<Document> collection;
|
||||||
|
|
||||||
|
public MovieAtlasSearchService(MongoTemplate mongoTemplate) {
|
||||||
|
MongoDatabase database = mongoTemplate.getDb();
|
||||||
|
this.collection = database.getCollection("movies");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void debug(List<Bson> 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<Bson> 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<Bson> 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<Document> moviesByKeywords(String keywords) {
|
||||||
|
List<Bson> 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<Document>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Document genresThroughTheDecades(String genre) {
|
||||||
|
List<Bson> 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();
|
||||||
|
}
|
||||||
|
}
|
@ -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<Document> 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);
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,4 @@
|
|||||||
spring.application.name=spring-boot-persistence-mongodb-3
|
spring.application.name=spring-boot-persistence-mongodb-3
|
||||||
|
|
||||||
|
com.baeldung.atlas-search.index.query=idx-queries
|
||||||
|
com.baeldung.atlas-search.index.facet=idx-facets
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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.uri=changeit
|
||||||
#spring.data.mongodb.database=changeit
|
#spring.data.mongodb.database=changeit
|
||||||
|
Loading…
x
Reference in New Issue
Block a user