Docs Menu
Docs Home
/
MongoDB Atlas
/ / / /

Paginate the Results

On this page

  • Usage
  • Considerations
  • Retrieve Point of Reference
  • Syntax
  • Output
  • Search After a Specific Point of Reference
  • searchAfter Syntax
  • Output
  • Search Before a Specific Point of Reference
  • searchBefore Syntax
  • searchBefore Output
  • Examples
  • searchSequenceToken Query
  • Subsequent searchAfter and searchBefore Queries

On Atlas clusters running MongoDB 6.0.13+ or 7.0.5+, use Atlas Search to retrieve your $search query results sequentially after or before a reference point. Use the $search searchAfter or searchBefore options to traverse results in-order and build "Next Page" and "Previous Page" functions in your application.

To retrieve paginated results, perform the following steps:

  1. Create an index on the fields you want to query.

  2. Run a $search query that returns a point of reference. To learn more, see Retrieve Point of Reference.

  3. Use the point of reference in your subsequent $search query to retrieve the next or previous set of documents in the results.

    • To learn more about retrieving results to build a "Next Page" function, see Search After a Specific Point of Reference.

    • To learn more about retrieving results to build a "Previous Page" function, see Search Before a Specific Point of Reference.

    • To jump to a page in your results, combine $skip and $limit with $search searchAfter or searchBefore options. For example, to jump to results from Page 3 to Page 5, with 10 results per page, do the following:

      1. Retrieve results using searchAfter with the point of reference for the last result on Page 3 (Result 30).

      2. Use $skip to skip the 10 results on Page 4 (Results 31-40) and $limit to limit the results to 10 documents.

      3. Return results for Page 5 (Results 41-50).

      Here, using $skip with the searchAfter option optimizes the query to skip only 1 page of results (10 documents). By comparison, if you use $skip without the $search searchAfter option, the query skips 4 pages of results (40 documents). To learn more, see Jump from Page 2 to Page 5 Using searchAfter and $skip.

A tie occurs when you sort on a field for which multiple documents have identical values. MongoDB doesn't guarantee ordering of tied query results, which can lead to duplication and inconsistency when you use searchAfter and searchBefore. Apply the following principles to ensure deterministic search behavior:

  • Sort your query by a unique field to prevent relevance score ties.

  • If you want to primarily sort by a non-unique field, add a secondary sort clause on a unique field to serve as a tiebreaker.

  • Sort your query results by an immutable field. Atlas Search reflects the updates you make to your collection between initial and subsequent queries. If you sort by a mutable field such as updated_time and you update your collection between your first and second queries, Atlas Search may order the same documents differently.

To learn how to sort your query results by an immutable or unique field, see Sort Atlas Search Results.

To retrieve query results at a certain point, you must provide the point of reference in your $search query. You can retrieve the reference point by using the $meta keyword searchSequenceToken in the $project stage after your $search stage.

searchSequenceToken Syntax
1[{
2 "$search": {
3 "index": "<index-name>",
4 "<operator-name>"|"<collector-name>": {
5 <operator-specification>|<collector-specification>
6 }
7 "sort": {
8 "score": {
9 "$meta": "searchScore", _id:1
10 }
11 }
12 ...
13 },
14 {
15 "$project": {
16 { "paginationToken" : { "$meta" : "searchSequenceToken" } }
17 },
18 ...
19}]

The searchSequenceToken generates a Base64-encoded token for each document in the results. The length of the token increases with the number of fields specified in the sort option of your query. The token isn't tied to a snapshot of the database.

The documents in the results are sorted in the default order, unless you specify the sort option in your query. To learn about sorting your results, see Sort Atlas Search Results.

To search after a reference point, you must specify the reference point in your $search query by using the searchAfter option with the token generated by searchSequenceToken. You can use the token generated by searchSequenceToken only when you rerun the $search query for which searchSequenceToken generated the token. The semantics (search fields and values) of the subsequent $search query in which you use the token must be identical to the query for which searchSequenceToken generated the token.

You can use the searchAfter option to build a "Next Page" function in your application. For a demonstration of this, see the example on this page.

searchAfter Syntax
1{
2 "$search": {
3 "index": "<index-name>",
4 "<operator-name>"|"<collector-name>": {
5 <operator-specification>|<collector-specification>
6 },
7 "searchAfter": "<base64-encoded-token>",
8 "sort": {
9 "score": {
10 "$meta": "searchScore", _id:1
11 }
12 }
13 ...
14 },
15 "$project": {
16 { "paginationToken" : { "$meta" : "searchSequenceToken" } }
17 },
18 ...
19}

Atlas Search returns the documents in the results after the specified token. Atlas Search returns the generated tokens for the documents in the results because you specified the searchSequenceToken in the $project stage after the $search stage (as shown in line 11). These tokens can be used as a reference point for another query with the same semantics.

The documents in the results are sorted in the default order, unless you specify the sort option in your query. To learn about sorting your results, see Sort Atlas Search Results.

To search before a reference point, you must specify the reference point in your $search query by using the searchBefore option with the token generated by searchSequenceToken. You can use the token generated by searchSequenceToken only when you rerun the $search query for which searchSequenceToken generated the token. The semantics (search fields and values) of the subsequent $search query in which you use the token must be identical to the query for which searchSequenceToken generated the token.

You can build a "Previous Page" function in your application using the searchBefore option. To build a "Previous Page" function, combine the following:

For a demonstration of this, see the searchBefore query examples on this page.

searchBefore Syntax
1{
2 "$search": {
3 "index": "<index-name>",
4 "<operator-name>"|"<collector-name>": {
5 <operator-specification>|<collector-specification>
6 },
7 "searchBefore": "<base64-encoded-token>",
8 "sort": {
9 "score": {
10 "$meta": "searchScore", _id:1
11 }
12 }
13 ...
14 },
15 "$project": {
16 { "paginationToken" : { "$meta" : "searchSequenceToken" } }
17 },
18 ...
19}

Atlas Search returns documents in the results preceding the specified token in reverse order. Atlas Search also returns the generated tokens for the documents in the results because you specified the searchSequenceToken in the $project stage after the $search stage (as shown in line 11). These tokens can be used as a reference point for another query with the same semantics.

The following examples use the sample-mflix.movies collection, which has an Atlas Search index named default with dynamic mappings. If you load the collection and create the index, you can run the following queries against the collection.

The following query uses the following pipeline stages:

  • $search to do the following:

    • Search for the term war in title field of the collection.

    • Sort the results first by score and then by the value of the released field in ascending order for documents with identical scores.

  • $limit stage to limit the results to 10 documents.

  • $project stage to do the following:

    • Include only the title and released fields.

    • Add the following fields:

      • Field named paginationToken that contains the Base64-encoded token for each document.

      • Field named score that contains the relevance score of each document.

1db.movies.aggregate([
2 {
3 "$search": {
4 "text": {
5 "path": "title",
6 "query": "war"
7 },
8 "sort": {score: {$meta: "searchScore"}, "released": 1}
9 }
10 },
11 {
12 "$limit": 10
13 },
14 {
15 "$project": {
16 "_id": 0,
17 "title": 1,
18 "released": 1,
19 "paginationToken" : { "$meta" : "searchSequenceToken" },
20 "score": {$meta: "searchScore"}
21 }
22 }
23])
1[
2 {
3 title: 'War',
4 released: ISODate('2002-03-14T00:00:00.000Z'),
5 paginationToken: 'CMFRGgYQup3BhQgaCSkAQCKS7AAAAA==',
6 score: 3.3774025440216064
7 },
8 {
9 title: 'War',
10 released: ISODate('2014-09-21T00:00:00.000Z'),
11 paginationToken: 'CMelARoGELqdwYUIGgkpAAiClUgBAAA=',
12 score: 3.3774025440216064
13 },
14 {
15 title: 'War Photographer',
16 paginationToken: 'CMBRGgYQuq+ngwgaAmAA',
17 score: 2.8268959522247314
18 },
19 {
20 title: "Troma's War",
21 released: ISODate('1989-11-18T00:00:00.000Z'),
22 paginationToken: 'CL8kGgYQuq+ngwgaCSkAbP8QkgAAAA==',
23 score: 2.8268959522247314
24 },
25 {
26 title: 'The War',
27 released: ISODate('1994-11-04T00:00:00.000Z'),
28 paginationToken: 'CI0wGgYQuq+ngwgaCSkAnIKEtgAAAA==',
29 score: 2.8268959522247314
30 },
31 {
32 title: 'War Stories',
33 released: ISODate('1996-05-09T00:00:00.000Z'),
34 paginationToken: 'CPIyGgYQuq+ngwgaCSkA/DifwQAAAA==',
35 score: 2.8268959522247314
36 },
37 {
38 title: "Gaston's War",
39 released: ISODate('1997-10-23T00:00:00.000Z'),
40 paginationToken: 'CMQ7GgYQuq+ngwgaCSkALPBSzAAAAA==',
41 score: 2.8268959522247314
42 },
43 {
44 title: 'Shooting War',
45 released: ISODate('2000-12-07T00:00:00.000Z'),
46 paginationToken: 'CMJJGgYQuq+ngwgaCSkAOOhG4wAAAA==',
47 score: 2.8268959522247314
48 },
49 {
50 title: "Varian's War",
51 released: ISODate('2001-04-22T00:00:00.000Z'),
52 paginationToken: 'CM5IGgYQuq+ngwgaCSkAGEkD5gAAAA==',
53 score: 2.8268959522247314
54 },
55 {
56 title: "Hart's War",
57 released: ISODate('2002-02-15T00:00:00.000Z'),
58 paginationToken: 'CMtJGgYQuq+ngwgaCSkAjBYH7AAAAA==',
59 score: 2.8268959522247314
60 }
61]

Note

The tokens for the documents in your results might be different. Replace the tokens in the subsequent queries with the tokens that Atlas Search returned in your results before running the searchAfter or searchBefore queries.

The following queries use the token from another query with the same semantics to retrieve paginated results after or before the specified token. If you ran the example searchSequenceToken Query, the tokens for the documents in your results might be different. Make sure to replace the tokens in the queries in this section with the tokens returned in your query result.

The queries use the following pipeline stages:

  • $search stage to do the following:

    • Search for documents from the Atlas Search results for the term war in title field.

    • Sort the results first by score and then by the value of the released field in ascending order for documents with identical scores.

  • $limit stage to limit the results to 10 documents.

  • $project stage to do the following:

    • Include only the title and released fields in the documents.

    • Add the following fields:

      • Field named paginationToken that contains the Base64-encoded token for each document.

      • Field named score that contains the relevance score of each document.

The following query requests documents in the Atlas Search results that contain the term war in title field after the specified reference point. It uses the token (on line 58) associated with the last document from the searchSequenceToken Query results as the reference point to retrieve the next 10 documents in the results.

1db.movies.aggregate([
2 {
3 "$search": {
4 "text": {
5 "path": "title",
6 "query": "war"
7 },
8 "sort": {score: {$meta: "searchScore"}, "released": 1},
9 "searchAfter": "CMtJGgYQuq+ngwgaCSkAjBYH7AAAAA=="
10 }
11 },
12 {
13 "$limit": 10
14 },
15 {
16 "$project": {
17 "_id": 0,
18 "title": 1,
19 "released": 1,
20 "paginationToken" : { "$meta" : "searchSequenceToken" },
21 "score": { "$meta": "searchScore" }
22 }
23 }
24])
1[
2 {
3 title: 'The War',
4 released: ISODate('2007-09-23T00:00:00.000Z'),
5 paginationToken: 'CP9xGgYQuq+ngwgaCSkA1KkvFQEAAA==',
6 score: 2.8268959522247314
7 },
8 {
9 title: 'War, Inc.',
10 released: ISODate('2008-06-13T00:00:00.000Z'),
11 paginationToken: 'COhuGgYQuq+ngwgaCSkAtDh/GgEAAA==',
12 score: 2.8268959522247314
13 },
14 {
15 title: 'War, Inc.',
16 released: ISODate('2008-06-13T00:00:00.000Z'),
17 paginationToken: 'COluGgYQuq+ngwgaCSkAtDh/GgEAAA==',
18 score: 2.8268959522247314
19 },
20 {
21 title: 'War Dance',
22 released: ISODate('2008-11-01T00:00:00.000Z'),
23 paginationToken: 'CONvGgYQuq+ngwgaCSkAYFlVHQEAAA==',
24 score: 2.8268959522247314
25 },
26 {
27 title: 'War Horse',
28 released: ISODate('2011-12-25T00:00:00.000Z'),
29 paginationToken: 'CJWFARoGELqvp4MIGgkpAEyEcjQBAAA=',
30 score: 2.8268959522247314
31 },
32 {
33 title: 'Cold War',
34 released: ISODate('2012-11-08T00:00:00.000Z'),
35 paginationToken: 'CJGUARoGELqvp4MIGgkpAPBQ3ToBAAA=',
36 score: 2.8268959522247314
37 },
38 {
39 title: 'Drug War',
40 released: ISODate('2013-04-04T00:00:00.000Z'),
41 paginationToken: 'CMWTARoGELqvp4MIGgkpAMRX0j0BAAA=',
42 score: 2.8268959522247314
43 },
44 {
45 title: 'War Story',
46 released: ISODate('2014-07-30T00:00:00.000Z'),
47 paginationToken: 'CJCdARoGELqvp4MIGgkpAPyQhEcBAAA=',
48 score: 2.8268959522247314
49 },
50 {
51 title: 'A War',
52 released: ISODate('2015-09-10T00:00:00.000Z'),
53 paginationToken: 'CL2kARoGELqvp4MIGgkpAECNtE8BAAA=',
54 score: 2.8268959522247314
55 },
56 {
57 title: 'War Pigs',
58 released: ISODate('2015-09-18T00:00:00.000Z'),
59 paginationToken: 'CJ6kARoGELqvp4MIGgkpACDA3U8BAAA=',
60 score: 2.8268959522247314
61 }
62]

The following query requests Atlas Search results for the term war in title field before the specified reference point. It uses the token (on line 59) associated with the last document in the sample searchAfter query results as the reference point to retrieve the preceding 10 documents from the results.

1db.movies.aggregate([
2 {
3 "$search": {
4 "text": {
5 "path": "title",
6 "query": "war"
7 },
8 "sort": {score: {$meta: "searchScore"}, "released": 1},
9 "searchBefore": "CJ6kARoGELqvp4MIGgkpACDA3U8BAAA="
10 }
11 },
12 {
13 "$limit": 10
14 },
15 {
16 "$project": {
17 "_id": 0,
18 "title": 1,
19 "released": 1,
20 "paginationToken" : { "$meta" : "searchSequenceToken" },
21 "score": { "$meta": "searchScore" }
22 }
23 }
24])
1[
2 {
3 title: 'A War',
4 released: ISODate('2015-09-10T00:00:00.000Z'),
5 paginationToken: 'CL2kARoGELqvp4MIGgkpAECNtE8BAAA=',
6 score: 2.8268959522247314
7 },
8 {
9 title: 'War Story',
10 released: ISODate('2014-07-30T00:00:00.000Z'),
11 paginationToken: 'CJCdARoGELqvp4MIGgkpAPyQhEcBAAA=',
12 score: 2.8268959522247314
13 },
14 {
15 title: 'Drug War',
16 released: ISODate('2013-04-04T00:00:00.000Z'),
17 paginationToken: 'CMWTARoGELqvp4MIGgkpAMRX0j0BAAA=',
18 score: 2.8268959522247314
19 },
20 {
21 title: 'Cold War',
22 released: ISODate('2012-11-08T00:00:00.000Z'),
23 paginationToken: 'CJGUARoGELqvp4MIGgkpAPBQ3ToBAAA=',
24 score: 2.8268959522247314
25 },
26 {
27 title: 'War Horse',
28 released: ISODate('2011-12-25T00:00:00.000Z'),
29 paginationToken: 'CJWFARoGELqvp4MIGgkpAEyEcjQBAAA=',
30 score: 2.8268959522247314
31 },
32 {
33 title: 'War Dance',
34 released: ISODate('2008-11-01T00:00:00.000Z'),
35 paginationToken: 'CONvGgYQuq+ngwgaCSkAYFlVHQEAAA==',
36 score: 2.8268959522247314
37 },
38 {
39 title: 'War, Inc.',
40 released: ISODate('2008-06-13T00:00:00.000Z'),
41 paginationToken: 'COluGgYQuq+ngwgaCSkAtDh/GgEAAA==',
42 score: 2.8268959522247314
43 },
44 {
45 title: 'War, Inc.',
46 released: ISODate('2008-06-13T00:00:00.000Z'),
47 paginationToken: 'COhuGgYQuq+ngwgaCSkAtDh/GgEAAA==',
48 score: 2.8268959522247314
49 },
50 {
51 title: 'The War',
52 released: ISODate('2007-09-23T00:00:00.000Z'),
53 paginationToken: 'CP9xGgYQuq+ngwgaCSkA1KkvFQEAAA==',
54 score: 2.8268959522247314
55 },
56 {
57 title: "Hart's War",
58 released: ISODate('2002-02-15T00:00:00.000Z'),
59 paginationToken: 'CMtJGgYQuq+ngwgaCSkAjBYH7AAAAA==',
60 score: 2.8268959522247314
61 }
62]

For the preceding query, note that Atlas Search results are in reverse order. The following query uses the $limit stage with the toArray() and reverse() methods to build a function similar to "Previous Page".

1db.movies.aggregate([
2 {
3 "$search": {
4 "text": {
5 "path": "title",
6 "query": "war"
7 },
8 "sort": {score: {$meta: "searchScore"}, "released": 1},
9 "searchBefore": "CJ6kARoGELqvp4MIGgkpACDA3U8BAAA="
10 }
11 },
12 {
13 "$limit": 10
14 },
15 {
16 "$project": {
17 "_id": 0,
18 "title": 1,
19 "released": 1,
20 "paginationToken" : { "$meta" : "searchSequenceToken" },
21 "score": { "$meta": "searchScore" }
22 }
23 }
24]).toArray().reverse()
1[
2 {
3 title: "Hart's War",
4 released: ISODate('2002-02-15T00:00:00.000Z'),
5 paginationToken: 'CMtJGgYQuq+ngwgaCSkAjBYH7AAAAA==',
6 score: 2.8268959522247314
7 },
8 {
9 title: 'The War',
10 released: ISODate('2007-09-23T00:00:00.000Z'),
11 paginationToken: 'CP9xGgYQuq+ngwgaCSkA1KkvFQEAAA==',
12 score: 2.8268959522247314
13 },
14 {
15 title: 'War, Inc.',
16 released: ISODate('2008-06-13T00:00:00.000Z'),
17 paginationToken: 'COhuGgYQuq+ngwgaCSkAtDh/GgEAAA==',
18 score: 2.8268959522247314
19 },
20 {
21 title: 'War, Inc.',
22 released: ISODate('2008-06-13T00:00:00.000Z'),
23 paginationToken: 'COluGgYQuq+ngwgaCSkAtDh/GgEAAA==',
24 score: 2.8268959522247314
25 },
26 {
27 title: 'War Dance',
28 released: ISODate('2008-11-01T00:00:00.000Z'),
29 paginationToken: 'CONvGgYQuq+ngwgaCSkAYFlVHQEAAA==',
30 score: 2.8268959522247314
31 },
32 {
33 title: 'War Horse',
34 released: ISODate('2011-12-25T00:00:00.000Z'),
35 paginationToken: 'CJWFARoGELqvp4MIGgkpAEyEcjQBAAA=',
36 score: 2.8268959522247314
37 },
38 {
39 title: 'Cold War',
40 released: ISODate('2012-11-08T00:00:00.000Z'),
41 paginationToken: 'CJGUARoGELqvp4MIGgkpAPBQ3ToBAAA=',
42 score: 2.8268959522247314
43 },
44 {
45 title: 'Drug War',
46 released: ISODate('2013-04-04T00:00:00.000Z'),
47 paginationToken: 'CMWTARoGELqvp4MIGgkpAMRX0j0BAAA=',
48 score: 2.8268959522247314
49 },
50 {
51 title: 'War Story',
52 released: ISODate('2014-07-30T00:00:00.000Z'),
53 paginationToken: 'CJCdARoGELqvp4MIGgkpAPyQhEcBAAA=',
54 score: 2.8268959522247314
55 },
56 {
57 title: 'A War',
58 released: ISODate('2015-09-10T00:00:00.000Z'),
59 paginationToken: 'CL2kARoGELqvp4MIGgkpAECNtE8BAAA=',
60 score: 2.8268959522247314
61 }
62]

Back

4. Count Results