diff --git a/mflix/README-JAVA-SPRING.md b/mflix/README-JAVA-SPRING.md index d561d10..652ef18 100644 --- a/mflix/README-JAVA-SPRING.md +++ b/mflix/README-JAVA-SPRING.md @@ -58,7 +58,7 @@ Edit the `.env` file and set your MongoDB connection string: ```env # MongoDB Connection -MONGODB_URI=mongodb+srv://:@.mongodb.net/sample_mflix?retryWrites=true&w=majority +MONGODB_URI="mongodb+srv://:@.mongodb.net/sample_mflix?retryWrites=true&w=majority" # Voyage AI Configuration (optional - required for Vector Search) VOYAGE_API_KEY=your_voyage_api_key diff --git a/mflix/README-NODE-EXPRESS.md b/mflix/README-NODE-EXPRESS.md index d7c013a..64835bc 100644 --- a/mflix/README-NODE-EXPRESS.md +++ b/mflix/README-NODE-EXPRESS.md @@ -58,7 +58,7 @@ Edit the `.env` file and set your MongoDB connection string: ```env # MongoDB Connection # Replace with your MongoDB Atlas connection string or local MongoDB URI -MONGODB_URI=mongodb+srv://:@.mongodb.net/sample_mflix?retryWrites=true&w=majority +MONGODB_URI="mongodb+srv://:@.mongodb.net/sample_mflix?retryWrites=true&w=majority" # Voyage AI Configuration # API key for Voyage AI embedding model (required for Vector Search) diff --git a/mflix/README-PYTHON-FASTAPI.md b/mflix/README-PYTHON-FASTAPI.md index 38168be..6419bac 100644 --- a/mflix/README-PYTHON-FASTAPI.md +++ b/mflix/README-PYTHON-FASTAPI.md @@ -61,7 +61,7 @@ Edit the `.env` file and set your MongoDB connection string: ```env # MongoDB Connection -MONGODB_URI=mongodb+srv://:@.mongodb.net/sample_mflix?retryWrites=true&w=majority +MONGODB_URI="mongodb+srv://:@.mongodb.net/sample_mflix?retryWrites=true&w=majority" # Voyage AI Configuration (optional - required for Vector Search) VOYAGE_API_KEY=your_voyage_api_key diff --git a/mflix/server/java-spring/.env.example b/mflix/server/java-spring/.env.example index 96c3555..e6b4861 100644 --- a/mflix/server/java-spring/.env.example +++ b/mflix/server/java-spring/.env.example @@ -1,6 +1,6 @@ # MongoDB Connection # Replace with your MongoDB Atlas connection string or local MongoDB URI -MONGODB_URI=mongodb+srv://:@.mongodb.net/sample_mflix?retryWrites=true&w=majority +MONGODB_URI="mongodb+srv://:@.mongodb.net/sample_mflix?retryWrites=true&w=majority" # OPTIONAL: Voyage AI Configuration (required for Vector Search) # Get your API key from https://www.voyageai.com/ diff --git a/mflix/server/java-spring/pom.xml b/mflix/server/java-spring/pom.xml index a7b3ae8..43f1546 100644 --- a/mflix/server/java-spring/pom.xml +++ b/mflix/server/java-spring/pom.xml @@ -58,7 +58,7 @@ org.projectlombok lombok - true + provided @@ -118,11 +118,18 @@ - + org.apache.maven.plugins maven-compiler-plugin + + + org.projectlombok + lombok + ${lombok.version} + + -Xlint:-options diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/controller/MovieControllerImpl.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/controller/MovieControllerImpl.java index 519f13b..0ea67d7 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/controller/MovieControllerImpl.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/controller/MovieControllerImpl.java @@ -18,7 +18,6 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; -import java.time.Instant; import java.util.List; import java.util.Map; import org.bson.Document; @@ -103,10 +102,8 @@ public ResponseEntity>> getAllMovies( String message = "Found " + movies.size() + " movies"; SuccessResponse> response = SuccessResponse.>builder() - .success(true) .message(message) .data(movies) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.ok(response); @@ -122,10 +119,8 @@ public ResponseEntity>> getDistinctGenres() { List genres = movieService.getDistinctGenres(); SuccessResponse> response = SuccessResponse.>builder() - .success(true) .message("Found " + genres.size() + " distinct genres") .data(genres) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.ok(response); @@ -142,10 +137,8 @@ public ResponseEntity> getMovieById( Movie movie = movieService.getMovieById(id); SuccessResponse response = SuccessResponse.builder() - .success(true) .message("Movie retrieved successfully") .data(movie) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.ok(response); @@ -162,10 +155,8 @@ public ResponseEntity> createMovie( Movie movie = movieService.createMovie(request); SuccessResponse response = SuccessResponse.builder() - .success(true) - .message("Movie '" + request.getTitle() + "' created successfully") + .message("Movie '" + request.title() + "' created successfully") .data(movie) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.status(HttpStatus.CREATED).body(response); @@ -182,10 +173,8 @@ public ResponseEntity> createMoviesBatch( BatchInsertResponse result = movieService.createMoviesBatch(requests); SuccessResponse response = SuccessResponse.builder() - .success(true) - .message("Successfully created " + result.getInsertedCount() + " movies") + .message("Successfully created " + result.insertedCount() + " movies") .data(result) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.status(HttpStatus.CREATED).body(response); @@ -210,10 +199,8 @@ public ResponseEntity> updateMovie( Movie movie = movieService.updateMovie(id, request); SuccessResponse response = SuccessResponse.builder() - .success(true) .message("Movie updated successfully") .data(movie) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.ok(response); @@ -235,11 +222,9 @@ public ResponseEntity> updateMoviesBatch( BatchUpdateResponse result = movieService.updateMoviesBatch(filter, update); SuccessResponse response = SuccessResponse.builder() - .success(true) - .message("Update operation completed. Matched " + result.getMatchedCount() + - " documents, modified " + result.getModifiedCount() + " documents.") + .message("Update operation completed. Matched " + result.matchedCount() + + " documents, modified " + result.modifiedCount() + " documents.") .data(result) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.ok(response); @@ -257,10 +242,8 @@ public ResponseEntity> findAndDeleteMovie( Movie movie = movieService.findAndDeleteMovie(id); SuccessResponse response = SuccessResponse.builder() - .success(true) .message("Movie found and deleted successfully") .data(movie) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.ok(response); @@ -277,10 +260,8 @@ public ResponseEntity> deleteMovie( DeleteResponse result = movieService.deleteMovie(id); SuccessResponse response = SuccessResponse.builder() - .success(true) .message("Movie deleted successfully") .data(result) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.ok(response); @@ -301,10 +282,8 @@ public ResponseEntity> deleteMoviesBatch( DeleteResponse result = movieService.deleteMoviesBatch(filter); SuccessResponse response = SuccessResponse.builder() - .success(true) - .message("Delete operation completed. Removed " + result.getDeletedCount() + " documents.") + .message("Delete operation completed. Removed " + result.deletedCount() + " documents.") .data(result) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.ok(response); @@ -328,7 +307,7 @@ public ResponseEntity>> getMoviesW // Calculate total comments across all movies int totalComments = results.stream() - .mapToInt(result -> result.getTotalComments() != null ? result.getTotalComments() : 0) + .mapToInt(result -> result.totalComments() != null ? result.totalComments() : 0) .sum(); String message = movieId != null @@ -338,10 +317,8 @@ public ResponseEntity>> getMoviesW SuccessResponse> response = SuccessResponse.>builder() - .success(true) .message(message) .data(results) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.ok(response); @@ -359,10 +336,8 @@ public ResponseEntity>> getMoviesByYear SuccessResponse> response = SuccessResponse.>builder() - .success(true) .message(String.format("Aggregated statistics for %d years", results.size())) .data(results) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.ok(response); @@ -382,10 +357,8 @@ public ResponseEntity>> getDirect SuccessResponse> response = SuccessResponse.>builder() - .success(true) .message(String.format("Found %d directors with most movies", results.size())) .data(results) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.ok(response); @@ -440,10 +413,8 @@ public ResponseEntity> searchMovies( .build(); SuccessResponse response = SuccessResponse.builder() - .success(true) .message(String.format("Found %d movies matching the search criteria", movies.size())) .data(searchResponse) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.ok(response); @@ -465,10 +436,8 @@ public ResponseEntity>> vectorSearchMov List results = movieService.vectorSearchMovies(q, limit); SuccessResponse> response = SuccessResponse.>builder() - .success(true) .message(String.format("Found %d similar movies for query: '%s'", results.size(), q)) .data(results) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.ok(response); @@ -489,10 +458,8 @@ public ResponseEntity>> findSimilarMovies( List movies = movieService.findSimilarMovies(movieId, limit); SuccessResponse> response = SuccessResponse.>builder() - .success(true) .message(String.format("Found %d similar movies", movies.size())) .data(movies) - .timestamp(Instant.now().toString()) .build(); return ResponseEntity.ok(response); diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/exception/GlobalExceptionHandler.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/exception/GlobalExceptionHandler.java index 3776dae..101b5c3 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/exception/GlobalExceptionHandler.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/exception/GlobalExceptionHandler.java @@ -29,7 +29,6 @@ public ResponseEntity handleResourceNotFoundException( logger.error("Resource not found: {}", ex.getMessage()); ErrorResponse errorResponse = ErrorResponse.builder() - .success(false) .message(ex.getMessage()) .error(ErrorResponse.ErrorDetails.builder() .message(ex.getMessage()) @@ -47,7 +46,6 @@ public ResponseEntity handleValidationException( logger.error("Validation error: {}", ex.getMessage()); ErrorResponse errorResponse = ErrorResponse.builder() - .success(false) .message("Validation failed") .error(ErrorResponse.ErrorDetails.builder() .message(ex.getMessage()) @@ -67,7 +65,6 @@ public ResponseEntity handleMissingServletRequestParameter( String message = String.format("Required parameter '%s' is missing", ex.getParameterName()); ErrorResponse errorResponse = ErrorResponse.builder() - .success(false) .message(message) .error(ErrorResponse.ErrorDetails.builder() .message(message) @@ -85,7 +82,6 @@ public ResponseEntity handleServiceUnavailableException( logger.error("Service unavailable: {}", ex.getMessage()); ErrorResponse errorResponse = ErrorResponse.builder() - .success(false) .message(ex.getMessage()) .error(ErrorResponse.ErrorDetails.builder() .message(ex.getMessage()) @@ -103,7 +99,6 @@ public ResponseEntity handleVoyageAuthException( logger.error("Voyage AI authentication error: {}", ex.getMessage()); ErrorResponse errorResponse = ErrorResponse.builder() - .success(false) .message(ex.getMessage()) .error(ErrorResponse.ErrorDetails.builder() .message(ex.getMessage()) @@ -122,7 +117,6 @@ public ResponseEntity handleVoyageAPIException( logger.error("Voyage AI API error: {}", ex.getMessage()); ErrorResponse errorResponse = ErrorResponse.builder() - .success(false) .message("Vector search service unavailable") .error(ErrorResponse.ErrorDetails.builder() .message(ex.getMessage()) @@ -140,7 +134,6 @@ public ResponseEntity handleDatabaseOperationException( logger.error("Database operation error: {}", ex.getMessage()); ErrorResponse errorResponse = ErrorResponse.builder() - .success(false) .message("Database operation failed") .error(ErrorResponse.ErrorDetails.builder() .message(ex.getMessage()) @@ -168,7 +161,6 @@ public ResponseEntity handleMongoWriteException( } ErrorResponse errorResponse = ErrorResponse.builder() - .success(false) .message(message) .error(ErrorResponse.ErrorDetails.builder() .message(message) @@ -187,7 +179,6 @@ public ResponseEntity handleGenericException( logger.error("Unexpected error occurred", ex); ErrorResponse errorResponse = ErrorResponse.builder() - .success(false) .message(ex.getMessage() != null ? ex.getMessage() : "Internal server error") .error(ErrorResponse.ErrorDetails.builder() .message(ex.getMessage() != null ? ex.getMessage() : "Internal server error") diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/Movie.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/Movie.java index b896b4e..770a5cb 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/Movie.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/Movie.java @@ -3,11 +3,16 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Date; import java.util.List; +import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; import org.bson.types.ObjectId; +import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; /** @@ -18,15 +23,16 @@ * for awards, IMDB ratings, and Tomatoes ratings. * *

