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.
-
Percorra cada item na matriz e obtenha a chave e o valor da entrada
-
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.
-
Teste o comprimento da variável externa para pilha , e se estiver vazio (primeira vez), pressione o valor de0
como visto no último elemento aninhado ao final da chave "$cond" no documento.
-
Se já havia algo lá (comprimento> 0), pegue esse valor e pressione como o terceiro valor na chave "$cond" do documento.
-
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.