Design a Bookshelf with Book CRUD and a Stable Bookmark
GoogleSoftware Engineer · OnsiteLast updated
Tests object-oriented design, separation of entity identity from physical layout, reference integrity during deletions, and optimistic concurrency for content updates.
Problem Statement
Design an in-memory bookshelf management system that supports book lifecycle operations (CRUD), custom positional ordering on the shelf, and stable bookmarks that track a reader's progress.
Your design must cleanly decouple a book's immutable identity (book_id) from its mutable layout position (index on the shelf). In addition, bookmarks must reference books by their stable identifier so that reordering books does not break bookmarks, and deleting a book avoids dangling or corrupt bookmark states.
Implement the Bookshelf class with the following methods:
class Bookshelf:
def __init__(self):
pass
def add_book(self, book_id: str, title: str, content: str, position: int | None = None) -> bool:
"""Adds a book. If position is None, append to the end. Returns False if book_id exists or position is invalid."""
pass
def get_book(self, book_id: str) -> dict | None:
"""Returns book data {'id': str, 'title': str, 'content': str, 'version': int} or None."""
pass
def update_book(self, book_id: str, new_title: str | None, new_content: str | None, expected_version: int) -> bool:
"""Updates title/content using optimistic concurrency. Increments version on success. Returns False on mismatch or missing book."""
pass
def remove_book(self, book_id: str) -> bool:
"""Removes the book and cascades deletion/invalidation to all associated bookmarks. Returns False if not found."""
pass
def move_book(self, book_id: str, new_position: int) -> bool:
"""Moves book to a new 0-indexed position [0, N-1]. Returns False if invalid."""
pass
def get_order(self) -> list[str]:
"""Returns ordered list of book_id values on the shelf."""
pass
def set_bookmark(self, bookmark_id: str, book_id: str, char_offset: int) -> bool:
"""Sets or updates a bookmark. Returns False if book_id does not exist or offset is out of range [0, len(content)]."""
pass
def get_bookmark(self, bookmark_id: str) -> dict | None:
"""Returns {'bookmark_id': str, 'book_id': str, 'char_offset': int} or None if not found or invalidated."""
pass
Example
shelf = Bookshelf()
shelf.add_book("b1", "Title 1", "Hello World") # Returns True; order: ['b1']
shelf.add_book("b2", "Title 2", "Sample Content") # Returns True; order: ['b1', 'b2']
# Create bookmark at offset 6 ('W')
shelf.set_bookmark("mark1", "b1", 6) # Returns True
# Move b2 to position 0
shelf.move_book("b2", 0) # Returns True; order: ['b2', 'b1']
# Bookmark still correctly points to b1
assert shelf.get_bookmark("mark1")["book_id"] == "b1"
# Optimistic concurrency update
shelf.update_book("b1", None, "Hello Universe", expected_version=1) # Returns True, version becomes 2
shelf.update_book("b1", None, "Conflict", expected_version=1) # Returns False (version is now 2)
# Deleting book cleans up bookmarks
shelf.remove_book("b1") # Returns True
assert shelf.get_bookmark("mark1") is None # Bookmark removed/invalidated
Constraints & Edge Cases
- Book titles and content are non-empty strings.
char_offsetmust be within0 <= char_offset <= len(content). - Shelf positions are 0-indexed. If
positionis out of bounds[0, len(books)]duringadd_bookor[0, len(books)-1]duringmove_book, returnFalse. book_idandbookmark_idare unique alphanumeric strings.- Calling
remove_bookmust ensure subsequent calls toget_bookmarkfor associated bookmarks returnNone.
Hints
Hint 1
Separate storage into an ID-to-entity map for O(1) lookups and an ordered collection (such as a list) for shelf position.
Hint 2
Maintain a reverse index from `book_id` to its set of `bookmark_id`s so that cascading deletion does not require scanning all bookmarks.
Hint 3
Store an integer `version` field initialized to 1 on each book to implement optimistic concurrency control during updates.
Follow-up questions
- How would you migrate or adjust bookmark character offsets if book content is edited in-place rather than overwritten completely (e.g., using operational transforms or diff-match-patch)?
- If the shelf contains millions of books and reordering requires frequent arbitrary insertions, how would you optimize `move_book` to avoid O(N) list shifts?
- How would you extend this design to support thread-safe read/write operations under high concurrency?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.