457. Design Category Hierarchy System
Asked in
Design Category Hierarchy System

Design a category hierarchy system that stores categories, subcategories, and products. A category may contain both products and any number of subcategories.

The system must retrieve all products from a selected category and its descendant categories while applying optional filters. The filter design must allow new filter types to be added later without changing the public product-search method.

Class

CategoryHierarchySystem

Constructor

public CategoryHierarchySystem()

  • Initializes an empty system with no categories or products.

Methods

addCategory

public boolean addCategory( String categoryId, String categoryName, String parentCategoryId)

  • Adds a category using the supplied category ID and name.
  • An empty parentCategoryId creates a root category.
  • A nonempty parentCategoryId places the new category directly under the specified existing category.
  • Returns true when the category is added successfully.
  • Returns false when the category ID or name is blank, the category ID already exists, or the specified parent category does not exist.
  • A rejected call does not modify the hierarchy.

addProduct

public boolean addProduct( String productId, String categoryId, String productName, long priceInCents)

  • Adds a product directly to the specified existing category.
  • Each product belongs to exactly one category.
  • Returns true when the product is added successfully.
  • Returns false when the product ID, category ID, or product name is blank, the product ID already exists, the category does not exist, or priceInCents is negative.
  • A rejected call does not create or modify a product.

getProducts

public List<String> getProducts( String categoryId, List<String> filters)

  • Searches the specified category and all of its direct and indirect subcategories.
  • Applies every supplied filter to the products in that category subtree.
  • All filters use AND logic, so a product must satisfy every filter.
  • An empty filter list returns every product in the selected category subtree.
  • Returns an empty list when the category does not exist or any filter is invalid.
  • The method does not modify categories or products.

Hierarchy Rules

  • The system may contain multiple root categories.
  • A category must be added before any of its subcategories or products.
  • A category may simultaneously contain direct products and subcategories.
  • Because categories cannot be moved and a parent must already exist, category cycles cannot be created.
  • Category and product ID comparisons are case-sensitive.
  • Category names do not need to be unique.

Filter Rules

Each filter is represented by one string in one of these formats:

  • "nameEquals=<text>" requires the complete product name to equal the supplied text.
  • "nameContains=<text>" requires the product name to contain the supplied text as a contiguous substring.
  • "minPriceInCents=<value>" requires the product price to be greater than or equal to the supplied value.
  • "maxPriceInCents=<value>" requires the product price to be less than or equal to the supplied value.
  • Name filters are case-insensitive. English letters from A-Z are treated the same as their corresponding letters from a-z.
  • Price boundaries are inclusive.
  • The order of filters does not affect the result.
  • Each filter type may appear at most once in a call.
  • A filter is invalid when its format is malformed, its value is empty, its type is unsupported, its type is repeated, or its price value is not a nonnegative integer.
  • The filter list is also invalid when minPriceInCents is greater than maxPriceInCents.

Extensibility Requirement

  • Each filter type must be implemented as an independent product-matching rule or strategy.
  • Adding another filter type must not require changing the signature of getProducts, category traversal logic, or existing filter implementations.

Result Format and Ordering

  • Each returned product is represented as "productId,productName,categoryId,priceInCents".
  • Products are ordered by productId in ascending case-sensitive lexicographical order using Java's String.compareTo behavior.
  • Each matching product appears exactly once.

Constraints

  • All method arguments and all elements of filters are non-null.
  • A string is blank exactly when Java's String.isBlank() method returns true.
  • Successfully stored category IDs and product IDs contain between 1 and 100 letters, digits, underscores, or hyphens.
  • Successfully stored category and product names contain between 1 and 200 letters, digits, spaces, underscores, or hyphens.
  • 0 ≤ priceInCents ≤ 1,000,000,000 for every stored product.
  • 0 ≤ filters.size() ≤ 4
  • The category hierarchy depth is at most 1,000.
  • At most 100,000 categories and 100,000 products are stored in one system instance.
  • At most 100,000 public method calls are made on one system instance.
  • Across all getProducts calls, at most 200,000 product rows are returned.

Examples

Parameter names are shown in the calls for readability.

Example 1

CategoryHierarchySystem catalog = new CategoryHierarchySystem()

catalog.addCategory( categoryId = "store", categoryName = "Store", parentCategoryId = "")

Output: true

catalog.addCategory( categoryId = "electronics", categoryName = "Electronics", parentCategoryId = "store")

Output: true

catalog.addCategory( categoryId = "computers", categoryName = "Computers", parentCategoryId = "electronics")

Output: true

catalog.addCategory( categoryId = "accessories", categoryName = "Accessories", parentCategoryId = "electronics")

Output: true

catalog.addProduct( productId = "p40", categoryId = "electronics", productName = "Travel Charger", priceInCents = 2499)

Output: true

catalog.addProduct( productId = "p10", categoryId = "computers", productName = "Orbit Laptop", priceInCents = 85000)

Output: true

catalog.addProduct( productId = "p20", categoryId = "accessories", productName = "Laptop Sleeve", priceInCents = 1999)

Output: true

catalog.addProduct( productId = "p30", categoryId = "computers", productName = "Mini Desktop", priceInCents = 55000)

Output: true

catalog.getProducts( categoryId = "store", filters = List.of("nameContains=laptop"))

Output: ["p10,Orbit Laptop,computers,85000", "p20,Laptop Sleeve,accessories,1999"]

Both products match the case-insensitive name filter and belong to descendants of store.

catalog.getProducts( categoryId = "electronics", filters = List.of( "minPriceInCents=2000", "maxPriceInCents=60000"))

Output: ["p30,Mini Desktop,computers,55000", "p40,Travel Charger,electronics,2499"]

The result includes a product stored directly in electronics and another stored in its computers subcategory.

Example 2

CategoryHierarchySystem catalog = new CategoryHierarchySystem()

catalog.addCategory( categoryId = "home", categoryName = "Home", parentCategoryId = "")

Output: true

catalog.addCategory( categoryId = "kitchen", categoryName = "Kitchen", parentCategoryId = "home")

Output: true

catalog.addProduct( productId = "p7", categoryId = "kitchen", productName = "Steel Kettle", priceInCents = 3200)

Output: true

catalog.addProduct( productId = "p2", categoryId = "kitchen", productName = "Glass Kettle", priceInCents = 2800)

Output: true

catalog.addProduct( productId = "p5", categoryId = "kitchen", productName = "Coffee Grinder", priceInCents = 4100)

Output: true

catalog.getProducts( categoryId = "kitchen", filters = List.of("nameEquals=glass kettle"))

Output: ["p2,Glass Kettle,kitchen,2800"]

catalog.getProducts( categoryId = "home", filters = List.of())

Output: ["p2,Glass Kettle,kitchen,2800", "p5,Coffee Grinder,kitchen,4100", "p7,Steel Kettle,kitchen,3200"]

catalog.addProduct( productId = "p2", categoryId = "home", productName = "Replacement Kettle", priceInCents = 3500)

Output: false

The product ID already exists, so the original product remains unchanged.

catalog.getProducts( categoryId = "home", filters = List.of("brand=Nova"))

Output: []

The filter type is not currently supported, so the complete filter list is invalid.



Please use Laptop/Desktop or any other large screen to add/edit code.