search?q=m
search?q=ma
search?q=man
search?q=mang
search?q=mango
QPS: 10,000,000 * 10 / 86400
TRAFFIC: Mixed workload, (read + write heavy)
We can have a key value store to store our data as it is optimised for performance and retrieval can be done in O(1) time. Also using relational DB here will be inefficient as we need to return the top K elements in O(1) time.
Let's break down the system :
(root)
/ \
b
{bee:25,best:20 / \
,be:15} / \
/ \
be:15 bu
/ \ \
{bee:25,
beer:10} bee:25 bes:5 buy
| \
beer:10 best:20
a . We can limit the length of the search query typed by user . As user rarely types long search queries. TC: O(Prefix length) to O(1).
b. We can cache the top k search queries at each node. TC: O(1).
So to search , the steps are:
So in O(1) our algorithm will return top k queries.
Updating the trie on every will slow down the query service. Top suggestions such as Google keywords usually don't change much . Thus it is unnecessary to build trie on every query.
Data used to build our trie is from analytics or logging service.
a. We can use AJAX request to get to results. The main benefit is it does not refresh the entire web page when sending/receiving a request/response.
b. Browser caching
c. We can do Data sampling. As logging each and every query is quite intensive work. We can randomly log 1 out of every N request in the system.
const searchQueries = [
"weather forecast",
"best restaurants near me",
"javascript tutorial",
"latest tech news",
"how to cook pasta",
"vacation destinations 2023",
"movie reviews",
];
function getRandomSample(arr, sampleSize) {
const result = [];
const arrCopy = [...arr];
for (let i = 0; i < sampleSize; i++) {
const randomIndex = Math.floor(Math.random() * arrCopy.length);
const selectedElement = arrCopy.splice(randomIndex, 1)[0];
result.push(selectedElement);
}
return result;
}
const sampleSize = 3;
const randomSample = getRandomSample(searchQueries, sampleSize);
Create: Trie is created by workers using aggregated logs from Analytics.
Update: Weekly the trie is updated.A new trie created will replace the old trie.
Delete: A Filter can be added in front of trie cache to delete violent abusive search terms. Asynchronously , this date be remove from database also to build a new trie in the next cycle.
We can have a shard logic built using the pattern from historical data gathered and main a lookup db to know where the rows are stored. Like if most of the queries starts from letter 'b' and rest of the queries lies in 'b', 'c','d',e'. We can have 2 shards built.
AnalyticsLogs: It stores raw data about search queries and simple append it.
Aggregators: Since the analytics logs will be large , we need to aggregate it so that it can be processed by our system.
Here we don't need to aggregate data very often so we can get it once a week. We assume trie is built weekly.
Aggregated Data:
Query Time(start time of week) Freq
tree. 2024-06-06. 15000
Mango. 2024-06-06. 150
summer. 2024-06-06. 2000
Workers:
These servers perform asynchronous jobs at regular interval and build trie data structures and store it in trie DB.
Trie Cache:
This is a distributed cache system and keeps tries in memory for fast read .
Trie DB:
We can use a key value store to represent a trie.
Key : prefix term in trie
value: data on each node
Key Value
b [bee:25,best:20,be:15]
be [bee:25,best:20,be:15]
bee [bee:25]
best [best:20].
e,g: Amazon dynamo db
No real time update of search terms.We are updating out trie weekly as of now to avoid the load but let's say like twitter we need our search to be in real time then we can do it more frequently.
Also we are doing random sampling with our data as storing all the searched terms and forming trie with it is not efficient.
We can also do filter out more on our search data using decay factor to filter out the popularity of a search term.
Example Scenario:
Initial Score: Suppose an article starts with a popularity score of 100.
Decay Rate: The popularity score decreases by 10 units per hour.
Let's calculate the popularity score of the article after different time intervals:
After 0 hours (Initial Score): Score = 100
After 1 hour: Score=100−10=90
After 2 hours: Score=100-2*10= 80
After 3 hours: Score=100−3×10= 70
And we can define a threshold to reject a search term to remove it from our popularity list.
Loss of network. We have handled it by caching the data in client side as well.