如何将嵌套哈希提取到数据库表中?

时间:2014-01-02 22:38:41

标签: mysql ruby mailchimp

我试图从Mailchimp中提取一些嵌套哈希中返回的数据。这是我得到的结果的精简版。对于每封电子邮件,有多个GROUPINGS,每个GROUPING有多个GROUPS。

我的目标是将其放入一个mysql表,其布局如下:email_addr,list,grouping1_id,grouping1_name,group1_name,group1_interest,group2_name,group2_interest,grouping2_id,grouping2_name等。因此,每个订阅者都有一行所有分组和组信息。

{"email"=>"dummy@gmail.com", "merges"=>{"EMAIL"=>"dummy@gmail.com", 
"GROUPINGS"=>[{"id"=>1, "name"=>"Grouping One", "groups"=>[{"name"=>"Group One", 
"interested"=>false}, {"name"=>"Group", "interested"=>true}, 
{"name"=>"Group Three", "interested"=>true}]}, {"id"=>2, "name"=>"Grouping Two", 
"groups"=>[{"name"=>"Group Four", "interested"=>false}, 
{"name"=>"Group Five", "interested"=>false}]}]}}

现在,我运行的代码运行并将嵌套块的结果插入表中,但每次通过groups.each_with_index语句都有一行。到目前为止,我的方法看起来过于复杂,但我不确定如何正确处理数据。

感谢任何帮助。

更新: 我稍微清理了逻辑并将数据库写入分离到散列处理的每个级别。现在正确地在数据库中插入和更新数据。虽然这仍然感觉非常不优雅。

