如何从命令行简化 aws DynamoDB 查询 JSON 输出?
Posted
技术标签:
【中文标题】如何从命令行简化 aws DynamoDB 查询 JSON 输出?【英文标题】:How to simplify aws DynamoDB query JSON output from the command line? 【发布时间】:2015-04-20 00:51:50 【问题描述】:我正在与The AWS Command Line Interface for DynamoDB 合作。
当我们查询一个项目时,我们会得到一个非常详细的 JSON 输出。你会得到这样的东西(它是从get-item
构建的,以便几乎详尽无遗(NULL
类型已被省略)aws command line help:
"Count": 1,
"Items": [
"Id":
"S": "app1"
,
"Parameters":
"M":
"nfs":
"M":
"IP" :
"S" : "172.16.0.178"
,
"defaultPath":
"S": "/mnt/ebs/"
,
"key":
"B": "dGhpcyB0ZXh0IGlzIGJhc2U2NC1lbmNvZGVk"
,
"activated":
"BOOL": true
,
"ws" :
"M" :
"number" :
"N" : "5"
,
"values" :
"L" : [
"S" : "12253456346346",
"S" : "23452353463464",
"S" : "23523453461232",
"S" : "34645745675675",
"S" : "46456745757575"
]
,
"Oldtypes":
"typeSS" : "SS" : ["foo", "bar", "baz"],
"typeNS" : "NS" : ["0", "1", "2", "3", "4", "5"],
"typeBS" : "BS" : ["VGVybWluYXRvcgo=", "VGVybWluYXRvciAyOiBKdWRnbWVudCBEYXkK", "VGVybWluYXRvciAzOiBSaXNlIG9mIHRoZSBNYWNoaW5lcwo=", "VGVybWluYXRvciA0OiBTYWx2YXRpb24K","VGVybWluYXRvciA1OiBHZW5lc2lzCg=="]
],
"ScannedCount": 1,
"ConsumedCapacity": null
有什么方法可以为Items
部分获得更简单的输出?像这样:
"ConsumedCapacity": null,
"Count": 1,
"Items": [
"Id": "app1",
"Parameters":
"nfs":
"IP": "172.16.0.178",
"activated": true,
"defaultPath": "/mnt/ebs/",
"key": "dGhpcyB0ZXh0IGlzIGJhc2U2NC1lbmNvZGVk"
,
"ws":
"number": 5,
"values": ["12253456346346","23452353463464","23523453461232","34645745675675","46456745757575"]
,
"Oldtypes":
"typeBS": ["VGVybWluYXRvcgo=", "VGVybWluYXRvciAyOiBKdWRnbWVudCBEYXkK", "VGVybWluYXRvciAzOiBSaXNlIG9mIHRoZSBNYWNoaW5lcwo=", "VGVybWluYXRvciA0OiBTYWx2YXRpb24K", "VGVybWluYXRvciA1OiBHZW5lc2lzCg=="],
"typeNS": [0, 1, 2, 3, 4, 5],
"typeSS": ["foo","bar","baz"]
],
"ScannedCount": 1
dynamodb - AWS CLI 1.7.10 documentation 中没有任何帮助。
我们必须从命令行获取结果。如有必要,我愿意使用其他命令行工具,如jq
,但这样的jq
映射对我来说似乎很复杂。
更新 1:基于jq
的解决方案(借助 DanielH 的回答)
使用jq
很简单,但不是很漂亮,您可以执行以下操作:
$> aws dynamodb query --table-name ConfigCatalog --key-conditions ' "Id" : "AttributeValueList": ["S":"app1"], "ComparisonOperator": "EQ"' | jq -r '.Items[0].Parameters.M."nfs#IP".S'
结果将是:172.16.0.178
jq
-r
选项为您提供原始输出。
更新 2:基于 jq
的解决方案(在 @jeff-mercado 的帮助下)
这是 Jeff Mercado jq
函数的更新和评论版本,用于解组 DynamoDB 输出。它会给你预期的输出:
$> cat unmarshal_dynamodb.jq
def unmarshal_dynamodb:
# DynamoDB string type
(objects | .S)
# DynamoDB blob type
// (objects | .B)
# DynamoDB number type
// (objects | .N | strings | tonumber)
# DynamoDB boolean type
// (objects | .BOOL)
# DynamoDB map type, recursion on each item
// (objects | .M | objects | with_entries(.value |= unmarshal_dynamodb))
# DynamoDB list type, recursion on each item
// (objects | .L | arrays | map(unmarshal_dynamodb))
# DynamoDB typed list type SS, string set
// (objects | .SS | arrays | map(unmarshal_dynamodb))
# DynamoDB typed list type NS, number set
// (objects | .NS | arrays | map(tonumber))
# DynamoDB typed list type BS, blob set
// (objects | .BS | arrays | map(unmarshal_dynamodb))
# managing others DynamoDB output entries: "Count", "Items", "ScannedCount" and "ConsumedCapcity"
// (objects | with_entries(.value |= unmarshal_dynamodb))
// (arrays | map(unmarshal_dynamodb))
# leaves values
// .
;
unmarshal_dynamodb
如果您将DynamoDB
查询输出保存到文件中,比如说ddb-query-result.json
,您可以执行以获得所需的结果:
$> jq -f unmarshal_dynamodb.jq ddb-query-result.json
【问题讨论】:
嗯,对象的键名是否表明了它们的类型?就像“S”代表字符串,“M”代表地图,“N”代表数字?你实际上可以用它做一些非常好的事情。 您的unmarshal_dynamodb.jq
解决方案非常出色,感谢您和@JeffMercado。我一直试图解决的使用//
的一个缺陷是,任何返回false 的过滤器都不会被转换。这对于实际设置为 false 的布尔值很重要 - 它们保留 BOOL
或 B
键。我已经添加了一行来部分解决这个问题,但仍然没有找到一种方法来完全修复它而无需第二遍:// (objects | if has("BOOL") or has("B") then [false] else null end)
这将false
添加为 1 元素数组,需要在 "#管理他人……”一行。
@DaveStern:我修改了这里使用的方法来正确处理虚假值。现在应该有一个整体更清洁的实现。
如果您的架构中有 BOOL,则值得在下面使用@JeffMercado 的答案。
我通常不发表评论,但真的很棒。谢谢!
【参考方案1】:
这是jq
解决方案的更新版本,可以处理空值。
$> cat unmarshal_dynamodb.jq
def unmarshal_dynamodb:
# null
walk( if type == "object" and .NULL then . |= null else . end ) |
# DynamoDB string type
(objects | .S)
# DynamoDB blob type
// (objects | .B)
# DynamoDB number type
// (objects | .N | strings | tonumber)
# DynamoDB boolean type
// (objects | .BOOL)
# DynamoDB map type, recursion on each item
// (objects | .M | objects | with_entries(.value |= unmarshal_dynamodb))
# DynamoDB list type, recursion on each item
// (objects | .L | arrays | map(unmarshal_dynamodb))
# DynamoDB typed list type SS, string set
// (objects | .SS | arrays | map(unmarshal_dynamodb))
# DynamoDB typed list type NS, number set
// (objects | .NS | arrays | map(tonumber))
# DynamoDB typed list type BS, blob set
// (objects | .BS | arrays | map(unmarshal_dynamodb))
# managing others DynamoDB output entries: "Count", "Items", "ScannedCount" and "ConsumedCapcity"
// (objects | with_entries(.value |= unmarshal_dynamodb))
// (arrays | map(unmarshal_dynamodb))
# leaves values
// .
;
unmarshal_dynamodb
$> jq -f unmarshal_dynamodb.jq ddb-query-result.json
感谢 @jeff-mercado 和 @herve 的原始版本。
【讨论】:
【参考方案2】:这是节点中的一个脚本来执行此操作。
我将文件命名为reformat.js
,但你可以随意命名
'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.
第 1 步)通过 CLI 运行 dynamoDB 查询并将其保存到 JSON 文件中。要保存来自 CLI 的响应,只需添加 > somefile.json
。为方便起见,我将其保存在与重新格式化文件相同的目录中
// 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
表达式属性.json
":v1": "S": "SOMEVAL"
如果您需要有关我如何查询 DynamoDB 的更多信息,请查看文档 https://docs.aws.amazon.com/cli/latest/reference/dynamodb/query.html#examples 中的这些示例
现在您有了一个 JSON 数据文件,您需要重新格式化从终端运行 format.js 脚本
步骤 2)
// Run this in your terminal
$ node reformat.js > formatted.js
如果你想要一个 JSON 对象输出,你应该有一个干净的 JS 对象输出,只需在脚本末尾的 console.dir
中添加一个 JSON.stringify(result)
【讨论】:
【参考方案3】:实现帖子目标的另一种方法是使用node.js
扩展名,例如node-dynamodb
或dynamodb-marshaler
,并构建node
命令行工具。
使用commander
package 构建node.js
命令行应用程序的有趣教程:Creating Your First Node.js Command-line Application
这是一个快速而肮脏的 oneliner,它从标准输入读取一条记录并以简化形式打印出来:
node -e 'console.log(JSON.stringify(require("aws-sdk").DynamoDB.Converter.unmarshall(JSON.parse(require("fs").readFileSync(0, "utf-8")))))'
【讨论】:
【参考方案4】:您可以使用精心设计的函数对值进行递归解码。看起来键名对应一个类型:
S -> string
N -> number
M -> map
如果可能,请处理您要解码的每个案例,否则将其过滤掉。您可以使用各种type filters 和alternative operator 来执行此操作。
$ 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
【讨论】:
感谢@jeff-mercado 的帮助。我发布了他的decode_ddb.jq
函数的扩展版本作为原始帖子的更新。【参考方案5】:
这是另一种方法。这可能有点残酷,但它显示了基本思想。
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!
示例运行(假设 filter.jq
中的过滤器和 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
【讨论】:
【参考方案6】:据我所知,没有其他输出像您发布的“详细”输出。因此我认为,你不能避免像jq
oder sed
这样的中间工具
这篇文章中有几个关于转换原始发电机数据的建议:
Export data from DynamoDB
也许您可以将这些脚本之一与jq
或sed
结合使用
【讨论】:
使用jq
很简单,但并不安静漂亮,您可以执行以下操作:aws dynamodb query --table-name ConfigCatalog --key-conditions ' "Id" : "AttributeValueList": ["S":"app1"], "ComparisonOperator": "EQ"' | jq -r '.Items[0].Parameters.M."nfs#IP".S'
结果将是:172.16.0.178
。查看更新后的帖子。以上是关于如何从命令行简化 aws DynamoDB 查询 JSON 输出?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 AWS Lambda 按名称查询 dynamoDB 表
如何从命令行向 AWS AppSync 发送 GraphQL 查询?