Generating a Structure for Aggregation

前端 未结 2 726
心在旅途
心在旅途 2020-11-29 13:16

So here\'s a question. What I want to do is generate a data structure given a set of input values.

Since this is a multiple language submission, let\'s consider the

相关标签:
2条回答
  • 2020-11-29 13:27

    First thank you Neil for your help with this here, this workout great for me and it's really fast. For those who use mongoid, this is what I used to create the weight parameter where recommended_user_ids is an array:

        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
    

    So to add this to the pipeline simply merge the hash like this:

    {"$project" => {:id => 1,:posted_at => 1}.merge(project_recommended_weight(options[:recommended_user_ids]))}
    
    0 讨论(0)
  • 2020-11-29 13:38

    When I had a moment to think about this, I ran back home to perl and worked this out:

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

    So the process was blindingly simple.

    1. Go through each item in the array and get the key and value for the entry

    2. Create a new "document" that has in array argument to the "$cond" key just two of required three entries. These are the values assigned to test the "$user_id" and the returned "weight" value.

    3. Test the length of the outside variable for stack, and if it was empty (first time through) then push the value of 0 as seen in the last nested element to the end of the "$cond" key in the document.

    4. If there was something already there (length > 0) then take that value and push it as the third value in the "$cond" key for the document.

    5. Put that document back as the value of stack and repeat for the next item

    So there are a few things in the listing such as reversing the order of the input, which isn't required but produces a natural order in the nested output. Also, my choice for that outside "stack" was an array because the test operators seemed simple. But it really is just a singular value that keeps getting re-used, augmented and replaced.

    Also the JSON printing is just there to show the output. All that is really wanted is the resulting value of stack to be merged into the structure.

    Then I converted the logic to ruby, as was the language used by the OP from where I got the inspiration for how to generate this nested structure:

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

    And then eventually into the final form to generate the pipeline that the OP wanted:

    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 )
    

    So that was a way to generate a structure to be passed into aggregate in order to apply "weights" that are specific to a user_id and sort the results in the collection.

    0 讨论(0)
提交回复
热议问题