MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Generieren einer Struktur für die Aggregation

Als ich einen Moment Zeit hatte, darüber nachzudenken, rannte ich zurück nach Hause zu Perl und arbeitete Folgendes aus:

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] );

Der Vorgang war also verblüffend einfach.

  1. Gehen Sie jedes Element im Array durch und erhalten Sie den Schlüssel und den Wert für den Eintrag

  2. Erstellen Sie ein neues "Dokument", das im Array-Argument für die Taste "$cond" nur zwei der erforderlichen drei Einträge enthält. Dies sind die Werte, die zugewiesen wurden, um die "$user_id" und den zurückgegebenen "weight"-Wert zu testen.

  3. Testen Sie die Länge der äußeren Variablen für stack , und wenn es leer war (beim ersten Mal), dann drücken den Wert von 0 wie im letzten verschachtelten Element bis zum Ende des "$cond"-Schlüssels im Dokument zu sehen.

  4. Wenn dort schon etwas war (Länge> 0), dann nimm diesen Wert und drücke es als dritten Wert im "$cond"-Schlüssel für das Dokument.

  5. Legen Sie dieses Dokument als Wert von stack zurück und für das nächste Element wiederholen

Es gibt also ein paar Dinge in der Auflistung, wie das Umkehren der Reihenfolge der Eingabe, was nicht erforderlich ist, aber eine natürliche Reihenfolge in der verschachtelten Ausgabe erzeugt. Außerdem war meine Wahl für diesen äußeren "Stack" ein Array, weil die Testoperatoren einfach zu sein schienen. Aber es ist wirklich nur ein einzelner Wert, der immer wieder verwendet, erweitert und ersetzt wird.

Auch der JSON-Druck dient nur dazu, die Ausgabe anzuzeigen. Alles, was wirklich gesucht wird, ist der resultierende Wert von stack in die Struktur eingebunden werden.

Dann konvertierte ich die Logik in Ruby, ebenso wie die vom OP verwendete Sprache, aus der ich die Inspiration für die Generierung dieser verschachtelten Struktur erhielt:

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])

Und dann schließlich in die endgültige Form, um die Pipeline zu generieren, die das OP wollte:

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 )

Das war also eine Möglichkeit, eine Struktur zu generieren, die an das Aggregat übergeben werden soll, um "Gewichte" anzuwenden, die für eine user_id spezifisch sind und die Ergebnisse in der Sammlung sortieren.