MongoDB $bottomN Accumulator

Beginner
⏱️ 10 min read
📚 Updated: Jul 2026
🎯 5 Examples
Aggregation

What You’ll Learn

The $bottomN accumulator collects the N lowest-ranked documents in each $group bucket—perfect for bottom-3 leaderboards, cheapest products, or earliest orders when one record is not enough.

01

N docs per group

Array of bottom-ranked records.

02

n + sortBy + output

Three required properties.

03

Flexible output

Project fields or use arrays.

04

vs $bottom

Many records vs one record.

05

Short groups OK

Returns all docs if fewer than N.

06

MongoDB 5.2+

Part of $top/$bottom family.

Definition and Usage

$bottomN is a MongoDB accumulator used in the $group stage. It sorts documents within each group using sortBy, then returns the last N documents in that ordering as an array.

💡
Beginner tip

“Bottom” means the end of the sorted list—not automatically “smallest number.” To get the three lowest scores, MongoDB docs use sortBy: { score: -1 } (descending). High scores sort first; the bottom three slots hold the lowest values.

Choose $bottomN when dashboards need a tail of ranked records (bottom 5 sellers, 3 slowest response times). Use $bottom when only the single worst record matters, or $topN for the top of the ranking.

📝 Syntax

$bottomN requires an object with n, sortBy, and output:

mongosh
{
  $group: {
    _id: <expression>,
    <field>: {
      $bottomN: {
        n: <positive integer expression>,
        sortBy: { <field>: <1 | -1>, ... },
        output: <expression> | { <field>: <expression>, ... }
      }
    }
  }
}

Syntax Rules

  • n — how many bottom documents to collect; must be a positive integer (constant or expression tied to _id).
  • sortBy — required; same format as $sort (1 = ascending, -1 = descending).
  • output — shapes each array element: projection object, scalar field, or array like ["$player", "$score"].
  • Array result — unlike $bottom, the accumulator value is always an array (possibly empty if the group is empty).
  • Fewer than N — if the group has 2 documents and n: 3, MongoDB returns 2 elements.
  • Version — available in MongoDB 5.2 and later.
mongosh
db.gamescores.aggregate([
  {
    $group: {
      _id: "$gameId",
      bottomThree: {
        $bottomN: {
          n: 3,
          sortBy: { score: -1 },
          output: { player: "$playerId", score: "$score" }
        }
      }
    }
  }
]);

⚡ Quick Reference

QuestionAnswer
Stage$group accumulator
Required keysn, sortBy, output
ReturnsArray of up to N items per group
Lowest scores patternsortBy: { score: -1 }
Group smaller than NReturns all documents in the group
MongoDB version5.2+
Bottom 3
n: 3

Collect three bottom-ranked docs

Lowest scores
sortBy: { score: -1 }

Desc → low scores at bottom

Tuple output
output: [
  "$playerId",
  "$score"
]

Pair values per element

Dynamic n
n: {
  $cond: [
    { $eq: ["$gameId", "G2"] },
    1, 3
  ]
}

n from group key

🧰 Parameters

The $bottomN object has three required properties:

n Required

Positive integer limiting how many bottom documents to return. Can be a constant (3) or an expression that depends on the group _id (e.g. $cond).

n: 5
n: { $cond: [ …, 1, 3 ] }
sortBy Required

Sort specification within each group before selecting the bottom N. Add secondary keys to break ties deterministically.

sortBy: { score: -1 }
sortBy: { score: -1, playerId: 1 }
output Required

Expression defining each element in the returned array—projection object, single field, or array of fields.

output: "$score"
output: { name: "$player", score: "$score" }
null / missing Edge case

Missing fields become null and are kept in results. With descending numeric sort, nulls tend to sort toward the bottom.

sortBy: { score: -1, playerId: 1 }

Examples Gallery

Game scores grouped by gameId—find the three lowest scorers per game and explore advanced patterns.

📚 Getting Started

Sample game scores and the core $bottomN grouping pattern.

Example 1 — Sample gamescores collection

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 It Works

Two games (G1, G2) with four players each—enough data to verify bottom-3 results per group.

Example 2 — Three lowest scores in one game

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

/* Result:
[
  {
    _id: "G1",
    bottomThree: [
      ["PlayerB", 33],
      ["PlayerA", 31],
      ["PlayerD", 1]
    ]
  }
]
*/

How It Works

Descending sort lists 99 → 33 → 31 → 1. The bottom three positions are 33, 31, and 1—the three lowest scores.

📈 Practical Patterns

Multiple games, compare with $bottom, and vary n per group.

Example 3 — Bottom 3 per game (all groups)

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

