Kiedy miałem chwilę, żeby się nad tym zastanowić, pobiegłem z powrotem do domu do perla i wymyśliłem to:
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] );
Proces był więc oślepiająco prosty.
-
Przejrzyj każdy element w tablicy i uzyskaj klucz i wartość wpisu
-
Utwórz nowy „dokument”, który ma w argumencie tablicy do klucza „$cond” tylko dwa z wymaganych trzech wpisów. Są to wartości przypisane do testowania „$user_id” i zwróconej wartości „weight”.
-
Przetestuj długość zmiennej zewnętrznej dla stosu , a jeśli była pusta (za pierwszym razem), wciśnij wartość
0
jak widać w ostatnim zagnieżdżonym elemencie na końcu klawisza „$cond” w dokumencie. -
Jeśli coś już tam było (długość> 0), weź tę wartość i pchnij jest to trzecia wartość w kluczu „$cond” dokumentu.
-
Odłóż ten dokument z powrotem jako wartość stosu i powtórz dla następnego elementu
Na liście znajduje się więc kilka rzeczy, takich jak odwrócenie kolejności danych wejściowych, co nie jest wymagane, ale tworzy naturalną kolejność w zagnieżdżonych danych wyjściowych. Ponadto moim wyborem dla tego zewnętrznego „stosu” była tablica, ponieważ operatory testu wydawały się proste. Ale tak naprawdę jest to tylko pojedyncza wartość, która jest ponownie wykorzystywana, rozszerzana i zastępowana.
Również drukowanie JSON jest tylko po to, aby pokazać dane wyjściowe. Wszystko, czego naprawdę potrzeba, to wynikowa wartość stosu do połączenia ze strukturą.
Następnie przekonwertowałem logikę na ruby, podobnie jak język używany przez OP, z którego czerpałem inspirację do generowania tej zagnieżdżonej struktury:
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])
A potem w końcu do ostatecznej formy, aby wygenerować potok, którego chciał OP:
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 )
Był to więc sposób na wygenerowanie struktury, która zostanie przekazana do agregatu w celu zastosowania „wag” specyficznych dla user_id
i posortuj wyniki w kolekcji.