🎥 Movie Recommendation Tool
Authors:
Anna Hovakimyan
Lili Kostanyan
Created: 09/05/2025
Capstone — BS in Data Science, American University of Armenia
- Overview
This project presents a content-based movie recommendation system that leverages natural language processing, semantic search, and genre filtering to deliver relevant and engaging movie suggestions. Users interact through a web interface built with Streamlit, supported by a backend powered by FastAPI, Elasticsearch, and Google BigQuery.
At its core, the system encodes user-provided plot descriptions using Sentence-BERT (SBERT) embeddings and performs a hybrid search — combining semantic similarity (cosine distance on vector embeddings) and lexical relevance (BM25 scoring) in a 70:30 ratio. The interface allows users to apply strict or relaxed genre filters, choose how many recommendations to receive, and even interact via a conversational Gemini AI assistant for more natural queries.
Movie synopses are preprocessed, chunked, and embedded before being indexed in Elasticsearch for fast retrieval. Final recommendations are enriched with metadata (such as titles, ratings, posters, and actors) fetched from Google BigQuery to provide a visually rich and informative experience.
The proposed system demonstrates how hybrid embedding-based retrieval, combined with genre filtering, can offer accurate, flexible, and user-friendly movie recommendations.
- Goal
To recommend movies based on:
- A user-provided description or preference
- Genre-based filtering (strict or relaxed)
- An optional conversational AI assistant powered by Gemini API
- Key Features
- Hybrid Retrieval: Combines semantic similarity (SBERT embeddings) with BM25 ranking.
- Interactive UI: Streamlit app lets users select genres, adjust filters, and input free-text queries.
- Genre Filtering: Supports both strict (AND) and relaxed (OR) filtering across genres.
- Two Modes of Recommendation:
- Our Model (SBERT + BM25)
- Gemini AI (chatbot-style experience)
- Visual Results: Includes movie posters, actors, ratings, and summaries.
- Scalable Infrastructure: All components containerized using Docker.
- Architecture
- ETL Service: Loads vectorized
.jsonmovie synopses into Elasticsearch. - Elasticsearch: Stores embeddings and handles similarity search.
- Kibana: Offers visual inspection of indexed data.
- Backend: FastAPI app that performs semantic search and data enrichment from BigQuery.
- Frontend: Streamlit UI for user interaction and displaying recommendations.
- Technology Stack
- Python 3.10+
- Playwright, BeautifulSoup, SBERT, and aiohttp (for scraping and preprocessing)
- Google BigQuery
- Sentence-BERT (SentenceTransformers)
- Docker & Docker Compose
- Elasticsearch + Kibana
- FastAPI
- Streamlit
- Gemini AI (optional)
- Project Structure
Movie_Rec_Tool/
├── app/
│ ├── etl/ # Data loader for Elasticsearch
│ ├── back/ # FastAPI backend
│ ├── front/ # Streamlit frontend
│ ├── pipeline/ # Chunking, embedding, uploading logic
│ ├── docker-compose.yml
│ └── client_secrets/ # Google credentials
└──
- More detailed:
Movie_Rec_Tool/
├── app/
│ ├── etl/
│ │ ├── .env (NOT committed)
│ │ ├── .env.example (committed)
│ │ ├── load.py
│ │ ├── Dockerfile
│ │ ├── requirements.txt
│ │ └── ... # Other helper modules
│ ├── back/
│ │ ├── .env (NOT committed)
│ │ ├── .env.example (committed)
│ │ ├── main.py
│ │ ├── Dockerfile
│ │ ├── requirements.txt
│ │ └── ... # Other backend modules
│ ├── front/
│ │ ├── .env (NOT committed)
│ │ ├── .env.example (committed)
│ │ ├── app.py
│ │ ├── Dockerfile
│ │ ├── requirements.txt
│ │ └── ... # UI logic and styling
│ ├── pipeline/
│ │ ├── .env (NOT committed)
│ │ ├── .env.example (committed)
│ │ ├── main_pipeline.py
│ │ └── ... # All data processing and embedding scripts
│ └── docker-compose.yml # Defines and runs multi-container Docker services
│
├── client_secrets/ # (NOT committed)
│ └── your-service-account.json # service account JSON file
│
├── README.md
└── ... # Additional helper files
- Installation
Before getting started, ensure you have the following:
- Clone the repository:
git clone https://github.com/lilykostanyan/Movie_Rec_Tool.git
- Open the project directory (Movie_Rec_Tool) and change directory:
cd app
- To run this project end-to-end, you will need the following files, which will be shared via a Google Drive link (inside
env.txtfor all of them):
a. .env files for each service (folder):
etl/.envback/.envfront/.envpipeline/.env
Place the .env files into their respective folders (etl/, back/, front/, pipeline/)
- Copy the example file:
cp .env.example .env
Open each .env file and fill in any missing values (lines with VARIABLE_NAME=)
b. Google service credentials:
- client_secrets/your-service-account.json
(this folder must be created manually in the app folder)
app/
├── client_secrets/
│ └── your-service-account.json
│ └── ...
└──
-
Open
back/.env,pipeline/.envand update this line: SERVICE_ACCOUNT_FILE=client_secrets/your-service-account.json -
Open the provided with a ZIP file containing preprocessed
.jsonfiles from Google Drive. -
Place the
.zipfile inside the following folder:
Example folder structure after placement:
app/
├── etl/
│ └── data/
│ │ └── jsons/
│ │ ├── move_data.zip
│ └── ...
└──
- Build and start the Docker containers:
docker-compose up --build
- Open new terminal:
cd app/pipeline
- From inside the project directory, install the required Python packages by running:
pip install -r requirements.txt
and then, install the necessary Playwright browser binaries:
playwright install
- Run the pipeline (no need to wait until Docker is fully up)
python main_pipeline.py
- Access the Application
After running docker-compose up --build, you can access the different parts of the application using the following URLs:
- Elasticsearch: http://localhost:9200
Elasticsearch stores all the synopses and powers the search functionality. Visiting this URL confirms the server is running (There should be some JSON output).
- Kibana Dashboard: http://localhost:5601
Kibana provides a UI to visually inspect and manage your Elasticsearch index: 1. Click on the menu icon (☰) in the top-left corner of the page. 2. Scroll down and click on Dev Tools. 3. In the console that appears, you can run queries like:
- counts documents in the index
GET movies-bm25-vector/_count
- retrieves documents from the index
GET movies-bm25-vector/_search
- FastAPI Backend: http://localhost:8000
The FastAPI backend handles the core recommendation logic and communication with Elasticsearch. Use the built-in Swagger UI to explore and test API endpoints.
Finally, open the Streamlit application.
- Streamlit Frontend: http://localhost:8501
This is the main user interface for the application.
- Choose between Gemini AI or Our Model
- Enter a custom movie description
- Optionally filter by genre
- See detailed movie suggestions with posters, ratings, actors, and synopsis previews
- Services (Docker Compose)
services:
elasticsearch:
container_name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:8.5.1
restart: always
ports:
- "9200:9200"
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- ES_JAVA_OPTS=-Xms512m -Xmx512m
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9200/_cluster/health"]
interval: 5s
timeout: 3s
retries: 20
volumes:
- esdata:/usr/share/elasticsearch/data
kibana:
container_name: kibana
restart: always
image: docker.elastic.co/kibana/kibana:8.5.1
ports:
- "5601:5601"
depends_on:
elasticsearch:
condition: service_healthy
environment:
- ELASTICSEARCH_URL=http://elasticsearch:9200
etl:
container_name: etl
build:
context: ./etl
dockerfile: Dockerfile
depends_on:
elasticsearch:
condition: service_healthy
restart: on-failure
env_file:
- ./etl/.env
volumes:
- ./etl/data:/etl/data
- ./shared_logs:/etl/logs
environment:
- PYTHONPATH=/app
back:
container_name: back
build:
context: ./back
dockerfile: Dockerfile
depends_on:
elasticsearch:
condition: service_healthy
env_file:
- ./back/.env
ports:
- "8000:8000"
volumes:
- ./back:/back
- ~/.cache/huggingface:/root/.cache/huggingface
- ./client_secrets/enduring-brace-451209-q3-35cf0810d57c.json:/back/client_secrets/enduring-brace-451209-q3-35cf0810d57c.json:ro
- ./shared_logs:/back/logs
environment:
- PYTHONPATH=/app
front:
container_name: front
build:
context: ./front
dockerfile: Dockerfile
env_file:
- ./front/.env
ports:
- "8501:8501"
volumes:
- ./front:/front
- ./shared_logs:/app/shared_logs
environment:
- PYTHONPATH=/app
depends_on:
- back
volumes:
esdata:
Note:
Before accessing any URLs, make sure:
- Docker is running on your machine
- You’ve created a
.envfile based on the provided.env.example- All required environment variables are filled in correctly —
(if you see a line likeVARIABLE_NAME=with nothing after the=, it means
you still need to enter a value manually)- Your Google service account JSON file is placed inside a folder named
client_secrets/(this folder must be created manually)- The variable
SERVICE_ACCOUNT_FILEin your.envfile points to the correct path (e.g.,client_secrets/your-service-account.json)