Manipulação de aggregation pipelines complexas com c# C#
Avalie esse Tutorial
Conforme mostrado em meu último artigo sobre os fundamentos da execução de pipelines de agregação com o driver C# do MongoDB, há muitas maneiras de configurar e executar pipelines de agregação a partir de um aplicação .NET . O driver C# do MongoDB traz um driver LINQ avançado que simplifica a criação de pipelines de agregação para desenvolvedores experientes com C# e menos com MongoDB.
Para desenvolvedores experientes, ou quando você tiver pipelines de agregação complexos, provavelmente usará o MongoDB Compass ou outra ferramenta para configurar, testar e ajustar um pipeline de agregação que deseja executar a partir de um aplicação C# posteriormente. Usando esse fluxo de trabalho fácil, você pode usar todos os estágios do pipeline de agregação e criar o pipeline em um conjunto de dados realista antes de incluí-lo no código. Além disso, para desenvolvedores MongoDB experientes, pode ser mais fácil ler o pipeline de agregação no formulário JSON em vez de declarações C#.
Este tutorial se concentra em uma abordagem que permite que desenvolvedores experientes do MongoDB usem pipelines do MongoDB Compass e os incluam no código C#. Esta abordagem completa os métodos básicos mostrados no meu último tutorial e pode ser usada para cenários complexos.
As amostras usadas ao longo deste artigo são baseadas na coleção de filmes no banco de banco de dados amostras_mflix . Usaremos as seguintes classes de objetos de transferência de dados (DTO):
1 [ ]2 [ ]3 public class Movie 4 { 5 [ ]6 public required string Title { get; set; } 7 8 [ ]9 public required int Year { get; set; } 10 11 [ ]12 public List<string> Cast { get; set; } = new(); 13 14 [ ]15 public Imdb Imdb { get; set; } = new(); 16 } 17 18 [ ]19 public class Imdb 20 { 21 [ ]22 public double Rating { get; set; } 23 } 24 25 public class RatingByYear 26 { 27 [ ]28 public int Year { get; set; } 29 30 [ ]31 public double AvgRating { get; set; } 32 }
Ao configurar um agregação pipeline no MongoDB Compass, você pode recuperar o JSON para seu pipeline alternando para a visualização Texto:
Isso mostra o pipeline completo para que você possa copiar facilmente o JSON para a área de transferência. Como primeira etapa, você pode incluir o JSON em uma string de múltiplas linhas C#. Em nosso caso, usamos a agregação do último artigo para fins de demonstração:
1 var pipelineStr = """ 2 [ 3 { 4 $match: { 5 cast: "Robert De Niro" 6 } 7 }, 8 { 9 $group: { 10 _id: "$year", 11 rating: { $avg: "$imdb.rating" } 12 } 13 }, 14 { 15 $sort: { 16 rating: -1 17 } 18 } 19 ] 20 """; 21 22 PipelineDefinition<Movie, RatingByYear> pipeline = BsonSerializer.Deserialize<BsonArray>(pipelineStr) 23 .Select(x => x.AsBsonDocument) 24 .ToArray(); 25 26 var result = await (await movies.AggregateAsync<RatingByYear>(pipeline)).ToListAsync();
O código acima desserializa o pipeline da string de múltiplas linhas e o converte em uma array de
BsonDocument
objetos. Há um operador de conversão implícita que permite converter a array em um PipelineDefinition<Movie, RatingByYear>
objeto. Essa definição de pipeline pode ser fornecida como uma variável para o AggregateAsync
método que executa a agregação e retorna o resultado.Em vez de incluir o pipeline como uma string em seu código C#, você também pode lê-lo a partir de um arquivo que você distribui com seu aplicação ou incorporar um arquivo JSON como um recurso em seu aplicação:
Isso fornece uma melhor separação entre o código C# e os pipelines. Você pode acessar o recurso assim:
1 // Be sure to use the correct resource name, the schema is as follows: 2 // <PROJECT ROOT NAMESPACE>.<Folders>.<FileName> 3 // If the resource name is invalid, null is returned 4 using var stream = typeof(Program).Assembly.GetManifestResourceStream("MyNamespace.Pipelines.RatingByYear.json"); 5 using var reader = new StreamReader(stream!); 6 var pipelineStr = await reader.ReadToEndAsync(); 7 8 PipelineDefinition<Movie, RatingByYear> pipeline = BsonSerializer.Deserialize<BsonArray>(pipelineStr) 9 .Select(x => x.AsBsonDocument) 10 .ToArray(); 11 12 var result = await (await movies.AggregateAsync(pipeline)).ToListAsync();
Ter um pipeline estático é um bom início, mas e se você quiser fazer ajustes ao executar o agregação pipeline? Em nosso exemplo, podemos estar interessados em consultar outros membros do elenco e retornar apenas um número configurável de anos com as melhores classificações.
1 [ 2 { 3 "$match": { 4 "$expr": { 5 "$in": [ 6 "$$castMember", 7 { 8 "$ifNull": [ 9 "$cast", 10 [] 11 ] 12 } 13 ] 14 } 15 } 16 }, 17 { 18 "$group": { 19 "_id": "$year", 20 "rating": { "$avg": "$imdb.rating" } 21 } 22 }, 23 { 24 "$setWindowFields": { 25 "sortBy": { "rating": -1 }, 26 "output": { 27 "docNo": { 28 "$documentNumber": {} 29 } 30 } 31 } 32 }, 33 { 34 "$match": { 35 "$expr": { 36 "$lte": [ "$docNo", "$$topN" ] 37 } 38 } 39 }, 40 { 41 "$unset": "docNo" 42 } 43 ]
O pipeline acima foi ajustado para usar as variáveis
$$castMember
e $$topN
. Analisaremos mais de perto esses ajustes na seção a seguir. Por enquanto, nos concentramos em executar o pipeline em C#. Para fornecer valores para as variáveis, um AggregationOptions
objeto é utilizado, como mostrado abaixo:1 using var stream = typeof(Program).Assembly.GetManifestResourceStream("MyNamespace.Pipelines.RatingByYearParameterized.json"); 2 using var reader = new StreamReader(stream!); 3 var pipelineStr = await reader.ReadToEndAsync(); 4 5 PipelineDefinition<Movie, RatingByYear> pipeline = BsonSerializer.Deserialize<BsonArray>(pipelineStr) 6 .Select(x => x.AsBsonDocument) 7 .ToArray(); 8 9 var options = new AggregateOptions() 10 { 11 Let = new BsonDocument() 12 { 13 { "castMember", "Al Pacino" }, 14 { "topN", 10 }, 15 } 16 }; 17 18 var result = await (await movies.AggregateAsync(pipeline, options)).ToListAsync();
Como você viu, o pipeline foi ajustado para usar as variáveis. Isso é necessário porque nem todos os estágios usam os valores de variáveis como esperado. Por exemplo, o primeiro
$match
estágio não avalia uma variável ao usar a $match
sintaxe padrão :1 { 2 $match: { 3 cast: "$$castMember" 4 } 5 },
Como a condição como tal é válida para MongoDB, isso não gera um erro, mas leva a um conjunto de resultados vazio, pois não há nenhum filme com um membro do elenco que atenda pelo nome incomum $$castMember. O uso de
$expr
na condição afirma que as variáveis são substituídas por seus valores ao executar a comparação:1 { 2 "$match": { 3 "$expr": { 4 "$in": [ 5 "$$castMember", 6 { 7 "$ifNull": [ 8 "$cast", 9 [] 10 ] 11 } 12 ] 13 } 14 } 15 },
Além disso, a sintaxe da expressão lida com valores nulos de maneira diferente, portanto, precisamos adicionar uma
$ifNull
expressão que afirme que os valores ausentes ou nulos são substituídos por uma array vazia.Na maioria dos casos, no entanto, o MongoDB gera um erro quando a variável não pode ser usada. Em nossa amostra, o pipeline não pode usar o
$limit
estágio porque esse estágio espera um valor estático para o número máximo de documentos que devem ser retornados. Portanto, temos que substituir o simples $limit
estágio por uma combinação de:$setWindowFields
que classifica os documentos e adiciona uma propriedadedocNo
para o número do documento.$match
que mantém apenas o número de documentos especificados na variável (também com uma$expr
condição).$unset
para remover adocNo
propriedade, pois não precisamos dela em nosso conjunto de resultados.
1 { 2 "$setWindowFields": { 3 "sortBy": { "rating": -1 }, 4 "output": { 5 "docNo": { 6 "$documentNumber": {} 7 } 8 } 9 } 10 }, 11 { 12 "$match": { 13 "$expr": { 14 "$lte": [ "$docNo", "$$topN" ] 15 } 16 } 17 }, 18 { 19 "$unset": "docNo" 20 }
Ao testar seu pipeline, esteja ciente de que você também precisa procurar conjuntos de resultados inesperados e, especialmente, vazios ao introduzir variáveis em um pipeline.
Este tutorial descreve várias maneiras de lidar com pipelines de agregação complexos em C# e, ao mesmo tempo, manter a definição original de pipeline do MongoDB disponível. Você pode usar os melhores e mais recentes pipelines de agregação do MongoDB em sua forma original. Além disso, para desenvolvedores experientes com MongoDB, o formato JSON do pipeline pode ser mais fácil de ler. Ao aplicar alterações ao pipeline mais tarde, você pode testar e analisar o pipeline exato no MongoDB Compass ou no MongoDB shell.
Qual é o seu método favorito para lidar com pipelines de agregação desafiadores com o C# driver MongoDB C#? Informe-nos nos MongoDB Developer Community fóruns da comunidade de desenvolvedores MongoDB !
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.