Note: We use Lombok annotations to reduce boilerplate code: - * - @Data: Generates getters, setters, toString, equals, and hashCode + * - @Getter @Setter @ToString @EqualsAndHashCode: Generates getters, setters, toString, equals, and hashCode * - @Builder: Provides a fluent builder pattern for object construction - * - @NoArgsConstructor: Generates a no-argument constructor (required by MongoDB driver) - * - @AllArgsConstructor: Generates a constructor with all fields */ -@Data +@Getter +@Setter +@ToString(onlyExplicitlyIncluded = true) +@EqualsAndHashCode(onlyExplicitlyIncluded = true) @Builder -@NoArgsConstructor -@AllArgsConstructor +@AllArgsConstructor(access = AccessLevel.PROTECTED) // needed for Spring Data and MongoDB mapping +@NoArgsConstructor(access = AccessLevel.PROTECTED) // needed for Spring Data and MongoDB mapping @Document(collection = "movies") public class Movie { @@ -77,16 +83,21 @@ private Fields() { * Can be null for new documents (MongoDB will generate it). */ @JsonProperty("_id") + @Id + @ToString.Include + @EqualsAndHashCode.Include private ObjectId id; /** * Movie title (required field). */ + @ToString.Include private String title; /** * Release year. */ + @ToString.Include private Integer year; /** @@ -177,10 +188,11 @@ private Fields() { /** * Nested class representing awards information. */ - @Data + @Getter + @Setter @Builder - @NoArgsConstructor - @AllArgsConstructor + @AllArgsConstructor(access = AccessLevel.PROTECTED) // needed for Spring Data and MongoDB mapping + @NoArgsConstructor(access = AccessLevel.PROTECTED) // needed for Spring Data and MongoDB mapping public static class Awards { /** * Number of awards won. @@ -201,10 +213,11 @@ public static class Awards { /** * Nested class representing IMDB rating information. */ - @Data + @Getter + @Setter @Builder - @NoArgsConstructor - @AllArgsConstructor + @AllArgsConstructor(access = AccessLevel.PROTECTED) // needed for Spring Data and MongoDB mapping + @NoArgsConstructor(access = AccessLevel.PROTECTED) // needed for Spring Data and MongoDB mapping public static class Imdb { /** * IMDB rating (0.0 to 10.0). @@ -225,10 +238,11 @@ public static class Imdb { /** * Nested class representing Rotten Tomatoes rating information. */ - @Data + @Getter + @Setter @Builder - @NoArgsConstructor - @AllArgsConstructor + @AllArgsConstructor(access = AccessLevel.PROTECTED) // needed for Spring Data and MongoDB mapping + @NoArgsConstructor(access = AccessLevel.PROTECTED) // needed for Spring Data and MongoDB mapping public static class Tomatoes { /** * Viewer ratings information. @@ -263,10 +277,11 @@ public static class Tomatoes { /** * Nested class for viewer ratings. */ - @Data + @Getter + @Setter @Builder - @NoArgsConstructor - @AllArgsConstructor + @AllArgsConstructor(access = AccessLevel.PROTECTED) // needed for Spring Data and MongoDB mapping + @NoArgsConstructor(access = AccessLevel.PROTECTED) // needed for Spring Data and MongoDB mapping public static class Viewer { /** * Viewer rating (0.0 to 5.0). @@ -287,10 +302,11 @@ public static class Viewer { /** * Nested class for critic ratings. */ - @Data + @Getter + @Setter @Builder - @NoArgsConstructor - @AllArgsConstructor + @AllArgsConstructor(access = AccessLevel.PROTECTED) // needed for Spring Data and MongoDB mapping + @NoArgsConstructor(access = AccessLevel.PROTECTED) // needed for Spring Data and MongoDB mapping public static class Critic { /** * Critic rating (0.0 to 5.0). diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/BatchInsertResponse.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/BatchInsertResponse.java index 94edcd0..becaaeb 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/BatchInsertResponse.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/BatchInsertResponse.java @@ -1,19 +1,12 @@ package com.mongodb.samplemflix.model.dto; import java.util.Collection; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; import org.bson.BsonValue; /** * Response DTO for batch insert operations. */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class BatchInsertResponse { - private int insertedCount; - private Collection insertedIds; -} +public record BatchInsertResponse ( + int insertedCount, + Collection insertedIds) {} diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/BatchUpdateResponse.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/BatchUpdateResponse.java index 354bdbc..8558aa4 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/BatchUpdateResponse.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/BatchUpdateResponse.java @@ -1,17 +1,8 @@ package com.mongodb.samplemflix.model.dto; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - /** * Response DTO for batch update operations. */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class BatchUpdateResponse { - private long matchedCount; - private long modifiedCount; -} - +public record BatchUpdateResponse ( + long matchedCount, + long modifiedCount) {} diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/CreateMovieRequest.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/CreateMovieRequest.java index c333064..cb91792 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/CreateMovieRequest.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/CreateMovieRequest.java @@ -2,88 +2,81 @@ import jakarta.validation.constraints.NotBlank; import java.util.List; -import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; /** * Data Transfer Object for creating a new movie. * *

