linq 如何使用AggregateToCollection()将IMongoQueryable的结果存储在集合中

pftdvrlh  于 2023-02-27  发布在  Go
关注(0)|答案(1)|浏览(106)

除非我遗漏了什么,否则C# MongoDb驱动程序的文档中似乎有很大的空白。
我尝试获取一个IMongoQueryable(它是各种LINQ Where、Select等操作的结果),并将结果存储在数据库端的一个集合中。当然,我可以在客户端迭代它并以这种方式持久化它,但即使是批处理,效率也不高,在shell中,使用$merge$out是一个相当容易的操作。
在一个集合上,有一个方法AggregateToCollection<TResult>(),我相信这正是我想要的,但是它有一个PiplineDefinition<TDocument, TResult>参数,我不知道如何从IMongoQueryable中生成。
我一直在钻兔子洞,在IMongoQueryable上使用GetExecutionModel()来获得BsonDocument或Json字符串,但我仍然不知道如何将其转换为我需要的PipelineDefinition
我本以为我会找到的是IMongoQueryable上的一个扩展方法,它允许我发送它以合并到一个集合中。
到目前为止,为了能够将IMongoQueryable发送到集合,我已经编写了一些代码:

var executionModelDocument = queryable.GetExecutionModel().ToBsonDocument();

    // somehow turn the document into pipeline stages and a pipeline??

    await _database.GetCollection<TDocument>().AggregateToCollectionAsync<TResult>(pipeline);

我该如何实现这一目标?

l3zydbqr

l3zydbqr1#

好吧,这最终比预期的容易,但仍然令人沮丧。
首先,你可以通过简单地调用ToString()来从IMongoQueryable获取JSON形式的管道。虽然我说的是JSON,但它并不完全是-输出包含了管道的完整JSON,作为一个阶段数组,但在前面有一个标签,并在括号中包含JSON。我在这里走了一条捷径,只取了一个脏的子字符串:

var queryableJson = queryable.ToString();
var trimmedDocument = queryableJson.Substring(10, queryableJson.Length - 11); // TODO: more reliably get the true json rather than blindly removing what should be "aggregate(" and ")"

接下来,我将JSON重新序列化回BsonDocument数组,并从中生成PipelineDefinitionBsonDocument[]可以隐式转换为PipelineDefinition):

PipelineDefinition<TDocument, TResult> pipelineQueryable = BsonSerializer.Deserialize<BsonDocument[]>(trimmedDocument);

请注意,虽然www.example.com上的文档http://mongodb.github.io/mongo-csharp-driver/2.4/reference/driver/definitions/#pipelines说我可以隐式地为管道强制转换单个BsonDocument,而不是阶段数组,但这并不正确,除非在另一个名称空间中存在我没有找到的重载。
现在我们已经为IMongoQueryable定义了一个管道,我们可以简单地向它添加阶段来获得我们想要的结果(在本例中,将管道的结果合并到另一个集合中)。您可以指定MergeStageOptions<TResult>对象的属性来控制行为,但默认值对我来说很好:

var stageMerge = PipelineStageDefinitionBuilder.Merge<TResult, TResult>(_database.GetCollection<TResult>(), new MergeStageOptions<TResult>());
var mergePipeline = pipelineQueryable.AppendStage(stageMerge);

使用我们新增强的管道,我们可以将其应用于源集合,以将输出合并到目标集合中:

_database.GetCollection<TDocument>().AggregateToCollection(mergePipeline);

为了简单起见,我在这里演示的不是异步的,但是我在实际代码中利用了驱动程序中的可等待方法,因为同步方法只是简单地 Package 了可等待版本。
这就是它的全部!当我有更多的时间,我会回去尝试跳过序列化-反序列化步骤,因为它明显很慢,不应该是必要的。我还计划把它变成一个扩展方法,为了流畅。

相关问题