När jag hade en stund på mig att tänka på detta sprang jag tillbaka hem till perl och räknade ut det här:
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] );
Så processen var bländande enkel.
-
Gå igenom varje objekt i arrayen och hämta nyckeln och värdet för posten
-
Skapa ett nytt "dokument" som har i array-argument till "$cond"-nyckeln bara två av de tre nödvändiga posterna. Dessa är värdena som tilldelats för att testa "$user_id" och det returnerade "weight"-värdet.
-
Testa längden på den yttre variabeln för stack , och om det var tomt (första gången genom), tryck värdet på
0
som ses i det sista kapslade elementet till slutet av "$cond"-nyckeln i dokumentet. -
Om det redan fanns något där (längd> 0) så ta det värdet och tryck det som det tredje värdet i "$cond"-nyckeln för dokumentet.
-
Lägg tillbaka det dokumentet som värdet på stack och upprepa för nästa post
Så det finns några saker i listan, som att vända ordningen på inmatningen, vilket inte krävs men ger en naturlig ordning i den kapslade utmatningen. Mitt val för den utanför "stacken" var också en array eftersom testoperatorerna verkade enkla. Men det är verkligen bara ett unikt värde som hela tiden återanvänds, utökas och ersätts.
JSON-utskriften finns bara där för att visa resultatet. Allt som verkligen önskas är det resulterande värdet av stack ska slås samman i strukturen.
Sedan konverterade jag logiken till ruby, liksom språket som användes av OP där jag fick inspirationen till hur man genererar denna kapslade struktur:
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])
Och sedan så småningom in i den slutliga formen för att generera pipeline som OP ville ha:
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 )
Så det var ett sätt att skapa en struktur som skulle överföras till aggregat för att tillämpa "vikter" som är specifika för ett user_id
och sortera resultaten i samlingen.