Katkat

Add Backend Search and Filters to the Items API of a Library Lending System

InstacartSoftware Engineer · Online Assessment

Last updated

Implement and test backend search, multi-attribute filtering, sorting, and pagination logic for a library lending system's items catalog.

Problem Statement

You are tasked with extending the backend items API of a library lending management system. The frontend team needs to allow users to search inventory by title, filter items by media type, and toggle an "available now" filter.

Implement the function search_items(catalog, params) that filters, sorts, and paginates an in-memory catalog of library items according to user-supplied query parameters.

Function Signature

def search_items(catalog: list[dict], params: dict) -> dict:
    pass

Requirements

  1. Title Search (query): Optional string. Performs a case-insensitive substring match against item["title"]. Leading and trailing whitespace in query must be stripped. If missing, None, or empty after stripping, no title filtering is applied.
  2. Type Filter (item_type): Optional string. Exact case-insensitive match against item["type"] (e.g., "book", "audiobook", "magazine"). If missing, None, or empty, no type filtering is applied.
  3. Availability Filter (available_only): Optional boolean (default False). An item is available if item["copies_total"] - item["copies_borrowed"] > 0. If True, exclude all items with 0 available copies.
  4. Sorting (sort_by): Optional string (default "id"). Supported options:
    • "id": Sort by item id ascending.
    • "title": Sort by title alphabetically (case-insensitive), with ties broken by id ascending.
    • "available_copies": Sort by number of available copies descending, with ties broken by id ascending.
  5. Pagination (page, page_size):
    • page: 1-indexed integer (default 1).
    • page_size: Integer between 1 and 50 inclusive (default 10). Clamp values outside this range to the nearest boundary.
    • Return a dictionary with the following schema:
{
  "items": [...],
  "total_count": 0,
  "page": 1,
  "page_size": 10,
  "total_pages": 0
}

total_pages should be 0 if total_count is 0, otherwise ceil(total_count / page_size).

Constraints

  • 0 = 0), copies_borrowed(int >= 0, wherecopies_borrowed = 1`

Example

catalog = [
    {"id": 1, "title": "The Hobbit", "type": "book", "copies_total": 3, "copies_borrowed": 3},
    {"id": 2, "title": "Dune", "type": "book", "copies_total": 5, "copies_borrowed": 2},
    {"id": 3, "title": "Dune Part 2", "type": "audiobook", "copies_total": 2, "copies_borrowed": 0},
]

params = {
    "query": "dune",
    "item_type": "book",
    "available_only": True,
    "sort_by": "title",
    "page": 1,
    "page_size": 10,
}

# Output:
# {
#   "items": [{"id": 2, "title": "Dune", "type": "book", "copies_total": 5, "copies_borrowed": 2}],
#   "total_count": 1,
#   "page": 1,
#   "page_size": 10,
#   "total_pages": 1
# }

Hints

Hint 1

Normalize and sanitize incoming parameters first: strip whitespace from the search string, normalize types to lowercase, and clamp page_size between 1 and 50.

Hint 2

Apply all filter predicates in a single pass over the list before sorting to keep sorting overhead proportional to the filtered result set.

Hint 3

When sorting by title or available copies, construct a composite tuple key like `(primary_key, item['id'])` to ensure deterministic tie-breaking.

Follow-up questions

  • How would you translate these in-memory filtering and sorting rules into a SQL query with proper indexes for a table with millions of rows?
  • If the title search needed to handle typo tolerance (fuzzy matching) and multi-token queries across authors and titles, how would you redesign the search architecture?
  • How would you design a test suite covering boundary cases (e.g., requested page beyond total pages, zero-result queries, negative page sizes) to validate your endpoint without pre-existing automated tests?