简体   繁体   English

MongoDB如何从单个查询中的集合中间开始获取K文档?

[英]MongoDB how to get K documents start from the middle of the collection in a single query?

I have N records which match a query q in a collection (eg. messages) of MongoDB. 我有N条记录与MongoDB的集合(例如消息)中的query q匹配。 And I want to get the documents in range [N/2, N/2 + 100) . 我想得到范围[N/2, N/2 + 100)

Without knowing the value of N , I can do this by issue 2 query: 在不知道N的值的情况下,我可以通过问题2查询来做到这一点:

  1. use N = db.messages.find(q).count() to get N , then compute the offset with skipCount = N / 2 - 1 ; 使用N = db.messages.find(q).count()得到N ,然后用skipCount = N / 2 - 1计算偏移量;
  2. use db.messages.find(q).skip(skipCount).limit(100) to get the results 使用db.messages.find(q).skip(skipCount).limit(100)来获得结果

Is there a way (especially in .net MongoDB.Driver 2.7.2 ) to merge the 2 query into a single one to improve the performance? 有没有办法(特别是在.net MongoDB.Driver 2.7.2 )将2个查询合并为一个查询以提高性能?

You need $facet operator to run more than one aggregation pipeline simultaneously and then process the result using result returned by multiple pipelines. 您需要$ facet运算符同时运行多个聚合管道,然后使用多个管道返回的结果处理结果。

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"
        }
    }
])

Each stage defined in $facet will return an array however we know that count should contain only one element so we can use $unwind on it. $facet定义的每个阶段都将返回一个数组,但是我们知道count应该只包含一个元素,因此我们可以在其上使用$ unwind Second field ( docs ) will contain all the elements that were returned after q query. 第二个字段( docs )将包含q查询后返回的所有元素。 To take last k elements you can use $slice passing negative value as second parameter (takes last k elements). 要获取最后k元素,您可以使用$ slice传递负值作为第二个参数(获取最后k元素)。 Then you need to transformed sliced array back to original shape so you need $unwind and $replaceRoot . 然后你需要将切片阵列转换回原始形状,因此你需要$unwind$ replaceRoot

Since the aggregation is a bit complicated probably the best option in C# is to use BsonDocument class, try: 由于聚合有点复杂,C#中最好的选择是使用BsonDocument类,尝试:

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>();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM