MongoDB $topN Accumulator

Beginner
6 min read
Updated: Oct 2026
4 examples
Live preview

What Is $topN?

$topN is a MongoDB accumulator for the $group stage. It ranks documents in each group with sortBy, then returns the first N documents as an array—ideal for top-3 scorers or a short leaderboard when one record is not enough.

Remember
$topN = first N docs after sortBy (per group)

n       → how many top documents to keep
sortBy  → ranking (same style as $sort)
output  → shape of each array element

vs $top      → one document; $topN → array of up to N
vs $bottomN → top of ranking vs bottom of ranking
MongoDB 5.2+
UsePrefer
Top-k ranked list per group$topN
Single best / highest-ranked record$top
Bottom-k ranked list$bottomN
N largest numbers only (no full docs)$maxN

How to Use $topN in MongoDB

Three keys drive every $topN call inside $group.

  1. Put $topN inside $group on an output field.
  2. Set n to how many top-ranked documents you want.
  3. Set sortBy so “top” matches your ranking (for highest scores: { score: -1 }).
  4. Set output to the fields each array element should include.
KeyRequired?Role
nYesPositive integer (or expression from group _id)
sortByYesSort spec like $sort (1 / -1)
outputYesProjection, field path, or array of fields

Pseudocode

Pseudocode
for each group:
    ranked = sort(documents, sortBy)
    top = first n items of ranked   // or all if fewer than n
    result = [ output(doc) for doc in top ]
    emit { _id: groupKey, field: result }

Cheat Sheet

Keep these facts handy while writing a top-N report.

TopicDetail
Stage$group only (as an accumulator)
Minimal keysn, sortBy, output
Highest scoressortBy: { score: -1 } (high first → top slots)
vs $topOne document vs array of up to N
Short groupsReturns all docs if group size < n
VersionMongoDB 5.2+
Sample checkG1 scores 99, 33, 31, 1 → top 3: 99, 33, 31

Live Preview

Watch $topN pick the three highest scores after a descending sortBy. Sample: G1 (99, 33, 31, 1) and G2 (80, 66, 14, 10).

Simulates sortBy: { score: -1 } then taking the first 3 (same idea as the mongosh examples below).

Result
G1 sort desc: 99 → 33 → 31 → 1
top 3 → [99, 33, 31]

G2 sort desc: 80 → 66 → 14 → 10
top 3 → [80, 66, 14]

[
  { _id: "G1", topThree: [["PlayerC", 99], ["PlayerB", 33], ["PlayerA", 31]] },
  { _id: "G2", topThree: [["PlayerD", 80], ["PlayerC", 66], ["PlayerB", 14]] }
]

Worked Walkthrough — Top 3 in Game G1

Four G1 scores. Descending sort puts 99 first; the top three slots hold the highest scores.

Calculation
Documents (game G1):
  PlayerC 99, PlayerB 33, PlayerA 31, PlayerD 1

sortBy: { score: -1 }  (high → low)
  99 → 33 → 31 → 1

top n = 3  (first three in that order)
  99, 33, 31

output: ["$playerId", "$score"]
  [["PlayerC", 99], ["PlayerB", 33], ["PlayerA", 31]]

Note: "top" means start of the sorted list —
match sortBy to your definition of best.

MongoDB Examples

We use a tiny gamescores collection. First we take the three highest scorers in one game, then per game, then compare with $top. Run these in mongosh after inserting the sample data once.

Example 1 — Sample Data

Two games, four players each. Notice G1 has a clear high score (99) and a clear low score (1)—perfect for checking top-3 results.

mongosh
db.gamescores.insertMany([
  { playerId: "PlayerA", gameId: "G1", score: 31 },
  { playerId: "PlayerB", gameId: "G1", score: 33 },
  { playerId: "PlayerC", gameId: "G1", score: 99 },
  { playerId: "PlayerD", gameId: "G1", score: 1 },
  { playerId: "PlayerA", gameId: "G2", score: 10 },
  { playerId: "PlayerB", gameId: "G2", score: 14 },
  { playerId: "PlayerC", gameId: "G2", score: 66 },
  { playerId: "PlayerD", gameId: "G2", score: 80 }
]);

How the Data Is Stored in MongoDB

After the insert, MongoDB keeps 8 documents in gamescores. Run find() to see them:

mongosh
db.gamescores.find()

Read each document like this:

Field in MongoDBMeaningExample
playerIdWho played"PlayerC"
gameIdWhich game"G1"
scorePoints scored99
Picture
G1 scores: 99, 33, 31, 1
G2 scores: 80, 66, 14, 10

Next: $topN keeps the highest 3 per game.

How It Works

  1. Insert once. The sample command adds 8 documents to gamescores.
  2. MongoDB stores fields as written. Each document keeps the same field names and values you inserted.
  3. find() shows the same shape. What you inserted is what you get back—ready for $topN in the next examples.

Example 2 — Three Highest Scores in One Game

