Skip to content

Commit 24b407a

Browse files
Dachary feedback
1 parent dbb1dbf commit 24b407a

5 files changed

Lines changed: 17 additions & 253 deletions

File tree

client/app/lib/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ export async function fetchMoviesWithComments(
410410
const queryParams = new URLSearchParams();
411411
queryParams.append('limit', limit.toString());
412412
if (movieId) {
413-
queryParams.append('movie_id', movieId);
413+
queryParams.append('movieId', movieId);
414414
}
415415

416416
console.log(`Fetching comments from: ${API_BASE_URL}/api/movies/aggregations/reportingByComments?${queryParams}`);

server/express/src/controllers/movieController.ts

Lines changed: 4 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -719,8 +719,8 @@ export async function vectorSearchMovies(req: Request, res: Response): Promise<v
719719
return;
720720
}
721721

722-
// Validate and set limit (default: 10, min: 1, max: 50)
723-
const limitNum = Math.min(Math.max(parseInt(limit as string) || 10, 1), 50);
722+
// Validate and set limit (default: 20, min: 1, max: 50)
723+
const limitNum = Math.min(Math.max(parseInt(limit as string) || 20, 1), 50);
724724

725725
try {
726726
// Generate embedding using Voyage AI REST API
@@ -736,7 +736,7 @@ export async function vectorSearchMovies(req: Request, res: Response): Promise<v
736736
index: "vector_index",
737737
path: "plot_embedding_voyage_3_large",
738738
queryVector: queryVector,
739-
numCandidates: limitNum * 15, // Search more candidates for better results
739+
numCandidates: limitNum * 20, // We recommend searching 20 times higher than the limit to improve result relevance
740740
limit: limitNum,
741741
},
742742
},
@@ -847,121 +847,6 @@ export async function vectorSearchMovies(req: Request, res: Response): Promise<v
847847
}
848848
}
849849

850-
/**
851-
* GET /api/movies/find-similar-movies
852-
*
853-
* Find similar movies using vector search based on a movie ID.
854-
* Demonstrates finding semantically similar movies using plot embeddings.
855-
*/
856-
export async function findSimilarMovies(req: Request, res: Response): Promise<void> {
857-
const { movieId, limit = "10" } = req.query;
858-
859-
// Validate movieId parameter
860-
if (!movieId || typeof movieId !== "string") {
861-
res
862-
.status(400)
863-
.json(
864-
createErrorResponse(
865-
"Query parameter 'movieId' is required",
866-
"MISSING_MOVIE_ID"
867-
)
868-
);
869-
return;
870-
}
871-
872-
if (!ObjectId.isValid(movieId)) {
873-
res
874-
.status(400)
875-
.json(
876-
createErrorResponse("Invalid movie ID format", "INVALID_OBJECT_ID")
877-
);
878-
return;
879-
}
880-
881-
// Validate and set limit (default: 10, min: 1, max: 50)
882-
const limitNum = Math.min(Math.max(parseInt(limit as string) || 10, 1), 50);
883-
884-
try {
885-
const embeddedMoviesCollection = getCollection("embedded_movies");
886-
887-
// First, get the target movie's embedding
888-
const targetMovie = await embeddedMoviesCollection.findOne({
889-
_id: new ObjectId(movieId),
890-
});
891-
892-
if (!targetMovie) {
893-
res
894-
.status(404)
895-
.json(createErrorResponse("Movie not found", "MOVIE_NOT_FOUND"));
896-
return;
897-
}
898-
899-
if (!targetMovie.plot_embedding_voyage_3_large) {
900-
res
901-
.status(400)
902-
.json(
903-
createErrorResponse(
904-
"Movie does not have embedding data",
905-
"NO_EMBEDDING_DATA"
906-
)
907-
);
908-
return;
909-
}
910-
911-
// Use the target movie's embedding to find similar movies
912-
const pipeline = [
913-
{
914-
$vectorSearch: {
915-
index: "vector_index",
916-
path: "plot_embedding_voyage_3_large",
917-
queryVector: targetMovie.plot_embedding_voyage_3_large,
918-
numCandidates: (limitNum + 1) * 15, // +1 to account for self-match
919-
limit: limitNum + 1, // +1 to exclude self from results
920-
},
921-
},
922-
{
923-
$match: {
924-
_id: { $ne: new ObjectId(movieId) }, // Exclude the original movie
925-
},
926-
},
927-
{
928-
$limit: limitNum,
929-
},
930-
{
931-
$project: {
932-
_id: 1,
933-
title: 1,
934-
year: 1,
935-
plot: 1,
936-
fullplot: 1,
937-
genres: 1,
938-
directors: 1,
939-
cast: 1,
940-
poster: 1,
941-
imdb: 1,
942-
},
943-
},
944-
];
945-
946-
const results = await embeddedMoviesCollection.aggregate(pipeline).toArray();
947-
948-
res.json(
949-
createSuccessResponse(results, `Found ${results.length} similar movies`)
950-
);
951-
} catch (error) {
952-
console.error("Find similar movies error:", error);
953-
res
954-
.status(500)
955-
.json(
956-
createErrorResponse(
957-
"Failed to find similar movies",
958-
"SIMILAR_MOVIES_ERROR",
959-
error instanceof Error ? error.message : "Unknown error"
960-
)
961-
);
962-
}
963-
}
964-
965850
/**
966851
* GET /api/movies/aggregations/comments
967852
*
@@ -1079,7 +964,7 @@ export async function getMoviesWithMostRecentComments(
1079964
title: result.title,
1080965
year: result.year,
1081966
genres: result.genres,
1082-
imdb: result.imdbRating ? { rating: result.imdbRating } : undefined,
967+
imdbRating: result.imdbRating,
1083968
recentComments: result.recentComments.map((comment: AggregationComment) => ({
1084969
_id: comment._id?.toString(),
1085970
userName: comment.userName,

server/express/src/routes/movies.ts

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,36 +46,28 @@ router.get("/search", asyncHandler(movieController.searchMovies));
4646
router.get("/vector-search", asyncHandler(movieController.vectorSearchMovies));
4747

4848
/**
49-
* GET /api/movies/find-similar-movies
50-
*
51-
* Find similar movies using vector search based on a movie ID.
52-
* Demonstrates finding semantically similar movies using plot embeddings.
53-
*/
54-
router.get("/find-similar-movies", asyncHandler(movieController.findSimilarMovies));
55-
56-
/**
57-
* GET /api/movies/aggregations/comments
49+
* GET /api/movies/aggregations/reportingByComments
5850
*
5951
* Aggregate movies with their most recent comments.
6052
* Demonstrates MongoDB $lookup aggregation to join collections.
6153
*/
62-
router.get("/aggregations/comments", asyncHandler(movieController.getMoviesWithMostRecentComments));
54+
router.get("/aggregations/reportingByComments", asyncHandler(movieController.getMoviesWithMostRecentComments));
6355

