algorithm
related items
common tags
search algorithm
recommendation system

Algorithm that searches for related items based on common tags

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

A related-items algorithm based on common tags is usually a ranking problem, not just a search problem. The basic approach is to index items by tag, gather candidates that share tags with the target item, score those candidates by overlap quality, and return the highest-ranked results.

The Simplest Useful Model

Suppose each item has a set of tags.

python
1items = {
2    1: {"python", "data", "pandas"},
3    2: {"python", "api", "async"},
4    3: {"sql", "data", "mysql"},
5    4: {"python", "data", "async"},
6}

If the target is item 1, a basic relatedness score is just the number of shared tags.

  • item 2 shares python
  • item 3 shares data
  • item 4 shares python and data

So item 4 should rank highest.

This baseline is often enough to build a good first version.

Build An Inverted Index

To avoid scanning every item for every query, build an inverted index from tag to item IDs.

python
1from collections import defaultdict
2
3items = {
4    1: {"python", "data", "pandas"},
5    2: {"python", "api", "async"},
6    3: {"sql", "data", "mysql"},
7    4: {"python", "data", "async"},
8}
9
10index = defaultdict(set)
11for item_id, tags in items.items():
12    for tag in tags:
13        index[tag].add(item_id)
14
15print(dict(index))

Now, for a target item, you can fetch only the items that share at least one tag instead of scanning the whole corpus.

Score Candidates By Overlap

A simple related-items function using raw shared-tag count looks like this:

python
1from collections import defaultdict
2
3
4def related_items(target_id, items, index, top_n=5):
5    target_tags = items[target_id]
6    scores = defaultdict(int)
7
8    for tag in target_tags:
9        for candidate_id in index[tag]:
10            if candidate_id != target_id:
11                scores[candidate_id] += 1
12
13    ranked = sorted(scores.items(), key=lambda pair: pair[1], reverse=True)
14    return ranked[:top_n]
15
16print(related_items(1, items, index))

This is easy to implement and often performs well enough for small or medium datasets.

Improve The Score With Jaccard Similarity

Raw overlap favors items with many tags. A normalized score is often better. Jaccard similarity compares the size of the intersection to the size of the union.

python
1
2def jaccard(a, b):
3    return len(a & b) / len(a | b)
4
5
6def related_items_jaccard(target_id, items, index, top_n=5):
7    target_tags = items[target_id]
8    candidate_ids = set()
9
10    for tag in target_tags:
11        candidate_ids |= index[tag]
12
13    candidate_ids.discard(target_id)
14
15    ranked = sorted(
16        ((cid, jaccard(target_tags, items[cid])) for cid in candidate_ids),
17        key=lambda pair: pair[1],
18        reverse=True,
19    )
20    return ranked[:top_n]
21
22print(related_items_jaccard(1, items, index))

This often gives more sensible rankings when some items have long tag lists and others have short ones.

Rare Tags Should Often Matter More

In many systems, common tags such as news or tech are less informative than rare tags such as grpc or vector-db. You can improve ranking by weighting tags inversely by how common they are.

A simple inverse-frequency weight is:

  • common tag: low weight
  • rare tag: high weight

This idea is related to TF-IDF-style weighting in information retrieval.

You do not always need the full math on day one, but you should be aware that raw overlap treats all tags as equally informative, which is often not true.

Filtering And Business Rules

A production related-items system usually applies rules beyond tag similarity.

Examples:

  • exclude the target item itself
  • exclude unpublished or deleted items
  • require the same language or tenant
  • boost recent items
  • diversify repeated categories

The similarity score gives you a candidate ranking, but business constraints usually decide the final list a user sees.

Complexity And Scalability

The inverted-index approach scales much better than comparing the target against every item.

For each query, the cost becomes roughly:

  • gather candidates from posting lists of the target tags
  • score only those candidates
  • sort the candidate scores

That is much cheaper than a full scan when the corpus is large and tags are reasonably selective.

If the dataset becomes very large, you can store the inverted index in a search engine or key-value system instead of keeping it in memory.

Common Pitfalls

  • Scanning every item instead of using an inverted index.
  • Ranking only by raw overlap when item tag counts vary widely.
  • Treating extremely common tags as equally informative as rare tags.
  • Forgetting business constraints such as visibility, language, or freshness.
  • Returning highly similar duplicates without any diversity control.

Summary

  • A tag-based related-items algorithm is usually an inverted-index plus ranking problem.
  • Start with shared-tag count or Jaccard similarity as a baseline.
  • Use an inverted index so queries do not scan the full corpus.
  • Consider weighting rare tags more heavily than common ones.
  • In production, combine similarity with business rules and filtering.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms