🎓 A1Learner
🔍
0 of 42 lessons completed

Keeping Suggestions Fresh

Autocomplete freshness is how recent the stored suggestions are, and search counts change every hour of the day. This lesson deep-dives the update path: workers count new searches, and the stored lists are rebuilt on a cycle.

New searches wait for a rebuild

The stored lists were built from the earlier log, and a new term has been searched since. The printout shows the stored answer unchanged, with the new term missing from it.

log = ["photo", "photos", "photo", "pizza", "photo editor", "photo", "photos", "photo print", "pizza", "photo", "photography", "photo", "photos", "pizza", "photo editor", "photo"]
new_searches = ["photo booth", "photo", "photo booth", "photo booth", "photos", "photo booth", "photo booth", "photo booth"]

def count_terms(entries):
    counts = {}
    for term in entries:
        counts[term] = counts.get(term, 0) + 1
    return counts

def build_store(counts):
    store = {}
    for prefix in ["ph", "pi"]:
        matches = [term for term in counts if term.startswith(prefix)]
        matches.sort(key=lambda term: (-counts[term], term))
        store[prefix] = matches[:5]
    return store

store = build_store(count_terms(log))
answer = store["ph"]
print("stored answer for ph:", answer)
print("photo booth in the answer:", "photo booth" in answer)
print("searches for photo booth since the rebuild:", new_searches.count("photo booth"))

Workers count between rebuilds

New searches wait in a queue as counting jobs, and a worker takes them one by one. The printout shows the pending counts and the emptied queue.

new_searches = ["photo booth", "photo", "photo booth", "photo booth", "photos", "photo booth", "photo booth", "photo booth"]
queue = []
for term in new_searches:
    queue.append(term)
pending = {}
while len(queue) > 0:
    job = queue.pop(0)
    pending[job] = pending.get(job, 0) + 1
for term in sorted(pending):
    print(term, pending[term])
print("jobs left in the queue:", len(queue))

The second rebuild picks it up

The rebuild adds the pending counts to the old counts and stores fresh lists. The printout shows the new term inside the answer, ranked by its count.

log = ["photo", "photos", "photo", "pizza", "photo editor", "photo", "photos", "photo print", "pizza", "photo", "photography", "photo", "photos", "pizza", "photo editor", "photo"]
new_searches = ["photo booth", "photo", "photo booth", "photo booth", "photos", "photo booth", "photo booth", "photo booth"]

def count_terms(entries):
    counts = {}
    for term in entries:
        counts[term] = counts.get(term, 0) + 1
    return counts

def build_store(counts):
    store = {}
    for prefix in ["ph", "pi"]:
        matches = [term for term in counts if term.startswith(prefix)]
        matches.sort(key=lambda term: (-counts[term], term))
        store[prefix] = matches[:5]
    return store

counts = count_terms(log)
for term in new_searches:
    counts[term] = counts.get(term, 0) + 1
store = build_store(counts)
answer = store["ph"]
print("stored answer for ph:", answer)
print("photo booth in the answer:", "photo booth" in answer)

Example

log = ["photo", "photos", "photo", "pizza", "photo editor", "photo", "photos", "photo print", "pizza", "photo", "photography", "photo", "photos", "pizza", "photo editor", "photo"]
new_searches = ["photo booth", "photo", "photo booth", "photo booth", "photos", "photo booth", "photo booth", "photo booth"]

def count_terms(entries):
    counts = {}
    for term in entries:
        counts[term] = counts.get(term, 0) + 1
    return counts

def build_store(counts):
    store = {}
    for prefix in ["ph", "pi"]:
        matches = [term for term in counts if term.startswith(prefix)]
        matches.sort(key=lambda term: (-counts[term], term))
        store[prefix] = matches[:5]
    return store

counts = count_terms(log)
store = build_store(counts)
print("cycle 1 answer for ph:", store["ph"])
print("cycle 1 answer for pi:", store["pi"])
for term in new_searches:
    counts[term] = counts.get(term, 0) + 1
store = build_store(counts)
print("cycle 2 answer for ph:", store["ph"])
print("cycle 2 answer for pi:", store["pi"])

Key points

  • Rebuild cycle: the set rhythm at which stored lists are rebuilt from the newest counts.
  • Stated choice: this design rebuilds its lists every hour.
  • Between rebuilds: new searches are logged and counted, but the stored lists stay as they are.
  • Stale window: a brand-new term appears only after the next rebuild, so suggestions lag real searches by up to one cycle.
  • In the demo, a new term searched 6 times still waits for the next rebuild.

In this example, two rebuild cycles run over the same growing counts. The first cycle answers without the new term, the second cycle answers with it, and the prefix nobody's counts changed keeps its old answer.

Note

a shorter cycle is fresher, and it also rebuilds more often; the trade-offs lesson weighs that cost.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

When does a brand-new search term first appear in the suggestions?