Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@
* - DELETE /api/movies/{id} - Delete a movie
* - DELETE /api/movies - Delete multiple movies
* - DELETE /api/movies/{id}/find-and-delete - Find and delete a movie
* - GET /api/movies/aggregations/comments - Aggregate movies with most comments
* - GET /api/movies/aggregations/years - Aggregate movies by year with statistics
* - GET /api/movies/aggregations/directors - Aggregate directors with most movies
* - GET /api/movies/aggregations/reportingByComments - Aggregate movies with most comments
* - GET /api/movies/aggregations/reportingByYear - Aggregate movies by year with statistics
* - GET /api/movies/aggregations/reportingByDirectors - Aggregate directors with most movies
* - GET /api/movies/search - Text search using MongoDB Search Index across multiple fields (plot, fullplot, directors, writers, cast)
* - GET /api/movies/vector-search - Vector search using Voyage AI embeddings to find movies with similar plots
* - GET /api/movies/find-similar-movies - Vector search to find similar movies based on plot embeddings
Expand Down Expand Up @@ -296,7 +296,7 @@ public ResponseEntity<SuccessResponse<DeleteResponse>> deleteMoviesBatch(
description = "Aggregates movies with their most recent comments using MongoDB $lookup (join) operation. " +
"Demonstrates how to combine data from the movies and comments collections."
)
@GetMapping("/aggregations/comments")
@GetMapping("/aggregations/reportingByComments")
public ResponseEntity<SuccessResponse<List<MovieWithCommentsResult>>> getMoviesWithMostRecentComments(
@Parameter(description = "Maximum number of movies to return (default: 10, max: 50)")
@RequestParam(defaultValue = "10") Integer limit,
Expand Down Expand Up @@ -331,7 +331,7 @@ public ResponseEntity<SuccessResponse<List<MovieWithCommentsResult>>> getMoviesW
description = "Aggregates movies by year with statistics including movie count and average rating. " +
"Demonstrates MongoDB $group operation for statistical aggregation."
)
@GetMapping("/aggregations/years")
@GetMapping("/aggregations/reportingByYear")
public ResponseEntity<SuccessResponse<List<MoviesByYearResult>>> getMoviesByYearWithStats() {

List<MoviesByYearResult> results = movieService.getMoviesByYearWithStats();
Expand All @@ -352,7 +352,7 @@ public ResponseEntity<SuccessResponse<List<MoviesByYearResult>>> getMoviesByYear
description = "Aggregates directors with the most movies and their statistics. " +
"Demonstrates MongoDB $unwind operation for array flattening and aggregation."
)
@GetMapping("/aggregations/directors")
@GetMapping("/aggregations/reportingByDirectors")
public ResponseEntity<SuccessResponse<List<DirectorStatisticsResult>>> getDirectorsWithMostMovies(
@Parameter(description = "Maximum number of directors to return (default: 20, max: 100)")
@RequestParam(defaultValue = "20") Integer limit) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,5 @@ public class DatabaseOperationException extends RuntimeException {
public DatabaseOperationException(String message) {
super(message);
}

public DatabaseOperationException(String message, Throwable cause) {
super(message, cause);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,12 @@
* Exception thrown when a requested resource is not found.
*
* This exception results in a 404 Not Found response.
*
* TODO: Phase 7 - Implement custom exception
*
*/
public class ResourceNotFoundException extends RuntimeException {

public ResourceNotFoundException(String message) {
super(message);
}

public ResourceNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,12 @@
* Exception thrown when request validation fails.
*
* This exception results in a 400 Bad Request response.
*
* TODO: Phase 7 - Implement custom exception
*
*/
public class ValidationException extends RuntimeException {

public ValidationException(String message) {
super(message);
}

public ValidationException(String message, Throwable cause) {
super(message, cause);
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class MovieWithCommentsResult {
/**
* Movie ID as string.
*/
private String id;
private String _id;

/**
* Movie title.
Expand Down Expand Up @@ -52,9 +52,9 @@ public class MovieWithCommentsResult {
private List<String> genres;

/**
* IMDB rating information.
* IMDB rating (0.0 to 10.0).
*/
private ImdbInfo imdb;
private Double imdbRating;

/**
* Most recent comments for this movie.
Expand All @@ -71,25 +71,6 @@ public class MovieWithCommentsResult {
*/
private Date mostRecentCommentDate;

/**
* Nested class for IMDB information.
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class ImdbInfo {
/**
* IMDB rating (0.0 to 10.0).
*/
private Double rating;

/**
* Number of votes.
*/
private Integer votes;
}

/**
* Nested class for comment information.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,13 +412,13 @@ public List<MovieWithCommentsResult> getMoviesWithMostRecentComments(Integer lim
// STAGE 7: Project final output with recent comments slice
// Shape the response and include only the 5 most recent comments per movie
Aggregation.project()
.and(ConditionalOperators.ifNull("_id").then("")).as("id")
.and(ConditionalOperators.ifNull("_id").then("")).as("_id")
.and("title").as("title")
.and("year").as("year")
.and("plot").as("plot")
.and("poster").as("poster")
.and("genres").as("genres")
.and("imdb").as("imdb")
.and("imdb.rating").as("imdbRating")
.and(ArrayOperators.Slice.sliceArrayOf("comments").itemCount(5)).as("recentComments")
.and("totalComments").as("totalComments")
.and("mostRecentCommentDate").as("mostRecentCommentDate")
Expand Down Expand Up @@ -540,15 +540,8 @@ public List<DirectorStatisticsResult> getDirectorsWithMostMovies(Integer limit)
* Helper method to map Document to MovieWithCommentsResult.
*/
private MovieWithCommentsResult mapToMovieWithCommentsResult(Document doc) {
// Extract IMDB info
MovieWithCommentsResult.ImdbInfo imdbInfo = null;
Document imdbDoc = doc.get("imdb", Document.class);
if (imdbDoc != null) {
imdbInfo = MovieWithCommentsResult.ImdbInfo.builder()
.rating(imdbDoc.getDouble("rating"))
.votes(imdbDoc.getInteger("votes"))
.build();
}
// Extract IMDB rating (just the number)
Double imdbRating = doc.getDouble("imdbRating");

// Extract recent comments
List<MovieWithCommentsResult.CommentInfo> recentComments = null;
Expand All @@ -569,21 +562,21 @@ private MovieWithCommentsResult mapToMovieWithCommentsResult(Document doc) {

// Extract movie ID - handle both String and ObjectId types
String movieId = null;
Object idObj = doc.get("id");
Object idObj = doc.get("_id");
if (idObj instanceof String) {
movieId = (String) idObj;
} else if (idObj instanceof ObjectId) {
movieId = ((ObjectId) idObj).toHexString();
}

return MovieWithCommentsResult.builder()
.id(movieId)
._id(movieId)
.title(doc.getString("title"))
.year(doc.getInteger("year"))
.plot(doc.getString("plot"))
.poster(doc.getString("poster"))
.genres(doc.getList("genres", String.class))
.imdb(imdbInfo)
.imdbRating(imdbRating)
.recentComments(recentComments)
.totalComments(doc.getInteger("totalComments"))
.mostRecentCommentDate(doc.getDate("mostRecentCommentDate"))
Expand Down Expand Up @@ -719,8 +712,6 @@ public List<Movie> searchMovies(MovieSearchRequest searchRequest) {
}
}


// TODO: Implement vector search
@Override
public List<Movie> findSimilarMovies(String movieId, Integer limit) {
// Validate movie ID
Expand Down
Loading