6456
/**
65-
* GET /api/movies/aggregations/years
57+
* GET /api/movies/aggregations/reportingByYear
6658
*
6759
* Aggregate movies by year with statistics.
6860
* Demonstrates MongoDB $group aggregation for statistical calculations.
6961
*/
70-
router.get("/aggregations/years", asyncHandler(movieController.getMoviesByYearWithStats));
62+
router.get("/aggregations/reportingByYear", asyncHandler(movieController.getMoviesByYearWithStats));
7163

7264
/**
73-
* GET /api/movies/aggregations/directors
65+
* GET /api/movies/aggregations/reportingByDirectors
7466
*
7567
* Aggregate directors with the most movies.
7668
* Demonstrates MongoDB $unwind and $group for array aggregation.
7769
*/
78-
router.get("/aggregations/directors", asyncHandler(movieController.getDirectorsWithMostMovies));
70+
router.get("/aggregations/reportingByDirectors", asyncHandler(movieController.getDirectorsWithMostMovies));
7971

8072
/**
8173
* GET /api/movies/:id

server/express/src/types/index.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,7 @@ export interface MovieWithCommentsResult {
232232
plot?: string;
233233
poster?: string;
234234
genres?: string[];
235-
imdb?: {
236-
rating?: number;
237-
votes?: number;
238-
};
235+
imdbRating?: number;
239236
recentComments: CommentInfo[];
240237
totalComments: number;
241238
mostRecentCommentDate?: Date;

server/express/tests/controllers/movieController.test.ts

Lines changed: 5 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ import {
117117
findAndDeleteMovie,
118118
searchMovies,
119119
vectorSearchMovies,
120-
findSimilarMovies,
121120
getMoviesWithMostRecentComments,
122121
getMoviesByYearWithStats,
123122
getDirectorsWithMostMovies,
@@ -922,123 +921,14 @@ describe("Movie Controller Tests", () => {
922921
});
923922
});
924923

925-
describe("findSimilarMovies", () => {
924+
// ==================== AGGREGATION ENDPOINTS TESTS ====================
925+
926+
describe("getMoviesWithMostRecentComments", () => {
926927
beforeEach(() => {
927-
// Reset mocks specifically for findSimilarMovies tests
928+
// Reset mocks for aggregation tests to avoid interference from vector search tests
928929
mockToArray.mockReset();
929930
});
930931

931-
it("should successfully find similar movies", async () => {
932-
const targetMovie = {
933-
_id: new ObjectId(TEST_MOVIE_ID),
934-
title: "Target Movie",
935-
plot_embedding_voyage_3_large: new Array(2048).fill(0.1),
936-
};
937-
938-
mockRequest.query = { movieId: TEST_MOVIE_ID, limit: "5" };
939-
mockFindOne.mockResolvedValue(targetMovie);
940-
mockToArray.mockResolvedValue(SAMPLE_SEARCH_RESULTS);
941-
942-
await findSimilarMovies(mockRequest as Request, mockResponse as Response);
943-
944-
expect(mockGetCollection).toHaveBeenCalledWith("embedded_movies");
945-
expect(mockFindOne).toHaveBeenCalledWith({
946-
_id: new ObjectId(TEST_MOVIE_ID),
947-
});
948-
expect(mockAggregate).toHaveBeenCalled();
949-
expect(mockCreateSuccessResponse).toHaveBeenCalledWith(
950-
SAMPLE_SEARCH_RESULTS,
951-
"Found 2 similar movies"
952-
);
953-
});
954-
955-
it("should return 400 when movieId is missing", async () => {
956-
mockRequest.query = {};
957-
958-
await findSimilarMovies(mockRequest as Request, mockResponse as Response);
959-
960-
expectErrorResponse(
961-
mockStatus,
962-
mockJson,
963-
400,
964-
"Query parameter 'movieId' is required",
965-
"MISSING_MOVIE_ID"
966-
);
967-
});
968-
969-
it("should return 400 for invalid movieId format", async () => {
970-
mockRequest.query = { movieId: INVALID_MOVIE_ID };
971-
972-
await findSimilarMovies(mockRequest as Request, mockResponse as Response);
973-
974-
expectErrorResponse(
975-
mockStatus,
976-
mockJson,
977-
400,
978-
"Invalid movie ID format",
979-
"INVALID_OBJECT_ID"
980-
);
981-
});
982-
983-
it("should return 404 when movie not found", async () => {
984-
mockRequest.query = { movieId: TEST_MOVIE_ID };
985-
mockFindOne.mockResolvedValue(null);
986-
987-
await findSimilarMovies(mockRequest as Request, mockResponse as Response);
988-
989-
expectErrorResponse(
990-
mockStatus,
991-
mockJson,
992-
404,
993-
"Movie not found",
994-
"MOVIE_NOT_FOUND"
995-
);
996-
});
997-
998-
it("should return 400 when movie has no embedding data", async () => {
999-
const targetMovie = {
1000-
_id: new ObjectId(TEST_MOVIE_ID),
1001-
title: "Target Movie",
1002-
// No plot_embedding_voyage_3_large field
1003-
};
1004-
1005-
mockRequest.query = { movieId: TEST_MOVIE_ID };
1006-
mockFindOne.mockResolvedValue(targetMovie);
1007-
1008-
await findSimilarMovies(mockRequest as Request, mockResponse as Response);
1009-
1010-
expectErrorResponse(
1011-
mockStatus,
1012-
mockJson,
1013-
400,
1014-
"Movie does not have embedding data",
1015-
"NO_EMBEDDING_DATA"
1016-
);
1017-
});
1018-
1019-
it("should use default limit when not provided", async () => {
1020-
const targetMovie = {
1021-
_id: new ObjectId(TEST_MOVIE_ID),
1022-
title: "Target Movie",
1023-
plot_embedding_voyage_3_large: new Array(2048).fill(0.1),
1024-
};
1025-
1026-
mockRequest.query = { movieId: TEST_MOVIE_ID };
1027-
mockFindOne.mockResolvedValue(targetMovie);
1028-
mockToArray.mockResolvedValue([]);
1029-
1030-
await findSimilarMovies(mockRequest as Request, mockResponse as Response);
1031-
1032-
expect(mockCreateSuccessResponse).toHaveBeenCalledWith(
1033-
[],
1034-
"Found 0 similar movies"
1035-
);
1036-
});
1037-
});
1038-
1039-
// ==================== AGGREGATION ENDPOINTS TESTS ====================
1040-
1041-
describe("getMoviesWithMostRecentComments", () => {
1042932
it("should successfully get movies with comments", async () => {
1043933
mockRequest.query = { limit: "10" };
1044934
mockToArray.mockResolvedValue(SAMPLE_COMMENTS_AGGREGATION);
@@ -1056,7 +946,7 @@ describe("Movie Controller Tests", () => {
1056946
title: result.title,
1057947
year: result.year,
1058948
genres: result.genres,
1059-
imdb: result.imdbRating ? { rating: result.imdbRating } : undefined,
949+
imdbRating: result.imdbRating,
1060950
recentComments: result.recentComments.map((comment) => ({
1061951
_id: comment._id?.toString(),
1062952
userName: comment.userName,

0 commit comments

Comments
 (0)