Skip to content

Conversation

@shuangela
Copy link
Collaborator

@shuangela shuangela commented Oct 24, 2025

Basic crud functionality and aggregation pipelines for python backend

This PR introduces basic CRUD functionality and aggregation pipelines for the FastAPI application. It sets up find, insert, delete, and find and delete operations. It also adds aggregations for movies by genre, by year, most recent comments (joining the movies collection with the comments collection), and by director.

Key Changes

  • Added CRUD functionality
  • Added four aggregation pipelines

Testing

  • Verified all endpoints locally via FastAPI docs (/docs)
  • Confirmed database connection and data persistence
  • Checked error responses for validation and connection issues

@shuangela shuangela changed the title Crud and Aggregations for Python feat: crud functionality and aggregations for python backend Oct 24, 2025
Copy link
Collaborator

@cbullinger cbullinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a couple comments/questions. nice job!

@router.get("/{id}", response_model=SuccessResponse[Movie])
async def get_movie_by_id(id: str):
# Validate ObjectId format
object_id = ObjectId(id)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we wrap this in a try-except? i.e. what happens if id isn't valid?

# Use findOne() to get a single document by _id
movie = await db.movies.find_one({"_id": object_id})

movie["_id"] = str(movie["_id"]) # Convert ObjectId to string
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same - what happens if movie is None?

print(f"Database name: {db.name if hasattr(db, 'name') else 'unknown'}")
print(f"Collection name: movies")

# For motor (async MongoDB driver), we need to await the aggregate call
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we weren't using Motor (instead using PyMongo's native async)

Suggested change
# For motor (async MongoDB driver), we need to await the aggregate call
# For async PyMongo driver, we need to await the aggregate call

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, we aren't using motor. Just async within the PyMongo driver.


# For motor (async MongoDB driver), we need to await the aggregate call
cursor = await db.movies.aggregate(pipeline)
results = await cursor.to_list(length=None) # Convert cursor to list
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want to point out why we're using to_list() for aggregations vs. async for for find queries (what you did in lines 105-108)

cursor = await db.movies.aggregate(pipeline)
results = await cursor.to_list(length=None) # Convert cursor to list

print(f"Aggregation returned {len(results)} results") # Debug logging
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a ticket to add proper logging?

Copy link
Collaborator

@tmcneil-mdb tmcneil-mdb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job!
These are some minor changes. Mostly to keep the code similar and adding in validation. I didn't get to the end of the file. I will get to find and delete on Monday.

I havent written an aggregation yet, so I might leave those for now. I will ping you, if I get to them.

object_id = ObjectId(id)

# Use findOne() to get a single document by _id
movie = await db.movies.find_one({"_id": object_id})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To grab the db, I added a function called get_collection from mongo_client.py file to make unit testing easier later. I am calling the db like this in the rest of the functions:

movies_collection = get_collection("movies")

genre (str): The genre of the movie.
year (int): The year the movie was released.
min_rating (float): The minimum IMDB rating.
max_rating (float): The maximum IMDB rating.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The request body for this the CreateMovieRequest object.

result = await db.movies.insert_one(movie_data)

# Retrieve the created document to return complete data
created_movie = await db.movies.find_one({"_id": result.inserted_id})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to verify that the document was created before querying it. A check that result is acknowledged.

SuccessResponse[Movie]: A response object containing the created movie data.
"""

@router.post("/", response_model=SuccessResponse[CreateMovieRequest], status_code=201)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be:
response_model=SuccessResponse[Movie]

We are returning the movie


@router.delete("/{id}", response_model=SuccessResponse[dict])
async def delete_movie_by_id(id: str):
object_id = ObjectId(id)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap in a try/catch. Id might not be valid.

result = await db.movies.delete_one({"_id": object_id})

if result.deleted_count == 0:
raise HTTPException(status_code=404, detail="Movie not found")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets use the create_error_response() to keep the errors consistent.

object_id = ObjectId(id)

# Use deleteOne() to remove a single document
result = await db.movies.delete_one({"_id": object_id})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above about accessing the db.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants