繁体   English   中英

使用jq在JSON数据结构中提取公共前缀

[英]Using jq to extract common prefixes in a JSON data structure

我有一个JSON数据集,从Redis商店中提取了大约870万个键值对,其中每个键保证是一个8位数字,键是8个字母数字字符值,即

[{
"91201544":"INXX0019",
"90429396":"THXX0020",
"20140367":"ITXX0043",
 ...
}]

为了减少Redis内存使用量,我想将其转换为哈希哈希值,其中哈希前缀键是键的前6个字符(请参阅此链接 ),然后将其存储回Redis。

具体来说,我想要我生成的JSON数据结构(我将编写一些代码来解析这个JSON结构并创建一个由HSET组成的Redis命令文件),看起来更像

[{
 "000000": { "00000023": "INCD1234",
             "00000027": "INCF1423",
              ....
           },
 ....
 "904293": { "90429300": "THXX0020",
             "90429302": "THXX0024",
             "90429305": "THXY0013"}
 }]

由于我对jq印象深刻,并且我想要更精通函数式编程,我想使用jq来完成这项任务。 到目前为止,我已经提出以下建议:

% jq '.[0] | to_entries | map({key: .key, pfx: .key[0:6], value: .value}) | group_by(.pfx)'

这给了我类似的东西

[
  [
    {
      "key": "00000130",
      "pfx": "000001",
      "value": "CAXX3231"
    },
    {
      "key": "00000162",
      "pfx": "000001",
      "value": "CAXX4606"
    }
  ],
  [
    {
      "key": "00000238",
      "pfx": "000002",
      "value": "CAXX1967"
    },
    {
      "key": "00000256",
      "pfx": "000002",
      "value": "CAXX0727"
    }
  ],
  ....
]

我尝试过以下方法:

% jq 'map(map({key: .pfx, value: {key, value}})) 
      | map(reduce .[] as $item ({}; {key: $item.key, value: [.value[], $item.value]} )) 
      | map( {key, value: .value | from_entries} ) 
      | from_entries'

这确实给了我正确的结果,但也打印出每个减少(我相信)的错误

jq: error: Cannot iterate over null

最终的结果是

{
   "000001": {
     "00000130": "CAXX3231",
     "00000162": "CAXX4606"
   },
   "000002": {
     "00000238": "CAXX1967",
     "00000256": "CAXX0727"
   },
   ...
}

这是正确的,但我怎么能避免抛出这个stderr警告呢?

我不确定这里有足够的数据来评估问题的根源。 我发现很难相信你所尝试的结果会导致这种情况发生。 我一路上都遇到了错误。

请尝试使用此过滤器:

.[0]
    | to_entries
    | group_by(.key[0:6])
    | map({
          key:   .[0].key[0:6],
          value: map(.key=.key[6:8]) | from_entries
      })
    | from_entries

给定的数据如下:

[{
    "91201544":"INXX0019",
    "90429396":"THXX0020",
    "20140367":"ITXX0043",
    "00000023":"INCD1234",
    "00000027":"INCF1423",
    "90429300":"THXX0020",
    "90429302":"THXX0024",
    "90429305":"THXY0013"
}]

结果如下:

{
  "000000": {
    "23": "INCD1234",
    "27": "INCF1423"
  },
  "201403": {
    "67": "ITXX0043"
  },
  "904293": {
    "00": "THXX0020",
    "02": "THXX0024",
    "05": "THXY0013",
    "96": "THXX0020"
  },
  "912015": {
    "44": "INXX0019"
  }
}

我知道这不是你要求的,但仅仅作为参考,我认为使用Redis的内置Lua脚本执行此操作会更快。

结果证明它更直接:

for _,key in pairs(redis.call('keys', '*')) do
  local val = redis.call('get', key)
  local short_key = string.sub(key, 0, -2)
  redis.call('hset', short_key, key, val)
  redis.call('del', key)
end

这将在没有从/向Redis传输和转换到/从JSON转换的情况下完成。

从控制台运行它:

$ redis-cli eval "$(cat script.lua)" 0

为了记录,jq的group_by依赖于排序,当输入足够大时,这当然会显着降低速度。 即使输入数组只有100,000个项目,以下速度也提高了约40%:

def compress:
  . as $in
  | reduce keys[] as $key ({};
      $key[0:6] as $k6
      | $key[6:] as $k2
      | .[$k6] += {($k2): $in[$key]} );

.[0] | compress

鉴于杰夫的输入,输出是相同的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM