Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Idiomaschevron-right
C#chevron-right

Manipulação de aggregation pipelines complexas com c# C#

Markus Wildgruber5 min read • Published Nov 15, 2024 • Updated Nov 15, 2024
.NETC#
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
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[BsonIgnoreExtraElements]
2[BsonNoId]
3public class Movie
4{
5 [BsonElement("title")]
6 public required string Title { get; set; }
7
8 [BsonElement("year")]
9 public required int Year { get; set; }
10
11 [BsonElement("cast")]
12 public List<string> Cast { get; set; } = new();
13
14 [BsonElement("imdb")]
15 public Imdb Imdb { get; set; } = new();
16}
17
18[BsonIgnoreExtraElements]
19public class Imdb
20{
21 [BsonElement("rating")]
22 public double Rating { get; set; }
23}
24
25public class RatingByYear
26{
27 [BsonId]
28 public int Year { get; set; }
29
30 [BsonElement("rating")]
31 public double AvgRating { get; set; }
32}

Analisando pipelines de agregação do JSON

Ao configurar um agregação pipeline no MongoDB Compass, você pode recuperar o JSON para seu pipeline alternando para a visualização Texto: Use o botão " Texto " para alternar para a visualização JSON do pipeline de agregação para facilitar a cópia
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:
1var 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
22PipelineDefinition<Movie, RatingByYear> pipeline = BsonSerializer.Deserialize<BsonArray>(pipelineStr)
23 .Select(x => x.AsBsonDocument)
24 .ToArray();
25
26var 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:
Defina a ação de compilação como "Recurso incorporado" para incluir o arquivo JSON como um recurso
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
4using var stream = typeof(Program).Assembly.GetManifestResourceStream("MyNamespace.Pipelines.RatingByYear.json");
5using var reader = new StreamReader(stream!);
6var pipelineStr = await reader.ReadToEndAsync();
7
8PipelineDefinition<Movie, RatingByYear> pipeline = BsonSerializer.Deserialize<BsonArray>(pipelineStr)
9 .Select(x => x.AsBsonDocument)
10 .ToArray();
11
12var result = await (await movies.AggregateAsync(pipeline)).ToListAsync();

Usando variáveis com pipelines de agregação

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:
1using var stream = typeof(Program).Assembly.GetManifestResourceStream("MyNamespace.Pipelines.RatingByYearParameterized.json");
2using var reader = new StreamReader(stream!);
3var pipelineStr = await reader.ReadToEndAsync();
4
5PipelineDefinition<Movie, RatingByYear> pipeline = BsonSerializer.Deserialize<BsonArray>(pipelineStr)
6 .Select(x => x.AsBsonDocument)
7 .ToArray();
8
9var options = new AggregateOptions()
10{
11 Let = new BsonDocument()
12 {
13 { "castMember", "Al Pacino" },
14 { "topN", 10 },
15 }
16};
17
18var result = await (await movies.AggregateAsync(pipeline, options)).ToListAsync();

Ajustando o pipeline para o uso de variáveis

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 propriedade docNo 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 a docNo 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.

Resumo

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.
Iniciar a conversa

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Tutorial

Trabalhando com transações MongoDB com C# e .NET Framework


Sep 11, 2024 | 3 min read
Artigo

Como usar o Realm de forma eficaz em um aplicativo Xamarin.Forms


Sep 09, 2024 | 18 min read
Tutorial

Criando um jogo de captura de espaço no Unity que sincroniza com o Realm e o MongoDB Atlas


Jun 26, 2024 | 24 min read
Notícias e Anúncios

Apresentamos o MongoDB Analyzer para .NET


Aug 05, 2024 | 6 min read
Sumário