Generando una Estructura para Agregación

Entonces aquí hay una pregunta. Lo que quiero hacer es generar una estructura de datos dado un conjunto de valores de entrada.

Como se trata de un envío de múltiples idiomas, consideremos que la lista de entrada es una matriz de pares clave / valor. Y, por lo tanto, una matriz de hash, mapa, diccionario o cualquier término que flote su bote. Mantendré toda la notación aquí como JSON, con la esperanza de que sea lo suficientemente universal para traducir / decodificar.

Entonces para la entrada, digamos que tenemos esto:

[ { "4": 10 }, { "7": 9 }, { "90": 7 }, { "1": 8 } ] 

Tal vez un poco redundante, pero sigamos con eso.

Entonces, desde esa entrada, quiero llegar a esta estructura. Le doy toda una estructura, pero la parte importante es lo que se devuelve por el valor en “peso” :

 [ { "$project": { "user_id": 1, "content": 1, "date": 1, "weight": { "$cond": [ { "$eq": ["$user_id": 4] }, 10, { "$cond": [ { "$eq": ["$user_id": 7] }, 9, { "$cond": [ { "$eq": ["$user_id": 90] }, 7, { "$cond": [ { "$eq": ["$user_id": 1] }, 8, 0 ]} ]} ]} ]} }} ] 

Entonces la solución que estoy buscando completa el contenido de la estructura para “peso” como se muestra en la estructura usando la entrada como se muestra.

Sí, los valores que parecen números en la estructura deben ser números y no cadenas, por lo que sea cual sea la implementación del lenguaje, la versión codificada JSON debe verse exactamente igual.

Alternativamente , dame un mejor enfoque para obtener el mismo resultado de asignar los valores de peso basados ​​en el user_id correspondiente.

¿Alguien tiene un enfoque para esto?

Estaría contento con cualquier implementación del lenguaje, ya que creo que es justo simplemente ver cómo se puede crear la estructura.

Trataré de agregarme a mí mismo, pero elogios va a las buenas implementaciones.

Feliz encoding.

Cuando tuve un momento para pensar sobre esto, volví corriendo a casa y descubrí 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] ); 

Entonces 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 en matriz con 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 externa para la stack , y si estaba vacía (la primera vez), presione el valor de 0 como se ve en el último elemento nested hasta el final de la tecla “$ cond” en el documento.

  4. Si ya había algo allí (longitud> 0), tome ese valor y empújelo como el tercer valor en la tecla “$ cond” para el documento.

  5. Pon ese documento de nuevo como el valor de la stack y repite para el siguiente elemento

De modo que 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 “stack” externa era una matriz porque los operadores de prueba parecían simples. Pero realmente es solo un valor singular que sigue siendo reutilizado, aumentado y reemplazado.

También la impresión JSON está allí para mostrar la salida. Todo lo que realmente se desea es que el valor resultante de la stack se fusione en la estructura.

Luego convertí la lógica a ruby, como era el lenguaje utilizado por el OP, de donde obtuve la inspiración de 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 cartera que el OP quería:

 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 que se pasaría a agregado para aplicar “ponderaciones” que son específicas de un user_id y ordenar los resultados en la colección.

En primer lugar, gracias Neil por tu ayuda con esto aquí , este entrenamiento es genial para mí y es realmente rápido. Para aquellos que usan mongoid, esto es lo que usé para crear el parámetro de ponderación donde recommended_user_ids es una matriz:

  def self.project_recommended_weight recommended_user_ids return {} unless recommended_user_ids.present? {:weight => create_weight_statement(recommended_user_ids.reverse)} end def self.create_weight_statement recommended_user_ids, index=0 return 0 if index == recommended_user_ids.count {"$cond" => [{ "$eq" => ["$user_id", recommended_user_ids[index]] },index+1,create_weight_statement(recommended_user_ids,index+1)]} end 

Entonces, para agregar esto a la tubería simplemente combine el hash como este:

 {"$project" => {:id => 1,:posted_at => 1}.merge(project_recommended_weight(options[:recommended_user_ids]))}