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>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-2</artifactId>
|
||||
<artifactId>parent-boot-3</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../parent-boot-2</relativePath>
|
||||
<relativePath>../../parent-boot-3</relativePath>
|
||||
</parent>
|
||||
|
||||
<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>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
@ -37,16 +22,6 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<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>
|
||||
<groupId>org.mongodb</groupId>
|
||||
@ -57,12 +32,13 @@
|
||||
<groupId>de.flapdoodle.embed</groupId>
|
||||
<artifactId>de.flapdoodle.embed.mongo</artifactId>
|
||||
<scope>test</scope>
|
||||
<version>${de.flapdoodle.embed.mongo.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<mongodb-crypt.version>1.7.3</mongodb-crypt.version>
|
||||
<mongodb-driver.version>4.9.1</mongodb-driver.version>
|
||||
<start-class>com.baeldung.boot.atlassearch.MongoDbAtlasSearchApplication</start-class>
|
||||
<mongodb-crypt.version>1.8.0</mongodb-crypt.version>
|
||||
<de.flapdoodle.embed.mongo.version>4.9.2</de.flapdoodle.embed.mongo.version>
|
||||
</properties>
|
||||
|
||||
</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
|
||||
|
||||
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.database=changeit
|
||||
|
Loading…
x
Reference in New Issue
Block a user