生成聚合结构

时间:2014-03-05 12:44:12

标签: mongodb aggregation-framework

所以这是一个问题。我想要做的是在给定一组输入值的情况下生成数据结构。

由于这是一个多语言提交,让我们将输入列表视为键/值对的数组。因此,哈希,地图,字典或任何浮动你的船的术语的数组。我将这里的所有符号都保留为JSON,希望它具有足够的通用性来翻译/解码。

所以对于输入,假设我们有这个:

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

也许有点多余,但让我们坚持下去。

所以从那个输入来看,我想要达到这个结构。我给出了一个完整的结构,但重要的部分是“weight”下的值返回的内容:

[
   { "$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
                   ]}
               ]}
           ]}
       ]}
   }}
]

所以我正在寻找的解决方案通过使用输入来填充“重量”的结构内容,如结构所示。

是结构中看起来像数字的值必须是数字而不是字符串,因此无论语言实现如何,JSON编码版本必须看起来完全相同。

或者,给我一个更好的方法来获得基于匹配user_id分配权重值的相同结果。

有没有人对此有所了解?

对任何语言实现都会感到满意,因为我认为只看到如何创建结构是公平的。

我会尝试添加自己,但荣誉归功于良好的实现。

快乐的编码。

2 个答案:

答案 0 :(得分:2)

当我有时间考虑这个问题时,我跑回到perl并将其解决了:

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

所以这个过程非常简单。

  1. 浏览数组中的每个项目并获取条目的键和值

  2. 创建一个新的“文档”,该文档在“$ cond”键的数组参数中只包含两个必需的三个条目。这些是分配用于测试“$ user_id”和返回的“权重”值的值。

  3. 测试堆栈的外部变量的长度,如果它是空的(第一次通过),则推送 0的值在最后一个嵌套元素中看到文档中“$ cond”键的结尾。

  4. 如果已经存在某些内容(长度> 0),则将该值和推送作为文档“$ cond”键中的第三个值。

  5. 将该文件作为堆叠的值重新放入,并重复下一个项目

  6. 因此,列表中有一些内容,例如反转输入的顺序,这不是必需的,但会在嵌套输出中产生自然顺序。另外,我对外部“堆栈”的选择是一个数组,因为测试运算符看起来很简单。但它确实只是一个单一的价值,不断被重复使用,增加和替换。

    此外,JSON打印仅用于显示输出。所有真正需要的是将堆栈的结果值合并到结构中。

    然后我将逻辑转换为ruby,就像OP使用的语言一样,我从中获得了如何生成这种嵌套结构的灵感:

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

    然后最终进入最终形式以生成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 )
    

    这是一种生成要传递到聚合的结构的方法,以便应用特定于user_id的“权重”并对结果进行排序。

答案 1 :(得分:2)

首先感谢Neil对此here的帮助,这对我来说很有用,而且速度非常快。对于那些使用mongoid的人来说,这就是我用来创建weight_user_ids是数组的权重参数:

    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

因此,要将此添加到管道,只需合并这样的哈希:

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