【问题标题】:Optimize JSON denormalization using JQ - "cartesian product" from 1:N使用 JQ 优化 JSON 非规范化 - 1:N 的“笛卡尔积”
【发布时间】:2018-07-19 16:06:55
【问题描述】:

我有一个 JSON 数据库更改日志,输出为 wal2json。它看起来像这样:

{"xid":1190,"timestamp":"2018-07-19 17:18:02.905354+02","change":[
    {"kind":"update","table":"mytable2","columnnames":["id","name","age"],"columnvalues":[401,"Update AA",20],"oldkeys":{"keynames":["id"],"keyvalues":[401]}},
    {"kind":"update","table":"mytable2","columnnames":["id","name","age"],"columnvalues":[401,"Update BB",20],"oldkeys":{"keynames":["id"],"keyvalues":[401]}}]}
 ...

每个顶级条目 (xid) 都是一个事务,change 中的每个项目都是一个变化。一行可以更改多次。

要导入功能集有限的 OLAP 系统,我需要明确说明顺序。所以我需要为事务中的每个更改添加一个sn。
此外,每个更改都必须是顶级条目 - OLAP 不能在一个条目中迭代子项。

{"xid":1190, "sn":1, "kind":"update", "data":{"id":401,"name":"Update AA","age":20} }
{"xid":1190, "sn":2, "kind":"update", "data":{"id":401,"name":"Update BB","age":20} }
{"xid":1191, "sn":1, "kind":"insert", "data":{"id":625,"name":"Inserted","age":20} }
{"xid":1191, "sn":2, "kind":"delete", "data":{"id":625} }

(原因是OLAP在导入过程中转换数据的能力有限,也没有顺序作为参数。)

所以,我使用jq:

function transformJsonDataStructure {
    ## First let's reformat it to XML, then transform using XPATH, then back to JSON.

    ## Example input:
    # {"xid":1074,"timestamp":"2018-07-18 17:49:54.719475+02","change":[
    #   {"kind":"update","table":"mytable2","columnnames":["id","name","age"],"columnvalues":[401,"Update AA",20],"oldkeys":{"keynames":["id"],"keyvalues":[401]}},
    #   {"kind":"update","table":"mytable2","columnnames":["id","name","age"],"columnvalues":[401,"Update BB",20],"oldkeys":{"keynames":["id"],"keyvalues":[401]}}]}
    cat "$1" | while read -r LINE ; do
        XID=`echo "$LINE" | jq -c '.xid'`;
        export SN=0;
        #serr "{xid: $XID, changes: $CHANGES}";
        echo "$LINE" | jq -c '.change[]' | while read -r CHANGE ; do
            SN=$((SN+=1))
            KIND=`echo "$CHANGE" | jq -c --raw-output .kind`;
            TABLE=`echo "$CHANGE" | jq -c --raw-output .table`;
            DEST_FILE="$TARGET_PATH-$TABLE.json";
            case "$KIND" in
                update|insert)
                    MAP=$(convertTwoArraysToMap "$(echo "$CHANGE" | jq -c ".columnnames")" "$(echo "$CHANGE" | jq -c ".columnvalues")") ;;
                delete)
                    MAP=$(convertTwoArraysToMap "$(echo "$CHANGE" | jq -c ".oldkeys.keynames")" "$(echo "$CHANGE" | jq -c ".oldkeys.keyvalues")") ;;
            esac
            #echo "{\"xid\":$XID, \"table\":\"$TABLE\", \"kind\":\"$KIND\", \"data\":$MAP }" >> "$DEST_FILE"; ;;
            echo "{\"xid\":$XID, \"sn\":$SN, \"kind\":\"$KIND\", \"data\":$MAP }" | tee --append "$DEST_FILE";
        done;
    done;

    return;
}

问题在于性能。我每次都给jq打了几次电话。这很慢,比不进行转换时慢了大约 1000 倍。

如何只使用一次pass进行上述转换?(jq不是必须的,也可以使用其他工具,但应该在CentOS包中。我想避免编码额外的工具。

从man jq 看来,它似乎能够一次性处理整个文件(每行的 JSON 条目)。我可以在 XSLT 中做到这一点,但我无法绕过 jq。尤其是 change 数组的迭代并将 columnnames 和 columnvalues 组合到一个地图。

  • 对于迭代,我认为可以使用map 或map_values。
  • 对于要映射的 2 个数组,我看到了 from_entries 和 with_entries 函数,但无法正常工作。

有没有jq高手给点意见?

【问题讨论】:

    标签: json performance jq wal json-query


    【解决方案1】:

    以下帮助函数将传入的数组转换为使用headers 作为键的对象:

    def objectify(headers):
      [headers, .] | transpose | map({(.[0]): .[1]}) | add;
    

    现在的诀窍是使用range(0;length) 生成.sn:

      {xid} +
      (.change
       | range(0;length) as $i
       | .[$i]
       | .columnnames as $header
       | {sn: ($i + 1),
          kind,
          data: (.columnvalues|objectify($header)) } )
    

    输出

    对于给定的日志条目,输出将是:

    {"xid":1190,"sn":1,"kind":"update","data":{"id":401,"name":"Update AA","age":20}}
    {"xid":1190,"sn":2,"kind":"update","data":{"id":401,"name":"Update BB","age":20}}
    

    道德

    如果一个解决方案看起来太复杂,它可能就是。

    【讨论】:

    • 看起来很有希望,谢谢!我会在早上尝试。
    猜你喜欢
    • 2012-03-15
    • 1970-01-01
    • 2020-05-08
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 2011-03-24
    • 2011-01-31
    相关资源
    最近更新 更多