As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.
Exemplos de semântica de transação do Neptune
Os exemplos a seguir ilustram diferentes casos de uso para a semântica de transação no Amazon Neptune.
Tópicos
Exemplo 1: Inserir uma propriedade somente se ela não existir
Suponha que você queira garantir que uma propriedade seja definida apenas uma vez. Por exemplo, suponha que várias consultas estejam tentando atribuir uma pontuação de crédito a uma pessoa simultaneamente. Você só quer que uma instância da propriedade seja inserida, e as outras consultas falhem porque a propriedade já foi definida.
# 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 .} }
A etapa property()
do Gremlin insere uma propriedade com a chave e o valor fornecidos. A etapa coalesce()
executa o primeiro argumento na primeira etapa e, se falhar, executa a segunda etapa:
Antes de inserir o valor da propriedade creditScore
para um determinado vértice person1
, uma transação deve tentar ler o valor de creditScore
possivelmente inexistente de person1
. Essa tentativa de leitura bloqueia o intervalo SP
para S=person1
e P=creditScore
no índice SPOG
em que o valor de creditScore
existe ou será gravado.
Usar esse bloqueio de intervalo impede que qualquer transação simultânea insira um valor de creditScore
simultaneamente. Quando há várias transações paralelas, no máximo uma delas pode atualizar o valor de cada vez. Isso exclui a anomalia de mais de uma propriedade creditScore
ser criada.
Exemplo 2: Afirmar que um valor de propriedade é globalmente exclusivo
Suponha que você queira inserir uma pessoa com um número de seguro social como uma chave primária. Você quer que sua consulta de mutação garanta que, em nível global, ninguém mais no banco de dados tenha o mesmo número de seguro social:
# 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 } }
Este exemplo é semelhante ao anterior. A principal diferença é que o bloqueio do intervalo é feito no índice POGS
em vez de no índice SPOG
.
A transação que executa a consulta deve ler o padrão, ?person :ssn 123456789
, no qual as posições P
e O
estão vinculadas. O bloqueio do intervalo é feito no índice POGS
para P=ssn
e O=123456789
.
Se o padrão existir, nenhuma ação será executada.
Se não existir, o bloqueio impedirá que qualquer transação simultânea insira esse número de seguro social também
Exemplo 3: Alterar uma propriedade se outra propriedade tiver um valor especificado
Suponha que vários eventos em um jogo movam uma pessoa do nível um para o nível dois e atribuam a ela uma nova propriedade level2Score
definida como zero. Você precisa ter certeza de que várias instâncias simultâneas dessa transação não possam criar várias instâncias da propriedade de pontuação de nível dois. As consultas em Gremlin e SPARQL podem ter a seguinte aparência.
# 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 .}
No Gremlin, quando Cardinality.single
é especificado, a etapa property()
adiciona uma nova propriedade ou substitui um valor de propriedade existente pelo novo valor especificado.
Qualquer atualização em um valor de propriedade, como aumentar o level
de 1 para 2, é implementada como uma exclusão do registro atual e a inserção de um novo registro com o novo valor de propriedade. Nesse caso, o registro com o nível de número 1 é excluído e um registro com o nível de número 2 é reinserido.
Para que a transação possa adicionar level2Score
e atualizar o level
de 1 para 2, ela deve primeiro validar se o valor de level
atualmente é igual a 1. Ao fazer isso, ele usa um bloqueio de intervalo no prefixo SPO
para S=person1
, P=level
e O=1
no índice SPOG
. Esse bloqueio impede que transações simultâneas excluam o triplo da versão 1 e, como resultado, nenhuma atualização simultânea conflitante pode ocorrer.
Exemplo 4: Substituir uma propriedade existente
Alguns eventos podem atualizar a pontuação de crédito de uma pessoa para um novo valor (aqui BBB
). Mas você quer ter certeza de que eventos simultâneos desse tipo não possam criar várias propriedades de pontuação de crédito para uma pessoa.
# 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 .}
Esse caso é semelhante ao exemplo 3, exceto que, em vez de bloquear o prefixo SPO
, o Neptune bloqueia o prefixo SP
apenas com S=person1
e P=creditScore
. Isso evita que transações simultâneas insiram ou excluam triplos com a propriedade creditScore
para o assunto person1
.
Exemplo 5: Evitar propriedades ou bordas pendentes
A atualização em uma entidade não deve deixar um elemento pendente, ou seja, uma propriedade ou borda associada a uma entidade que não é tipada. Isso é apenas um problema emSPARQL, porque o Gremlin tem restrições embutidas para evitar elementos pendentes.
# SPARQL: tx1: INSERT { :person1 :age 23 } WHERE { :person1 rdf:type :Person } tx2: DELETE { :person1 ?p ?o }
A consulta INSERT
deve ler e bloquear o prefixo SPO
com S=person1
, P=rdf:type
e O=Person
no índice SPOG
. O bloqueio impede que a consulta DELETE
seja bem-sucedida em paralelo.
Na corrida entre a tentativa da consulta DELETE
excluir o registro :person1 rdf:type :Person
e a consulta INSERT
ler o registro e criar um bloqueio de intervalo em seu SPO
no índice SPOG
, os seguintes resultados são possíveis:
Se a consulta
INSERT
for confirmada antes da consultaDELETE
ler e excluir todos os registros de:person1
,:person1
será removido completamente do banco de dados, incluindo o registro recém-inserido.Se a consulta
DELETE
for confirmada antes da consultaINSERT
tentar ler o registro:person1 rdf:type :Person
, a leitura observará a alteração confirmada. Ou seja, ela não encontra nenhum registro:person1 rdf:type :Person
e, portanto, torna-se um no-op.Se a
INSERT
consulta for lida antes daDELETE
consulta, a:person1 rdf:type :Person
tripla será bloqueada e aDELETE
consulta será bloqueada até que a INSERT consulta seja confirmada, como no primeiro caso anterior.Se a
DELETE
ler antes da consultaINSERT
, e a consultaINSERT
tentar ler e bloquear o prefixoSPO
do registro, será detectado um conflito. Isso ocorre porque o triplo foi marcado para remoção, e a consultaINSERT
falha.
Em todas essas diferentes sequências possíveis de eventos, nenhuma borda pendente é criada.