/* G1 bottom: PlayerD(1), PlayerA(31), PlayerB(33)
   G2 bottom: PlayerA(10), PlayerB(14), PlayerC(66) */
*/

How It Works

Each gameId group gets its own independent bottom-3 array—ideal for per-category leaderboards.

Example 4 — $bottomN vs $bottom

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

/* worstOnly:  ["PlayerD", 1]           — one doc
   worstThree: [["PlayerB",33], …, ["PlayerD",1]] — array */
*/

How It Works

Both use the same sortBy in one $group when a report needs the absolute minimum and the bottom-k list together.

Example 5 — Dynamic n based on group key

Return 1 bottom score for game G2 but 3 for every other game:

mongosh
db.gamescores.aggregate([
  {
    $group: {
      _id: { gameId: "$gameId" },
      bottomScores: {
        $bottomN: {
          n: {
            $cond: {
              if: { $eq: ["$gameId", "G2"] },
              then: 1,
              else: 3
            }
          },
          sortBy: { score: -1 },
          output: "$score"
        }
      }
    }
  },
  { $sort: { "_id.gameId": 1 } }
]);

/* G1 → [33, 31, 1]
   G2 → [10] */
*/

How It Works

n can depend on _id—useful when different categories need different tail sizes in one pipeline.

🧠 How $bottomN Works

1

Documents bucketed

$group assigns each document to a group via _id.

Group
2

Sorted within group

sortBy ranks every document inside each bucket.

Sort
3

Last N selected

MongoDB takes the final n documents from that ordering (or all if fewer exist).

Pick N
=

Array returned

Each element is shaped by output—objects, scalars, or field tuples.

📝 Notes

  • $bottomN requires MongoDB 5.2+—verify server version before deploying.
  • All three keys—n, sortBy, output—are mandatory.
  • Result is an array per group; use $bottom when you need a single object.
  • Groups with fewer than n documents return all available bottom-ranked items.
  • Null and missing values are preserved; add tie-break sort keys for predictable ordering.
  • Large groups are subject to the 100 MB per-group aggregation memory limit.
  • Previous topic: $bottom. Next: $first.

Conclusion

$bottomN answers “which N records sit at the low end of my ranking?” inside each group. Set n, define ranking with sortBy, and shape results with output.

Pair with $bottom for the single worst record, or explore $first when input order matters instead of explicit ranking.

💡 Best Practices

✅ Do

  • Document your sortBy choice—“bottom” follows sort order, not intuition alone
  • Use sortBy: { score: -1 } for lowest numeric scores (MongoDB official pattern)
  • Add secondary sort keys (playerId, _id) to break ties
  • Put $match before $group to reduce data processed per bucket
  • Test with groups smaller than n to confirm array length behavior

❌ Don’t

  • Confuse $bottomN with $bottom (array vs single object)
  • Assume ascending sort always means “lowest score”—verify against your sortBy
  • Expect $min or $minN to return player names—they return values only
  • Use on MongoDB versions before 5.2 without a fallback pipeline
  • Forget the 100 MB per-group memory cap on very large buckets

Key Takeaways

Knowledge Unlocked

Five things to remember about $bottomN

Use these points when building bottom-k reports inside $group.

5
Core concepts
🔢 02

n key

How many to collect.

Syntax
⬇️ 03

sortBy

Defines bottom ranking.

Ranking
👤 04

vs $bottom

Many vs one record.

Compare
📄 05

5.2+

Check server version.

Version

❓ Frequently Asked Questions

$bottomN returns an array of the N documents ranked at the bottom of each $group bucket according to sortBy. Use it for bottom-3 scorers, lowest-priced products, or earliest N orders—when you need more than one low-ranked record.
Inside $group: { bottomThree: { $bottomN: { n: 3, sortBy: { score: -1 }, output: { name: "$player", score: "$score" } } } }. All three keys—n, sortBy, and output—are required.
$bottom returns exactly one bottom-ranked document (a single object). $bottomN returns up to N documents as an array. Use $bottom for the single worst record; use $bottomN for a leaderboard tail or bottom-k list.
MongoDB ranks documents, then takes the last N in that order. For lowest numeric scores, use sortBy: { score: -1 } (descending)—high scores first, lowest scores at the bottom. Always match sortBy to your business definition of bottom.
$bottomN 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.
$bottomN was added in MongoDB 5.2 alongside $top, $bottom, and $topN. On older servers, approximate with $sort + $group patterns (less efficient).
Did you know?

$bottomN can use a dynamic n expression based on the group _id—handy when one pipeline must return different tail sizes per category. See the official $bottomN docs.

Continue the Accumulators Series

Master bottom-k arrays with $bottomN, then learn $first for first-document grouping.

Next: $first →

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.

5 people found this page helpful