I am using version 2.2 of MongoDB drivers for C#. I want to paginate a query : the response to the query must contain the items of the current page and the total count of items matching the query.
I want to do one query. With the mongo shell I can realize that as it :
var c = db.mycol.find({....}).skip(0).limit(10)
var total = c.count();
while (c.hasNext()) {
print(tojson(c.next()));
}
But with the C# driver, I don't know how to do it with only one query.
var find = collection
.Find(x => x.Valid == true)
.Skip(0)
.Limit(10);
var cursor = await find.ToCursorAsync(cancellationToken);
// How to get the count? There is no method in the IAsyncCursor interface.
Is it possible ? Some ideas ?
The limit() function in MongoDB is used to specify the maximum number of results to be returned. Only one parameter is required for this function.to return the number of the desired result. Sometimes it is required to return a certain number of results after a certain number of documents. The skip() can do this job.
MongoDB's cursor object has a method called skip , which as per documentation and definition, controls where MongoDB begins returning results. Thus in combination with function limit, one can easily have paginated results.
In MongoDB, the countDocuments() method counts the number of documents that matches to the selection criteria. It returns a numeric value that represents the total number of documents that match the selection criteria. It takes two arguments first one is the selection criteria and other is optional.
You can't accomplish your task by sending to DB only one query. The common practice is following
var query = GetCollection().Find(x => x.Valid == true);
var totalTask = query.CountAsync();
var itemsTask = query.Skip(0).Limit(10).ToListAsync();
await Task.WhenAll(totalTask, itemsTask);
return new Page{ Total = totalTask.Result, Items = itemsTask.Result};
Within MongoDB we have the ability to create a data processing pipeline that will get executed against our data once.
public class Person
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public string Id { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
}
public class Peger
{
public int Count { get; set; }
public int Page { get; set; }
public int Size { get; set; }
public IEnumerable<Person> Items { get; set; }
}
class Program
{
static async Task Main(string[] args)
{
var client = new MongoClient();
var database = client.GetDatabase("pager_test");
var collection = database.GetCollection<Person>(nameof(Person));
int page = 1;
int pageSize = 5;
var results = await GetPagerResultAsync(page, pageSize, collection);
}
private static async Task<Peger> GetPagerResultAsync(int page, int pageSize, IMongoCollection<Person> collection)
{
// count facet, aggregation stage of count
var countFacet = AggregateFacet.Create("countFacet",
PipelineDefinition<Person, AggregateCountResult>.Create(new[]
{
PipelineStageDefinitionBuilder.Count<Person>()
}));
// data facet, we’ll use this to sort the data and do the skip and limiting of the results for the paging.
var dataFacet = AggregateFacet.Create("dataFacet",
PipelineDefinition<Person, Person>.Create(new[]
{
PipelineStageDefinitionBuilder.Sort(Builders<Person>.Sort.Ascending(x => x.Surname)),
PipelineStageDefinitionBuilder.Skip<Person>((page - 1) * pageSize),
PipelineStageDefinitionBuilder.Limit<Person>(pageSize),
}));
var filter = Builders<Person>.Filter.Empty;
var aggregation = await collection.Aggregate()
.Match(filter)
.Facet(countFacet, dataFacet)
.ToListAsync();
var count = aggregation.First()
.Facets.First(x => x.Name == "countFacet")
.Output<AggregateCountResult>()
?.FirstOrDefault()
?.Count ?? 0;
var data = aggregation.First()
.Facets.First(x => x.Name == "dataFacet")
.Output<Person>();
return new Pager
{
Count = (int)count / pageSize,
Size = pageSize,
Page = page,
Items = data
};
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With