Goal: for game G1 only, return the three highest scorers as [playerId, score] pairs.

mongosh
db.gamescores.aggregate([
  { $match: { gameId: "G1" } },
  {
    $group: {
      _id: "$gameId",
      topThree: {
        $topN: {
          n: 3,
          sortBy: { score: -1 },
          output: ["$playerId", "$score"]
        }
      }
    }
  }
]);

How It Works

  1. Filter first. $match keeps only G1 documents so the group is small and clear.
  2. One bucket. _id: "$gameId" makes a single group labeled "G1".
  3. Rank high to low. sortBy: { score: -1 } orders scores as 99 → 33 → 31 → 1.
  4. Take the start. n: 3 keeps the first three: 99, 33, and 31 (PlayerD’s 1 drops off).
  5. Shape each row. output: ["$playerId", "$score"] turns each kept document into a two-value array.

Example 3 — Top 3 per Game

Goal: same top-3 rule for every gameId, with a projection object instead of tuples.

mongosh
db.gamescores.aggregate([
  {
    $group: {
      _id: "$gameId",
      topThree: {
        $topN: {
          n: 3,
          sortBy: { score: -1 },
          output: { player: "$playerId", score: "$score" }
        }
      }
    }
  },
  { $sort: { _id: 1 } }
]);

How It Works

  1. Split by game. Each distinct gameId gets its own group and its own top-3 array.
  2. Same ranking rule. Descending score still means “highest at the top” in every group.
  3. Friendly objects. output: { player, score } is easier to read in APIs than nested arrays.
  4. Stable report order. Final $sort on _id lists G1 before G2 (group order from $group alone is not guaranteed).

Example 4 — $topN vs $top

Same G1 filter and sortBy, two accumulators side by side: one best player vs the best three.

mongosh
db.gamescores.aggregate([
  { $match: { gameId: "G1" } },
  {
    $group: {
      _id: "$gameId",
      bestOnly: {
        $top: {
          sortBy: { score: -1 },
          output: ["$playerId", "$score"]
        }
      },
      bestThree: {
        $topN: {
          n: 3,
          sortBy: { score: -1 },
          output: ["$playerId", "$score"]
        }
      }
    }
  }
]);

How It Works

  1. Same sort, different size. Both use sortBy: { score: -1 }, so “top” means the same ranking.
  2. $top = one. bestOnly is a single value: PlayerC with score 99.
  3. $topN = many. bestThree is an array of three pairs starting at that same highest score.
  4. Pick by need. Dashboards that show only the winner use $top; top-k lists use $topN.

Edge Cases & Pitfalls

Check these before shipping a top-N report.

sortBy

“Top” follows sortBy

Highest scores need descending sortBy. Ascending score makes the lowest values sit in the top slots.

null

Nulls are kept

Missing fields become null and are not filtered out. With ascending numeric sort they often land in the top slots.

ties

Break ties explicitly

Equal scores can swap places. Add a secondary key, e.g. sortBy: { score: -1, playerId: 1 }.

version

Needs MongoDB 5.2+

Older servers reject $topN. Confirm version before deploying.

Performance Notes

ApproachCostBest for
$topNIn-group ranking; keeps up to N docsTop-k lists without a prior global sort
$sort + $group + sliceExtra stage; more memory on large setsLegacy workaround before 5.2
$maxNValues only (no full document ranking)N largest numbers when identity fields are unused

Keep n small and filter early with $match when you only need one game or category. Large groups are still subject to the aggregation memory limit (about 100 MB per group).

Key Takeaways

  • Role: array of the first N ranked documents inside $group.
  • Keys: always set n, sortBy, and output.
  • Highest scores: use sortBy: { score: -1 } so highs sit at the top.
  • vs $top: one document vs an array of up to N.

One line: $topN returns the first N documents after sortBy—set ranking carefully, then shape each element with output.

Frequently Asked Questions

$topN returns an array of the N documents ranked at the top of each $group bucket according to sortBy. Use it for top-3 scorers, highest-priced products, or latest N orders when one record is not enough.
Inside $group: { topThree: { $topN: { n: 3, sortBy: { score: -1 }, output: { name: "$player", score: "$score" } } } }. All three keys—n, sortBy, and output—are required.
$top returns exactly one top-ranked document (a single object). $topN returns up to N documents as an array. Use $top for the single best record; use $topN for a top-k list.
Use sortBy: { score: -1 } (descending)—highest scores sort first and land in the top N slots. For most recent records, use sortBy: { date: -1 }. Always match sortBy to your business definition of top.
$topN returns all documents in the group—never more than exist. If a class has only two students and n is 3, you get an array of two items.
$topN was added in MongoDB 5.2 alongside $top, $bottom, and $bottomN. On older servers, approximate with $sort + $group patterns (less efficient).

Back to Accumulators

Browse every MongoDB accumulator tutorial in one place.

Accumulators hub →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

9 people found this page helpful