MongoDB C# 驱动程序 - 如何在 .NET 中对已连接的集合强制执行投影?
MongoDB C# Driver - how to enforce projection on the joined collection in .NET?
代码如下:
ProjectionDefinition<Accountant> projDefAccountant = Builders<Accountant>.Projection
.Include(x => x.Id)
.Include(x => x.Name);
ProjectionDefinition<Client> projDefClient = Builders<Client>.Projection
.Include(c => c.Name)
.Include(c => c.Address)
.Include(c => c.Occupation);
IMongoCollection<Accountant> collection = mongoDatabase.GetCollection<Accountant>("accountants");
IMongoCollection<Client> foreignCollection = mongoDatabase.GetCollection<Client>("clients");
var results = collection.Aggregate()
.Project<Accountant>(projDefAccountant)
.Lookup<Accountant, Client, Accountant>(
foreignCollection: foreignCollection,
localField: ac => ac.BestClientsIds,
foreignField: c => c.Id,
@as: ac => ac.MyClients
).ToList().AsQueryable();
我可以使用第一个投影 "projDefAccountant"
来限制我想要从 "accountants"
集合中提取的字段。有没有办法在连接的 "clients"
集合上强制执行 "projDefClient"
投影,这样连接就不会 return 所有字段,而只会 "projDefClient"
中指定的字段?谢谢
您可以使用 $lookup with custom pipeline,您的聚合可能如下所示:
db.accountants.aggregate([
{ "$project" : { "_id" : 1, "Name" : 1, BestClientsIds: 1 } },
{
"$lookup" : {
"from" : "clients",
"let" : { "best_client_ids" : "$BestClientsIds" },
"pipeline" : [
{ "$match" : { "$expr" : { "$in" : [ "$_id", "$$best_client_ids"] } } },
{ "$project": { Name: 1, Address: 1, Occupation: 1} }
],
as: "MyClients"}
}
]);
在 C# 中,有一个 .Lookup
的重载版本,它允许您以几乎强类型的方式 运行 该方法。这是签名:
IAggregateFluent<TNewResult> Lookup<TForeignDocument, TAsElement, TAs, TNewResult>(
IMongoCollection<TForeignDocument> foreignCollection,
BsonDocument let,
PipelineDefinition<TForeignDocument, TAsElement> lookupPipeline,
FieldDefinition<TNewResult, TAs> @as,
AggregateLookupOptions<TForeignDocument, TNewResult> options = null)
where TAs : IEnumerable<TAsElement>;
您可以修改 projDefAccountant
使其包含 BestClientsIds
字段:
ProjectionDefinition<Accountant> projDefAccountant = Builders<Accountant>.Projection
.Include(x => x.Id)
.Include(x => x.Name)
.Include(x => x.BestClientsIds);
然后将 let
和 $match
阶段指定为 BsonDocument
更容易,但其余部分保持强类型:
var filter = new BsonDocumentFilterDefinition<Client>(BsonDocument.Parse("{ $expr: { $in: [ '$_id', '$$ids' ] } }"));
PipelineDefinition< Client, Client> pipeline = new PipelineStagePipelineDefinition<Client, Client>(
new IPipelineStageDefinition[]
{
PipelineStageDefinitionBuilder.Match(filter),
PipelineStageDefinitionBuilder.Project<Client, Client>(projDefClient),
});
ExpressionFieldDefinition<Accountant, Client[]> fieldDef
= new ExpressionFieldDefinition<Accountant, Client[]>(f => f.MyClients);
var letDef = BsonDocument.Parse("{ ids: '$BestClientsIds' }");
var results = collection.Aggregate()
.Project<Accountant>(projDefAccountant)
.Lookup<Client, Client, Client[], Accountant>(
foreignCollection: foreignCollection,
let: letDef,
lookupPipeline: pipeline,
@as: fieldDef
).ToList().AsQueryable();
代码如下:
ProjectionDefinition<Accountant> projDefAccountant = Builders<Accountant>.Projection
.Include(x => x.Id)
.Include(x => x.Name);
ProjectionDefinition<Client> projDefClient = Builders<Client>.Projection
.Include(c => c.Name)
.Include(c => c.Address)
.Include(c => c.Occupation);
IMongoCollection<Accountant> collection = mongoDatabase.GetCollection<Accountant>("accountants");
IMongoCollection<Client> foreignCollection = mongoDatabase.GetCollection<Client>("clients");
var results = collection.Aggregate()
.Project<Accountant>(projDefAccountant)
.Lookup<Accountant, Client, Accountant>(
foreignCollection: foreignCollection,
localField: ac => ac.BestClientsIds,
foreignField: c => c.Id,
@as: ac => ac.MyClients
).ToList().AsQueryable();
我可以使用第一个投影 "projDefAccountant"
来限制我想要从 "accountants"
集合中提取的字段。有没有办法在连接的 "clients"
集合上强制执行 "projDefClient"
投影,这样连接就不会 return 所有字段,而只会 "projDefClient"
中指定的字段?谢谢
您可以使用 $lookup with custom pipeline,您的聚合可能如下所示:
db.accountants.aggregate([
{ "$project" : { "_id" : 1, "Name" : 1, BestClientsIds: 1 } },
{
"$lookup" : {
"from" : "clients",
"let" : { "best_client_ids" : "$BestClientsIds" },
"pipeline" : [
{ "$match" : { "$expr" : { "$in" : [ "$_id", "$$best_client_ids"] } } },
{ "$project": { Name: 1, Address: 1, Occupation: 1} }
],
as: "MyClients"}
}
]);
在 C# 中,有一个 .Lookup
的重载版本,它允许您以几乎强类型的方式 运行 该方法。这是签名:
IAggregateFluent<TNewResult> Lookup<TForeignDocument, TAsElement, TAs, TNewResult>(
IMongoCollection<TForeignDocument> foreignCollection,
BsonDocument let,
PipelineDefinition<TForeignDocument, TAsElement> lookupPipeline,
FieldDefinition<TNewResult, TAs> @as,
AggregateLookupOptions<TForeignDocument, TNewResult> options = null)
where TAs : IEnumerable<TAsElement>;
您可以修改 projDefAccountant
使其包含 BestClientsIds
字段:
ProjectionDefinition<Accountant> projDefAccountant = Builders<Accountant>.Projection
.Include(x => x.Id)
.Include(x => x.Name)
.Include(x => x.BestClientsIds);
然后将 let
和 $match
阶段指定为 BsonDocument
更容易,但其余部分保持强类型:
var filter = new BsonDocumentFilterDefinition<Client>(BsonDocument.Parse("{ $expr: { $in: [ '$_id', '$$ids' ] } }"));
PipelineDefinition< Client, Client> pipeline = new PipelineStagePipelineDefinition<Client, Client>(
new IPipelineStageDefinition[]
{
PipelineStageDefinitionBuilder.Match(filter),
PipelineStageDefinitionBuilder.Project<Client, Client>(projDefClient),
});
ExpressionFieldDefinition<Accountant, Client[]> fieldDef
= new ExpressionFieldDefinition<Accountant, Client[]>(f => f.MyClients);
var letDef = BsonDocument.Parse("{ ids: '$BestClientsIds' }");
var results = collection.Aggregate()
.Project<Accountant>(projDefAccountant)
.Lookup<Client, Client, Client[], Accountant>(
foreignCollection: foreignCollection,
let: letDef,
lookupPipeline: pipeline,
@as: fieldDef
).ToList().AsQueryable();