MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Gerando uma estrutura para agregação


Quando tive um momento para pensar sobre isso, corri de volta para casa para perl e resolvi isso:
use Modern::Perl;

use Moose::Autobox;
use JSON;

my $encoder = JSON->new->pretty;

my $input = [ { 4 => 10 }, { 7 => 9 }, { 90 => 7 }, { 1 => 8 } ];

my $stack = [];

foreach my $item ( reverse @{$input} ) {

  while ( my ( $key, $value ) = each %{$item} ) {
    my $rec = {
      '$cond' => [
        { '$eq' => [ '$user_id', int($key) ] },
        $value
      ]
    };

    if ( $stack->length == 0 ) {
      $rec->{'$cond'}->push( 0 );
    } else {
      my $last = $stack->pop;
      $rec->{'$cond'}->push( $last );
    }

    $stack->push( $rec );
  }

}

say $encoder->encode( $stack->[0] );

Portanto, o processo era incrivelmente simples.

  1. Percorra cada item na matriz e obtenha a chave e o valor da entrada

  2. Crie um novo "documento" que tenha no argumento de matriz para a chave "$cond" apenas duas das três entradas necessárias. Esses são os valores atribuídos para testar o "$user_id" e o valor "weight" retornado.

  3. Teste o comprimento da variável externa para pilha , e se estiver vazio (primeira vez), pressione o valor de 0 como visto no último elemento aninhado ao final da chave "$cond" no documento.

  4. Se já havia algo lá (comprimento> 0), pegue esse valor e pressione como o terceiro valor na chave "$cond" do documento.

  5. Coloque esse documento de volta como o valor de pilha e repita para o próximo item

Portanto, há algumas coisas na listagem, como inverter a ordem da entrada, que não é necessária, mas produz uma ordem natural na saída aninhada. Além disso, minha escolha para essa "pilha" externa foi uma matriz porque os operadores de teste pareciam simples. Mas é realmente apenas um valor singular que continua sendo reutilizado, aumentado e substituído.

Além disso, a impressão JSON está lá apenas para mostrar a saída. Tudo o que realmente se deseja é o valor resultante de pilha para ser incorporado na estrutura.

Então converti a lógica para ruby, como era a linguagem usada pelo OP de onde tirei a inspiração de como gerar essa estrutura aninhada:
require 'json'

input = [ { 4 => 10 }, { 7 => 9 }, { 90 => 7 }, { 1 => 8 } ]

stack = []

input.reverse_each {|item|

  item.each {|key,value|
    rec = {
      '$cond' => [
        { '$eq' => [ '$user_id', key ] },
        value
      ]
    }

    if ( stack.length == 0 )
      rec['$cond'].push( 0 )
    else
      last = stack.pop
      rec['$cond'].push( last )
    end

    stack.push( rec )
  }

}

puts JSON.pretty_generate(stack[0])

E, eventualmente, na forma final para gerar o pipeline que o OP queria:
require 'json'

userWeights = [ { 4 => 10 }, { 7 => 9 }, { 90 => 7}, { 1 => 8 } ]

stack = []

userWeights.reverse_each {|item|

  item.each {|key,value|
    rec = {
      '$cond' => [
        { '$eq' => [ '$user_id', key ] },
        value
      ]
    }

    if ( stack.length == 0 )
      rec['$cond'].push( 0 )
    else
      last = stack.pop
      rec['$cond'].push( last )
    end

    stack.push( rec )
  }

}

pipeline = [
    { '$project' => {
        'user_id' => 1,
        'content' => 1,
        'date' => 1,
        'weight' => stack[0]
    }},
    { '$sort' => { 'weight' => -1, 'date' => -1 } }
]

puts JSON.pretty_generate( pipeline )

Então, essa foi uma maneira de gerar uma estrutura a ser passada em agregação para aplicar "pesos" específicos a um user_id e ordenar os resultados na coleção.