以下是我的用例:我有一个带有散列+范围键的Dynamo表。当我在表中放入新项目时,我想进行唯一性检查。有时我想保证散列是唯一的(忽略范围)。其他时候我想允许重复哈希,但保证哈希和范围组合是唯一的。我怎么能做到这一点?
我尝试了attribute_not_exists。它似乎处理第二种情况,它检查哈希+键组合。这是一个PHP示例:
$client->putItem(array(
'TableName' => 'test',
'Item' => array(
'hash' => array('S' => 'abcdefg'),
'range' => array('S' => 'some other value'),
'whatever' => array('N' => 233)
),
'ConditionExpression' => 'attribute_not_exists(hash)'
));
奇怪的是,如果我使用attribute_not_exists(hash)
或attribute_not_exists(range)
,这似乎并不重要。他们似乎都做了完全相同的事情。这是它应该如何运作的吗?
知道如何处理我只想检查hash
唯一性的情况吗?
答案 0 :(得分:39)
你做不到。 DynamoDB中的所有项目均由hash
或hash
+ range
编制索引(取决于您的表格)。
到目前为止发生了什么的总结:
hash
和range
键PutItem
个请求,必须同时提供hash
和range
ConditionExpression
或attribute_not_exists
属性名称上提供hash
range
attribute_not_exists
条件仅仅是检查具有该名称的属性是否存在,它不关心值让我们来看一个例子。让我们从带有此数据的hash
+ range
密钥表开始:
hash=A,range=1
hash=A,range=2
有四种可能的情况:
如果您尝试使用hash=A,range=3
和attribute_not_exists(hash)
放置项目,PutItem
将成功,因为attribute_not_exists(hash)
评估为true
。密钥hash=A,range=3
不存在满足attribute_not_exists(hash)
条件的项目。
如果您尝试使用hash=A,range=3
和attribute_not_exists(range)
放置项目,PutItem
将成功,因为attribute_not_exists(range)
评估为true
。密钥hash=A,range=3
不存在满足attribute_not_exists(range)
条件的项目。
如果您尝试将项目设为hash=A,range=1
和attribute_not_exists(hash)
,则PutItem
将失败,因为attribute_not_exists(hash)
评估为false
。存在一个键hash=A,range=1
但不满足attribute_not_exists(hash)
条件的项目。
如果您尝试将项目设为hash=A,range=1
和attribute_not_exists(range)
,则PutItem
将失败,因为attribute_not_exists(range)
评估为false
。存在一个键hash=A,range=1
但不满足attribute_not_exists(range)
条件的项目。
这意味着将发生以下两件事之一:
hash
+ range
对。
attribute_not_exists(hash)
必须为true
attribute_not_exists(range)
必须为true
hash
+ range
对。
attribute_not_exists(hash)
必须为false
attribute_not_exists(range)
必须为false
在这两种情况下,无论是将其放在哈希值还是范围键上,都会得到相同的结果。 hash
+ range
键标识整个表格中的单个项目,并且正在对该项目评估您的条件。
如果具有此hash
+ range
键的项目尚不存在,您实际上正在执行“此项目。
答案 1 :(得分:6)
对于Google员工:
attribute_not_exists
检查是否存在与要插入的项目具有相同主键的项目 attribute_not_exists
与主键(或分区键或范围键)一起使用,因为该键必须存在,所以检查(b)将始终通过,仅检查(a)将生效理由:
attribute_not_exists
表示它会检查项目中是否存在属性 注意:为防止新项目替换现有项目,请使用一个条件表达式,该条件表达式包含attribute_not_exists函数,该属性的名称用作表的分区键。由于每个记录都必须包含该属性,因此只有不存在匹配项时,attribute_not_exists函数才会成功。
答案 2 :(得分:1)
小心reserved keywords
如果提供的attributeName与保留列表中的单词匹配,则attribute_not_exists
将无法按预期工作。 hash
和range
都是保留的,因此需要work around that issue使用ExpressionAttributeNames
。
以下示例允许使用重复的分区键,只有在表中已经包含提供的分区AND排序键的项目时才会失败。
$client->putItem(array(
'TableName' => 'test',
'Item' => array(
'hash' => array('S' => 'abcdefg'),
'range' => array('S' => 'some other value'),
'whatever' => array('N' => 233)
),
'ConditionExpression' => 'attribute_not_exists(#h) AND attribute_not_exists(#r)',
'ExpressionAttributeNames' => array('#h' => 'hash', '#r' => 'range')
));
这个会确保名为hash
的分区键是唯一的。
$client->putItem(
'TableName' => 'test',
'Item' => array(
'hash' => array('S' => 'abcdefg'),
'range' => array('S' => 'some other value'),
'whatever' => array('N' => 233)
),
'ConditionExpression' => 'attribute_not_exists(#h)',
'ExpressionAttributeNames' => array('#h' => 'hash')
));
答案 3 :(得分:0)
如果您的表具有哈希值和范围
,则可以使用AND运算' ConditionExpression' => ' attribute_not_exists(hash)AND attribute_not_exists(range)'
答案 4 :(得分:0)
从amazon aws forum获取的此版本的解释说,搜索将查找与提供的哈希键匹配的项目,然后仅检查该记录中是否存在该属性。如果你有一个哈希和一个范围键,它应该是一样的,我想。
如果请求尝试使用哈希键查找现有项目 " b825501b-60d3-4e53-b737-02645d27c2ae&#34 ;.如果这是第一次这个id 正在使用,将没有现有的项目和 " attribute_not_exists(电子邮件)"将评估为真,Put请求将 经过。
如果已使用此ID,则会有现有项目。然后 条件表达式将在中查找现有的电子邮件属性 现有项目,如果有Put请求的电子邮件属性 失败,如果没有电子邮件属性,Put请求将通过。
无论哪种方式,都没有比较"电子邮件"属性和它 不检查表中的其他项目是否使用相同的"电子邮件"值。
如果电子邮件是哈希密钥,则请求将尝试查找现有密钥 带有散列键的项目" tielur@example.me"。
如果存在具有相同电子邮件值的其他项目,则现有项目将成为 被发现。由于电子邮件是哈希密钥,因此它必须存在于 现有项目和" attribute_not_exists(电子邮件)"将评估为假 和Put请求将失败。
如果"电子邮件"在找不到现有项目之前不使用值 和" attribute_not_exists(email)"将评估为真,因此Put 请求将通过。
答案 5 :(得分:0)
根据Jun711的回答,这是我在Kotlin中实现putItem only if hash doesn't exist
所做的工作。 DocId
是我的DynamoDB表的哈希键。
val item = Item().withPrimaryKey(...).withString(...)
val putItemSpec = PutItemSpec().withItem(item)
.withConditionExpression("attribute_not_exists(DocId)")
table.putItem(putItemSpec)
如果使用DynamoDBMapper注释,这是一个示例。
@DynamoDBTable(tableName = "Docs")
class Docs {
// Partition key
@get:DynamoDBHashKey(attributeName = "DocId")
var docId: String? = null
}
val doc = Docs().apply {
docId = "myId"
}
val mapper = DynamoDBMapper(AmazonDynamoDBClientBuilder.defaultClient())
// As suggested by http://rrevo.github.io/2018/03/09/dynamo-no-update/
val ifDocIdNotExists = DynamoDBSaveExpression().apply {
expected = mapOf("DocId" to ExpectedAttributeValue().apply {
isExists = false
})
}
mapper.save(doc, ifDocIdNotExists)
捕获com.amazonaws.services.dynamodbv2.model.ConditionalCheckFailedException
来处理哈希键已经存在的情况。