

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

# Bewährte Methoden für Neptune mit openCypher und Bolt
<a name="best-practices-opencypher"></a>

Befolgen Sie diese bewährten Methoden bei der Verwendung der openCypher-Abfragesprache mit Neptune. Informationen über die Verwendung von openCypher in Neptune finden Sie unter [Zugriff auf das Neptun-Diagramm mit openCypher](access-graph-opencypher.md).

**Topics**
+ [Erstellen einer neuen Verbindung nach einem Failover](#best-practices-opencypher-renew-connection)
+ [Verbindungsverwaltung für langlebige Anwendungen](#best-practices-opencypher-long-connections)
+ [Verbindungsmanagement für AWS Lambda](#best-practices-opencypher-lambda-connections)
+ [Bevorzugung direktionaler gegenüber bidirektionalen Edges in Abfragen](best-practices-opencypher-directed-edges.md)
+ [Neptune unterstützt nicht mehrere gleichzeitige Abfragen in einer Transaktion](best-practices-opencypher-multiple-queries.md)
+ [Schließen Sie anschließend Treiberobjekte](best-practices-opencypher-close-driver.md)
+ [Verwenden expliziter Transaktionsmodi zum Lesen und Schreiben](best-practices-opencypher-use-explicit-txs.md)
+ [Wiederholungslogik für Ausnahmen](best-practices-opencypher-retry-logic.md)
+ [Legen Sie mithilfe einer einzigen SET-Klausel mehrere Eigenschaften gleichzeitig fest](best-practices-content-0.md)
+ [Parametrisierte Abfragen verwenden](best-practices-content-2.md)
+ [Verwenden Sie in der UNWIND-Klausel abgeflachte Maps anstelle von verschachtelten Maps](best-practices-content-3.md)
+ [Platzieren Sie restriktivere Knoten in VLP-Ausdrücken (Variable-Length Path) auf der linken Seite](best-practices-content-4.md)
+ [Vermeiden Sie redundante Prüfungen von Knotenbezeichnungen, indem Sie detaillierte Beziehungsnamen verwenden](best-practices-content-5.md)
+ [Geben Sie nach Möglichkeit Kantenbeschriftungen an](best-practices-content-6.md)
+ [Vermeiden Sie nach Möglichkeit die WITH-Klausel](best-practices-content-7.md)
+ [Platzieren Sie restriktive Filter so früh wie möglich in der Abfrage](best-practices-content-8.md)
+ [Prüfen Sie explizit, ob Eigenschaften vorhanden sind](best-practices-content-9.md)
+ [Verwenden Sie keinen benannten Pfad (es sei denn, er ist erforderlich)](best-practices-content-10.md)
+ [Vermeiden Sie COLLECT (DISTINCT ())](best-practices-content-11.md)
+ [Ziehen Sie beim Abrufen aller Eigenschaftswerte die Eigenschaftenfunktion der Suche nach einzelnen Eigenschaften vor](best-practices-content-12.md)
+ [Führen Sie statische Berechnungen außerhalb der Abfrage durch](best-practices-content-13.md)
+ [Batch-Eingaben mit UNWIND anstelle von Einzelanweisungen](best-practices-content-14.md)
+ [Verwenden Sie lieber „Benutzerdefiniert“ IDs für Knoten/Beziehungen](best-practices-content-15.md)
+ [Vermeiden Sie es, \$1id-Berechnungen in der Abfrage durchzuführen](best-practices-content-16.md)
+ [Aktualisieren/Zusammenführen mehrerer Knoten](best-practices-merge-multiple-nodes.md)

## Erstellen einer neuen Verbindung nach einem Failover
<a name="best-practices-opencypher-renew-connection"></a>

Im Falle eines Failovers kann der Bolt-Treiber weiterhin eine Verbindung zur alten Writer-Instance und nicht zur neuen aktiven Instance herstellen, da der DNS-Name zu einer bestimmten IP-Adresse aufgelöst wurde.

Um dies zu verhindern, schließen Sie das `Driver`-Objekt und verbinden Sie es nach einem Failover erneut.

## Verbindungsverwaltung für langlebige Anwendungen
<a name="best-practices-opencypher-long-connections"></a>

Wenn Sie langlebige Anwendungen erstellen, z. B. solche, die in Containern oder auf Amazon-EC2-Instances ausgeführt werden, müssen Sie ein `Driver`-Objekt einmal instanziieren und dieses Objekt dann für die gesamte Lebensdauer der Anwendung wiederverwenden. Das `Driver`-Objekt ist threadsicher und es besteht ein erheblicher Initialisierungsaufwand.

## Verbindungsmanagement für AWS Lambda
<a name="best-practices-opencypher-lambda-connections"></a>

Bolt-Treiber werden aufgrund ihres Verbindungsaufwands und der Verwaltungsanforderungen nicht für den Einsatz innerhalb von AWS Lambda Funktionen empfohlen. Verwenden Sie stattdessen den [HTTPS-Endpunkt](access-graph-opencypher-queries.md).

# Bevorzugung direktionaler gegenüber bidirektionalen Edges in Abfragen
<a name="best-practices-opencypher-directed-edges"></a>

Wenn Neptune Abfrageoptimierungen durchführt, erschweren bidirektionale Edges die Erstellung optimaler Abfragepläne. Bei suboptimalen Plänen muss die Engine unnötige Arbeit verrichten, was zu einer schlechteren Leistung führt.

Verwenden Sie daher nach Möglichkeit direktionale anstelle von bidirektionalen Edges. Verwenden Sie beispielsweise:

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

anstelle von:

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

Die meisten Datenmodelle müssen Edges nicht wirklich in beide Richtungen durchqueren, so dass Abfragen durch die Umstellung auf die Verwendung direktionaler Edges zu erheblichen Leistungsverbesserungen führen können.

Wenn Ihr Datenmodell das Durchqueren bidirektionaler Edges erfordert, machen Sie den ersten Knoten (linke Seite) im `MATCH`-Muster zum Knoten mit der restriktivsten Filterung.

Nehmen wir das Beispiel „Finde alle `routes` zum und vom `ANC`-Flughafen“. Schreiben Sie diese Abfrage wie folgt so, dass sie am `ANC`-Flughafen beginnt:

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

Die Engine kann den geringsten Arbeitsaufwand zur Erfüllung der Abfrage wählen, da der Knoten mit den meisten Einschränkungen als erster Knoten (linke Seite) im Muster platziert wird. Die Engine kann dann die Abfrage optimieren.

Das ist weitaus besser, als den `ANC`-Flughafen am Ende des Musters wie folgt zu filtern:

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

Wenn der Knoten mit den meisten Einschränkungen nicht an erster Stelle im Muster steht, muss die Engine zusätzliche Arbeit leisten, da sie die Abfrage nicht optimieren kann und zusätzliche Suchvorgänge durchführen muss, um zu den Ergebnissen zu gelangen.

# Neptune unterstützt nicht mehrere gleichzeitige Abfragen in einer Transaktion
<a name="best-practices-opencypher-multiple-queries"></a>

Obwohl der Bolt-Treiber selbst gleichzeitige Abfragen in einer Transaktion zulässt, unterstützt Neptune nicht mehrere Abfragen in einer gleichzeitig laufenden Transaktion. Stattdessen verlangt Neptune, dass mehrere Abfragen in einer Transaktion nacheinander ausgeführt werden und dass die Ergebnisse jeder Abfrage vollständig verarbeitet werden, bevor die nächste Abfrage initiiert wird.

Das folgende Beispiel zeigt, wie Bolt verwendet wird, um mehrere Abfragen nacheinander in einer Transaktion auszuführen, so dass die Ergebnisse jeder Abfrage vollständig verbraucht werden, bevor die nächste beginnt:

```
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());
    }
  }
}
```

# Schließen Sie anschließend Treiberobjekte
<a name="best-practices-opencypher-close-driver"></a>

Es ist wichtig, den Client nach Abschluss des Vorgangs zu schließen, damit die Bolt-Verbindungen vom Server geschlossen und alle Ressourcen im Zusammenhang mit den Verbindungen freigegeben werden. Dies geschieht automatisch, wenn Sie den Treiber mit `driver.close()` schließen.

Wenn der Treiber nicht korrekt geschlossen wird, beendet Neptune alle inaktiven Bolt-Verbindungen nach 20 Minuten oder nach 10 Tagen, wenn Sie die IAM-Authentifizierung verwenden.

Neptune unterstützt nicht mehr als 1 000 gleichzeitige Bolt-Verbindungen. Wenn Sie Verbindungen nicht explizit schließen, wenn Sie mit ihnen fertig sind, und die Anzahl der Live-Verbindungen diese Grenze von 1 000 erreicht, schlagen alle neuen Verbindungsversuche fehl.

# Verwenden expliziter Transaktionsmodi zum Lesen und Schreiben
<a name="best-practices-opencypher-use-explicit-txs"></a>

Wenn Sie Transaktionen mit Neptune und dem Bolt-Treiber verwenden, ist es am besten, den Zugriffsmodus für Lese- und Schreibtransaktionen explizit auf die korrekten Einstellungen einzustellen.

## Nur-Lese-Transaktionen
<a name="best-practices-opencypher-read-txs"></a>

Wenn Sie bei schreibgeschützten Transaktionen beim Erstellen der Sitzung nicht die entsprechende Konfiguration für den Zugriffsmodus angeben, wird die Standardisolationsstufe verwendet, d. h. die Isolation von Mutationsabfragen. Daher ist es für schreibgeschützte Transaktionen wichtig, den Zugriffsmodus explizit auf `read` festzulegen.

**Beispiel für eine Lesetransaktion mit automatischem Commit:**

```
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()
}
```

**Beispiel für eine Lese-Transaktion:**

```
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)
    }
  }
);
```

In beiden Fällen wird die [`SNAPSHOT`-Isolation](transactions-isolation-levels.md) mithilfe der [Neptune-Nur-Lese-Transaktionssemantik](transactions-neptune.md#transactions-neptune-read-only) erreicht.

Da Lesereplikate nur schreibgeschützte Abfragen akzeptieren, wird jede Abfrage, die an ein Lesereplikat gesendet wird, mit `SNAPSHOT`-Isolationssemantik ausgeführt.

Für schreibgeschützte Transaktionen gibt es keine „Dirty Reads“ oder nicht wiederholbaren Lesevorgänge.

## Mutationstransaktionen
<a name="best-practices-opencypher-mutation-txs"></a>

Für Mutationsabfragen gibt es drei verschiedene Mechanismen, um eine Schreibtransaktion zu erstellen, die nachfolgend erläutert werden:

**Beispiel für eine implizite Schreibtransaktion:**

```
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)
    }
  }
);
```

**Beispiel für eine Auto-Commit-Schreibtransaktion:**

```
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()
}
```

**Beispiel für eine explizite Schreibtransaktion:**

```
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();
```

**Isolationsstufen für Schreibtransaktionen**
+ Lesevorgänge im Rahmen von Mutationsabfragen werden unter `READ COMMITTED`-Transaktionsisolation ausgeführt.
+ Für Lesevorgänge im Rahmen von Mutationsabfragen gibt es keine „Dirty Reads“.
+ Datensätze und Datensatzbereiche werden beim Lesen in einer Mutationsabfrage gesperrt.
+ Wenn ein Bereich des Indexes von einer Mutationstransaktion gelesen wurde, gibt es eine starke Garantie dafür, dass dieser Bereich bis zum Ende der Lesetransaktion nicht durch gleichzeitige Transaktionen geändert wird.

Mutationsabfragen sind nicht Thread-sicher.

Informationen zu Konflikten finden Sie unter [Konfliktlösung mithilfe von Sperrwartezeitüberschreitungen](transactions-neptune.md#transactions-neptune-conflicts).

Mutationsabfragen werden im Fehlerfall nicht automatisch wiederholt.

# Wiederholungslogik für Ausnahmen
<a name="best-practices-opencypher-retry-logic"></a>

Für alle Ausnahmen, die einen erneuten Versuch zulassen, wird generell empfohlen, eine [exponentielle Backoff- und Wiederholungsstrategie](https://docs.aws.amazon.com/general/latest/gr/api-retries.html) zu verwenden, die immer längere Wartezeiten zwischen den Wiederholungen vorsieht, um vorübergehende Probleme wie `ConcurrentModificationException`-Fehler besser behandeln zu können. Nachfolgend wird ein Beispiel für ein exponentielles Backoff- und Wiederholungsmuster gezeigt:

```
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);
  }
}
```

# Legen Sie mithilfe einer einzigen SET-Klausel mehrere Eigenschaften gleichzeitig fest
<a name="best-practices-content-0"></a>

 Anstatt mehrere SET-Klauseln zu verwenden, um einzelne Eigenschaften festzulegen, verwenden Sie eine Map, um mehrere Eigenschaften für eine Entität gleichzeitig festzulegen. 

 Sie können Folgendes verwenden: 

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

 Anstatt: 

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

 Die SET-Klausel akzeptiert entweder eine einzelne Eigenschaft oder eine Map. Wenn mehrere Eigenschaften einer einzelnen Entität aktualisiert werden, ermöglicht die Verwendung einer einzigen SET-Klausel mit einer Map, dass die Aktualisierungen in einem einzigen Vorgang statt in mehreren Vorgängen durchgeführt werden können, was effizienter ausgeführt werden kann. 

## Verwenden Sie die SET-Klausel, um mehrere Eigenschaften gleichzeitig zu entfernen
<a name="best-practices-content-1"></a>

 Wenn Sie die OpenCypher-Sprache verwenden, wird REMOVE verwendet, um Eigenschaften aus einer Entität zu entfernen. In Neptune erfordert jede Eigenschaft, die entfernt wird, eine separate Operation, wodurch die Abfragelatenz erhöht wird. Sie können SET stattdessen mit einer Map verwenden, um alle Eigenschaftswerte auf festzulegen`null`, was in Neptune dem Entfernen von Eigenschaften entspricht. Neptune wird eine höhere Leistung haben, wenn mehrere Eigenschaften auf einer einzelnen Entität entfernt werden müssen. 

Verwenden:

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

Anstatt:

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

# Parametrisierte Abfragen verwenden
<a name="best-practices-content-2"></a>

 Es wird empfohlen, bei Abfragen mit OpenCypher immer parametrisierte Abfragen zu verwenden. Die Abfrage-Engine kann wiederholte parametrisierte Abfragen für Funktionen wie den Abfrageplan-Cache nutzen, wobei durch wiederholtes Aufrufen derselben parametrisierten Struktur mit unterschiedlichen Parametern die zwischengespeicherten Pläne genutzt werden können. Der für parametrisierte Abfragen generierte Abfrageplan wird erst zwischengespeichert und wiederverwendet, wenn er innerhalb von 100 ms abgeschlossen ist und die Parametertypen entweder NUMBER, BOOLEAN oder STRING lauten. 

Verwenden:

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

Mit Parametern:

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

Anstatt:

```
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
```

# Verwenden Sie in der UNWIND-Klausel abgeflachte Maps anstelle von verschachtelten Maps
<a name="best-practices-content-3"></a>

 Eine tief verschachtelte Struktur kann die Fähigkeit der Abfrage-Engine einschränken, einen optimalen Abfrageplan zu generieren. Um dieses Problem teilweise zu beheben, werden anhand der folgenden definierten Muster optimale Pläne für die folgenden Szenarien erstellt: 
+  Szenario 1: UNWIND mit einer Liste von Chiffrierliteralen, die NUMBER, STRING und BOOLEAN umfasst. 
+  Szenario 2: UNWIND mit einer Liste von abgeflachten Zuordnungen, die nur Chiffrierliterale (NUMBER, STRING, BOOLEAN) als Werte enthält. 

 Verwenden Sie beim Schreiben einer Abfrage, die eine UNWIND-Klausel enthält, die obige Empfehlung, um die Leistung zu verbessern. 

Beispiel für Szenario 1:

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

Mit Parametern:

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

 Ein Beispiel für Szenario 2 ist die Generierung einer Liste von Knoten, die ERSTELLT oder ZUSAMMENGEFÜHRT werden sollen. Anstatt mehrere Anweisungen auszugeben, verwenden Sie das folgende Muster, um die Eigenschaften als eine Gruppe von abgeflachten Zuordnungen zu definieren: 

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

Mit Parametern:

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

Anstelle von verschachtelten Knotenobjekten wie:

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

Mit Parametern:

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

# Platzieren Sie restriktivere Knoten in VLP-Ausdrücken (Variable-Length Path) auf der linken Seite
<a name="best-practices-content-4"></a>

 Bei VLP-Abfragen (Variable-Length Path) optimiert die Abfrage-Engine die Auswertung, indem sie entscheidet, ob die Durchquerung auf der linken oder rechten Seite des Ausdrucks gestartet wird. Die Entscheidung basiert auf der Kardinalität der Muster auf der linken und rechten Seite. Die Kardinalität ist die Anzahl der Knoten, die dem angegebenen Muster entsprechen. 
+  Wenn das richtige Muster eine Kardinalität von eins hat, ist die rechte Seite der Ausgangspunkt. 
+  Wenn die linke und die rechte Seite die Kardinalität eins haben, wird die Expansion auf beiden Seiten geprüft und beginnt auf der Seite mit der kleineren Ausdehnung. Expansion ist die Anzahl der ausgehenden oder eingehenden Kanten für den Knoten auf der linken und den Knoten auf der rechten Seite des VLP-Ausdrucks. Dieser Teil der Optimierung wird nur verwendet, wenn die VLP-Beziehung unidirektional ist und der Beziehungstyp angegeben ist. 
+  Andernfalls wird die linke Seite der Ausgangspunkt sein. 

 Bei einer Kette von VLP-Ausdrücken kann diese Optimierung nur auf den ersten Ausdruck angewendet werden. Die anderen VLPs werden beginnend mit der linken Seite ausgewertet. Als Beispiel sei die Kardinalität von (a), (b) eins und die Kardinalität von (c) größer als eins. 
+  `(a)-[*1..]->(c)`: Die Bewertung beginnt mit (a). 
+  `(c)-[*1..]->(a)`: Die Bewertung beginnt mit (a). 
+  `(a)-[*1..]-(c)`: Die Bewertung beginnt mit (a). 
+  `(c)-[*1..]-(a)`: Die Bewertung beginnt mit (a). 

 Nun seien die eingehenden Kanten von (a) zwei und die ausgehenden Kanten von (a) drei, die eingehenden Kanten von (b) vier und die ausgehenden Kanten von (b) fünf. 
+  `(a)-[*1..]->(b)`: Die Auswertung beginnt mit (a), da die Ausgangskanten von (a) kleiner sind als die eingehenden Kanten von (b). 
+  `(a)<-[*1..]-(b)`: Die Auswertung beginnt mit (a), da die eingehenden Kanten von (a) kleiner sind als die ausgehenden Kanten von (b). 

 Als allgemeine Regel gilt: Platzieren Sie das restriktivere Muster auf der linken Seite eines VLP-Ausdrucks. 

# Vermeiden Sie redundante Prüfungen von Knotenbezeichnungen, indem Sie detaillierte Beziehungsnamen verwenden
<a name="best-practices-content-5"></a>

 Bei der Leistungsoptimierung ermöglicht die Verwendung von Beziehungsbezeichnungen, die ausschließlich für Knotenmuster gelten, den Labelfilter für Knoten zu entfernen. Stellen Sie sich ein Graphmodell vor, bei dem die Beziehung `likes` nur verwendet wird, um eine Beziehung zwischen zwei `person` Knoten zu definieren. Wir könnten die folgende Abfrage schreiben, um dieses Muster zu finden: 

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

 Die `person` Labelprüfung für n und m ist überflüssig, da wir die Beziehung so definiert haben, dass sie nur erscheint, wenn beide vom Typ sind`person`. Um die Leistung zu optimieren, können wir die Abfrage wie folgt schreiben: 

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

 Dieses Muster kann auch angewendet werden, wenn Eigenschaften nur für ein einzelnes Knotenlabel gelten. Gehen Sie davon aus, dass nur `person` Knoten über die Eigenschaft verfügen. `email` Daher `person` ist es überflüssig, zu überprüfen, ob die Knotenbezeichnungen übereinstimmen. Diese Abfrage schreiben als: 

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

 Ist weniger effizient als das Schreiben dieser Abfrage als: 

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

 Sie sollten dieses Muster nur anwenden, wenn Leistung wichtig ist und Sie in Ihrem Modellierungsprozess überprüft haben, um sicherzustellen, dass diese Kantenbeschriftungen nicht für Muster wiederverwendet werden, an denen andere Knotenbeschriftungen beteiligt sind. Wenn Sie später eine `email` Eigenschaft für eine andere Knotenbeschriftung hinzufügen`company`, z. B., dann unterscheiden sich die Ergebnisse zwischen diesen beiden Versionen der Abfrage. 

# Geben Sie nach Möglichkeit Kantenbeschriftungen an
<a name="best-practices-content-6"></a>

 Es wird empfohlen, bei der Angabe einer Kante in einem Muster nach Möglichkeit eine Kantenbeschriftung anzugeben. Stellen Sie sich die folgende Beispielabfrage vor, die verwendet wird, um alle in einer Stadt lebenden Personen mit allen Personen zu verknüpfen, die diese Stadt besucht haben. 

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

 Wenn Ihr Grafikmodell mithilfe mehrerer Kantenbeschriftungen Personen mit anderen Knoten als Städten verbindet, muss Neptune zusätzliche Pfade auswerten, die später verworfen werden, ohne die Endbeschriftung anzugeben. Da in der obigen Abfrage keine Kantenbeschriftung angegeben wurde, erledigt die Engine zunächst mehr Arbeit und filtert dann Werte heraus, um das richtige Ergebnis zu erhalten. Eine bessere Version der obigen Abfrage könnte sein: 

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

 Dies hilft nicht nur bei der Auswertung, sondern ermöglicht es dem Abfrageplaner auch, bessere Pläne zu erstellen. Sie könnten diese bewährte Methode sogar mit redundanten Prüfungen von Knotenbeschriftungen kombinieren, um die Prüfung der Ortsbeschriftung zu entfernen und die Abfrage wie folgt zu schreiben: 

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

# Vermeiden Sie nach Möglichkeit die WITH-Klausel
<a name="best-practices-content-7"></a>

 Die WITH-Klausel in OpenCypher dient als Grenze, an der alles vor der Ausführung ausgeführt wird. Anschließend werden die resultierenden Werte an die verbleibenden Teile der Abfrage übergeben. Die WITH-Klausel wird benötigt, wenn Sie eine Zwischenaggregation benötigen oder die Anzahl der Ergebnisse einschränken möchten. Abgesehen davon sollten Sie jedoch versuchen, die WITH-Klausel zu vermeiden. Die allgemeine Anleitung besteht darin, diese einfachen WITH-Klauseln (ohne Aggregation, Order by oder Limit) zu entfernen, damit der Abfrageplaner die gesamte Abfrage bearbeiten kann, um einen global optimalen Plan zu erstellen. Nehmen wir als Beispiel an, Sie haben eine Abfrage geschrieben, um alle Personen zurückzugeben, die in folgenden Ländern leben: `India` 

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

 In der obigen Version schränkt die WITH-Klausel die Platzierung des Musters `(city)-[:part_of]->(country {name: 'India'})` (was restriktiver ist) zuvor `(person)-[:lives_in]->(city)` ein. Das macht den Plan suboptimal. Eine Optimierung dieser Abfrage bestünde darin, die WITH-Klausel zu entfernen und den Planer den besten Plan berechnen zu lassen. 

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

# Platzieren Sie restriktive Filter so früh wie möglich in der Abfrage
<a name="best-practices-content-8"></a>

 In allen Szenarien trägt die frühzeitige Platzierung von Filtern in der Abfrage dazu bei, die Anzahl der Zwischenlösungen zu reduzieren, die ein Abfrageplan berücksichtigen muss. Das bedeutet, dass weniger Speicher und weniger Rechenressourcen für die Ausführung der Abfrage benötigt werden. 

 Das folgende Beispiel hilft Ihnen, diese Auswirkungen zu verstehen. Angenommen, Sie schreiben eine Abfrage, um alle Personen zurückzugeben, die in`India`. Eine Version der Abfrage könnte sein: 

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

 Die obige Version der Abfrage ist nicht der optimale Weg, um diesen Anwendungsfall zu erreichen. Der Filter `country.name = 'India'` erscheint später im Abfragemuster. Er erfasst zunächst alle Personen und ihren Wohnort und gruppiert sie nach Ländern. Anschließend wird nur nach der Gruppe für gefiltert`country.name = India`. Die optimale Methode, nur nach Personen abzufragen, die in diesem Land leben, `India` und dann die Sammelaggregation durchzuführen. 

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

 Eine allgemeine Regel besteht darin, einen Filter so bald wie möglich nach der Einführung der Variablen zu platzieren. 

# Prüfen Sie explizit, ob Eigenschaften vorhanden sind
<a name="best-practices-content-9"></a>

 Basierend auf der OpenCypher-Semantik entspricht der Zugriff auf eine Eigenschaft einem optionalen Join und muss alle Zeilen beibehalten, auch wenn die Eigenschaft nicht existiert. Wenn Sie anhand Ihres Diagrammschemas wissen, dass eine bestimmte Eigenschaft für diese Entität immer existieren wird, kann die Abfrage-Engine durch die explizite Überprüfung dieser Eigenschaft optimale Pläne erstellen und die Leistung verbessern. 

 Stellen Sie sich ein Graphmodell vor, bei dem Typknoten `person` immer über eine Eigenschaft verfügen`name`. Anstatt das zu tun: 

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

 Überprüfen Sie das Vorhandensein einer Eigenschaft in der Abfrage explizit mit einer IS-NOT-NULL-Prüfung: 

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

# Verwenden Sie keinen benannten Pfad (es sei denn, er ist erforderlich)
<a name="best-practices-content-10"></a>

 Ein benannter Pfad in einer Abfrage ist immer mit zusätzlichen Kosten verbunden, was zu zusätzlichen Kosten in Form einer höheren Latenz und Speicherauslastung führen kann. Betrachten Sie folgende Abfrage: 

```
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
```

 In der obigen Abfrage ist die Verwendung des Pfads „p“ unnötig, vorausgesetzt, wir wollen nur die Eigenschaften der Knoten kennen. Durch die Angabe des benannten Pfads als Variable wird die Aggregationsoperation mit DISTINCT sowohl in Bezug auf Zeit als auch in Bezug auf den Speicherverbrauch teuer. Eine optimiertere Version der obigen Abfrage könnte sein: 

```
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
```

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

**Anmerkung**  
Ab Engine-Version [1.4.7.0](engine-releases-1.4.7.0.md) ist dieses empfohlene Umschreiben nicht mehr erforderlich.

 COLLECT (DISTINCT ()) wird immer dann verwendet, wenn eine Liste mit unterschiedlichen Werten gebildet werden soll. COLLECT ist eine Aggregationsfunktion, und die Gruppierung erfolgt auf der Grundlage zusätzlicher Schlüssel, die in derselben Anweisung projiziert werden. Wenn distinct verwendet wird, wird die Eingabe in mehrere Blöcke aufgeteilt, wobei jeder Abschnitt eine Gruppe bezeichnet, die reduziert werden soll. Die Leistung wird beeinträchtigt, wenn die Anzahl der Gruppen zunimmt. In Neptune ist es viel effizienter, DISTINCT vor collecting/forming der eigentlichen Liste auszuführen. Auf diese Weise kann die Gruppierung direkt auf den Gruppierungstasten für den gesamten Block erfolgen. 

 Betrachten Sie folgende Abfrage: 

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

 Eine optimalere Art, diese Abfrage zu schreiben, ist: 

```
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
```

# Ziehen Sie beim Abrufen aller Eigenschaftswerte die Eigenschaftenfunktion der Suche nach einzelnen Eigenschaften vor
<a name="best-practices-content-12"></a>

 Die `properties()` Funktion wird verwendet, um eine Map zurückzugeben, die alle Eigenschaften für eine Entität enthält, und ist viel effizienter als die individuelle Rückgabe von Eigenschaften. 

 Angenommen, Ihre `Person` Knoten enthalten 5 Eigenschaften,`firstName`,`lastName`, und `age``dept`,, und`company`, dann wäre die folgende Abfrage vorzuziehen: 

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

 Anstatt zu verwenden: 

```
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
```

# Führen Sie statische Berechnungen außerhalb der Abfrage durch
<a name="best-practices-content-13"></a>

 Es wird empfohlen, statische Berechnungen (einfache mathematical/string Operationen) auf der Clientseite aufzulösen. Stellen Sie sich dieses Beispiel vor, bei dem Sie alle Personen suchen möchten, die ein Jahr oder jünger als der Autor sind: 

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

 Hier `$age` wird es über Parameter in die Abfrage eingefügt und dann zu einem festen Wert hinzugefügt. Dieser Wert wird dann mit verglichen`p.age`. Stattdessen wäre es besser, die Addition auf der Client-Seite vorzunehmen und den berechneten Wert als Parameter \$1ageplusone zu übergeben. Dies hilft der Abfrage-Engine, optimierte Pläne zu erstellen, und vermeidet statische Berechnungen für jede eingehende Zeile. Nach diesen Richtlinien wäre eine effizientere Version der Abfrage wie folgt: 

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

# Batch-Eingaben mit UNWIND anstelle von Einzelanweisungen
<a name="best-practices-content-14"></a>

 Wenn dieselbe Abfrage für verschiedene Eingaben ausgeführt werden muss, wäre es viel leistungsfähiger, eine Abfrage für einen Stapel von Eingaben auszuführen, anstatt eine Abfrage pro Eingabe auszuführen. 

 Wenn Sie auf einer Gruppe von Knoten zusammenführen möchten, besteht eine Option darin, eine Zusammenführungsabfrage pro Eingabe auszuführen: 

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

 Mit Parametern: 

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

 Die obige Abfrage muss für jede Eingabe ausgeführt werden. Dieser Ansatz funktioniert zwar, erfordert jedoch möglicherweise die Ausführung vieler Abfragen für einen großen Satz von Eingaben. In diesem Szenario kann Batching dazu beitragen, die Anzahl der auf dem Server ausgeführten Abfragen zu reduzieren und den Gesamtdurchsatz zu verbessern. 

 Verwenden Sie das folgende Muster: 

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

 Mit Parametern: 

```
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'}...]}
```

 Es wird empfohlen, mit verschiedenen Chargengrößen zu experimentieren, um herauszufinden, was für Ihren Workload am besten geeignet ist. 

# Verwenden Sie lieber „Benutzerdefiniert“ IDs für Knoten/Beziehungen
<a name="best-practices-content-15"></a>

 Neptune ermöglicht es Benutzern, Knoten und Beziehungen explizit zuzuweisen IDs . Die ID muss im Datensatz global eindeutig und deterministisch sein, um nützlich zu sein. Eine deterministische ID kann genau wie Eigenschaften als Such- oder Filtermechanismus verwendet werden. Die Verwendung einer ID ist jedoch aus Sicht der Abfrageausführung viel optimierter als die Verwendung von Eigenschaften. Die Verwendung von benutzerdefinierten IDs Funktionen bietet mehrere Vorteile - 
+  Eigenschaften können für eine bestehende Entität Null sein, aber die ID muss existieren. Dadurch kann die Abfrage-Engine während der Ausführung einen optimierten Join verwenden. 
+  Wenn Abfragen mit gleichzeitigen Mutationen ausgeführt werden, verringert sich die Wahrscheinlichkeit, dass [Ausnahmen (CMEs) gleichzeitig geändert](https://docs.aws.amazon.com//neptune/latest/userguide/transactions-exceptions.html) werden, wenn sie für den Zugriff auf Knoten verwendet IDs werden, erheblich, da aufgrund ihrer erzwungenen Eindeutigkeit weniger Sperren IDs als Eigenschaften übernommen werden. 
+  Durch die Verwendung IDs wird die Möglichkeit vermieden, dass doppelte Daten erstellt werden, da Neptune im Gegensatz zu Eigenschaften die Einzigartigkeit erzwingt. IDs 

 Im folgenden Abfragebeispiel wird eine benutzerdefinierte ID verwendet: 

**Anmerkung**  
 Die Eigenschaft `~id` wird verwendet, um die ID anzugeben, während sie einfach wie jede andere Eigenschaft gespeichert `id` wird. 

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

 Ohne eine benutzerdefinierte ID zu verwenden: 

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

 Wenn Sie den letztgenannten Mechanismus verwenden, wird die Eindeutigkeit nicht erzwungen und Sie könnten die Abfrage später ausführen: 

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

 Dadurch wird ein zweiter Knoten mit `id=1` named `john` erstellt. In diesem Szenario hätten Sie jetzt zwei Knoten mit `id=1` jeweils einem anderen Namen (alice und john). 

# Vermeiden Sie es, \$1id-Berechnungen in der Abfrage durchzuführen
<a name="best-practices-content-16"></a>

 Wenn Sie „custom“ IDs in den Abfragen verwenden, führen Sie immer statische Berechnungen außerhalb der Abfragen durch und geben Sie diese Werte in den Parametern an. Wenn statische Werte bereitgestellt werden, ist die Engine besser in der Lage, Suchvorgänge zu optimieren und zu vermeiden, dass diese Werte gescannt und gefiltert werden. 

 Wenn Sie Kanten zwischen Knoten erstellen möchten, die in der Datenbank vorhanden sind, könnte eine Option wie folgt aussehen: 

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

 Mit Parametern: 

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

 In der obigen Abfrage wird der `id` Wert des Abschnitts in der Abfrage berechnet. Da die Berechnung dynamisch ist, kann die Engine IDs nicht statisch einbinden und scannt am Ende alle Abschnittsknoten. Die Engine führt dann eine Nachfilterung nach den erforderlichen Knoten durch. Dies kann kostspielig sein, wenn die Datenbank viele Abschnittsknoten enthält. 

 Ein besserer Weg, dies zu erreichen, besteht darin, die IDs, die `Sec-` an die Datenbank übergeben werden, voranzustellen: 

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

 Mit Parametern: 

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

# Aktualisieren/Zusammenführen mehrerer Knoten
<a name="best-practices-merge-multiple-nodes"></a>

 Beim Ausführen `MERGE` von `CREATE` Abfragen auf mehreren Knoten wird empfohlen, eine `UNWIND` in Kombination mit einer einzelnen MERGE/CREATE Klausel zu verwenden, anstatt eine Klausel für jeden Knoten zu verwenden. MERGE/CREATE Abfragen, die eine Klausel für einen Knoten verwenden, führen zu einem ineffizienten Ausführungsplan, da jede Zeile optimiert werden muss. Dies führt dazu, dass der Großteil der Ausführungszeit der Abfrage für die statische Verarbeitung und nicht für die eigentliche Aktualisierung aufgewendet wird. 

 Eine Klausel pro Knoten ist nicht optimal, da sie nicht mit zunehmender Anzahl von Knoten skaliert: 

```
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'}
```

 Die Verwendung einer `UNWIND` in Verbindung mit einer MERGE/CREATE Klausel ermöglicht dasselbe Verhalten, aber einen optimaleren Ausführungsplan. Vor diesem Hintergrund würde die geänderte Abfrage wie folgt aussehen: 

```
## 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')
```