DynamoDB - 如果散列(或散列和范围组合)不存在,则放置项目

时间:2015-09-28 23:29:52

标签: amazon-dynamodb

以下是我的用例:我有一个带有散列+范围键的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唯一性的情况吗?

6 个答案:

答案 0 :(得分:39)

你做不到。 DynamoDB中的所有项目均由hashhash + range编制索引(取决于您的表格)。

到目前为止发生了什么的总结:

  • 单个哈希键可以有多个范围键。
  • 每个项目都有hashrange
  • 您正在发出PutItem个请求,必须同时提供hashrange
  • 您在ConditionExpressionattribute_not_exists属性名称上提供hash range
  • attribute_not_exists条件仅仅是检查具有该名称的属性是否存在,它不关心值

让我们来看一个例子。让我们从带有此数据的hash + range密钥表开始:

  1. hash=A,range=1
  2. hash=A,range=2
  3. 有四种可能的情况:

    1. 如果您尝试使用hash=A,range=3attribute_not_exists(hash)放置项目,PutItem将成功,因为attribute_not_exists(hash)评估为true。密钥hash=A,range=3不存在满足attribute_not_exists(hash)条件的项目。

    2. 如果您尝试使用hash=A,range=3attribute_not_exists(range)放置项目,PutItem将成功,因为attribute_not_exists(range)评估为true。密钥hash=A,range=3不存在满足attribute_not_exists(range)条件的项目。

    3. 如果您尝试将项目设为hash=A,range=1attribute_not_exists(hash),则PutItem将失败,因为attribute_not_exists(hash)评估为false。存在一个键hash=A,range=1但不满足attribute_not_exists(hash)条件的项目。

    4. 如果您尝试将项目设为hash=A,range=1attribute_not_exists(range),则PutItem将失败,因为attribute_not_exists(range)评估为false。存在一个键hash=A,range=1但不满足attribute_not_exists(range)条件的项目。

    5. 这意味着将发生以下两件事之一:

      1. 数据库中存在hash + range对。
        • attribute_not_exists(hash)必须为true
        • attribute_not_exists(range)必须为true
      2. 数据库中不存在hash + range对。
        • attribute_not_exists(hash)必须为false
        • attribute_not_exists(range)必须为false
      3. 在这两种情况下,无论是将其放在哈希值还是范围键上,都会得到相同的结果。 hash + range键标识整个表格中的单个项目,并且正在对该项目评估您的条件。

        如果具有此hash + range键的项目尚不存在,您实际上正在执行“此项目。

答案 1 :(得分:6)

对于Google员工:

  • (a)attribute_not_exists检查是否存在与要插入的项目具有相同主键的项目
  • (b)另外,它还会检查该项目是否存在,值无所谓
  • 如果只想防止覆盖,则将attribute_not_exists与主键(或分区键或范围键)一起使用,因为该键必须存在,所以检查(b)将始终通过,仅检查(a)将生效

理由:

  • 名称attribute_not_exists表示它会检查项目中是否存在属性
  • 但是表中有多个项目,它要检查哪个项目
  • 答案是它会检查与您放入的主键具有相同主键的项目
  • 所有条件表达式都会发生这种情况
  • 但与往常一样,它并没有得到适当的完整记录
  • 有关此功能,请参见下面的官方文档,并品尝其模糊性

注意:为防止新项目替换现有项目,请使用一个条件表达式,该条件表达式包含attribute_not_exists函数,该属性的名称用作表的分区键。由于每个记录都必须包含该属性,因此只有不存在匹配项时,attribute_not_exists函数才会成功。

Link

答案 2 :(得分:1)

小心reserved keywords 如果提供的attributeName与保留列表中的单词匹配,则attribute_not_exists将无法按预期工作。 hashrange都是保留的,因此需要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来处理哈希键已经存在的情况。