sql >> Base de Datos >  >> NoSQL >> MongoDB

Generación de una estructura para la agregación

Cuando tuve un momento para pensar en esto, volví corriendo a casa a perl y resolví esto:

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

Así que el proceso fue deslumbrantemente simple.

  1. Revise cada elemento de la matriz y obtenga la clave y el valor de la entrada

  2. Cree un nuevo "documento" que tenga un argumento de matriz para la tecla "$cond" solo dos de las tres entradas requeridas. Estos son los valores asignados para probar el "$user_id" y el valor de "peso" devuelto.

  3. Pruebe la longitud de la variable exterior para pila , y si estaba vacío (primera vez), entonces presione el valor de 0 como se ve en el último elemento anidado al final de la clave "$cond" en el documento.

  4. Si ya había algo allí (longitud> 0), tome ese valor y pulse como el tercer valor en la clave "$cond" del documento.

  5. Vuelva a colocar ese documento como el valor de stack y repita para el siguiente elemento

Entonces, hay algunas cosas en la lista, como invertir el orden de la entrada, que no es necesario pero produce un orden natural en la salida anidada. Además, mi elección para esa "pila" externa fue una matriz porque los operadores de prueba parecían simples. Pero en realidad es solo un valor singular que sigue siendo reutilizado, aumentado y reemplazado.

Además, la impresión JSON solo está ahí para mostrar la salida. Todo lo que realmente se quiere es el valor resultante de stack para fusionarse con la estructura.

Luego convertí la lógica a ruby, ya que era el lenguaje utilizado por el OP de donde obtuve la inspiración sobre cómo generar esta estructura anidada:

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

Y luego, finalmente, en la forma final para generar la canalización que quería el 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 )

Así que esa era una forma de generar una estructura para pasar a un agregado con el fin de aplicar "pesos" que son específicos de un user_id y ordenar los resultados en la colección.