MongoDB 如何在单个查询中从集合的中间开始获取 K 个文档?
MongoDB how to get K documents start from the middle of the collection in a single query?
我有 N
条记录与 MongoDB 的集合(例如消息)中的 query q
相匹配。我想获取 [N/2, N/2 + 100)
范围内的文档。
不知道 N
的值,我可以通过问题 2 查询来做到这一点:
- 使用
N = db.messages.find(q).count()
得到N
,然后用skipCount = N / 2 - 1
计算偏移量;
- 使用
db.messages.find(q).skip(skipCount).limit(100)
得到结果
有没有办法(尤其是在 .net MongoDB.Driver 2.7.2
中)将 2 个查询合并为一个以提高性能?
您需要 $facet 运算符同时 运行 多个聚合管道,然后使用多个管道 returned 的结果处理结果。
db.col.aggregate([
{
$match: q
},
{
$facet: {
count: [{ $count: "total" }],
docs: [ { $match: q } ] // this is supposed to pass all input documents to the output but we can't specify empty array here thus we can repeat q
}
},
{
$unwind: "$count"
},
{
$project: {
docs: {
$slice: [ "$docs", { $multiply: [ -1, { $ceil: { $divide: [ "$count.total", 2 ] } } ] } ]
}
}
},
{
$unwind: "$docs"
},
{
$replaceRoot: {
newRoot: "$docs"
}
}
])
$facet
中定义的每个阶段都将 return 一个数组,但是我们知道计数应该只包含一个元素,因此我们可以使用 $unwind on it. Second field (docs
) will contain all the elements that were returned after q
query. To take last k
elements you can use $slice passing negative value as second parameter (takes last k
elements). Then you need to transformed sliced array back to original shape so you need $unwind
and $replaceRoot.
由于聚合有点复杂,在 C# 中最好的选择可能是使用 BsonDocument
class,尝试:
FilterDefinition<BsonDocument> q = // your query
AggregateFacet<BsonDocument> countFacet =
AggregateFacet.Create<BsonDocument, BsonDocument>("count",
PipelineDefinition<BsonDocument, BsonDocument>.Create(new IPipelineStageDefinition[] {
new BsonDocumentPipelineStageDefinition<BsonDocument, BsonDocument>(BsonDocument.Parse("{ $count: \"total\" }"))
}));
AggregateFacet<BsonDocument> matchFacet =
AggregateFacet.Create<BsonDocument, BsonDocument>("docs",
PipelineDefinition<BsonDocument, BsonDocument>.Create(new IPipelineStageDefinition[] {
PipelineStageDefinitionBuilder.Match(q)
}));
var projection = new BsonDocumentProjectionDefinition<BsonDocument>(
BsonDocument.Parse("{ docs: { $slice: [ \"$docs\", { $multiply: [ -1, { $ceil: { $divide: [ \"$count.total\", 2 ] } } ] } ] } }"));
var replaceRoot = new BsonValueAggregateExpressionDefinition<BsonDocument, BsonDocument>("$docs");
var result = Col.Aggregate()
.Match(q)
.Facet<BsonDocument>(new[] { countFacet, matchFacet })
.Unwind("count")
.Project(projection)
.Unwind("docs")
.ReplaceRoot(replaceRoot)
.ToList<BsonDocument>();
我有 N
条记录与 MongoDB 的集合(例如消息)中的 query q
相匹配。我想获取 [N/2, N/2 + 100)
范围内的文档。
不知道 N
的值,我可以通过问题 2 查询来做到这一点:
- 使用
N = db.messages.find(q).count()
得到N
,然后用skipCount = N / 2 - 1
计算偏移量; - 使用
db.messages.find(q).skip(skipCount).limit(100)
得到结果
有没有办法(尤其是在 .net MongoDB.Driver 2.7.2
中)将 2 个查询合并为一个以提高性能?
您需要 $facet 运算符同时 运行 多个聚合管道,然后使用多个管道 returned 的结果处理结果。
db.col.aggregate([
{
$match: q
},
{
$facet: {
count: [{ $count: "total" }],
docs: [ { $match: q } ] // this is supposed to pass all input documents to the output but we can't specify empty array here thus we can repeat q
}
},
{
$unwind: "$count"
},
{
$project: {
docs: {
$slice: [ "$docs", { $multiply: [ -1, { $ceil: { $divide: [ "$count.total", 2 ] } } ] } ]
}
}
},
{
$unwind: "$docs"
},
{
$replaceRoot: {
newRoot: "$docs"
}
}
])
$facet
中定义的每个阶段都将 return 一个数组,但是我们知道计数应该只包含一个元素,因此我们可以使用 $unwind on it. Second field (docs
) will contain all the elements that were returned after q
query. To take last k
elements you can use $slice passing negative value as second parameter (takes last k
elements). Then you need to transformed sliced array back to original shape so you need $unwind
and $replaceRoot.
由于聚合有点复杂,在 C# 中最好的选择可能是使用 BsonDocument
class,尝试:
FilterDefinition<BsonDocument> q = // your query
AggregateFacet<BsonDocument> countFacet =
AggregateFacet.Create<BsonDocument, BsonDocument>("count",
PipelineDefinition<BsonDocument, BsonDocument>.Create(new IPipelineStageDefinition[] {
new BsonDocumentPipelineStageDefinition<BsonDocument, BsonDocument>(BsonDocument.Parse("{ $count: \"total\" }"))
}));
AggregateFacet<BsonDocument> matchFacet =
AggregateFacet.Create<BsonDocument, BsonDocument>("docs",
PipelineDefinition<BsonDocument, BsonDocument>.Create(new IPipelineStageDefinition[] {
PipelineStageDefinitionBuilder.Match(q)
}));
var projection = new BsonDocumentProjectionDefinition<BsonDocument>(
BsonDocument.Parse("{ docs: { $slice: [ \"$docs\", { $multiply: [ -1, { $ceil: { $divide: [ \"$count.total\", 2 ] } } ] } ] } }"));
var replaceRoot = new BsonValueAggregateExpressionDefinition<BsonDocument, BsonDocument>("$docs");
var result = Col.Aggregate()
.Match(q)
.Facet<BsonDocument>(new[] { countFacet, matchFacet })
.Unwind("count")
.Project(projection)
.Unwind("docs")
.ReplaceRoot(replaceRoot)
.ToList<BsonDocument>();