使用JQ-“笛卡尔积”(来自1:N

时间:2018-07-19 16:06:56

标签: json performance jq wal json-query

我有一个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倍。

如何仅通过一次即可完成上述转换?jq不是必须的,也可以使用其他工具,但应该在CentOS软件包中。我想避免为此编写一个额外的工具。

man jq看来,它可以一次处理整个文件(每行JSON条目)。我可以在XSLT中做到这一点,但无法将自己的头缠在jq上。特别是change数组的迭代,并将columnnamescolumnvalues组合到一个映射中。

  • 对于迭代,我认为可以使用mapmap_values
  • 对于要映射的2个数组,我看到了from_entrieswith_entries函数,但是无法正常工作。

jq名高手在劝告吗?

1 个答案:

答案 0 :(得分:0)

以下帮助器函数使用// @flow import { hello, bye } from 'internally-shared-libs'; import withValidation from 'internally-shared-libs/decorators/withValidation'; 作为键将传入的数组转换为对象:

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

道德

如果解决方案看起来过于复杂,那可能就是这样。