Skip to content

Commit 1e9dc15

Browse files
committed
Acknowledge year limitation of sample dataset
1 parent babe6a7 commit 1e9dc15

13 files changed

Lines changed: 288 additions & 19 deletions

File tree

mflix/README-JAVA-SPRING.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@ This is a full-stack movie browsing application built with Java Spring Boot and
1414
└── mvnw
1515
```
1616

17+
## Data Limitations
18+
19+
The `sample_mflix` dataset contains movies released up to **2015**. Searching for movies from 2016 or later will return no results. This is a limitation of the sample dataset, not the application.
20+
1721
## Prerequisites
1822

1923
- **Java 21** or higher
2024
- **Node.js 20** or higher
2125
- **MongoDB Atlas cluster or local deployment** with the `sample_mflix` dataset loaded
22-
- [Load sample data](https://www.mongodb.com/docs/atlas/sample-data/)
26+
- [Load sample data](https://www.mongodb.com/docs/atlas/sample-data/)
2327
- **Maven** (included via Maven Wrapper)
2428
- **Voyage AI API key** (For MongoDB Vector Search)
2529
- [Get a Voyage AI API key](https://www.voyageai.com/)

mflix/README-JAVASCRIPT-EXPRESS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ This is a full-stack movie browsing application built with Express.js and Next.j
1414
└── tsconfig.json
1515
```
1616

17+
## Data Limitations
18+
19+
The `sample_mflix` dataset contains movies released up to **2015**. Searching for movies from 2016 or later will return no results. This is a limitation of the sample dataset, not the application.
20+
1721
## Prerequisites
1822

1923
- **Node.js 22** or higher