def organize_members_subs
  @members_data = @members_subs["data"]
  @members_data.each do |member|
    @email_addr = member["email"]
    @db.query("INSERT INTO db.details
        (email_addr, list)
        VALUES  ('#{@email_addr}', '#{@list}' ) ")

    groupings = member["merges"]["GROUPINGS"]
    groupings.each_with_index do |grouping, index|
      @groupings_name = grouping["name"]
      @groupings_id = grouping["id"]
      @groupings_label = "grp#{index}_"
      @db.query("UPDATE db.details
        SET grouping#{index}_id = '#{@groupings_id}'
          , grouping#{index}_name = '#{@groupings_name}'
        WHERE email_addr = '#{@email_addr}' ")

      groups = member["merges"]["GROUPINGS"][index]["groups"] 
      groups.each_with_index do |group, index|
        @group_name = group["name"]
        @group_interested = group["interested"]
        @db.query("UPDATE db.details
        SET #{@groupings_label}group#{index}_name = '#{@group_name}'
          , #{@groupings_label}group#{index}_int = '#{@group_interested}'
        WHERE email_addr = '#{@email_addr}' ")

        break if index == groups.length
      end
      break if index == groupings.length
    end
  end
end

2 个答案:

答案 0 :(得分:1)

首先,也许是额外的,但我喜欢使用符号,因为我在Rails中做了很多工作。所以,让我们从这里偷取一个方法:How do I convert a Ruby hash so that all of its keys are symbols?

def recursive_symbolize_keys(h)
  case h
    when Hash
      Hash[
          h.map do |k, v|
            [ k.respond_to?(:to_sym) ? k.to_sym : k, recursive_symbolize_keys(v) ]
          end
      ]
    when Enumerable
      h.map { |v| recursive_symbolize_keys(v) }
    else
      h
  end
end

好的,让我们构建一个类,以便在需求变化时更容易操作和扩展:

class MemberSub
  attr_accessor :email, :groupings, :data_hash, :list, :data_row, :db_sql

  def initialize(data_hash)

    #convert all keys to symbols
    @data_hash = recursive_symbolize_keys(data_hash)
    @email = @data_hash[:email]
    @list = 'Members'
    @groupings = @data_hash[:merges][:GROUPINGS]
    @data_row  = data_row
    @db_sql = db_insert
  end

  def data_row
    #returns a data row for DB
    row_hash = {}
    row_hash['email'] = @email
    row_hash['list'] = @list
    gc = 1
    #iterate through groupings
    @groupings.each_with_index do |grouping, index|
      row_hash["grouping#{index + 1}_id"] = grouping[:id]
      row_hash["grouping#{index + 1}_name"] = grouping[:name]
      #iterate through the groups
      grouping[:groups].each do |group|
        row_hash["group#{gc}_name"] = group[:name]
        row_hash["group#{gc}_interest"] = group[:interested]
        gc += 1
      end
    end
    row_hash
  end

  def db_insert
    "INSERT INTO db.details (#{@data_row.keys}) VALUES (#{@data_row.values})".tr('[]','')
  end  

end

现在你可以使用迭代方法连续输入它并创建一个新对象:

row = MemberSub.new({"email"=>"dummy@gmail.com", "list"=>"Members", "merges"=>  
{"EMAIL"=>"dummy@gmail.com", "GROUPINGS"=>[{"id"=>1, "name"=>"Grouping One", "groups"=>  
[{"name"=>"Group One", "interested"=>false}, {"name"=>"Group Two", "interested"=>true},  
{"name"=>"Group Three", "interested"=>true}]}, {"id"=>2, "name"=>"Grouping Two", "groups"=>  
[{"name"=>"Group Four", "interested"=>false}, {"name"=>"Group Five", "interested"=>false}]}]}})

并进行查询:

db.query(row.db_sql)

db.query(INSERT INTO db.details ("email", "list", "grouping1_id", "grouping1_name", 
"group1_name", "group1_interest", "group2_name", "group2_interest", "group3_name", 
"group3_interest", "grouping2_id", "grouping2_name", "group4_name", "group4_interest",
 "group5_name", "group5_interest") VALUES ("dummy@gmail.com", "Members", 1, "Grouping One",
 "Group One", false, "Group Two", true, "Group Three", true, 2, "Grouping Two", "Group Four",
 false, "Group Five", false))

其他方法应该是自我解释的。您不必将它们全部用作attar_accessor,但我只是这样做了。

答案 1 :(得分:1)

首先,我想仔细看看你的哈希值。我没有自己重新格式化,而是这样做了:

require "awesome_print"
h = `{"email"=>..., "interested"=>false}]}]}}`
ap h

向下滚动到我的答案底部,查看ap的哈希格式。

我会回答你的问题,假设db结构是给定的,但是想提出几点:

  • 如果"id"对于每个grouping记录都是唯一的,您是否可以将其作为密钥,并取消index
  • 如果"name"对于每个grouping记录都是唯一的,那么您是否可以省略"id"index
  • 如果"name"对于每个group记录都是唯一的(对于给定的grouping),您是否只为每个组group["name"] => group["interested"]

继续您的代码,我还将假设您的哈希结构。稍后,我将重新审视这一假设。

我对你的代码提出的修改是相当小的,有些是纯粹的风格:

  • 将所有实例变量设为局部变量,这意味着必须将另外两个参数传递给def organize_members_subs
  • 有两个可能的例外,消除仅在定义后使用一次的局部变量。例如,而不是   groupings_id = grouping["id"],然后是SET grouping#{index}_id = '#{@groupings_id}',只有SET grouping#{index}_id = '#{grouping["id"]}'。 两个可能的例外是groupingsgroups。例如,你可以通过写作摆脱前者 member["merges"]["GROUPINGS"].each_with_index do |grouping, index_1|。我会把它们作为变量(所以我可以很容易地检查它们的值), 但这是一个风格决定。
  • index中的变量groupings.each_with_index do |grouping, index|在内部块的范围内,它使用具有相同名称的迭代器变量。 我认为后者优先,但它们的名称应该不同。我已将它们分别更改为index_outindex_in
  • index_out的范围从0groupings.length-1,因此永远不会执行break if index_out == groupings.length,因此可能会被删除。同上break if index_in == groups.length
  • 我向下移动了groupings_label = "grp#{index}_",以引起人们注意这样一个事实,即只在以后需要它,而不是在前面的SET表达式中。

这些更改导致以下结果:

    def organize_members_subs(db, list, @members_subs["data"])
      members_data.each do |member|
        email_addr = member["email"]
        db.query("INSERT INTO db.details
            (email_addr, list)
            VALUES  ('#{email_addr}', '#{list}' ) ")
        groupings = member["merges"]["GROUPINGS"]
        groupings.each_with_index do |grouping, index_out|
          db.query("UPDATE db.details
          SET grouping#{index_out}_id   = '#{grouping["id"]}'
            , grouping#{index_out}_name = '#{grouping["name"]}'
          WHERE email_addr = '#{email_addr}' ")
          groupings_label = "grp#{index_out}_"

          groups = member["merges"]["GROUPINGS"][index_out]["groups"] 
          groups.each_with_index do |group, index_in|
            db.query("UPDATE db.details
            SET #{groupings_label}group#{index_in}_name = '#{group["name"]}'
              , #{groupings_label}group#{index_in}_int = '#{group["interested"]}'
            WHERE email_addr = '#{email_addr}' ")   
          end
        end
      end
    end

看看你的哈希,我想知道你是否可以将它简化为以下内容(格式礼貌的真棒版画):

  {
      "email" => "dummy@gmail.com",
     "merges" => {
             "EMAIL" => "dummy@gmail.com",
         "GROUPINGS" => {
             1 => {
                   "name" => "Grouping One",
                 "groups" => {
                       "Group One" => false,
                       "Group Two" => true,
                     "Group Three" => true
                 }
             },
             2 => {
                   "name" => "Grouping Two",
                 "groups" => {
                     "Group Four" => false,
                     "Group Five" => false
                 }
             }
         }
     }
  }

甚至

{
     "email" => "dummy@gmail.com",
    "merges" => {
            "EMAIL" => "dummy@gmail.com",
        "GROUPINGS" => {
            "Grouping One" => {
                  "Group One" => false,
                  "Group Two" => true,
                "Group Three" => true
            },
            "Grouping Two" => {
                "Group Four" => false,
                "Group Five" => false
            }
        }
    }
}

这些不仅仅是建议,而只是值得深思。

应用于哈希的真棒打印:

ap h # =>
    {
         "email" => "dummy@gmail.com",
        "merges" => {
                "EMAIL" => "dummy@gmail.com",
            "GROUPINGS" => [
                [0] {
                        "id" => 1,
                      "name" => "Grouping One",
                    "groups" => [
                        [0] {
                                  "name" => "Group One",
                            "interested" => false
                        },
                        [1] {
                                  "name" => "Group",
                            "interested" => true
                        },
                        [2] {
                                  "name" => "Group Three",
                            "interested" => true
                        }
                    ]
                },
                [1] {
                        "id" => 2,
                      "name" => "Grouping Two",
                    "groups" => [
                        [0] {
                                  "name" => "Group Four",
                            "interested" => false
                        },
                        [1] {
                                  "name" => "Group Five",
                            "interested" => false
                        }
                    ]
                }
            ]
        }
    }