MongoDB, как получить K документов, начиная с середины коллекции в одном запросе? - PullRequest
0 голосов
/ 26 декабря 2018

У меня есть N записей, которые соответствуют query q в коллекции (например, сообщения) MongoDB.И я хочу получить документы в диапазоне [N/2, N/2 + 100).

Не зная значения N, я могу сделать это с помощью запроса 2:

  1. использовать N = db.messages.find(q).count() для получения N, а затем вычислить смещение с помощью skipCount = N / 2 - 1;
  2. используйте db.messages.find(q).skip(skipCount).limit(100) для получения результатов

Есть ли способ (особенно в .net MongoDB.Driver 2.7.2) объединить запрос 2 в одинулучшить производительность?

1 Ответ

0 голосов
/ 26 декабря 2018

Вам нужен оператор $ 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"
        }
    }
])

Каждый этап, определенный в $facetвернет массив, однако мы знаем, что count должен содержать только один элемент, поэтому мы можем использовать $ unwind .Второе поле (docs) будет содержать все элементы, которые были возвращены после запроса q.Для получения последних k элементов вы можете использовать $ slice , передавая отрицательное значение в качестве второго параметра (принимает последние k элементов).Затем вам нужно преобразовать секционированный массив обратно в исходную форму, поэтому вам нужно $unwind и $ replaceRoot .

Поскольку агрегация немного сложна, возможно, лучшим вариантом в 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>();
...