我正在使用 C# 针对 MongoDB 4.0 开发一些查询和索引MongoDB.Driver 2.12.4
MongoDB 一切都很好。不幸的是,我必须使用 AWS DocumentDB 作为生产数据库,它声称与 MongoDB 兼容,但我发现了几个问题:
- 索引是有限的https://docs.aws.amazon.com/documentdb/latest/developerguide/mongo-apis.html#mongo-apis-index
- 它不支持排序索引或查询(已删除,通过其他方式设法进行不区分大小写的搜索)
- 它不支持 facet(我用它来进行有效的分页)
"message": "Command aggregate failed: Aggregation stage not supported: '$facet'.",
问题是:使用 AWS DocumentDB 对结果进行分页的最佳方式是什么?是否有任何选项可以使用单个查询来执行此操作?或者我必须两次访问数据库?
这是我使用 Facet 和聚合 facet 结果的原始实现。
public async Task<PagedResult<TDocument>> GetAll<TDocument>(
Expression<Func<TDocument, bool>> filter,
Sort<TDocument> sort,
Pagination pagination,
CancellationToken cancellationToken = new())
where TDocument : class, IDocument
{
filter ??= doc => true;
sort ??= Sort<TDocument>.Default;
if (pagination == null) throw new ArgumentNullException(nameof(pagination));
var collection = _collectionRetrieverService.GetCollection<TDocument>();
var sortDefinition = GetSortDefinition(sort);
var pageNumber = pagination.PageNumber;
var pageSize = pagination.PageSize;
var countPipelineDefinition =
PipelineDefinition<TDocument, AggregateCountResult>
.Create(new[]
{
PipelineStageDefinitionBuilder.Count<TDocument>()
});
var dataPipelineDefinition =
PipelineDefinition<TDocument, TDocument>
.Create(new[]
{
PipelineStageDefinitionBuilder.Sort(sortDefinition),
PipelineStageDefinitionBuilder.Skip<TDocument>((pageNumber -1) * pageSize),
PipelineStageDefinitionBuilder.Limit<TDocument>(pageSize)
});
const string countFacetName = "count";
const string dataFacetName = "data";
var countFacet = AggregateFacet.Create(countFacetName, countPipelineDefinition);
var dataFacet = AggregateFacet.Create(dataFacetName, dataPipelineDefinition);
var aggregateOptions =
new AggregateOptions
{
// Collation is not currently supported in AWS DocumentDB. See https://docs.aws.amazon.com/documentdb/latest/developerguide/mongo-apis.html#mongo-apis-index
//Collation = CollationOptions.CaseInsensitiveCollation
};
var aggregation =
await collection
.Aggregate(aggregateOptions)
.Match(filter)
.Facet(countFacet, dataFacet)
.ToListAsync(cancellationToken);
var count =
aggregation
.First()
.Facets
.First(x => x.Name == countFacetName)
.Output<AggregateCountResult>()
?.FirstOrDefault()
?.Count;
if (count is null)
{
var emptyResult =
new PagedResult<TDocument>()
{
Items = Enumerable.Empty<TDocument>(),
PageNumber = pageNumber,
PageSize = pageSize,
TotalPages = pageNumber,
TotalCount = 0
};
return emptyResult;
}
var totalPages = (int)Math.Ceiling((double)count/ pageSize);
var data =
aggregation
.First()
.Facets
.First(x => x.Name == dataFacetName)
.Output<TDocument>();
var result =
new PagedResult<TDocument>
{
Items = data,
PageNumber = pageNumber,
PageSize = pageSize,
TotalPages = totalPages,
TotalCount = count.Value
};
return result;
}
更新 2021-07-06
这是我的解决方法,它查询两次数据库,因为我需要匹配过滤条件的项目总数。
public async Task<PagedResult<TDocument>> GetAll<TDocument>(
Expression<Func<TDocument, bool>> filter,
Sort<TDocument> sort,
Pagination pagination,
CancellationToken cancellationToken = new())
where TDocument : class, IDocument
{
filter ??= doc => true;
sort ??= Sort<TDocument>.Default;
if (pagination == null) throw new ArgumentNullException(nameof(pagination));
var collection = _collectionRetrieverService.GetCollection<TDocument>();
var sortDefinition = GetSortDefinition(sort);
var pageNumber = pagination.PageNumber;
var pageSize = pagination.PageSize;
var skipCount = pageSize * (pageNumber - 1);
var count = await collection.CountDocumentsAsync(filter, cancellationToken: cancellationToken);
if (count == 0)
{
var emptyResult =
new PagedResult<TDocument>()
{
Items = Enumerable.Empty<TDocument>(),
PageNumber = pageNumber,
PageSize = pageSize,
TotalPages = pageNumber,
TotalCount = count
};
return emptyResult;
}
var totalPages = (int)Math.Ceiling((double)count/ pageSize);
var findOptions =
new FindOptions<TDocument,TDocument>
{
AllowPartialResults = false,
Sort = sortDefinition,
Limit = pageSize,
Skip = skipCount
// Collation is not currently supported in AWS DocumentDB. See https://docs.aws.amazon.com/documentdb/latest/developerguide/mongo-apis.html#mongo-apis-index
//Collation = CollationOptions.CaseInsensitiveCollation
};
var queryResult = await collection.FindAsync(filter, findOptions, cancellationToken);
var results = await queryResult.ToListAsync(cancellationToken: cancellationToken);
var result =
new PagedResult<TDocument>
{
Items = results,
PageNumber = pageNumber,
PageSize = pageSize,
TotalPages = totalPages,
TotalCount = count
};
return result;
}