MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Génération d'une structure pour l'agrégation

Quand j'ai eu un moment pour réfléchir à cela, je suis rentré chez perl et j'ai trouvé ceci :

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

Le processus était donc d'une simplicité aveuglante.

  1. Parcourez chaque élément du tableau et obtenez la clé et la valeur de l'entrée

  2. Créez un nouveau "document" qui a en argument de tableau la clé "$cond" seulement deux des trois entrées requises. Ce sont les valeurs assignées pour tester le "$user_id" et la valeur "weight" renvoyée.

  3. Testez la longueur de la variable externe pour pile , et s'il était vide (la première fois), alors appuyez la valeur de 0 comme on le voit dans le dernier élément imbriqué à la fin de la clé "$cond" dans le document.

  4. S'il y avait déjà quelque chose (longueur> 0), prenez cette valeur et appuyez comme troisième valeur dans la clé "$cond" pour le document.

  5. Remettez ce document en tant que valeur de stack et répétez pour l'élément suivant

Il y a donc quelques éléments dans la liste, tels que l'inversion de l'ordre de l'entrée, qui n'est pas obligatoire mais produit un ordre naturel dans la sortie imbriquée. De plus, mon choix pour cette "pile" extérieure était un tableau car les opérateurs de test semblaient simples. Mais ce n'est vraiment qu'une valeur singulière qui ne cesse d'être réutilisée, augmentée et remplacée.

De plus, l'impression JSON est juste là pour montrer la sortie. Tout ce qui est vraiment recherché est la valeur résultante de stack à fusionner dans la structure.

Ensuite, j'ai converti la logique en ruby, tout comme le langage utilisé par l'OP d'où je me suis inspiré pour générer cette structure imbriquée :

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

Et puis finalement dans le formulaire final pour générer le pipeline que l'OP voulait :

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 )

C'était donc un moyen de générer une structure à passer dans l'agrégat afin d'appliquer des "pondérations" spécifiques à un user_id et trier les résultats dans la collection.