Example solution: `/info`, `/search` and `/movie/{movie_id}` endpoints
Here are example solutions for the first three endpoints, with explanations of key concepts and implementation decisions.
Endpoint 1: /info - Dataset Information
Example Solution
@app.get("/info", response_model=InfoResponse)
def get_dataset_info():
"""
Get basic information about the dataset
- Total movie count
- Some example movies
"""
try:
with connect_to_weaviate() as client:
movies = client.collections.use(CollectionName.MOVIES)
movies_count = len(movies)
sample_movies_response = movies.query.fetch_objects(limit=5).objects
sample_movies = [o.properties for o in sample_movies_response]
return InfoResponse(movies_count=movies_count, sample_movies=sample_movies)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
Key Points
len(collection): Efficiently get the total countfetch_objects(): Returns objects with additional metadata (e.g. UUID); and we extract.properties- Context manager:
with connect_to_weaviate()handles connection cleanup automatically - Error handling: The try/catch provides graceful error responses
Endpoint 2: /search - Hybrid Search with Filtering
Example Solution
@app.get("/search", response_model=SearchResponse)
def search_movies(
q: str = Query(..., description="Search query for movies"),
page: int = Query(1, ge=1, le=10, description="Page number (1-10)"),
year_min: Optional[int] = Query(
None, description="Filter by release year - from this year"
),
year_max: Optional[int] = Query(
None, description="Filter by release year - to this year"
),
):
"""
Search for movies using hybrid search
- Return 20 movies per page
- Support pagination, up to 10 pages
- Optional year filtering
"""
try:
if page >= 1:
offset = PAGE_SIZE * (page - 1)
filters = None
if year_min and year_max:
filters = (
Filter.by_property("year").greater_or_equal(year_min)
& Filter.by_property("year").less_or_equal(year_max)
)
elif year_min:
filters = Filter.by_property("year").greater_or_equal(year_min)
elif year_max:
filters = Filter.by_property("year").less_or_equal(year_max)
with connect_to_weaviate() as client:
movies = client.collections.use(CollectionName.MOVIES)
response = movies.query.hybrid(
query=q,
offset=offset,
limit=PAGE_SIZE,
filters=filters,
target_vector="default",
)
return SearchResponse(
movies=[o.properties for o in response.objects],
current_page=page,
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
Key Concepts
Hybrid search: Combines vector similarity (target_vector="default") with keyword matching for the best of both worlds.
Filter logic: Three cases handled:
- Both min and max: Combined with
&operator - Min only: Single greater-or-equal filter
- Max only: Single less-or-equal filter
- Neither:
filters=None(no filtering)
Pagination: Calculate offset based on page size to skip already-seen results.
Target vector: Specifies which vector to search for the semantic component of hybrid search.
Endpoint 3: /movie/{movie_id} - Movie Details and Similarity
Example Solution
@app.get("/movie/{movie_id}", response_model=MovieDetailResponse)
def get_movie_details(movie_id: str):
"""
Get detailed information about a specific movie, using the Weaviate object UUID
- Returns movie metadata
- Returns top 15 most similar movies
"""
try:
with connect_to_weaviate() as client:
movies = client.collections.use(CollectionName.MOVIES)
movie = movies.query.fetch_objects(
filters=Filter.by_property("movie_id").equal(int(movie_id)), limit=1
).objects[0]
response = movies.query.near_object(
near_object=movie.uuid, target_vector="default", limit=PAGE_SIZE
)
similar_movies = [
o.properties for o in response.objects[1:] # Exclude itself
]
return MovieDetailResponse(
movie=movie.properties, similar_movies=similar_movies
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
Key Concepts
Property filtering: Use equal() to find objects by specific field values. Note the type conversion to int(movie_id) to match the expected type.
Near-object search: Uses the object's UUID to find similar items.
UUID usage: Weaviate uses UUIDs as the unique object identifier in each collection. The movie.uuid gives you direct access.
Result exclusion: Near-object search includes the original object as the first result (distance 0), so we skip it with [1:].
Understanding the Patterns
These three endpoints demonstrate core patterns you'll use throughout Weaviate applications:
- Collection access: Fetch (
.use()) the collection first, then perform operations - Query response management: Each query includes a
.objectsattribute; each of which includes properties (.properties) and metadata (e.g. uuid / vectors if requested) - Filter construction: Construct filters step-by-step for complex conditions
- Vector targeting: Specify which vector to use for different search strategies
- Error handling: Wrap operations in try/catch for robust APIs
Now that you've mastered the fundamentals, let's implement the more advanced endpoints that demonstrate targeted vector search and AI-powered recommendations using RAG.