你如何" upsert" DynamoDB行的属性。例如。某个项目的SET address.state = "MA"
,当address
尚不存在时?
我觉得我有鸡蛋问题,因为DynamoDB不能让你提前定义一个草率的架构。
如果该address
类型M
已存在SET #address.#state = :value
(对于地图),则互联网会告诉我可以发出类似以下内容的UpdateExpression:
#address
#state
,:value
和address
分别适当地映射到state
,MA
和address
。
但如果address.state
属性不已存在,则会出错:
''' ValidationException:更新表达式中提供的文档路径对于更新无效 '''
所以..看起来我要么:
SET address = {}; SET address.state = 'MA'
(例如,SET address = {};
在一个命令中)或
public int getID(String resourceName,Context context){
Resources resources = context.getResources();
final int resourceId = resources.getIdentifier(resourceName, "drawable",
context.getPackageName());
return resourceId;
}
失败,然后再试一次。如果是后者......我该如何设置空白地图?!?
呃..我喜欢迪纳摩,但除非我错过了一些明显的东西,否则这有点疯狂..
答案 0 :(得分:4)
如果父文档不存在,则无法设置嵌套属性。由于address
不存在,因此您无法在其中设置属性province
。如果在创建项目时将address
设置为空地图,则可以实现目标。然后,您可以使用以下参数来调整尚未存在的属性address.province
的更新。
var params = {
TableName: 'Image',
Key: {
Id: 'dynamodb.png'
},
UpdateExpression: 'SET address.province = :ma',
ConditionExpression: 'attribute_not_exists(address.province)',
ExpressionAttributeValues: {
':ma': 'MA'
},
ReturnValues: 'ALL_NEW'
};
docClient.update(params, function(err, data) {
if (err) ppJson(err); // an error occurred
else ppJson(data); // successful response
});
顺便说一句,我不得不用省来取代州,因为州是一个保留词。
答案 1 :(得分:2)
你可以通过两次往返来完成,第一次有条件地设置address
的空地图(如果它还不存在),第二次设置state
:
db.update({
UpdateExpression: 'SET #a = {}',
ConditionExpression: 'attribute_not_exists(#a)',
AttributeExpressionNames: {
'#a': 'address'
}
}, ...);
然后:
db.update({
UpdateExpression: 'SET #a.#b = :v',
AttributeExpressionNames: {
'#a': 'address',
'#b': 'state'
},
AttributeExpressionValues: {
':v': 'whatever'
}
}, ...);
答案 2 :(得分:0)
另一种完全不同的方法是在创建父文档时简单地创建address
节点。例如,假设您的散列键为id
,您可以执行以下操作:
db.put({
Item: {
id: 42,
address: {}
}
}, ...);
这样您就可以设置address.state
值,因为address
地图已经存在:
db.update({
UpdateExpression: 'SET #a.#b = :v',
AttributeExpressionNames: {
'#a': 'address',
'#b': 'state'
},
AttributeExpressionValues: {
':v': 'whatever'
}
}, ...);
答案 3 :(得分:0)
一些kotlin代码以递归方式执行此操作,而不管其深度如何。它将父路径的存在设置为条件,如果条件检查失败,则首先递归创建这些路径。它必须位于库的程序包中,以便它可以访问那些程序包的私有字段/类。
package com.amazonaws.services.dynamodbv2.xspec
import com.amazonaws.services.dynamodbv2.document.Table
import com.amazonaws.services.dynamodbv2.model.ConditionalCheckFailedException
import com.amazonaws.services.dynamodbv2.xspec.ExpressionSpecBuilder.attribute_exists
fun Table.updateItemByPaths(hashKeyName: String, hashKeyValue: Any, updateActions: List<UpdateAction>) {
val parentPaths = updateActions.map { it.pathOperand.path.parent() }
.filter { it.isNotEmpty() }
.toSet() // to remove duplicates
try {
val builder = ExpressionSpecBuilder()
updateActions.forEach { builder.addUpdate(it) }
if (parentPaths.isNotEmpty()) {
var condition: Condition = ComparatorCondition("=", LiteralOperand(true), LiteralOperand(true))
parentPaths.forEach { condition = condition.and(attribute_exists<Any>(it)) }
builder.withCondition(condition)
}
this.updateItem(hashKeyName, hashKeyValue, builder.buildForUpdate())
} catch (e: ConditionalCheckFailedException) {
this.updateItemByPaths(hashKeyName, hashKeyValue, parentPaths.map { M(it).set(mapOf<String, Any>()) })
this.updateItemByPaths(hashKeyName, hashKeyValue, updateActions)
}
}
private fun String.parent() = this.substringBeforeLast('.', "")
答案 4 :(得分:0)
这是我在 Typescript 中编写的一个辅助函数,它使用递归方法进行单层嵌套。
我将顶级属性称为列。
//usage
await setKeyInColumn('customerA', 'address', 'state', "MA")
// Updates a map value to hold a new key value pair. It will create a top-level address if it doesn't exist.
static async setKeyInColumn(primaryKeyValue: string, colName: string, key: string, value: any, _doNotCreateColumn?:boolean) {
const obj = {};
obj[key] = value; // creates a nested value like {address:value}
// Some conditions depending on whether the column already exists or not
const ConditionExpression = _doNotCreateColumn ? undefined:`attribute_not_exists(${colName})`
const AttributeValue = _doNotCreateColumn? value : obj;
const UpdateExpression = _doNotCreateColumn? `SET ${colName}.${key} = :keyval `: `SET ${colName} = :keyval ` ;
try{
const updateParams = {
TableName: TABLE_NAME,
Key: {key:primaryKeyValue},
UpdateExpression,
ExpressionAttributeValues: {
":keyval": AttributeValue
},
ConditionExpression,
ReturnValues: "ALL_NEW",
}
const resp = await docClient.update(updateParams).promise()
if (resp && resp[colName]) {
return resp[colName];
}
}catch(ex){
//if the column already exists, then rerun and do not create it
if(ex.code === 'ConditionalCheckFailedException'){
return this.setKeyInColumn(primaryKeyValue,colName,key, value, true)
}
console.log("Failed to Update Column in DynamoDB")
console.log(ex);
return undefined
}
}