2014-07-18 39 views
3

我有一個JSON數據集,其中每個密鑰保證是一個8位數字,並且密鑰是8個字母數字字符的值即使用jq提取JSON數據結構中的通用前綴

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

爲了減少Redis的內存使用情況,我想變換成散列,其中散列前綴鍵是關鍵的前6個字符的散列本(參見this link),然後存儲該回到Redis。

具體來說,我希望我得到的JSON數據結構(我會再編寫一些代碼來解析這個JSON結構,並創建一個Redis的命令文件由HSET的,等等)看起來更像

[{ 
"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" 
    }, 
    ... 
} 

這是正確的,但如何才能避免拋出,以及這個標準錯誤的警告?

回答

2

我不確定這裏有足夠的數據來評估問題的根源。我覺得很難相信你所嘗試的結果。我一直在收到錯誤。

試試這個過濾器來代替:

.[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" 
    } 
} 
+0

這是使用jq-1.4,但是,你的方法肯定更好。我遺漏的一點是,你可以通過在'map'內使用'map'將數組向下推一級,即'map({key:。[0] .key [0:6],value:map .key = .key [6:8])| from_entries})' – EJT

+0

是的,我無法讓你的過濾器工作[這裏](https://jqplay.org/)。他們應該使用1.4。 –

+0

我在第一次提取時出錯,現在我已經修正了,應該是'key [0:6]',而不是'key [0-6]'。它確實出現在jqplay.com上,問題在於嘗試使用'reduce'。 _jqplay_只是打印出我在命令行上得到的相同錯誤,'jq:error:無法遍歷空值'。當我在真實的命令行環境中運行它時,jq將打印該錯誤,但繼續併成功打印出結果。 – EJT

0

我明白,這是不是你是什麼但是,僅供參考,我認爲使用Redis的內置Lua sc做到這一點會快得多ripting。

而且事實證明,這是一個有點更直截了當:

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 
+0

你說得對。在大約5分半鐘時間內,我的2.xGHz筆記本電腦上的_lua_工作速度更快。原始數據庫中有8MM +個密鑰。使用解壓縮到.rdb,然後rdb到json,使用jq處理json,然後使用redis-cli --pipe重新執行共花費大約11分鐘。 – EJT

0

爲了記錄在案,JQ的group_by依靠排序,當輸入足夠大,這當然會明​​顯慢下來。即使當輸入陣列只有10萬個項目時,以下約快40%:

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

.[0] | compress 

鑑於Jeff的輸入,輸出是相同的。