Search code examples
node.jsmongodbexpressaggregation-frameworkmongodb-atlas-search

MongoDB get total count aggregation pipeline with $search


I have to implement search using search indexes in MongoDB Atlas as well as normal browse feature. This includes filtering, match, sort, skip, limit (pagination). I have made an aggregation pipeline to achieve all this. First I push the search query to my pipeline, then match, then sort, then skip and finally the limit query.

Here's how it goes:

query = [];
query.push({
    $search: {
      index: 'default'
      text: {
        query: searchQuery
        path: {  }
      }
    }
  });
 query.push({
      $sort: sort,
    });
 query.push({
    $match: {
      type: match
    },

query.push({
  $skip: skip
});
query.push({
  $limit: perPage
});

let documents = await collection.aggregate(query);

The results I get so far are correct. However, for pagination, I also want to get the total count of documents. The count must take the "match" and "searchQuery" (if any) take into account. I have tried $facet but it gives error $_internalSearchMongotRemote is not allowed to be used within a $facet stage


Solution

  • So, I see a few challenges with this query.

    1. The $sort stage may not be needed. All search queries are sorted by relevance score by default. If you need to sort on some other criterion, then it may be appropriate.

    2. The $match stage is probably not needed. What most people are looking for when they are trying to match is a compound filter. As you can see from the docs, a filter behaves like a $match in ordinary MongoDB. From where I am, your query should probably look something like:

    If you would like a fast count of the documents returned, you need to use the new count operator. It is available on 4.4.11 and 5.0.4 clusters. You can read about it here.

    
    query = [];
    query.push({
        $search: {
          index: 'default'
          "compound": {
          "filter": [{
            "text": {
              "query": match,
              "path": type
            }
          }],
          "must": [{
            "text": {
              "query": searchQuery,
              "path": { }
            }
          }]
        }, 
         "count": {
          "type": "total"
         }
       }
      });
    
    query.push({
      $skip: skip
    });
    
    query.push({
      $limit: perPage
    });
    
    /* let's you use the count, can obviously project more fields */
    query.push({
      $project: "$$SEARCH_META"
    });
    
    let documents = await collection.aggregate(query);
    
    
    
      [1]: https://docs.atlas.mongodb.com/reference/atlas-search/compound/#mongodb-data-filter
      [2]: https://docs.atlas.mongodb.com/reference/atlas-search/counting/#std-label-count-ref