Implement the LocalSearch class to search a large collection of locally stored items. Each item has a unique identifier, type, name, optional content and last-updated time.
Search must be case-insensitive and must match the query against both the item's name and content. Results must be ranked using the following priority:
Within the same priority group, return the most recently updated item first. If two items also have the same update time, return the item with the lexicographically smaller identifier first.
The collection must support incremental additions, updates and removals. Searching or changing one item must not require rebuilding the entire search index.
LocalSearch()
Creates an empty local search collection.
void addOrUpdateItem(String id, String type, String name, String content, long updatedAt)
id: The unique identifier of the item.type: The item's type, such as document, task or record.name: The searchable name of the item.content: The searchable content of the item. An empty string represents an item without content.updatedAt: The time at which the item was last updated.Add the item if its identifier is not already stored. If an item with the same identifier exists, replace all of its fields with the supplied values. Update only the affected search-index entries.
boolean removeItem(String id)
id: The identifier of the item to remove.Remove the item and its associated index entries, then return true. Return false if the identifier is not stored.
List<String> search(String query, String typeFilter, int limit)
query: The text to find in item names or content.typeFilter: The required item type. An empty string disables type filtering.limit: The maximum number of identifiers to return.Return the identifiers of at most limit matching items using the ranking rules described above. Name, content and type comparisons are case-insensitive.
An item whose name contains the query must be placed in its corresponding name-match group even when its content also contains the query. A content-only match applies only when the item's name does not contain the query.
1 ≤ id.length() ≤ 1001 ≤ type.length() ≤ 501 ≤ name.length() ≤ 5000 ≤ content.length() ≤ 10,0001 ≤ updatedAt ≤ 1,000,000,0001 ≤ query.length() ≤ 5000 ≤ typeFilter.length() ≤ 501 ≤ limit ≤ 1,000null.1,000,000 items are stored at one time.1,000,000 method calls are made in total.LocalSearch()
Creates an empty local search collection.
addOrUpdateItem(id = "d4", type = "document", name = "Project Plan", content = "Milestones and delivery schedule", updatedAt = 120)
addOrUpdateItem(id = "t2", type = "task", name = "Plan Meeting", content = "Discuss the project timeline", updatedAt = 150)
addOrUpdateItem(id = "r7", type = "record", name = "Archived Project Plan", content = "Previous milestones", updatedAt = 180)
addOrUpdateItem(id = "d1", type = "document", name = "Delivery Notes", content = "The project plan was approved", updatedAt = 200)
search(query = "plan", typeFilter = "", limit = 10)
Output: ["t2","r7","d4","d1"]
The name of t2 starts with the query, so it belongs to the first priority group. The names of r7 and d4 contain the query elsewhere, so they belong to the second group. r7 appears before d4 because it was updated more recently. d1 matches only through its content and therefore belongs to the third group.
LocalSearch()
Creates another empty local search collection.
addOrUpdateItem(id = "a3", type = "task", name = "Budget Review", content = "Review quarterly spending", updatedAt = 500)
addOrUpdateItem(id = "a4", type = "document", name = "Budget Analysis", content = "Quarterly spending analysis", updatedAt = 500)
addOrUpdateItem(id = "a1", type = "document", name = "Budget Report", content = "Quarterly financial summary", updatedAt = 500)
addOrUpdateItem(id = "a2", type = "document", name = "Annual Budget", content = "Forecast for next year", updatedAt = 600)
search(query = "BUDGET", typeFilter = "DOCUMENT", limit = 5)
Output: ["a1","a4","a2"]
Query and type comparisons are case-insensitive. The type filter excludes a3. The names of a1 and a4 start with the query and have the same update time, so the lexicographically smaller identifier a1 comes first. The name of a2 contains the query elsewhere, so it appears after both prefix matches even though it was updated more recently.
LocalSearch()
Creates another empty local search collection.
addOrUpdateItem(id = "x2", type = "record", name = "Customer Notes", content = "Waiting for approval", updatedAt = 700)
search(query = "approved", typeFilter = "", limit = 3)
Output: []
Neither the name nor the content contains the complete query "approved".
addOrUpdateItem(id = "x2", type = "record", name = "Approved Customer", content = "Request completed", updatedAt = 750)
search(query = "approved", typeFilter = "", limit = 3)
Output: ["x2"]
Updating x2 replaces all its old fields. Its new name starts with the query, so the updated item is returned.
removeItem(id = "x2")
Output: true
search(query = "approved", typeFilter = "", limit = 3)
Output: []
Removing x2 also removes all its associated search-index entries.
removeItem(id = "x2")
Output: false
The second removal returns false because the identifier is no longer stored.