mflix/README-PYTHON-FASTAPI.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ This is a full-stack movie browsing application built with Python FastAPI and Ne
1717
└── requirements.txt
1818
```
1919

20+
## Data Limitations
21+
22+
The `sample_mflix` dataset contains movies released up to **2015**. Searching for movies from 2016 or later will return no results. This is a limitation of the sample dataset, not the application.
23+
2024
## Prerequisites
2125

2226
- **Python 3.10** to **Python 3.13**

mflix/client/app/components/FilterBar/FilterBar.module.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,21 @@
9393
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
9494
}
9595

96+
.inputWarning {
97+
border-color: #f59e0b;
98+
}
99+
100+
.inputWarning:focus {
101+
border-color: #f59e0b;
102+
box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.15);
103+
}
104+
105+
.yearWarning {
106+
font-size: 0.7rem;
107+
color: #b45309;
108+
margin-top: 0.25rem;
109+
}
110+
96111
.ratingGroup {
97112
display: flex;
98113
align-items: center;

mflix/client/app/components/FilterBar/FilterBar.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ const SORT_OPTIONS = [
1010
{ value: 'imdb.rating', label: 'IMDB Rating' },
1111
];
1212

13+
// The sample_mflix dataset only contains movies up to 2015
14+
const MAX_DATASET_YEAR = 2015;
15+
1316
interface FilterBarProps {
1417
onFilterChange: (filters: MovieFilterParams) => void;
1518
isLoading?: boolean;
@@ -137,14 +140,19 @@ export default function FilterBar({
137140
<label className={styles.filterLabel}>Year</label>
138141
<input
139142
type="number"
140-
className={styles.filterInput}
141-
placeholder="e.g. 2020"
143+
className={`${styles.filterInput} ${filters.year && filters.year > MAX_DATASET_YEAR ? styles.inputWarning : ''}`}
144+
placeholder="e.g. 2010"
142145
value={filters.year || ''}
143146
onChange={(e) => handleFilterChange('year', e.target.value ? parseInt(e.target.value) : undefined)}
144147
disabled={isLoading}
145148
min={1900}
146149
max={2030}
147150
/>
151+
{filters.year && filters.year > MAX_DATASET_YEAR && (
152+
<span className={styles.yearWarning}>
153+
Dataset only contains movies up to {MAX_DATASET_YEAR}
154+
</span>
155+
)}
148156
</div>
149157

150158
<div className={styles.filterGroup}>

mflix/client/app/components/MovieCard/MovieCard.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ import React from "react";
1414
* such as image error handling and selection checkbox.
1515
*/
1616

17+
/**
18+
* Validates that a poster URL is valid for Next.js Image component.
19+
* Must be an absolute URL (http/https) or a relative path starting with /
20+
*/
21+
const isValidPosterUrl = (url: string | undefined): boolean => {
22+
if (!url || typeof url !== 'string') return false;
23+
return url.startsWith('http://') || url.startsWith('https://') || url.startsWith('/');
24+
};
25+
1726
interface MovieCardProps {
1827
movie: Movie;
1928
isSelected?: boolean;
@@ -48,9 +57,9 @@ export default function MovieCard({ movie, isSelected = false, onSelectionChange
4857
)}
4958

5059
<div className={movieStyles.moviePoster}>
51-
{movie.poster ? (
60+
{isValidPosterUrl(movie.poster) ? (
5261
<Image
53-
src={movie.poster}
62+
src={movie.poster!}
5463
alt={`${movie.title} poster`}
5564
fill
5665
sizes="(max-width: 480px) 100vw, (max-width: 768px) 50vw, 280px"

mflix/client/app/movie/[id]/page.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ import { Movie } from '@/types/movie';
1111
import { ROUTES } from '@/lib/constants';
1212
import pageStyles from './page.module.css';
1313

14+
/**
15+
* Validates that a poster URL is valid for Next.js Image component.
16+
* Must be an absolute URL (http/https) or a relative path starting with /
17+
*/
18+
const isValidPosterUrl = (url: string | undefined): boolean => {
19+
if (!url || typeof url !== 'string') return false;
20+
return url.startsWith('http://') || url.startsWith('https://') || url.startsWith('/');
21+
};
22+
1423
interface MovieDetailsPageProps {
1524
params: Promise<{
1625
id: string;
@@ -202,7 +211,7 @@ export default function MovieDetailsPage({ params }: MovieDetailsPageProps) {
202211
) : (
203212
<div className={pageStyles.movieDetails}>
204213
<div className={pageStyles.posterSection}>
205-
{movie.poster ? (
214+
{isValidPosterUrl(movie.poster) ? (
206215
<div className={pageStyles.posterContainer}>
207216
<Image
208217
src={movie.poster!}

mflix/server/java-spring/src/main/java/com/mongodb/samplemflix/controller/MovieControllerImpl.java

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.mongodb.samplemflix.controller;
22

3+
import com.mongodb.samplemflix.exception.ValidationException;
34
import com.mongodb.samplemflix.model.Movie;
45
import com.mongodb.samplemflix.model.dto.BatchInsertResponse;
56
import com.mongodb.samplemflix.model.dto.BatchUpdateResponse;
@@ -85,7 +86,27 @@ public ResponseEntity<SuccessResponse<List<Movie>>> getAllMovies(
8586
@RequestParam(defaultValue = "title") String sortBy,
8687
@Parameter(description = "Sort order: 'asc' or 'desc' (default: asc)")
8788
@RequestParam(defaultValue = "asc") String sortOrder) {
88-
89+
90+
// The sample_mflix dataset only contains movies up to 2015
91+
final int MAX_DATASET_YEAR = 2015;
92+
final int MIN_VALID_YEAR = 1800;
93+
String yearWarning = null;
94+
95+
// Validate year if provided
96+
if (year != null) {
97+
if (year < MIN_VALID_YEAR) {
98+
throw new ValidationException(
99+
String.format("Invalid year: %d. Year must be %d or later.", year, MIN_VALID_YEAR)
100+
);
101+
}
102+
if (year > MAX_DATASET_YEAR) {
103+
yearWarning = String.format(
104+
"Note: The sample_mflix dataset only contains movies up to %d. Your search for year %d may return no results.",
105+
MAX_DATASET_YEAR, year
106+
);
107+
}
108+
}
109+
89110
MovieSearchQuery query = MovieSearchQuery.builder()
90111
.q(q)
91112
.genre(genre)
@@ -97,16 +118,22 @@ public ResponseEntity<SuccessResponse<List<Movie>>> getAllMovies(
97118
.sortBy(sortBy)
98119
.sortOrder(sortOrder)
99120
.build();
100-
121+
101122
List<Movie> movies = movieService.getAllMovies(query);
102-
123+
124+
// Build response message, including year warning if applicable
125+
String message = "Found " + movies.size() + " movies";
126+
if (yearWarning != null) {
127+
message = message + ". " + yearWarning;
128+
}
129+
103130
SuccessResponse<List<Movie>> response = SuccessResponse.<List<Movie>>builder()
104131
.success(true)
105-
.message("Found " + movies.size() + " movies")
132+
.message(message)
106133
.data(movies)
107134
.timestamp(Instant.now().toString())
108135
.build();
109-
136+
110137
return ResponseEntity.ok(response);
111138
}
112139

mflix/server/java-spring/src/test/java/com/mongodb/samplemflix/controller/MovieControllerTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,47 @@ void testGetAllMovies_WithQueryParams() throws Exception {
125125
.andExpect(jsonPath("$.data").isArray());
126126
}
127127

128+
@Test
129+
@DisplayName("GET /api/movies - Should return 400 for year before 1800")
130+
void testGetAllMovies_InvalidYearBefore1800() throws Exception {
131+
// Act & Assert
132+
mockMvc.perform(get("/api/movies")
133+
.param("year", "1700"))
134+
.andExpect(status().isBadRequest())
135+
.andExpect(jsonPath("$.success").value(false))
136+
.andExpect(jsonPath("$.error.code").value("VALIDATION_ERROR"));
137+
}
138+
139+
@Test
140+
@DisplayName("GET /api/movies - Should include warning for year after 2015")
141+
void testGetAllMovies_YearAfter2015IncludesWarning() throws Exception {
142+
// Arrange
143+
List<Movie> movies = Arrays.asList();
144+
when(movieService.getAllMovies(any(MovieSearchQuery.class))).thenReturn(movies);
145+
146+
// Act & Assert
147+
mockMvc.perform(get("/api/movies")
148+
.param("year", "2020"))
149+
.andExpect(status().isOk())
150+
.andExpect(jsonPath("$.success").value(true))
151+
.andExpect(jsonPath("$.message", containsString("2015")));
152+
}
153+
154+
@Test
155+
@DisplayName("GET /api/movies - Should not include warning for year 2015 or earlier")
156+
void testGetAllMovies_Year2015NoWarning() throws Exception {
157+
// Arrange
158+
List<Movie> movies = Arrays.asList(testMovie);
159+
when(movieService.getAllMovies(any(MovieSearchQuery.class))).thenReturn(movies);
160+
161+
// Act & Assert
162+
mockMvc.perform(get("/api/movies")
163+
.param("year", "2015"))
164+
.andExpect(status().isOk())
165+
.andExpect(jsonPath("$.success").value(true))
166+
.andExpect(jsonPath("$.message", not(containsString("sample_mflix"))));
167+
}
168+
128169
// ==================== GET MOVIE BY ID TESTS ====================
129170

130171
@Test

mflix/server/js-express/src/controllers/movieController.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,32 @@ export async function getAllMovies(req: Request, res: Response): Promise<void> {
8787
filter.genres = { $regex: new RegExp(genre, "i") };
8888
}
8989

90-
// Year filtering
90+
// Year filtering and validation
91+
// The sample_mflix dataset only contains movies up to 2015
92+
const MAX_DATASET_YEAR = 2015;
93+
const MIN_VALID_YEAR = 1800;
94+
let yearWarning: string | undefined;
95+
9196
if (year) {
92-
filter.year = parseInt(year);
97+
const yearNum = parseInt(year);
98+
99+
// Validate year is within reasonable bounds
100+
if (yearNum < MIN_VALID_YEAR) {
101+
res.status(400).json(
102+
createErrorResponse(
103+
`Invalid year: ${yearNum}. Year must be ${MIN_VALID_YEAR} or later.`,
104+
"INVALID_YEAR"
105+
)
106+
);
107+
return;
108+
}
109+
110+
// Warn if searching for years beyond the dataset's range
111+
if (yearNum > MAX_DATASET_YEAR) {
112+
yearWarning = `Note: The sample_mflix dataset only contains movies up to ${MAX_DATASET_YEAR}. Your search for year ${yearNum} may return no results.`;
113+
}
114+
115+
filter.year = yearNum;
93116
}
94117

95118
// Rating range filtering
@@ -131,8 +154,14 @@ export async function getAllMovies(req: Request, res: Response): Promise<void> {
131154
.skip(skipNum)
132155
.toArray();
133156

157+
// Build response message, including year warning if applicable
158+
let message = `Found ${movies.length} movies`;
159+
if (yearWarning) {
160+
message = `${message}. ${yearWarning}`;
161+
}
162+
134163
// Return successful response
135-
res.json(createSuccessResponse(movies, `Found ${movies.length} movies`));
164+
res.json(createSuccessResponse(movies, message));
136165
}
137166

138167
/**

0 commit comments

Comments
 (0)