

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á.

# Práticas recomendadas para o Neptune ao usar openCypher e Bolt
<a name="best-practices-opencypher"></a>

Siga estas práticas recomendadas ao usar a linguagem de consulta openCypher e o protocolo Bolt com Neptune. Para obter informações sobre como usar o openCypher no Neptune, consulte [Acessar o grafo do Neptune com o openCypher](access-graph-opencypher.md).

**Topics**
+ [Criar uma conexão após o failover](#best-practices-opencypher-renew-connection)
+ [Tratamento de conexões para aplicações de longa duração](#best-practices-opencypher-long-connections)
+ [Manipulação de conexão para AWS Lambda](#best-practices-opencypher-lambda-connections)
+ [Preferir direcionar para bordas bidirecionais nas consultas](best-practices-opencypher-directed-edges.md)
+ [O Neptune não é compatível com várias consultas simultâneas em uma transação.](best-practices-opencypher-multiple-queries.md)
+ [Fechar objetos de driver ao concluir](best-practices-opencypher-close-driver.md)
+ [Usar modos de transação explícitos para leitura e gravação](best-practices-opencypher-use-explicit-txs.md)
+ [Lógica de novas tentativas para exceções](best-practices-opencypher-retry-logic.md)
+ [Definir várias propriedades de uma vez usando uma única cláusula SET](best-practices-content-0.md)
+ [Usar consultas parametrizadas](best-practices-content-2.md)
+ [Use mapas nivelados em vez de mapas aninhados na cláusula UNWIND](best-practices-content-3.md)
+ [Coloque nós mais restritivos no lado esquerdo em expressões de caminho de comprimento variável (VLP)](best-practices-content-4.md)
+ [Evitar verificações redundantes de rótulos de nó usando nomes de relacionamento granulares](best-practices-content-5.md)
+ [Especificar rótulos de borda sempre que possível](best-practices-content-6.md)
+ [Evitar usar a cláusula WITH quando possível](best-practices-content-7.md)
+ [Colocar filtros restritivos o mais cedo possível na consulta](best-practices-content-8.md)
+ [Verificar explicitamente se as propriedades existem](best-practices-content-9.md)
+ [Não usar o caminho nomeado (a menos que seja necessário)](best-practices-content-10.md)
+ [Evitar COLLECT(DISTINCT())](best-practices-content-11.md)
+ [Optar pela função de propriedades em vez da pesquisa de propriedades individuais ao recuperar todos os valores da propriedade](best-practices-content-12.md)
+ [Executar cálculos estáticos fora da consulta](best-practices-content-13.md)
+ [Agrupar entradas usando UNWIND em vez de declarações individuais](best-practices-content-14.md)
+ [Prefiro usar o personalizado IDs para nó/relacionamento](best-practices-content-15.md)
+ [Evitar fazer cálculos de \$1id na consulta](best-practices-content-16.md)
+ [Atualizar/mesclar vários nós](best-practices-merge-multiple-nodes.md)

## Criar uma conexão após o failover
<a name="best-practices-opencypher-renew-connection"></a>

No caso de um failover, o driver do Bolt pode continuar se conectando à instância de gravador antiga e não à nova instância ativa, porque o nome do DNS foi resolvido para um endereço IP específico.

Para evitar isso, feche e reconecte o objeto `Driver` após qualquer failover.

## Tratamento de conexões para aplicações de longa duração
<a name="best-practices-opencypher-long-connections"></a>

Ao criar aplicações de longa duração, como aqueles executados em contêineres ou em instâncias do Amazon EC2, instancie um objeto `Driver` uma vez e, depois, reutilize esse objeto durante toda a vida útil da aplicação. O objeto `Driver` é seguro para threads, e a sobrecarga de inicializá-lo é considerável.

## Manipulação de conexão para AWS Lambda
<a name="best-practices-opencypher-lambda-connections"></a>

Os drivers de parafuso não são recomendados para uso em AWS Lambda funções, devido à sobrecarga de conexão e aos requisitos de gerenciamento. Em vez disso, use o [endpoint HTTPS](access-graph-opencypher-queries.md).

# Preferir direcionar para bordas bidirecionais nas consultas
<a name="best-practices-opencypher-directed-edges"></a>

Quando o Neptune realiza otimizações de consulta, as bordas bidirecionais dificultam a criação de planos de consulta ideais. Planos abaixo do ideal exigem que o mecanismo faça um trabalho desnecessário e ocasionam degradação do desempenho.

Portanto, use bordas direcionadas em vez de bidirecionais sempre que possível. Por exemplo, use:

```
MATCH p=(:airport {code: 'ANC'})-[:route]->(d) RETURN p)
```

em vez de:

```
MATCH p=(:airport {code: 'ANC'})-[:route]-(d) RETURN p)
```

Na verdade, a maioria dos modelos de dados não precisa percorrer bordas nas duas direções, portanto, as consultas podem obter melhorias significativas no desempenho ao passar a usar bordas direcionadas.

Se o modelo de dados exigir percurso por bordas bidirecionais, faça do primeiro nó (lado esquerdo) no padrão `MATCH` o nó com a filtragem mais restritiva.

Veja o exemplo: “Encontre-me em todas as `routes` para e do aeroporto `ANC`”. Escreva essa consulta para começar no aeroporto `ANC`, da seguinte forma:

```
MATCH p=(src:airport {code: 'ANC'})-[:route]-(d) RETURN p
```

O mecanismo pode realizar a quantidade mínima de trabalho para atender à consulta, porque o nó mais restrito é colocado como o primeiro nó (lado esquerdo) no padrão. O mecanismo pode então otimizar a consulta.

Essa é de longe a opção preferencial em comparação a filtrar o aeroporto `ANC` no final do padrão, desta forma:

```
MATCH p=(d)-[:route]-(src:airport {code: 'ANC'}) RETURN p
```

Quando o nó mais restrito não é colocado primeiro no padrão, o mecanismo precisa realizar um trabalho adicional porque não pode otimizar a consulta e precisa realizar pesquisas adicionais para chegar aos resultados.

# O Neptune não é compatível com várias consultas simultâneas em uma transação.
<a name="best-practices-opencypher-multiple-queries"></a>

Embora o próprio driver do Bolt permita consultas simultâneas em uma transação, o Neptune não é compatível com várias consultas em uma transação executada simultaneamente. Em vez disso, o Neptune exige que várias consultas em uma transação sejam executadas sequencialmente e que os resultados de cada consulta sejam completamente consumidos antes que a próxima consulta seja iniciada.

O exemplo abaixo mostra como usar o Bolt para executar várias consultas sequencialmente em uma transação, para que os resultados de cada uma sejam completamente consumidos antes do início da próxima:

```
final String query = "MATCH (n) RETURN n";

try (Driver driver = getDriver(HOST_BOLT, getDefaultConfig())) {
  try (Session session = driver.session(readSessionConfig)) {
    try (Transaction trx = session.beginTransaction()) {
      final Result res_1 = trx.run(query);
      Assert.assertEquals(10000, res_1.list().size());
      final Result res_2 = trx.run(query);
      Assert.assertEquals(10000, res_2.list().size());
    }
  }
}
```

# Fechar objetos de driver ao concluir
<a name="best-practices-opencypher-close-driver"></a>

Feche o cliente quando terminar de usá-lo, para que as conexões do Bolt sejam encerradas pelo servidor e todos os recursos associados à conexão sejam liberados. Isso acontecerá automaticamente se você fechar o driver usando `driver.close()`.

Se o driver não for fechado corretamente, o Neptune encerrará todas as conexões inativas do Bolt após vinte minutos ou após dez dias se você estiver usando a autenticação do IAM.

O Neptune não é compatível mais de mil conexões simultâneas do Bolt. Se você não fechar explicitamente as conexões ao terminar de usá-las e o número de conexões ativas atingir o limite de mil, qualquer nova tentativa de conexão falhará.

# Usar modos de transação explícitos para leitura e gravação
<a name="best-practices-opencypher-use-explicit-txs"></a>

Ao usar transações com o Neptune e o driver do Bolt, é melhor definir explicitamente o modo de acesso para transações de leitura e gravação como as configurações corretas.

## Transações somente leitura
<a name="best-practices-opencypher-read-txs"></a>

Para transações somente leitura, se você não transmitir a configuração apropriada do modo de acesso ao criar a sessão, o nível de isolamento padrão será usado, que é o isolamento da consulta de mutação. Como resultado, para as transações somente leitura, é importante definir o modo de acesso como `read` explicitamente.

**Exemplo de transação de leitura de confirmação automática:**

```
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withFetchSize(1000)
  .withDefaultAccessMode(AccessMode.READ)
  .build();
Session session = driver.session(sessionConfig);
try {
  (Add your application code here)
} catch (final Exception e) {
  throw e;
} finally {
  driver.close()
}
```

**Leia o exemplo de transação:**

```
Driver driver = GraphDatabase.driver(url, auth, config);
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withDefaultAccessMode(AccessMode.READ)
  .build();
driver.session(sessionConfig).readTransaction(
  new TransactionWork<List<String>>() {
    @Override
    public List<String> execute(org.neo4j.driver.Transaction tx) {
      (Add your application code here)
    }
  }
);
```

Nos dois casos, o [isolamento de `SNAPSHOT`](transactions-isolation-levels.md) é obtido usando a [semântica de transação somente leitura do Neptune](transactions-neptune.md#transactions-neptune-read-only).

Como as réplicas de leitura só aceitam consultas somente leitura, qualquer consulta enviada a uma réplica de leitura é executada sob a semântica de isolamento `SNAPSHOT`.

Não há leituras sujas ou não repetíveis para transações somente leitura.

## Transações de mutação
<a name="best-practices-opencypher-mutation-txs"></a>

Para consultas de mutação, há três mecanismos diferentes para criar uma transação de gravação, cada um dos quais é ilustrado abaixo:

**Exemplo de transação de gravação implícita:**

```
Driver driver = GraphDatabase.driver(url, auth, config);
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withDefaultAccessMode(AccessMode.WRITE)
  .build();
driver.session(sessionConfig).writeTransaction(
  new TransactionWork<List<String>>() {
    @Override
    public List<String> execute(org.neo4j.driver.Transaction tx) {
      (Add your application code here)
    }
  }
);
```

**Exemplo de transação de gravação de confirmação automática:**

```
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withFetchSize(1000)
  .withDefaultAccessMode(AccessMode.Write)
  .build();
Session session = driver.session(sessionConfig);
try {
  (Add your application code here)
} catch (final Exception e) {
    throw e;
} finally {
    driver.close()
}
```

**Exemplo de transação de gravação explícita:**

```
Driver driver = GraphDatabase.driver(url, auth, config);
SessionConfig sessionConfig = SessionConfig
  .builder()
  .withFetchSize(1000)
  .withDefaultAccessMode(AccessMode.WRITE)
  .build();
Transaction beginWriteTransaction = driver.session(sessionConfig).beginTransaction();
  (Add your application code here)
beginWriteTransaction.commit();
driver.close();
```

**Níveis de isolamento para transações de gravação**
+ As leituras feitas como parte das consultas de mutação são executadas sob isolamento de transações `READ COMMITTED`.
+ Não há leituras sujas para leituras feitas como parte de consultas de mutação.
+ Registros e intervalos de registros são bloqueados durante a leitura de uma consulta de mutação.
+ Quando um intervalo do índice tiver sido lido por uma transação de mutação, há uma forte garantia de que esse intervalo não será modificado por nenhuma transação simultânea até o final da transação de leitura.

As consultas de mutação não são livres de threads.

Sobre conflitos, consulte [Resolução de conflitos usando tempos limite de espera de bloqueio](transactions-neptune.md#transactions-neptune-conflicts).

Em caso de falha, as consultas de mutação não são repetidas automaticamente.

# Lógica de novas tentativas para exceções
<a name="best-practices-opencypher-retry-logic"></a>

Para todas as exceções que permitem uma nova tentativa, geralmente é melhor usar uma [estratégia de recuo exponencial e repetição](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) que forneça tempos de espera progressivamente maiores entre as novas tentativas, a fim de lidar melhor com problemas transitórios, como erros `ConcurrentModificationException`. Veja um exemplo de padrão de recuo exponencial e repetição:

```
public static void main() {
  try (Driver driver = getDriver(HOST_BOLT, getDefaultConfig())) {
    retriableOperation(driver, "CREATE (n {prop:'1'})")
        .withRetries(5)
        .withExponentialBackoff(true)
        .maxWaitTimeInMilliSec(500)
        .call();
  }
}

protected RetryableWrapper retriableOperation(final Driver driver, final String query){
  return new RetryableWrapper<Void>() {
    @Override
    public Void submit() {
      log.info("Performing graph Operation in a retry manner......");
      try (Session session = driver.session(writeSessionConfig)) {
        try (Transaction trx =  session.beginTransaction()) {
            trx.run(query).consume();
            trx.commit();
        }
      }
      return null;
    }

    @Override
    public boolean isRetryable(Exception e) {
      if (isCME(e)) {
        log.debug("Retrying on exception.... {}", e);
        return true;
      }
      return false;
    }

    private boolean isCME(Exception ex) {
      return ex.getMessage().contains("Operation failed due to conflicting concurrent operations");
    }
  };
}



/**
 * Wrapper which can retry on certain condition. Client can retry operation using this class.
 */
@Log4j2
@Getter
public abstract class RetryableWrapper<T> {

  private long retries = 5;
  private long maxWaitTimeInSec = 1;
  private boolean exponentialBackoff = true;

  /**
   * Override the method with custom implementation, which will be called in retryable block.
   */
  public abstract T submit() throws Exception;

  /**
   * Override with custom logic, on which exception to retry with.
   */
  public abstract boolean isRetryable(final Exception e);

  /**
   * Define the number of retries.
   *
   * @param retries -no of retries.
   */
  public RetryableWrapper<T> withRetries(final long retries) {
    this.retries = retries;
    return this;
  }

  /**
   * Max wait time before making the next call.
   *
   * @param time - max polling interval.
   */
  public RetryableWrapper<T> maxWaitTimeInMilliSec(final long time) {
    this.maxWaitTimeInSec = time;
    return this;
  }

  /**
   * ExponentialBackoff coefficient.
   */
  public RetryableWrapper<T> withExponentialBackoff(final boolean expo) {
    this.exponentialBackoff = expo;
    return this;
  }

  /**
   * Call client method which is wrapped in submit method.
   */
  public T call() throws Exception {
    int count = 0;
    Exception exceptionForMitigationPurpose = null;
    do {
      final long waitTime = exponentialBackoff ? Math.min(getWaitTimeExp(retries), maxWaitTimeInSec) : 0;
      try {
          return submit();
      } catch (Exception e) {
        exceptionForMitigationPurpose = e;
        if (isRetryable(e) && count < retries) {
          Thread.sleep(waitTime);
          log.debug("Retrying on exception attempt - {} on exception cause - {}", count, e.getMessage());
        } else if (!isRetryable(e)) {
          log.error(e.getMessage());
          throw new RuntimeException(e);
        }
      }
    } while (++count < retries);

    throw new IOException(String.format(
          "Retry was unsuccessful.... attempts %d. Hence throwing exception " + "back to the caller...", count),
          exceptionForMitigationPurpose);
  }

  /*
   * Returns the next wait interval, in milliseconds, using an exponential backoff
   * algorithm.
   */
  private long getWaitTimeExp(final long retryCount) {
    if (0 == retryCount) {
      return 0;
    }
    return ((long) Math.pow(2, retryCount) * 100L);
  }
}
```

# Definir várias propriedades de uma vez usando uma única cláusula SET
<a name="best-practices-content-0"></a>

 Em vez de usar várias cláusulas SET para definir propriedades individuais, use um mapa para definir várias propriedades para uma entidade de uma vez. 

 Você pode usar: 

```
MATCH (n:SomeLabel {`~id`: 'id1'})
SET n += {property1 : 'value1',
property2 : 'value2',
property3 : 'value3'}
```

 Em vez de: 

```
MATCH (n:SomeLabel {`~id`: 'id1'})
SET n.property1 = 'value1'
SET n.property2 = 'value2'
SET n.property3 = 'value3'
```

 A cláusula SET aceita uma única propriedade ou um mapa. Ao atualizar várias propriedades em uma única entidade, usar uma única cláusula SET com um mapa permite que as atualizações sejam realizadas em uma única operação em vez de várias operações, que podem ser executadas com mais eficiência. 

## Usar a cláusula SET para remover várias propriedades de uma só vez
<a name="best-practices-content-1"></a>

 Ao usar a linguagem openCypher, REMOVE é usado para remover propriedades de uma entidade. No Neptune, cada propriedade removida requer uma operação separada, adicionando latência de consulta. Em vez disso, você pode usar SET com um mapa para definir todos os valores das propriedades como `null`, o que em Neptune é o equivalente a remover propriedades. O Neptune terá um desempenho aprimorado quando for necessário remover várias propriedades em uma única entidade. 

Use:

```
WITH {prop1: null, prop2: null, prop3: null} as propertiesToRemove 
MATCH (n) 
SET n += propertiesToRemove
```

Em vez de:

```
MATCH (n) 
REMOVE n.prop1, n.prop2, n.prop3
```

# Usar consultas parametrizadas
<a name="best-practices-content-2"></a>

 É recomendável sempre usar consultas parametrizadas ao fazer consultas usando o openCypher. O mecanismo de consulta pode aproveitar consultas parametrizadas repetidas para recursos como cache do plano de consulta, em que a invocação repetida da mesma estrutura parametrizada com parâmetros diferentes pode aproveitar os planos em cache. O plano de consulta gerado para consultas parametrizadas é armazenado em cache e reutilizado somente quando é concluído em 100 ms e os tipos de parâmetros são NUMBER, BOOLEAN ou STRING. 

Use:

```
MATCH (n:foo) WHERE id(n) = $id RETURN n
```

Com parâmetros:

```
parameters={"id": "first"}
parameters={"id": "second"}
parameters={"id": "third"}
```

Em vez de:

```
MATCH (n:foo) WHERE id(n) = "first" RETURN n
MATCH (n:foo) WHERE id(n) = "second" RETURN n
MATCH (n:foo) WHERE id(n) = "third" RETURN n
```

# Use mapas nivelados em vez de mapas aninhados na cláusula UNWIND
<a name="best-practices-content-3"></a>

 Uma estrutura aninhada profunda pode restringir a capacidade do mecanismo de consulta de gerar um plano de consulta ideal. Para aliviar parcialmente esse problema, os seguintes padrões definidos criarão planos ideais para os seguintes cenários: 
+  Cenário 1: UNWIND com uma lista de literais cifrados, que inclui NUMBER, STRING e BOOLEAN. 
+  Cenário 2: UNWIND com uma lista de mapas nivelados, que inclui somente literais cifrados (NUMBER, STRING, BOOLEAN) como valores. 

 Ao escrever uma consulta contendo a cláusula UNWIND, use a recomendação acima para melhorar o desempenho. 

Exemplo do cenário 1:

```
UNWIND $ids as x
MATCH(t:ticket {`~id`: x})
```

Com parâmetros:

```
parameters={
  "ids": [1, 2, 3]
}
```

 Um exemplo do Cenário 2 é gerar uma lista de nós para CREATE ou MERGE. Em vez de emitir várias declarações, use o seguinte padrão para definir as propriedades como um conjunto de mapas nivelados: 

```
UNWIND $props as p
CREATE(t:ticket {title: p.title, severity:p.severity})
```

Com parâmetros:

```
parameters={
  "props": [
    {"title": "food poisoning", "severity": "2"},
    {"title": "Simone is in office", "severity": "3"}
  ]
}
```

Em vez de objetos de nós aninhados, como:

```
UNWIND $nodes as n
CREATE(t:ticket n.properties)
```

Com parâmetros:

```
parameters={
  "nodes": [
    {"id": "ticket1", "properties": {"title": "food poisoning", "severity": "2"}},
    {"id": "ticket2", "properties": {"title": "Simone is in office", "severity": "3"}}
  ]
}
```

# Coloque nós mais restritivos no lado esquerdo em expressões de caminho de comprimento variável (VLP)
<a name="best-practices-content-4"></a>

 Nas consultas de caminho de comprimento variável (VLP), o mecanismo de consulta otimiza a avaliação escolhendo iniciar o percurso do lado esquerdo ou direito da expressão. A decisão é baseada na cardinalidade de padrões no lado esquerdo e direito. A cardinalidade é o número de nós que correspondem ao padrão especificado. 
+  Se o padrão correto tiver uma cardinalidade de um, o lado direito será o ponto de partida. 
+  Se o lado esquerdo e o direito tiverem cardinalidade de um, a expansão é verificada em ambos os lados e começa no lado com a expansão menor. Expansão é o número de bordas de entrada ou saída do nó à esquerda e à direita da expressão VLP. Essa parte da otimização é usada somente se o relacionamento VLP for unidirecional e for fornecido. 
+  Caso contrário, o lado esquerdo será o ponto de partida. 

 Para uma cadeia de expressões VLP, essa otimização só pode ser aplicada à primeira expressão. Os outros VLPs são avaliados começando pelo lado esquerdo. Como exemplo, a cardinalidade de (a), (b) deve ser um e a cardinalidade de (c) maior que um. 
+  `(a)-[*1..]->(c)`: a avaliação começa com (a). 
+  `(c)-[*1..]->(a)`: a avaliação começa com (a). 
+  `(a)-[*1..]-(c)`: a avaliação começa com (a). 
+  `(c)-[*1..]-(a)`: a avaliação começa com (a). 

 Agora, permita que as bordas de entrada de (a) sejam dois, as bordas de saída de (a) três, as formas de entrada de (b) quatro e as bordas de saída de (b), cinco. 
+  `(a)-[*1..]->(b)`: a avaliação começa com (a), pois as bordas de saída de (a) são menores do que as bordas de entrada de (b). 
+  `(a)<-[*1..]-(b)`: a avaliação começa com (a), pois as bordas de entrada de (a) são menores do que as bordas de saída de (b). 

 Como regra geral, coloque o padrão mais restritivo no lado esquerdo de uma expressão VLP. 

# Evitar verificações redundantes de rótulos de nó usando nomes de relacionamento granulares
<a name="best-practices-content-5"></a>

 Ao otimizar o desempenho, usar rótulos de relacionamento exclusivos dos padrões do nó permite a remoção da filtragem de rótulos nos nós. Considere um modelo de grafo em que o relacionamento `likes` é usado somente para definir um relacionamento entre dois nós `person`. Poderíamos escrever a seguinte consulta para encontrar esse padrão: 

```
MATCH (n:person)-[:likes]->(m:person)
RETURN n, m
```

 A verificação do rótulo `person` em n e m é redundante, pois definimos que o relacionamento só apareça quando ambos forem do tipo `person`. Para otimizar o desempenho, podemos escrever a consulta da seguinte forma: 

```
MATCH (n)-[:likes]->(m)
RETURN n, m
```

 Esse padrão também pode ser aplicado quando as propriedades são exclusivas a um rótulo de nó único. Suponha que somente os nós `person` tenham a propriedade `email`, portanto, verificar a correspondência do rótulo do nó `person` é redundante. Escrever a consulta como: 

```
MATCH (n:person)
WHERE n.email = 'xxx@gmail.com'
RETURN n
```

 É menos eficiente do que escrever a consulta como: 

```
MATCH (n)
WHERE n.email = 'xxx@gmail.com'
RETURN n
```

 Adote esse padrão somente quando o desempenho for importante e tiver verificações em seu processo de modelagem para garantir que esses rótulos de borda não sejam reutilizados para padrões envolvendo os rótulos de nó. Se você introduzir posteriormente uma propriedade `email` em outro rótulo de nó, como `company`, os resultados serão diferentes entre essas duas versões da consulta. 

# Especificar rótulos de borda sempre que possível
<a name="best-practices-content-6"></a>

 É recomendável fornecer um rótulo de borda sempre que possível ao especificar uma borda em um padrão. Considere o exemplo de consulta a seguir, usado para vincular todas as pessoas que moram em uma cidade a todas as pessoas que visitaram essa cidade. 

```
MATCH (person)-->(city {country: "US"})-->(anotherPerson)
RETURN person, anotherPerson
```

 Se seu modelo de grafo vincular pessoas a nós que não sejam somente cidades usando vários rótulos de borda, ao não especificar o rótulo final, o Neptune precisará avaliar caminhos adicionais que serão descartados posteriormente. Na consulta acima, como um rótulo de borda não foi fornecido, o mecanismo trabalha mais e depois filtra os valores para obter o resultado correto. Uma versão aprimorada da consulta acima pode ser: 

```
MATCH (person)-[:livesIn]->(city {country: "US"})-[:visitedBy]->(anotherPerson)
RETURN person, anotherPerson
```

 Isso não só ajuda na avaliação, mas permite que o planejador de consultas crie planos melhores. Você pode até mesmo combinar essa prática recomendada com verificações redundantes de rótulos de nós para remover a verificação de rótulos de cidades e escrever a consulta como: 

```
MATCH (person)-[:livesIn]->({country: "US"})-[:visitedBy]->(anotherPerson)
RETURN person, anotherPerson
```

# Evitar usar a cláusula WITH quando possível
<a name="best-practices-content-7"></a>

 A cláusula WITH no openCypher atua como um limite em que tudo antes dela é executado e os valores resultantes são passados para as partes restantes da consulta. A cláusula WITH é necessária quando você precisa de agregação provisória ou deseja limitar o número de resultados, mas, além desses casos, evite o uso dessa cláusula. A orientação geral é remover essas cláusulas WITH simples (sem agregação, ordem ou limite) para permitir que o planejador de consultas trabalhe em toda a consulta para criar um plano globalmente ideal. Por exemplo, suponha que você escreveu uma consulta para retornar todas as pessoas que moram em `India`: 

```
MATCH (person)-[:lives_in]->(city)
WITH person, city
MATCH (city)-[:part_of]->(country {name: 'India'})
RETURN collect(person) AS result
```

 Na versão acima, a cláusula WITH restringe o posicionamento do padrão `(city)-[:part_of]->(country {name: 'India'})` (que é mais restritivo) antes de `(person)-[:lives_in]->(city)`. Isso deixa o plano abaixo do ideal. Uma otimização dessa consulta seria remover a cláusula WITH e permitir que o planejador calculasse o melhor plano. 

```
MATCH (person)-[:lives_in]->(city)
MATCH (city)-[:part_of]->(country {name: 'India'})
RETURN collect(person) AS result
```

# Colocar filtros restritivos o mais cedo possível na consulta
<a name="best-practices-content-8"></a>

 Em todos os cenários, colocar filtros na consulta antecipadamente ajuda a reduzir as soluções intermediárias que um plano de consulta deve considerar. Isso significa que menos memória e menos recursos computacionais são necessários para executar a consulta. 

 O exemplo a seguir ajuda você a entender esses impactos. Suponha que você escreva uma consulta para retornar todas as pessoas que moram em `India`. Uma versão da consulta pode ser: 

```
MATCH (n)-[:lives_in]->(city)-[:part_of]->(country)
WITH country, collect(n.firstName + " "  + n.lastName) AS result
WHERE country.name = 'India'
RETURN result
```

 A versão de consulta acima não é a melhor maneira de alcançar esse caso de uso. O filtro `country.name = 'India'` aparece posteriormente no padrão de consulta. Primeiro, ele coletará todas as pessoas e onde elas moram, as agrupará por país e, em seguida, filtrará somente o grupo para `country.name = India`. O ideal é consultar somente as pessoas que moram em `India` e depois realizar a agregação de coleta. 

```
MATCH (n)-[:lives_in]->(city)-[:part_of]->(country)
WHERE country.name = 'India'
RETURN collect(n.firstName + " "  + n.lastName) AS result
```

 Uma regra geral é colocar um filtro o mais rápido possível após a introdução da variável. 

# Verificar explicitamente se as propriedades existem
<a name="best-practices-content-9"></a>

 Com base na semântica do openCypher, quando uma propriedade é acessada, ela é equivalente a uma junção opcional e deve reter todas as linhas, mesmo que a propriedade não exista. Se você souber, com base em seu esquema de grafos, que uma propriedade específica sempre existirá para essa entidade, verificar explicitamente a existência dessa propriedade permite que o mecanismo de consulta crie planos ideais e melhore o desempenho. 

 Considere um modelo de grafos em que nós do tipo `person` sempre tenham uma propriedade `name`. Em vez de escrever: 

```
MATCH (n:person)
RETURN n.name
```

 Verifique explicitamente a existência da propriedade na consulta com uma verificação IS NOT NULL: 

```
MATCH (n:person)
WHERE n.name IS NOT NULL
RETURN n.name
```

# Não usar o caminho nomeado (a menos que seja necessário)
<a name="best-practices-content-10"></a>

 O caminho nomeado em uma consulta sempre tem um custo adicional, o que pode adicionar penalidades em termos de maior latência e uso de memória. Considere a seguinte consulta: 

```
MATCH p = (n)-[:commentedOn]->(m)
WITH p, m, n, n.score + m.score as total
WHERE total > 100 
MATCH (m)-[:commentedON]->(o)
WITH p, m, n, distinct(o) as o1
RETURN p, m.name, n.name, o1.name
```

 Na consulta acima, supondo que queremos somente saber as propriedades dos nós, o uso do caminho “p” é desnecessário. Ao especificar o caminho nomeado como uma variável, a operação de agregação que usa DISTINCT será cara em termos de tempo e uso de memória. Uma versão otimizada da consulta acima poderia ser: 

```
MATCH (n)-[:commentedOn]->(m)
WITH m, n, n.score + m.score as total
WHERE total > 100 
MATCH (m)-[:commentedON]->(o)
WITH m, n, distinct(o) as o1
RETURN m.name, n.name, o1.name
```

# Evitar COLLECT(DISTINCT())
<a name="best-practices-content-11"></a>

**nota**  
A partir da versão [1.4.7.0](engine-releases-1.4.7.0.md) do motor, essa reescrita recomendada não é mais necessária.

 COLLECT(DISTINCT()) é usada sempre que uma lista deve ser formada contendo valores distintos. COLLECT é uma função de agregação e o agrupamento é feito com base em chaves adicionais projetadas na mesma instrução. Quando “distinct” é usado, a entrada é dividida em vários blocos, e cada pedaço denota um grupo para redução. O desempenho será afetado à medida que o número de grupos aumentar. Em Neptune, é muito mais eficiente executar DISTINCT antes collecting/forming de realmente fazer a lista. Isso permite que o agrupamento seja feito diretamente nas chaves de agrupamento de todo o bloco. 

 Considere a seguinte consulta: 

```
MATCH (n:Person)-[:commented_on]->(p:Post)
WITH n, collect(distinct(p.post_id)) as post_list
RETURN n, post_list
```

 Uma maneira mais ideal de escrever essa consulta é: 

```
MATCH (n:Person)-[:commented_on]->(p:Post)
WITH DISTINCT n, p.post_id as postId
WITH n, collect(postId) as post_list
RETURN n, post_list
```

# Optar pela função de propriedades em vez da pesquisa de propriedades individuais ao recuperar todos os valores da propriedade
<a name="best-practices-content-12"></a>

 A função `properties()` é usada para retornar um mapa contendo todas as propriedades de uma entidade e é muito mais eficiente do que retornar propriedades individualmente. 

 Supondo que seus nós `Person` contenham cinco propriedades (`firstName`, `lastName`, `age`, `dept` e `company`), a seguinte consulta seria preferível: 

```
MATCH (n:Person)
WHERE n.dept = 'AWS'
RETURN properties(n) as personDetails
```

 Em vez de usar: 

```
MATCH (n:Person)
WHERE n.dept = 'AWS'
RETURN n.firstName, n.lastName, n.age, n.dept, n.company
    
=== OR ===
    
MATCH (n:Person)
WHERE n.dept = 'AWS'
RETURN {firstName: n.firstName, lastName: n.lastName, age: n.age, 
department: n.dept, company: n.company} as personDetails
```

# Executar cálculos estáticos fora da consulta
<a name="best-practices-content-13"></a>

 É recomendável resolver cálculos estáticos ( mathematical/string operações simples) no lado do cliente. Considere este exemplo em que você deseja encontrar todas as pessoas um ano mais velhas ou mais novas que o autor: 

```
MATCH (m:Message)-[:HAS_CREATOR]->(p:person)
WHERE p.age <= ($age + 1)
RETURN m
```

 Aqui, `$age` é injetado na consulta por meio de parâmetros e, em seguida, adicionado a um valor fixo. Esse valor é então comparado com `p.age`. Em vez disso, uma abordagem melhor seria fazer a adição no lado do cliente e passar o valor calculado como um parâmetro \$1ageplusone. Isso ajuda o mecanismo de consulta a criar planos otimizados e evita cálculos estáticos para cada linha de entrada. Seguindo essas diretrizes, uma versão mais eficiente da consulta seria: 

```
MATCH (m:Message)-[:HAS_CREATOR]->(p:person)
WHERE p.age <= $ageplusone
RETURN m
```

# Agrupar entradas usando UNWIND em vez de declarações individuais
<a name="best-practices-content-14"></a>

 Sempre que a mesma consulta precisar ser executada para entradas diferentes, em vez de executar uma consulta por entrada, seria muito mais eficiente executar uma consulta para um lote de entradas. 

 Se você quiser mesclar um conjunto de nós, uma opção é executar uma consulta de mesclagem por entrada: 

```
MERGE (n:Person {`~id`: $id})
SET n.name = $name, n.age = $age, n.employer = $employer
```

 Com parâmetros: 

```
params = {id: '1', name: 'john', age: 25, employer: 'Amazon'}
```

 A consulta acima precisa ser executada para cada entrada. Embora essa abordagem funcione, ela pode exigir que muitas consultas sejam executadas para um grande conjunto de entradas. Nesse cenário, o agrupamento em lotes pode ajudar a reduzir o número de consultas executadas no servidor, bem como melhorar o throughput geral. 

 Use o seguinte padrão: 

```
UNWIND $persons as person
MERGE (n:Person {`~id`: person.id})
SET n += person
```

 Com parâmetros: 

```
params = {persons: [{id: '1', name: 'john', age: 25, employer: 'Amazon'}, 
{id: '2', name: 'jack', age: 28, employer: 'Amazon'},
{id: '3', name: 'alice', age: 24, employer: 'Amazon'}...]}
```

 Recomenda-se experimentar com diferentes tamanhos de lote para determinar o que funciona melhor para sua workload. 

# Prefiro usar o personalizado IDs para nó/relacionamento
<a name="best-practices-content-15"></a>

 O Neptune permite que os usuários IDs atribuam explicitamente nós e relacionamentos. O ID deve ser globalmente exclusivo no conjunto de dados e determinístico para ser útil. Um ID determinístico pode ser usado como um mecanismo de pesquisa ou filtragem, assim como propriedades; no entanto, do ponto de vista da execução da consulta, usar um ID é um método muito mais otimizado do que usar propriedades. Há vários benefícios em usar o personalizado IDs - 
+  As propriedades podem ser nulas para uma entidade existente, mas o ID deve existir. Isso permite que o mecanismo de consulta use uma junção otimizada durante a execução. 
+  Quando consultas de mutação simultâneas são executadas, as chances de [exceções de modificação simultânea](https://docs.aws.amazon.com//neptune/latest/userguide/transactions-exceptions.html) (CMEs) são reduzidas significativamente quando usadas para acessar nós, porque menos bloqueios IDs estão ocorrendo IDs do que propriedades devido à sua exclusividade imposta. 
+  O uso IDs evita a chance de criar dados duplicados, pois Neptune impõe exclusividade em propriedades diferentes. IDs 

 O exemplo de consulta a seguir usa um ID personalizado: 

**nota**  
 A propriedade `~id` é usada para especificar o ID, já `id` é armazenada somente como qualquer outra propriedade. 

```
CREATE (n:Person {`~id`: '1', name: 'alice'})
```

 Sem usar um ID personalizado: 

```
CREATE (n:Person {id: '1', name: 'alice'})
```

 Se estiver usando o último mecanismo, não há imposição de exclusividade e você poderá executar a consulta posteriormente: 

```
CREATE (n:Person {id: '1', name: 'john'})
```

 Isso cria um segundo nó com `id=1` chamado `john`. Nesse cenário, agora você teria dois nós com `id=1`, cada um com um nome diferente (alice e john). 

# Evitar fazer cálculos de \$1id na consulta
<a name="best-practices-content-16"></a>

 Ao usar IDs customização nas consultas, sempre realize cálculos estáticos fora das consultas e forneça esses valores nos parâmetros. Quando valores estáticos são fornecidos, o mecanismo é mais capaz de otimizar as pesquisas e evitar a digitalização e a filtragem desses valores. 

 Se você quiser criar bordas entre os nós que existem no banco de dados, uma opção pode ser: 

```
UNWIND $sections as section
MATCH (s:Section {`~id`: 'Sec-' + section.id})
MERGE (s)-[:IS_PART_OF]->(g:Group {`~id`: 'g1'})
```

 Com parâmetros: 

```
parameters={sections: [{id: '1'}, {id: '2'}]}
```

 Na consulta acima, o `id` da seção está sendo computado na consulta. Como o cálculo é dinâmico, o mecanismo não pode embutir identificações estaticamente e acaba escaneando todos os nós da seção. O mecanismo então executa a pós-filtragem dos nós necessários. Isso pode ser caro se houver muitos nós de seção no banco de dados. 

 A melhor maneira de fazer isso é prefixar `Sec-` nos ids que estão sendo passados para o banco de dados: 

```
UNWIND $sections as section
MATCH (s:Section {`~id`: section.id})
MERGE (s)-[:IS_PART_OF]->(g:Group {`~id`: 'g1'})
```

 Com parâmetros: 

```
parameters={sections: [{id: 'Sec-1'}, {id: 'Sec-2'}]}
```

# Atualizar/mesclar vários nós
<a name="best-practices-merge-multiple-nodes"></a>

 Ao executar `MERGE` ou `CREATE` consultar em vários nós, é recomendável usar um `UNWIND` em combinação com uma única MERGE/CREATE cláusula em vez de usar uma MERGE/CREATE cláusula para cada nó. As consultas que usam uma cláusula por nó levam a um plano de execução ineficiente devido à necessidade de otimizar cada linha. Isso faz com que a maior parte do tempo de execução da consulta seja gasto no processamento estático em vez da atualização real. 

 Uma cláusula por nó não é ideal, pois não é escalável com o aumento do número de nós: 

```
MERGE (p1:Person {name: 'NameA'})
ON CREATE SET p1 += {prop1: 'prop1V1', prop2: 'prop2V1'}
MERGE (p2:Person {name: 'NameB'})
ON CREATE SET p2 += {prop1: 'prop1V2', prop2: 'prop2V2'}
MERGE (p3:Person {name: 'NameC'})
ON CREATE SET p3 += {prop1: 'prop1V3', prop2: 'prop1V3'}
```

 Usar um `UNWIND` em conjunto com uma MERGE/CREATE cláusula permite o mesmo comportamento, mas um plano de execução mais otimizado. Com isso em mente, a consulta alterada ficaria assim: 

```
## If not using custom id for nodes/relationship
UNWIND [{name: 'NameA', prop1: 'prop1V1', prop2: 'prop2V1'}, {name: 'NameB', prop1: 'prop1V2', prop2: 'prop2V2'}, {name: 'NameC', prop1: 'prop1V3', prop2: 'prop1V3'}] AS props
MERGE (p:Person {name: props.name})
ON CREATE SET p = props

## If using custom id for nodes/relationship
UNWIND [{`~id`: '1', 'name': 'NameA', 'prop1: 'prop1V1', prop2: 'prop2V1'}, {`~id`: '2', name: 'NameB', prop1: 'prop1V2', prop2: 'prop2V2'}, {`~id`: '3', name: 'NameC', prop1: 'prop1V3', prop2: 'prop1V3'}] AS props
MERGE (p:Person {`~id`: props.id})
ON CREATE SET p = removeKeyFromMap(props, '~id')
```