We can use a trie based data structure for storing the query data. Each node will represent a character in the query. Each node will also have a boolean flag representing if it is a terminal character. It also has a list of suggested queries with prefix as the string starting from the root to that node. This is order by ranking (combination of recency and frequency). So we can use a PriorityQueue here.
The schema looks like this
Trie :-
root: TrieNode
TrieNode:-
nextChars: Map
topSuggestions: PriorityQueue
terminal: boolean
frequency: Integer
Suggestion:-
query: String
frequency: Integer
lastSearched: DateTime
QueryService handles api when a complete query is searched.
TrieService updates the Trie data when it receives a query.
SuggestionsService handles api which returns the top suggestions when user enters a prefix using the trie data.
When query service receives a query, it sends the query data to the trie service
TrieService updates the Trie data when it receives a query.
When suggestion service receives a request to get the suggestions based on a query, it traverses the trie to get the list of suggestions for that prefix and returns the list of suggestions
When query service receives a query, it sends the query data to the trie service. We can batch the requests to a fixed configurable batch size, we can aggregate the local frequencies and send them.
TrieService updates the Trie data when it receives a query. While traversing each character of the query, it traverses down the trie level by level and updates the frequency and lastSearched. And again it traverses way up in the same path (we can use stack or recursion) and at each node, it updates topSuggestions. topSuggestions is a priorityqueue which sorts itself on every update by rank. The rank is caluclated by taking frequency and timeSinceLastSearched (which we can get from lastSearched) as parameters and multiplying them with fixed weights which are configurable and adding the product. We can have negative weight for timeSinceLastSearched.
Suggestion service traverses the trie along the characters of the input prefix and returns the topSuggestions list (or priorityqueue) at the node representing the last character of the prefix.
We chose batching over sending every word, because we are dealing with high traffic scnarios. We are receiving approx 12000 queries (1b / 24*60*60) every second. The data will be eventually correct. Since we are also taking frequency into consideration, the delay in consistency may not be that critical.
Solutions for Failure scenarios/bottlenecks:
Improvements: