

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# Neptune トランザクションセマンティクスの例
<a name="transactions-examples"></a>

次の例は、Amazon Neptune でのトランザクションセマンティクスのさまざまなユースケースを示しています。

**Topics**
+ [プロパティの条件付き挿入](#transactions-examples-conditional-insertion)
+ [プロパティ値の一意性](#transactions-examples-unique-property)
+ [条件付きのプロパティ変更](#transactions-examples-conditional-edit)
+ [プロパティの置き換え](#transactions-examples-replace)
+ [ダングリング要素の回避](#transactions-examples-dangling)

## 例 1 – 存在しない場合にのみプロパティを挿入する
<a name="transactions-examples-conditional-insertion"></a>

プロパティが 1 回のみ設定されることを保証したいとします。たとえば、複数のクエリが 1 人の人に同時にクレジットスコアを割り当てようとしているとします。プロパティのインスタンスを 1 つだけ挿入し、他のクエリはプロパティが既に設定されているため失敗するようにしたいとします。

```
# GREMLIN:
g.V('person1').hasLabel('Person').coalesce(has('creditScore'), property('creditScore', 'AAA+'))

# SPARQL:
INSERT { :person1 :creditScore "AAA+" .}
WHERE  { :person1 rdf:type :Person .
         FILTER NOT EXISTS { :person1 :creditScore ?o .} }
```

Gremlin `property()` ステップは、指定されたキーと値を持つプロパティを挿入します。`coalesce()` ステップは最初のステップで最初の引数を実行し、失敗した場合、2 番目のステップを実行します。

特定の `person1` 頂点の `creditScore` プロパティの値を挿入する前に、トランザクションは `person1` の存在しない可能性のある `creditScore` 値を読み取ろうとします。この試行された読み取りは、`creditScore` 値が存在するか書き込まれる `SPOG` インデックスの `S=person1` および `P=creditScore` の `SP` 範囲がロックされます。

この範囲ロックを使用すると、同時トランザクションが同時に `creditScore` 値を挿入できなくなります。複数の並列トランザクションがある場合、それらのうちの 1 つが一度に値を更新できます。これにより、複数の `creditScore` プロパティが作成される異常が除外されます。

## 例 2 – プロパティ値がグローバルに一意であるというアサート
<a name="transactions-examples-unique-property"></a>

社会保障番号をプライマリキーとして、人を挿入するとします。ミューテーションクエリでは、グローバルレベルで、データベース内の他の誰も同じ社会保障番号を持っていないことを保証する必要があります。

```
# GREMLIN:
g.V().has('ssn', 123456789).fold()
  .coalesce(__.unfold(),
            __.addV('Person').property('name', 'John Doe').property('ssn', 123456789'))

# SPARQL:
INSERT { :person1 rdf:type :Person .
         :person1 :name "John Doe" .
         :person1 :ssn 123456789 .}
WHERE  { FILTER NOT EXISTS { ?person :ssn 123456789 } }
```

この例は、前の例に似ています。主な違いは、範囲ロックが `SPOG` インデックスではなく `POGS` インデックスに取られることです。

クエリを実行するトランザクションは、`P` および `O` の位置がバインドされているパターン `?person :ssn 123456789` を読み取る必要があります。範囲ロックは、`POGS` インデックスの `P=ssn` と `O=123456789` で取得されます。
+ パターンが存在する場合、アクションは実行されません。
+ 存在しない場合、ロックは同時トランザクションがその社会保障番号を挿入するのを防ぎます。

## 例 3 – 別のプロパティに指定した値がある場合のプロパティの変更
<a name="transactions-examples-conditional-edit"></a>

ゲームのさまざまなイベントが、レベル 1 からレベル 2 に人を移動し、それらにゼロに設定された新しい `level2Score` プロパティを割り当てるとします。このようなトランザクションの複数の同時インスタンスが、レベル 2 スコアプロパティの複数のインスタンスを作成できないことを確認する必要があります。Gremlin と SPARQL のクエリは次のようになります。

```
# GREMLIN:
g.V('person1').hasLabel('Person').has('level', 1)
 .property('level2Score', 0)
 .property(Cardinality.single, 'level', 2)

# SPARQL:
DELETE { :person1 :level 1 .}
INSERT { :person1 :level2Score 0 .
         :person1 :level 2 .}
WHERE  { :person1 rdf:type :Person .
         :person1 :level 1 .}
```

Gremlin で、`Cardinality.single` が指定されている場合、`property()` ステップは新しいプロパティを追加するか、既存のプロパティの値を指定された新しい値に置き換えます。

`level` を 1 から 2 に増やすなど、プロパティ値に対する更新は、現在のレコードの削除と新しいプロパティ値を持つ新しいレコードの挿入として実装されます。この場合、レベル番号 1 のレコードは削除され、レベル番号 2 のレコードが再挿入されます。

トランザクションが `level2Score` を追加し、`level` を 1 から 2 に更新できるようにするには、まず `level` 値が現在 1 に等しいことを検証する必要があります。その際、`SPOG` インデックスの `S=person1`、`P=level`、および `O=1` の `SPO` プレフィックスで範囲ロックを取得します。このロックにより、同時トランザクションがバージョン 1 のトリプルを削除できなくなり、その結果、競合する同時更新は発生しません。

## 例 4 – 既存のプロパティの置き換え
<a name="transactions-examples-replace"></a>

特定のイベントでは、個人のクレジットスコアが新しい値 (ここでは `BBB`) に更新される場合があります。ただし、そのタイプの同時イベントが 1 人の人に対して複数のクレジットスコアプロパティを作成できないようにする必要があります。

```
# GREMLIN:
g.V('person1').hasLabel('Person')
 .sideEffect(properties('creditScore').drop())
 .property('creditScore', 'BBB')

# SPARQL:
DELETE { :person1 :creditScore ?o .}
INSERT { :person1 :creditScore "BBB" .}
WHERE  { :person1 rdf:type :Person .
         :person1 :creditScore ?o .}
```

このケースは例 3 に似ていますが、`SPO` プレフィックスをロックするのではなく、Neptune は `S=person1` と `P=creditScore` のみで `SP` プレフィックスをロックします。これにより、同時トランザクションが、`person1` サブジェクトの `creditScore` プロパティを持つトリプルを挿入または削除することを防ぎます。

## 例 5 – ダングリングプロパティまたはエッジを回避する
<a name="transactions-examples-dangling"></a>

エンティティの更新は、ダングリング要素、つまり、型付けされていないエンティティに関連付けられたプロパティまたはエッジを残すことはできません。Gremlin には、ダングリング要素を防ぐための制約が組み込まれているため、これは SPARQL でのみ問題になります。

```
# SPARQL:
tx1: INSERT { :person1 :age 23 } WHERE { :person1 rdf:type :Person }
tx2: DELETE { :person1 ?p ?o }
```

`INSERT` クエリでは、`SPOG` インデックスの `S=person1` および `P=rdf:type` の `SPO` プレフィックス、および `O=Person` を読み取り、ロックする必要があります。ロックにより、 `DELETE` クエリが並列で成功するのを防ぎます。

`:person1 rdf:type :Person` レコードを削除しようとする `DELETE` クエリと、レコードを読み取り `SPOG` インデックスでその `SPO` で範囲ロックを作成する `INSERT` クエリとの競合では、次のような結果が生じる可能性があります。
+ `DELETE` クエリが `:person1` のすべてのレコードを読み取り、削除する前に `INSERT` クエリがコミットされた場合、`:person1` は、新しく挿入されたレコードを含め、データベースから完全に削除されます。
+ `INSERT` クエリが `:person1 rdf:type :Person` レコードを読み取る前に `DELETE` クエリがコミットされた場合、読み取りはコミットされた変更を確認します。つまり、`:person1 rdf:type :Person` レコードが見つからないため、no-op になります。
+ `DELETE` クエリが実行される前に `INSERT` クエリが読み取ると、最初の例のように、`:person1 rdf:type :Person` トリプルがロックされ、INSERT クエリがコミットされるまで `DELETE` クエリがブロックされます。
+ `DELETE` がクエリ `INSERT` の前に読み取り、`INSERT` クエリがレコードの `SPO` プレフィックスを読み取ってロックしようとすると、競合が検出されます。これは、トリプルが削除対象としてマークされ、`INSERT` が失敗するためです。

これらのさまざまなイベントシーケンスでは、ダングリングエッジは作成されません。