This DTO is used for POST /api/movies requests. * It includes validation annotations to ensure required fields are present. - * Only the title field is required; all other fields are optional. + * Only the title field is required, all other fields are optional. */ -@Data @Builder -@NoArgsConstructor -@AllArgsConstructor -public class CreateMovieRequest { +public record CreateMovieRequest ( /** * Movie title (required). * Must not be blank. */ @NotBlank(message = "Title is required") - private String title; + String title, /** * Release year (optional). */ - private Integer year; + Integer year, /** * Short plot summary (optional). */ - private String plot; + String plot, /** * Full plot description (optional). */ - private String fullplot; + String fullplot, /** * List of genres (optional). */ - private List genres; + List genres, /** * List of directors (optional). */ - private List directors; + List directors, /** * List of writers (optional). */ - private List writers; + List writers, /** * List of cast members (optional). */ - private List cast; + List cast, /** * List of countries (optional). */ - private List countries; + List countries, /** * List of languages (optional). */ - private List languages; + List languages, /** * Movie rating (optional). */ - private String rated; + String rated, /** * Runtime in minutes (optional). */ - private Integer runtime; + Integer runtime, /** * Poster image URL (optional). */ - private String poster; -} + String poster) {} diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/DeleteResponse.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/DeleteResponse.java index b7ddc85..eecf120 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/DeleteResponse.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/DeleteResponse.java @@ -1,16 +1,8 @@ package com.mongodb.samplemflix.model.dto; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - /** * Response DTO for delete operations. */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class DeleteResponse { - private long deletedCount; -} +public record DeleteResponse ( + long deletedCount) {} diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/DirectorStatisticsResult.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/DirectorStatisticsResult.java index 6bf04dc..591c8b7 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/DirectorStatisticsResult.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/DirectorStatisticsResult.java @@ -1,10 +1,8 @@ package com.mongodb.samplemflix.model.dto; import com.fasterxml.jackson.annotation.JsonInclude; -import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.With; /** * DTO for director statistics aggregation result. @@ -12,26 +10,22 @@ *

This class represents the result of the reportingByDirectors aggregation * which finds directors with the most movies and their statistics. */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor @JsonInclude(JsonInclude.Include.NON_NULL) -public class DirectorStatisticsResult { - +@Builder +public record DirectorStatisticsResult ( /** * Director name. */ - private String director; + String director, /** * Number of movies directed by this director. */ - private Integer movieCount; + Integer movieCount, /** * Average IMDB rating of this director's movies. */ - private Double averageRating; -} + @With + Double averageRating) {} diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MovieSearchQuery.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MovieSearchQuery.java index dc0ea46..8a4a1a3 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MovieSearchQuery.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MovieSearchQuery.java @@ -1,9 +1,6 @@ package com.mongodb.samplemflix.model.dto; -import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; /** * Data Transfer Object for movie search query parameters. @@ -11,57 +8,53 @@ *

This DTO is used to parse and validate query parameters for GET /api/movies requests. * It supports full-text search, filtering by genre/year/rating, sorting, and pagination. */ -@Data @Builder -@NoArgsConstructor -@AllArgsConstructor -public class MovieSearchQuery { +public record MovieSearchQuery ( /** * Full-text search query. * Searches across plot, title, and fullplot fields using MongoDB text index. */ - private String q; + String q, /** * Filter by genre (case-insensitive partial match). */ - private String genre; + String genre, /** * Filter by exact year. */ - private Integer year; + Integer year, /** * Minimum IMDB rating (inclusive). */ - private Double minRating; + Double minRating, /** * Maximum IMDB rating (inclusive). */ - private Double maxRating; + Double maxRating, /** * Number of results to return (default: 20, max: 100). */ - private Integer limit; + Integer limit, /** * Number of results to skip for pagination (default: 0). */ - private Integer skip; + Integer skip, /** * Field to sort by (e.g., "title", "year", "imdb.rating"). * Default: "title" */ - private String sortBy; + String sortBy, /** * Sort order: "asc" or "desc". * Default: "asc" */ - private String sortOrder; -} + String sortOrder) {} diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MovieSearchRequest.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MovieSearchRequest.java index f309079..7d1d47a 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MovieSearchRequest.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MovieSearchRequest.java @@ -1,9 +1,6 @@ package com.mongodb.samplemflix.model.dto; -import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; /** * Data Transfer Object for MongoDB Search query parameters. @@ -11,53 +8,50 @@ *

This DTO is used to parse and validate query parameters for GET /api/movies/search requests. * It supports searching across multiple fields using MongoDB Search with compound operators. */ -@Data @Builder -@NoArgsConstructor -@AllArgsConstructor -public class MovieSearchRequest { +public record MovieSearchRequest ( /** * Search query for the plot field. * Uses phrase operator for exact phrase matching. */ - private String plot; + String plot, /** * Search query for the fullplot field. * Uses phrase operator for exact phrase matching. */ - private String fullplot; + String fullplot, /** * Search query for the directors field. * Uses text operator with fuzzy matching (maxEdits=1, prefixLength=5). */ - private String directors; + String directors, /** * Search query for the writers field. * Uses text operator with fuzzy matching (maxEdits=1, prefixLength=5). */ - private String writers; + String writers, /** * Search query for the cast field. * Uses text operator with fuzzy matching (maxEdits=1, prefixLength=5). */ - private String cast; + String cast, /** * Maximum number of results to return. * Default: 20, Range: 1-100 */ - private Integer limit; + Integer limit, /** * Number of results to skip for pagination. * Default: 0, Minimum: 0 */ - private Integer skip; + Integer skip, /** * Compound search operator to use. @@ -71,7 +65,7 @@ public class MovieSearchRequest { *

  • filter - Clauses must match but don't affect scoring
  • * */ - private String searchOperator; + String searchOperator) { /** * Checks if at least one search field is provided. diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MovieWithCommentsResult.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MovieWithCommentsResult.java index 62e70c4..25c3d05 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MovieWithCommentsResult.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MovieWithCommentsResult.java @@ -3,10 +3,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import java.util.Date; import java.util.List; -import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; /** * DTO for movies with their most recent comments aggregation result. @@ -14,95 +11,88 @@ *

    This class represents the result of the reportingByComments aggregation * which joins movies with their comments and returns movies with the most comments. */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor @JsonInclude(JsonInclude.Include.NON_NULL) -public class MovieWithCommentsResult { +@Builder +public record MovieWithCommentsResult ( /** * Movie ID as string. */ - private String _id; + String _id, /** * Movie title. */ - private String title; + String title, /** * Release year. */ - private Integer year; + Integer year, /** * Short plot summary. */ - private String plot; + String plot, /** * Poster image URL. */ - private String poster; + String poster, /** * List of genres. */ - private List genres; + List genres, /** * IMDB rating (0.0 to 10.0). */ - private Double imdbRating; + Double imdbRating, /** * Most recent comments for this movie. */ - private List recentComments; + List recentComments, /** * Total number of comments for this movie. */ - private Integer totalComments; + Integer totalComments, /** * Date of the most recent comment. */ - private Date mostRecentCommentDate; + Date mostRecentCommentDate) { /** - * Nested class for comment information. + * Nested record for comment information. */ - @Data @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class CommentInfo { + public record CommentInfo ( /** * Comment ID as string. */ - private String id; + String id, /** * Commenter name. */ - private String name; + String name, /** * Commenter email. */ - private String email; + String email, /** * Comment text. */ - private String text; + String text, /** * Comment date. */ - private Date date; - } + Date date) {} } diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MoviesByYearResult.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MoviesByYearResult.java index 1ab4da2..13abc2b 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MoviesByYearResult.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/MoviesByYearResult.java @@ -1,10 +1,8 @@ package com.mongodb.samplemflix.model.dto; import com.fasterxml.jackson.annotation.JsonInclude; -import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.With; /** * DTO for movies aggregated by year with statistics. @@ -12,41 +10,38 @@ *

    This class represents the result of the reportingByYear aggregation * which groups movies by release year and calculates statistics per year. */ -@Data @Builder -@NoArgsConstructor -@AllArgsConstructor @JsonInclude(JsonInclude.Include.NON_NULL) -public class MoviesByYearResult { +public record MoviesByYearResult ( /** * Release year. */ - private Integer year; + Integer year, /** * Number of movies released in this year. */ - private Integer movieCount; + Integer movieCount, /** * Average IMDB rating for movies in this year. */ - private Double averageRating; + @With + Double averageRating, /** * Highest IMDB rating for movies in this year. */ - private Double highestRating; + Double highestRating, /** * Lowest IMDB rating for movies in this year. */ - private Double lowestRating; + Double lowestRating, /** * Total number of IMDB votes for all movies in this year. */ - private Long totalVotes; -} + Long totalVotes) {} diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/SearchMoviesResponse.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/SearchMoviesResponse.java index 8dd2343..b33bec6 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/SearchMoviesResponse.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/SearchMoviesResponse.java @@ -2,10 +2,7 @@ import com.mongodb.samplemflix.model.Movie; import java.util.List; -import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; /** * Response wrapper for movie search results. @@ -13,20 +10,16 @@ *

    This DTO wraps the search results with pagination metadata, * matching the structure returned by the Python backend. */ -@Data @Builder -@NoArgsConstructor -@AllArgsConstructor -public class SearchMoviesResponse { +public record SearchMoviesResponse ( /** * List of movies matching the search criteria. */ - private List movies; + List movies, /** * Total count of movies matching the search criteria. */ - private Integer totalCount; -} + Integer totalCount) {} diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/UpdateMovieRequest.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/UpdateMovieRequest.java index f3a674f..2f692bf 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/UpdateMovieRequest.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/UpdateMovieRequest.java @@ -1,10 +1,7 @@ package com.mongodb.samplemflix.model.dto; import java.util.List; -import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; /** * Data Transfer Object for updating an existing movie. @@ -13,74 +10,70 @@ * All fields are optional since partial updates are allowed. * Any field that is null will not be updated in the database. */ -@Data @Builder -@NoArgsConstructor -@AllArgsConstructor -public class UpdateMovieRequest { +public record UpdateMovieRequest ( /** * Movie title (optional). */ - private String title; + String title, /** * Release year (optional). */ - private Integer year; + Integer year, /** * Short plot summary (optional). */ - private String plot; + String plot, /** * Full plot description (optional). */ - private String fullplot; + String fullplot, /** * List of genres (optional). */ - private List genres; + List genres, /** * List of directors (optional). */ - private List directors; + List directors, /** * List of writers (optional). */ - private List writers; + List writers, /** * List of cast members (optional). */ - private List cast; + List cast, /** * List of countries (optional). */ - private List countries; + List countries, /** * List of languages (optional). */ - private List languages; + List languages, /** * Movie rating (optional). */ - private String rated; + String rated, /** * Runtime in minutes (optional). */ - private Integer runtime; + Integer runtime, /** * Poster image URL (optional). */ - private String poster; -} + String poster) {} diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/VectorSearchResult.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/VectorSearchResult.java index 00dc38e..e1662cd 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/VectorSearchResult.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/dto/VectorSearchResult.java @@ -1,9 +1,7 @@ package com.mongodb.samplemflix.model.dto; -import lombok.AllArgsConstructor; +import java.util.List; import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; /** * Data Transfer Object for vector search results. @@ -11,55 +9,51 @@ *

    This DTO represents the result of a MongoDB Vector Search query, * containing the movie information and similarity score. */ -@Data @Builder -@NoArgsConstructor -@AllArgsConstructor -public class VectorSearchResult { - +public record VectorSearchResult ( + /** * Movie ObjectId as a string. */ - private String id; - + String id, + /** * Movie title. */ - private String title; - + String title, + /** * Movie plot summary. */ - private String plot; - + String plot, + /** * Movie poster URL. */ - private String poster; - + String poster, + /** * Movie release year. */ - private Integer year; - + Integer year, + /** * Movie genres. */ - private java.util.List genres; - + List genres, + /** * Movie directors. */ - private java.util.List directors; - + List directors, + /** * Movie cast members. */ - private java.util.List cast; - + List cast, + /** * Vector search similarity score (0.0 to 1.0, higher = more similar). */ - private Double score; -} + Double score) {} diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/response/ApiResponse.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/response/ApiResponse.java index 1374efb..3427244 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/response/ApiResponse.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/response/ApiResponse.java @@ -17,12 +17,12 @@ public interface ApiResponse { * * @return true for successful responses, false for error responses */ - boolean isSuccess(); + boolean success(); /** - * Gets the timestamp when the response was generated. + * Returns the timestamp when the response was generated. * * @return ISO 8601 formatted timestamp string */ - String getTimestamp(); + String timestamp(); } diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/response/ErrorResponse.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/response/ErrorResponse.java index 6fab419..2f028e1 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/response/ErrorResponse.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/response/ErrorResponse.java @@ -2,10 +2,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import java.time.Instant; -import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; /** * Error response wrapper for API error responses. @@ -23,56 +20,53 @@ * timestamp: string * } */ -@Data @Builder -@NoArgsConstructor -@AllArgsConstructor @JsonInclude(JsonInclude.Include.NON_NULL) -public class ErrorResponse implements ApiResponse { +public record ErrorResponse ( /** * Always false for error responses. */ - @Builder.Default - private boolean success = false; + boolean success, /** * High-level error message. */ - private String message; + String message, /** * Detailed error information. */ - private ErrorDetails error; + ErrorDetails error, /** * ISO 8601 timestamp when the error occurred. */ - @Builder.Default - private String timestamp = Instant.now().toString(); + String timestamp) implements ApiResponse { + + // Partial builder declaration to provide defaults for records (like @Builder.Default for classes) + public static class ErrorResponseBuilder { + private boolean success = false; + private String timestamp = Instant.now().toString(); + } /** * Nested class for detailed error information. */ - @Data @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class ErrorDetails { + public record ErrorDetails ( /** * Detailed error message. */ - private String message; + String message, /** * Error code (e.g., "VALIDATION_ERROR", "NOT_FOUND"). */ - private String code; + String code, /** * Additional error details (optional). */ - private Object details; - } + Object details) {} } diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/response/SuccessResponse.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/response/SuccessResponse.java index 69550ee..34393cb 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/response/SuccessResponse.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/model/response/SuccessResponse.java @@ -2,10 +2,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import java.time.Instant; -import lombok.AllArgsConstructor; import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; /** * Success response wrapper for API responses. @@ -21,66 +18,63 @@ * pagination?: { page, limit, total, pages } * } */ -@Data @Builder -@NoArgsConstructor -@AllArgsConstructor @JsonInclude(JsonInclude.Include.NON_NULL) -public class SuccessResponse implements ApiResponse { +public record SuccessResponse ( /** * Always true for success responses. */ - @Builder.Default - private boolean success = true; + boolean success, /** * Optional success message. */ - private String message; + String message, /** * The response data (generic type). */ - private T data; + T data, /** * ISO 8601 timestamp when the response was generated. */ - @Builder.Default - private String timestamp = Instant.now().toString(); + String timestamp, /** * Optional pagination metadata (for list responses). */ - private Pagination pagination; + Pagination pagination) implements ApiResponse { + + // Partial builder declaration to provide defaults for records (like @Builder.Default for classes) + public static class SuccessResponseBuilder { + private boolean success = true; + private String timestamp = Instant.now().toString(); + } /** * Nested class for pagination metadata. */ - @Data @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class Pagination { + public record Pagination ( /** * Current page number (1-based). */ - private int page; + int page, /** * Number of items per page. */ - private int limit; + int limit, /** * Total number of items. */ - private long total; + long total, /** * Total number of pages. */ - private int pages; - } + int pages) {} } diff --git a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/service/MovieServiceImpl.java b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/service/MovieServiceImpl.java index 3ccb9d5..5ce4188 100644 --- a/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/service/MovieServiceImpl.java +++ b/mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/service/MovieServiceImpl.java @@ -77,11 +77,11 @@ public MovieServiceImpl(MovieRepository movieRepository, MongoTemplate mongoTemp public List getAllMovies(MovieSearchQuery query) { Query mongoQuery = buildQuery(query); - int limit = Math.clamp(query.getLimit() != null ? query.getLimit() : 20, 1, 100); - int skip = Math.max(query.getSkip() != null ? query.getSkip() : 0, 0); + int limit = Math.clamp(query.limit() != null ? query.limit() : 20, 1, 100); + int skip = Math.max(query.skip() != null ? query.skip() : 0, 0); mongoQuery.skip(skip).limit(limit); - mongoQuery.with(buildSort(query.getSortBy(), query.getSortOrder())); + mongoQuery.with(buildSort(query.sortBy(), query.sortOrder())); return mongoTemplate.find(mongoQuery, Movie.class); } @@ -116,24 +116,24 @@ public Movie getMovieById(String id) { @Override public Movie createMovie(CreateMovieRequest request) { - if (request.getTitle() == null || request.getTitle().trim().isEmpty()) { + if (request.title() == null || request.title().trim().isEmpty()) { throw new ValidationException("Title is required"); } Movie movie = Movie.builder() - .title(request.getTitle()) - .year(request.getYear()) - .plot(request.getPlot()) - .fullplot(request.getFullplot()) - .genres(request.getGenres()) - .directors(request.getDirectors()) - .writers(request.getWriters()) - .cast(request.getCast()) - .countries(request.getCountries()) - .languages(request.getLanguages()) - .rated(request.getRated()) - .runtime(request.getRuntime()) - .poster(request.getPoster()) + .title(request.title()) + .year(request.year()) + .plot(request.plot()) + .fullplot(request.fullplot()) + .genres(request.genres()) + .directors(request.directors()) + .writers(request.writers()) + .cast(request.cast()) + .countries(request.countries()) + .languages(request.languages()) + .rated(request.rated()) + .runtime(request.runtime()) + .poster(request.poster()) .build(); // Spring Data MongoDB's save() method inserts or updates @@ -148,26 +148,26 @@ public BatchInsertResponse createMoviesBatch(List requests) for (int i = 0; i < requests.size(); i++) { CreateMovieRequest request = requests.get(i); - if (request.getTitle() == null || request.getTitle().trim().isEmpty()) { + if (request.title() == null || request.title().trim().isEmpty()) { throw new ValidationException("Movie at index " + i + ": Title is required"); } } List movies = requests.stream() .map(request -> Movie.builder() - .title(request.getTitle()) - .year(request.getYear()) - .plot(request.getPlot()) - .fullplot(request.getFullplot()) - .genres(request.getGenres()) - .directors(request.getDirectors()) - .writers(request.getWriters()) - .cast(request.getCast()) - .countries(request.getCountries()) - .languages(request.getLanguages()) - .rated(request.getRated()) - .runtime(request.getRuntime()) - .poster(request.getPoster()) + .title(request.title()) + .year(request.year()) + .plot(request.plot()) + .fullplot(request.fullplot()) + .genres(request.genres()) + .directors(request.directors()) + .writers(request.writers()) + .cast(request.cast()) + .countries(request.countries()) + .languages(request.languages()) + .rated(request.rated()) + .runtime(request.runtime()) + .poster(request.poster()) .build()) .toList(); @@ -302,30 +302,30 @@ private Query buildQuery(MovieSearchQuery query) { Query mongoQuery = new Query(); // Text search - if (query.getQ() != null && !query.getQ().trim().isEmpty()) { - TextCriteria textCriteria = TextCriteria.forDefaultLanguage().matching(query.getQ()); + if (query.q() != null && !query.q().trim().isEmpty()) { + TextCriteria textCriteria = TextCriteria.forDefaultLanguage().matching(query.q()); mongoQuery.addCriteria(textCriteria); } // Genre filter (case-insensitive regex) - if (query.getGenre() != null && !query.getGenre().trim().isEmpty()) { + if (query.genre() != null && !query.genre().trim().isEmpty()) { mongoQuery.addCriteria(Criteria.where(Movie.Fields.GENRES) - .regex(Pattern.compile(query.getGenre(), Pattern.CASE_INSENSITIVE))); + .regex(Pattern.compile(query.genre(), Pattern.CASE_INSENSITIVE))); } // Year filter - if (query.getYear() != null) { - mongoQuery.addCriteria(Criteria.where(Movie.Fields.YEAR).is(query.getYear())); + if (query.year() != null) { + mongoQuery.addCriteria(Criteria.where(Movie.Fields.YEAR).is(query.year())); } // Rating range filter - if (query.getMinRating() != null || query.getMaxRating() != null) { + if (query.minRating() != null || query.maxRating() != null) { Criteria ratingCriteria = Criteria.where(Movie.Fields.IMDB_RATING); - if (query.getMinRating() != null) { - ratingCriteria = ratingCriteria.gte(query.getMinRating()); + if (query.minRating() != null) { + ratingCriteria = ratingCriteria.gte(query.minRating()); } - if (query.getMaxRating() != null) { - ratingCriteria = ratingCriteria.lte(query.getMaxRating()); + if (query.maxRating() != null) { + ratingCriteria = ratingCriteria.lte(query.maxRating()); } mongoQuery.addCriteria(ratingCriteria); } @@ -491,12 +491,11 @@ public List getMoviesByYearWithStats() { // Round average rating to 2 decimal places return results.getMappedResults().stream() - .peek(result -> { - if (result.getAverageRating() != null) { - result.setAverageRating( - Math.round(result.getAverageRating() * 100.0) / 100.0 - ); + .map(result -> { + if (result.averageRating() != null) { + return result.withAverageRating(Math.round(result.averageRating() * 100.0) / 100.0); } + return result; }) .collect(Collectors.toList()); } @@ -547,12 +546,11 @@ public List getDirectorsWithMostMovies(Integer limit) // Round average rating to 2 decimal places return results.getMappedResults().stream() - .peek(result -> { - if (result.getAverageRating() != null) { - result.setAverageRating( - Math.round(result.getAverageRating() * 100.0) / 100.0 - ); + .map(result -> { + if (result.averageRating() != null) { + return result.withAverageRating(Math.round(result.averageRating() * 100.0) / 100.0); } + return result; }) .collect(Collectors.toList()); } @@ -614,8 +612,8 @@ public List searchMovies(MovieSearchRequest searchRequest) { } // Validate search operator - String operator = searchRequest.getSearchOperator() != null ? - searchRequest.getSearchOperator() : "must"; + String operator = searchRequest.searchOperator() != null ? + searchRequest.searchOperator() : "must"; if (!operator.equals("must") && !operator.equals("should") && !operator.equals("mustNot") && !operator.equals("filter")) { @@ -627,27 +625,27 @@ public List searchMovies(MovieSearchRequest searchRequest) { // Validate and set defaults for pagination int resultLimit = Math.clamp( - searchRequest.getLimit() != null ? searchRequest.getLimit() : 20, 1, 100 + searchRequest.limit() != null ? searchRequest.limit() : 20, 1, 100 ); int resultSkip = Math.max( - searchRequest.getSkip() != null ? searchRequest.getSkip() : 0, 0 + searchRequest.skip() != null ? searchRequest.skip() : 0, 0 ); // Build search phrases list java.util.List searchPhrases = new java.util.ArrayList<>(); // Add plot search if provided (using phrase operator) - if (searchRequest.getPlot() != null && !searchRequest.getPlot().trim().isEmpty()) { + if (searchRequest.plot() != null && !searchRequest.plot().trim().isEmpty()) { searchPhrases.add(new Document("phrase", new Document() - .append("query", searchRequest.getPlot().trim()) + .append("query", searchRequest.plot().trim()) .append("path", Movie.Fields.PLOT) )); } // Add fullplot search if provided (using phrase operator) - if (searchRequest.getFullplot() != null && !searchRequest.getFullplot().trim().isEmpty()) { + if (searchRequest.fullplot() != null && !searchRequest.fullplot().trim().isEmpty()) { searchPhrases.add(new Document("phrase", new Document() - .append("query", searchRequest.getFullplot().trim()) + .append("query", searchRequest.fullplot().trim()) .append("path", Movie.Fields.FULLPLOT) )); } @@ -658,8 +656,8 @@ public List searchMovies(MovieSearchRequest searchRequest) { // 2. text match without fuzzy (high score) - all terms present, exact spelling // 3. text match with fuzzy (lower score) - typo-tolerant fallback; update fuzzy settings as needed // For more details, see: https://www.mongodb.com/docs/atlas/atlas-search/operators-collectors/text/ - if (searchRequest.getDirectors() != null && !searchRequest.getDirectors().trim().isEmpty()) { - String directorsQuery = searchRequest.getDirectors().trim(); + if (searchRequest.directors() != null && !searchRequest.directors().trim().isEmpty()) { + String directorsQuery = searchRequest.directors().trim(); searchPhrases.add(new Document("compound", new Document() .append("should", java.util.Arrays.asList( // Highest score: exact phrase match @@ -686,8 +684,8 @@ public List searchMovies(MovieSearchRequest searchRequest) { } // Add writers search if provided (see directors comments for compound scoring hierarchy) - if (searchRequest.getWriters() != null && !searchRequest.getWriters().trim().isEmpty()) { - String writersQuery = searchRequest.getWriters().trim(); + if (searchRequest.writers() != null && !searchRequest.writers().trim().isEmpty()) { + String writersQuery = searchRequest.writers().trim(); searchPhrases.add(new Document("compound", new Document() .append("should", java.util.Arrays.asList( new Document("phrase", new Document() @@ -710,8 +708,8 @@ public List searchMovies(MovieSearchRequest searchRequest) { } // Add cast search if provided (see directors comments for compound scoring hierarchy) - if (searchRequest.getCast() != null && !searchRequest.getCast().trim().isEmpty()) { - String castQuery = searchRequest.getCast().trim(); + if (searchRequest.cast() != null && !searchRequest.cast().trim().isEmpty()) { + String castQuery = searchRequest.cast().trim(); searchPhrases.add(new Document("compound", new Document() .append("should", java.util.Arrays.asList( new Document("phrase", new Document() diff --git a/mflix/server/java-spring/src/test/java/com/mongodb/samplemflix/integration/README.md b/mflix/server/java-spring/src/test/java/com/mongodb/samplemflix/integration/README.md index a2fc932..ceae7b0 100644 --- a/mflix/server/java-spring/src/test/java/com/mongodb/samplemflix/integration/README.md +++ b/mflix/server/java-spring/src/test/java/com/mongodb/samplemflix/integration/README.md @@ -42,7 +42,7 @@ export MONGODB_URI="mongodb+srv://username:password@cluster.mongodb.net/sample_m Or use a `.env` file in the `server/java-spring` directory: ``` -MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/sample_mflix?retryWrites=true&w=majority +MONGODB_URI="mongodb+srv://username:password@cluster.mongodb.net/sample_mflix?retryWrites=true&w=majority" ``` ### Run the Tests diff --git a/mflix/server/java-spring/src/test/java/com/mongodb/samplemflix/service/MovieServiceTest.java b/mflix/server/java-spring/src/test/java/com/mongodb/samplemflix/service/MovieServiceTest.java index 8c0db87..6aa4f34 100644 --- a/mflix/server/java-spring/src/test/java/com/mongodb/samplemflix/service/MovieServiceTest.java +++ b/mflix/server/java-spring/src/test/java/com/mongodb/samplemflix/service/MovieServiceTest.java @@ -270,8 +270,8 @@ void testCreateMoviesBatch_Success() { // Assert assertNotNull(result); - assertEquals(2, result.getInsertedCount()); - assertNotNull(result.getInsertedIds()); + assertEquals(2, result.insertedCount()); + assertNotNull(result.insertedIds()); verify(movieRepository).saveAll(anyList()); } @@ -364,7 +364,7 @@ void testDeleteMovie_Success() { // Assert assertNotNull(result); - assertEquals(1L, result.getDeletedCount()); + assertEquals(1L, result.deletedCount()); verify(movieRepository).existsById(testId); verify(movieRepository).deleteById(testId); } @@ -474,11 +474,11 @@ void testGetMoviesWithMostRecentComments_Success() { // Assert assertNotNull(results); assertEquals(1, results.size()); - assertEquals("Test Movie", results.get(0).getTitle()); - assertEquals(2024, results.get(0).getYear()); - assertEquals(5, results.get(0).getTotalComments()); - assertNotNull(results.get(0).getRecentComments()); - assertEquals(1, results.get(0).getRecentComments().size()); + assertEquals("Test Movie", results.get(0).title()); + assertEquals(2024, results.get(0).year()); + assertEquals(5, results.get(0).totalComments()); + assertNotNull(results.get(0).recentComments()); + assertEquals(1, results.get(0).recentComments().size()); verify(mongoTemplate).aggregate(any(Aggregation.class), eq("movies"), eq(Document.class)); } @@ -523,7 +523,7 @@ void testGetMoviesByYearWithStats_Success() { MoviesByYearResult result1 = MoviesByYearResult.builder() .year(2024) .movieCount(10) - .averageRating(7.5) + .averageRating(7.501) .highestRating(9.0) .lowestRating(6.0) .totalVotes(5000L) @@ -550,10 +550,10 @@ void testGetMoviesByYearWithStats_Success() { // Assert assertNotNull(results); assertEquals(2, results.size()); - assertEquals(2024, results.get(0).getYear()); - assertEquals(10, results.get(0).getMovieCount()); - assertEquals(7.5, results.get(0).getAverageRating()); - assertEquals(2023, results.get(1).getYear()); + assertEquals(2024, results.get(0).year()); + assertEquals(10, results.get(0).movieCount()); + assertEquals(7.5, results.get(0).averageRating()); + assertEquals(2023, results.get(1).year()); verify(mongoTemplate).aggregate(any(Aggregation.class), eq("movies"), eq(MoviesByYearResult.class)); } @@ -587,10 +587,10 @@ void testGetDirectorsWithMostMovies_Success() { // Assert assertNotNull(results); assertEquals(2, results.size()); - assertEquals("Christopher Nolan", results.get(0).getDirector()); - assertEquals(10, results.get(0).getMovieCount()); - assertEquals(8.5, results.get(0).getAverageRating()); - assertEquals("Steven Spielberg", results.get(1).getDirector()); + assertEquals("Christopher Nolan", results.get(0).director()); + assertEquals(10, results.get(0).movieCount()); + assertEquals(8.5, results.get(0).averageRating()); + assertEquals("Steven Spielberg", results.get(1).director()); verify(mongoTemplate).aggregate(any(Aggregation.class), eq("movies"), eq(DirectorStatisticsResult.class)); } @@ -634,8 +634,8 @@ void testUpdateMoviesBatch_Success() { // Assert assertNotNull(result); - assertEquals(5L, result.getMatchedCount()); - assertEquals(5L, result.getModifiedCount()); + assertEquals(5L, result.matchedCount()); + assertEquals(5L, result.modifiedCount()); verify(mongoTemplate).updateMulti(any(Query.class), any(org.springframework.data.mongodb.core.query.Update.class), eq(Movie.class)); } @@ -679,7 +679,7 @@ void testDeleteMoviesBatch_Success() { // Assert assertNotNull(result); - assertEquals(10L, result.getDeletedCount()); + assertEquals(10L, result.deletedCount()); verify(mongoTemplate).remove(any(Query.class), eq(Movie.class)); } diff --git a/mflix/server/js-express/.env.example b/mflix/server/js-express/.env.example index 8b264f6..c8ac03d 100644 --- a/mflix/server/js-express/.env.example +++ b/mflix/server/js-express/.env.example @@ -1,6 +1,6 @@ # MongoDB Connection # Replace with your MongoDB Atlas connection string or local MongoDB URI -MONGODB_URI=mongodb+srv://:@.mongodb.net/sample_mflix?retryWrites=true&w=majority +MONGODB_URI="mongodb+srv://:@.mongodb.net/sample_mflix?retryWrites=true&w=majority" # OPTIONAL: Voyage AI Configuration (required for Vector Search) # Get your API key from https://www.voyageai.com/ diff --git a/mflix/server/python-fastapi/.env.example b/mflix/server/python-fastapi/.env.example index f0d77b2..4dc3fe8 100644 --- a/mflix/server/python-fastapi/.env.example +++ b/mflix/server/python-fastapi/.env.example @@ -1,6 +1,6 @@ # MongoDB Connection # Replace with your MongoDB Atlas connection string or local MongoDB URI -MONGODB_URI=mongodb+srv://:@.mongodb.net/sample_mflix?retryWrites=true&w=majority +MONGODB_URI="mongodb+srv://:@.mongodb.net/sample_mflix?retryWrites=true&w=majority" # OPTIONAL: Voyage AI Configuration (required for Vector Search) # Get your API key from https://www.voyageai.com/