How to simplify aws DynamoDB query JSON output from the command line?

前端 未结 5 544
暖寄归人
暖寄归人 2020-12-25 15:14

I\'m working with The AWS Command Line Interface for DynamoDB.

When we query an item, we get a very detailed JSON output. You get something like this (it has been bu

相关标签:
5条回答
  • 2020-12-25 15:16

    As far as I know, there is no other output like the "verbose" one you've posted. Therefore I think, you can't avoid intermediate tools like jq oder sed

    There are several proposals in this post for converting the raw dynamo data:

    Export data from DynamoDB

    Maybe you can adapt one of these scripts in conjunction with jq or sed

    0 讨论(0)
  • 2020-12-25 15:19

    You can decode the values recursively with a well crafted function. It looks like the key names correspond to a type:

    S -> string
    N -> number
    M -> map
    

    Handle each of the cases you want to decode if possible, otherwise filter it out. You can make use of the various type filters and the alternative operator to do so.

    $ cat input.json
    {
      "Count": 1,
      "Items": [
        {
          "Id": { "S": "app1" },
          "Parameters": {
            "M": {
              "nfs#IP": { "S": "192.17.0.13" },
              "maxCount": { "N": "1" },
              "nfs#defaultPath": { "S": "/mnt/ebs/" }
            }
          }
        }
      ],
      "ScannedCount": 1,
      "ConsumedCapacity": null
    }
    
    $ cat ~/.jq
    def decode_ddb:
        def _sprop($key): select(keys == [$key])[$key];                 # single property objects only
           ((objects | { value: _sprop("S") })                          # string (from string)
        // (objects | { value: _sprop("B") })                           # blob (from string)
        // (objects | { value: _sprop("N") | tonumber })                # number (from string)
        // (objects | { value: _sprop("BOOL") })                        # boolean (from boolean)
        // (objects | { value: _sprop("M") | map_values(decode_ddb) })  # map (from object)
        // (objects | { value: _sprop("L") | map(decode_ddb) })         # list (from encoded array)
        // (objects | { value: _sprop("SS") })                          # string set (from string array)
        // (objects | { value: _sprop("NS") | map(tonumber) })          # number set (from string array)
        // (objects | { value: _sprop("BS") })                          # blob set (from string array)
        // (objects | { value: map_values(decode_ddb) })                # all other non-conforming objects
        // (arrays | { value: map(decode_ddb) })                        # all other non-conforming arrays
        // { value: . }).value                                          # everything else
        ;
    
    $ jq 'decode_ddb' input.json
    {
      "Count": 1,
      "Items": [
        {
          "Id": "app1",
          "Parameters": {
            "nfs#IP": "192.17.0.13",
            "maxCount": 1,
            "nfs#defaultPath": "/mnt/ebs/"
          }
        }
      ],
      "ScannedCount": 1,
      "ConsumedCapacity": null
    }
    
    0 讨论(0)
  • 2020-12-25 15:30

    Another way to achieve the post's goal would be to use a node.js extension like node-dynamodb or dynamodb-marshaler and build a node command line tool.

    Interesting tutorial to build a node.js command line application with commander package: Creating Your First Node.js Command-line Application


    Here's a quick and dirty oneliner that reads one record from stdin and prints it in simplified form:

    node -e 'console.log(JSON.stringify(require("aws-sdk").DynamoDB.Converter.unmarshall(JSON.parse(require("fs").readFileSync(0, "utf-8")))))'
    
    0 讨论(0)
  • 2020-12-25 15:32

    Here is a script in node to do this.

    I named the file reformat.js but you can call it whatever you want

    'use strict';
    
    /**
     * This script will parse the AWS dynamo CLI JSON response into JS.
     * This parses out the type keys in the objects.
     */
    
    const fs = require('fs');
    
    const rawData = fs.readFileSync('response.json'); // Import the raw response from the dynamoDB CLI query
    const response = JSON.parse(rawData); // Parse to JS to make it easier to work with.
    
    function shallowFormatData(data){
      // Loop through the object and replace the Type key with the value.
      for(const key in data){
        const innerRawObject = data[key]
        const innerKeys = Object.keys(innerRawObject)
        innerKeys.forEach(innerKey => {
          const innerFormattedObject = innerRawObject[innerKey]
          if(typeof innerFormattedObject == 'object'){
            data[key] = shallowFormatData(innerFormattedObject) // Recursively call formatData if there are nested objects
          }else{
            // Null items come back with a type of "NULL" and value of true. we want to set the value to null if the type is "NULL"
            data[key] = innerKey == 'NULL' ? null : innerFormattedObject
          }
        })
      }
      return data
    }
    
    // this only gets the Items and not the meta data.
    const result = response.Items.map(item => {
      return shallowFormatData(item)
    })
    
    console.dir(result, {'maxArrayLength': null}); // There is a default limit on how big a console.log can be, this removes that limit.
    

    Step 1) run your dynamoDB query via the CLI and save it to a JSON file. To save the response from the CLI just add > somefile.json. For convenience, I saved this in the same directory as my reformat file

    // Example: Run in CLI
    
    $ aws dynamodb query --table-name stage_requests-service_FoxEvents \
     --key-condition-expression "PK = :v1" \
     --expression-attribute-values file://expression-attributes.json > response.json
    

    expression-attributes.json

    {
      ":v1": {"S": "SOMEVAL"}
    }
    
    

    If you need more information on how I queried DynamoDB look at these examples in the documentation https://docs.aws.amazon.com/cli/latest/reference/dynamodb/query.html#examples

    Now that you have a JSON file of the data you need to reformat run the format.js script from your terminal

    Step 2)

    // Run this in your terminal
    $ node reformat.js > formatted.js 
    

    You should have a clean JS Object output if you want a JSON object output just put a JSON.stringify(result) in the console.dir at the end of the script

    0 讨论(0)
  • 2020-12-25 15:35

    Here is another approach. This may be a little brutal but it shows the basic idea.

    def unwanted:    ["B","BOOL","M","S","L","BS","SS"];
    def fixpath(p):  [ p[] | select( unwanted[[.]]==[] ) ];
    def fixnum(p;v):
        if   p[-2]=="NS" then [p[:-2]+p[-1:],(v|tonumber)]
        elif p[-1]=="N" then [p[:-1], (v|tonumber)]
        else [p,v] end;
    
    reduce (tostream|select(length==2)) as [$p,$v] (
        {}
      ; fixnum(fixpath($p);$v) as [$fp,$fv]      
      | setpath($fp;$fv)
    )
    

    Try it online!

    Sample Run (assuming filter in filter.jq and data in data.json)

    $ jq -M -f filter.jq data.json
    {
      "ConsumedCapacity": null,
      "Count": 1,
      "Items": [
        {
          "Id": "app1",
          "Oldtypes": {
            "typeBS": [
              "VGVybWluYXRvcgo=",
              "VGVybWluYXRvciAyOiBKdWRnbWVudCBEYXkK",
              "VGVybWluYXRvciAzOiBSaXNlIG9mIHRoZSBNYWNoaW5lcwo=",
              "VGVybWluYXRvciA0OiBTYWx2YXRpb24K",
              "VGVybWluYXRvciA1OiBHZW5lc2lzCg=="
            ],
            "typeNS": [
              0,
              1,
              2,
              3,
              4,
              5
            ],
            "typeSS": [
              "foo",
              "bar",
              "baz"
            ]
          },
          "Parameters": {
            "nfs": {
              "IP": "172.16.0.178",
              "activated": true,
              "defaultPath": "/mnt/ebs/",
              "key": "dGhpcyB0ZXh0IGlzIGJhc2U2NC1lbmNvZGVk"
            },
            "ws": {
              "number": 5,
              "values": [
                "12253456346346",
                "23452353463464",
                "23523453461232",
                "34645745675675",
                "46456745757575"
              ]
            }
          }
        }
      ],
      "ScannedCount": 1
    }
    
    0 讨论(0)
提交回复
热议问题