

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Consulta de un gráfico de Neptune
<a name="access-graph-queries"></a>

Neptune admite los siguientes lenguajes de consulta de gráficos para obtener acceso a un gráfico:
+ [Gremlin](https://tinkerpop.apache.org/gremlin.html), definido por [Apache TinkerPop](https://tinkerpop.apache.org/) para crear y consultar gráficos de propiedades.

  En Gremlin, una consulta es un recorrido compuesto por pasos discretos, cada uno de los cuales sigue un borde hasta un nodo.

  Consulte [Acceso al gráfico de Neptune con Gremlin](access-graph-gremlin.md) para obtener información sobre el uso de Gremlin en Neptune y [Conformidad con los estándares de Gremlin en Amazon Neptune](access-graph-gremlin-differences.md) para encontrar detalles específicos sobre la implementación de Gremlin en Neptune.
+ [openCypher](access-graph-opencypher.md) es un lenguaje de consulta declarativo para gráficos de propiedades que desarrolló originalmente Neo4j, luego de código abierto en 2015, y que contribuyó al proyecto [openCypher](http://www.opencypher.org/) en virtud de una licencia de código abierto Apache 2. Su sintaxis está documentada en la [especificación de OpenCypher](https://s3.amazonaws.com/artifacts.opencypher.org/openCypher9.pdf).
+ [SPARQL](https://www.w3.org/TR/sparql11-overview/) es un lenguaje declarativo basado en la coincidencia de patrones de gráficos para consultar datos [RDF](https://www.w3.org/2001/sw/wiki/RDF). Cuenta con el apoyo del [World Wide Web Consortium](https://www.w3.org/).

  Consulte [Acceso al gráfico de Neptune con SPARQL](access-graph-sparql.md) para obtener información sobre el uso de SPARQL en Neptune y [Conformidad con los estándares de SPARQL en Amazon Neptune](feature-sparql-compliance.md) para encontrar detalles específicos sobre la implementación de SPARQL en Neptune.

**nota**  
Tanto Gremlin como openCypher se pueden usar para consultar cualquier dato de gráfico de propiedades almacenado en Neptune, independientemente de cómo se haya cargado.

**Topics**
+ [Colas de consultas en Amazon Neptune](access-graph-queuing.md)
+ [Memoria caché de planes de consultas en Amazon Neptune](access-graph-qpc.md)
+ [Inserte un identificador personalizado en una consulta de Neptune Gremlin o SPARQL](features-query-id.md)
+ [Acceso al gráfico de Neptune con Gremlin](access-graph-gremlin.md)
+ [Acceso al gráfico de Neptune con openCypher](access-graph-opencypher.md)
+ [Acceso al gráfico de Neptune con SPARQL](access-graph-sparql.md)

# Colas de consultas en Amazon Neptune
<a name="access-graph-queuing"></a>

Al desarrollar y ajustar aplicaciones gráficas, puede ser útil conocer las implicaciones de cómo la base de datos está colocando en cola las consultas. En Amazon Neptune, la cola de consultas se produce de la siguiente manera:
+ El número máximo de consultas que se pueden colocar en cola por instancia, con independencia del tamaño de instancia, es 8.192. Cualquier consulta superior a este número se rechaza y se genera un error con una `ThrottlingException`.
+ El número máximo de consultas que se pueden ejecutar a la vez viene determinado por el número de subprocesos de trabajo asignados, que generalmente se establece en el doble del número de núcleos de CPU virtuales (vCPUs) disponibles.
+ La latencia de consulta incluye el tiempo que una consulta pasa en la cola, así como los viajes de ida y vuelta a la red y el tiempo que realmente tarda en ejecutarse.

## Determinación del número de consultas en la cola en un momento dado
<a name="access-graph-queuing-count"></a>

La `MainRequestQueuePendingRequests` CloudWatch métrica registra el número de solicitudes en espera en la cola de entrada con una granularidad de cinco minutos (consulte). [Métricas de Neptune CloudWatch](cw-metrics.md)

Para Gremlin, puede obtener un recuento actual de consultas en la cola utilizando el valor `acceptedQueryCount` devuelto por el [API del estado de la consulta de Gremlin](gremlin-api-status.md). Tenga en cuenta, sin embargo, que el valor `acceptedQueryCount` devuelto por el [API de estado de la consulta SPARQL](sparql-api-status.md) incluye todas las consultas aceptadas desde que se inició el servidor, incluidas las consultas completadas.

## Cómo puede afectar a los tiempos de espera la cola de consultas
<a name="access-graph-queuing-timeouts"></a>

Como se señaló anteriormente, la latencia de consulta incluye el tiempo que pasa una consulta en la cola, así como el tiempo que tarda en ejecutarse.

Dado que el período de tiempo de espera de una consulta generalmente se mide a partir del momento en que entra en la cola, una cola con movimiento lento puede hacer que muchas consultas agoten el tiempo de espera en cuanto se eliminan de la cola. Obviamente, esto no es deseable, por lo que es bueno evitar poner en cola un gran número de consultas a menos que se puedan ejecutar rápidamente.

# Memoria caché de planes de consultas en Amazon Neptune
<a name="access-graph-qpc"></a>

 Cuando se envía una consulta a Neptune, la cadena de consulta se analiza, optimiza y transforma en un plan de consulta que, después, el motor ejecuta. Las aplicaciones suelen estar respaldadas por patrones de consulta comunes para los que se crean instancias con diferentes valores. La caché de planes de consultas puede reducir la latencia general al almacenar en caché los planes de consulta y, por lo tanto, evitar el análisis y la optimización de dichos patrones repetidos. 

 La memoria caché del plan de consultas se puede utilizar para **OpenCypher**consultas, tanto no parametrizadas como parametrizadas. Está habilitada para READ, HTTP y Bolt. **No** es compatible con consultas de mutación de OC. **No** es compatible con consultas de Gremlin o SPARQL. 

## Cómo forzar la habilitación o deshabilitación de la caché de planes de consultas
<a name="access-graph-qpc-enable"></a>

 La caché de planes de consultas está habilitada de forma predeterminada para consultas parametrizadas de baja latencia. El plan de una consulta parametrizada se almacena en caché solo cuando la latencia es inferior al umbral de **100 ms**. La sugerencia de consulta `QUERY:PLANCACHE` en el ámbito de la consulta permite anular este comportamiento en cada consulta (parametrizada o no). Debe usarse con la cláusula `USING`. La sugerencia de consulta acepta como valor `enabled` o `disabled`. 

```
# Forcing plan to be cached or reused
% curl -k https://<endpoint>:<port>/opencypher \
  -d "query=Using QUERY:PLANCACHE \"enabled\" MATCH(n) RETURN n LIMIT 1"
  
% curl -k https://<endpoint>:<port>/opencypher \
  -d "query=Using QUERY:PLANCACHE \"enabled\" RETURN \$arg" \
  -d "parameters={\"arg\": 123}"
  
# Forcing plan to be neither cached nor reused
% curl -k https://<endpoint>:<port>/opencypher \
  -d "query=Using QUERY:PLANCACHE \"disabled\" MATCH(n) RETURN n LIMIT 1"
```

## Cómo determinar si un plan está almacenado en caché o no
<a name="access-graph-qpc-status"></a>

 En el caso de HTTP READ, si la consulta se ha enviado y el plan se ha almacenado en caché, `explain` mostraría detalles relevantes sobre la caché de planes de consultas. 

```
% curl -k https://<endpoint>:<port>/opencypher \
  -d "query=Using QUERY:PLANCACHE \"enabled\" MATCH(n) RETURN n LIMIT 1" \
  -d "explain=[static|details]"

Query: <QUERY STRING>
Plan cached by request: <REQUEST ID OF FIRST TIME EXECUTION>
Plan cached at: <TIMESTAMP OF FIRST TIME EXECUTION>
Parameters: <PARAMETERS, IF QUERY IS PARAMETERIZED QUERY>
Plan cache hits: <NUMBER OF CACHE HITS FOR CACHED PLAN>
First query evaluation time: <LATENCY OF FIRST TIME EXECUTION>

The query has been executed based on a cached query plan. Detailed explain with operator runtime statistics can be obtained by running the query with plan cache disabled (using HTTP parameter planCache=disabled).
```

 Cuando se utiliza Bolt, no se admite la característica explain. 

## Expulsión
<a name="access-graph-qpc-eviction"></a>

 Un plan de consultas se expulsa por el tiempo de vida (TTL) de la caché o cuando se alcanza el número máximo de planes de consulta almacenados en caché. Cuando se alcanza el plan de consultas, se actualiza el TTL. Los valores predeterminados son: 
+  1000: el número máximo de planes que se pueden almacenar en caché por instancia. 
+  TTL: 300 000 milisegundos o 5 minutos. El acierto de la caché reinicia el TTL y lo restablece a 5 minutos. 

## Condiciones que impiden que el plan se almacene en caché
<a name="access-graph-qpc-conditions"></a>

 La caché de planes de consultas no se utilizará en las siguientes condiciones: 

1.  Cuando se envía una consulta con la sugerencia de consulta `QUERY:PLANCACHE "disabled"`. Puede volver a ejecutar la consulta y eliminar `QUERY:PLANCACHE "disabled"` para habilitar la caché de planes de consultas. 

1.  Si la consulta enviada no es una consulta parametrizada y no contiene la sugerencia `QUERY:PLANCACHE "enabled"`. 

1.  Si el tiempo de evaluación de la consulta es superior al umbral de latencia, la consulta no se almacena en caché y se considera una consulta de larga duración que no se beneficiará de la caché de planes de consultas. 

1.  Si la consulta contiene un patrón que no devuelve ningún resultado. 
   +  Por ejemplo, `MATCH (n:nonexistentLabel) return n` cuando no hay nodos con la etiqueta especificada. 
   +  Por ejemplo, `MATCH (n {name: $param}) return n` con `parameters={"param": "abcde"}` cuando no hay nodos que contengan `name=abcde`. 

1.  Si el parámetro de consulta es de tipo compuesto, como `list` o `map`. 

   ```
   curl -k https://<endpoint>:<port>/opencypher \
     -d "query=Using QUERY:PLANCACHE \"enabled\" RETURN \$arg" \
     -d "parameters={\"arg\": [1, 2, 3]}"
   
   curl -k https://<endpoint>:<port>/opencypher \
     -d "query=Using QUERY:PLANCACHE \"enabled\" RETURN \$arg" \
     -d "parameters={\"arg\": {\"a\": 1}}"
   ```

1.  Si el parámetro de consulta es una cadena que no ha formado parte de una operación de carga o inserción de datos. Por ejemplo, si se ejecuta `CREATE (n {name: "X"})` para insertar `"X"`, entonces `RETURN "X"` se almacena en caché, mientras que `RETURN "Y"` no se almacenará en caché, ya que `"Y"` no se ha insertado y no existe en la base de datos. 

# Inserte un identificador personalizado en una consulta de Neptune Gremlin o SPARQL
<a name="features-query-id"></a>

De forma predeterminada, Neptune asigna un valor `queryId` único a cada consulta. Puede utilizar este ID para obtener información sobre una consulta en ejecución (consulte [API del estado de la consulta de Gremlin](gremlin-api-status.md) o [API de estado de la consulta SPARQL](sparql-api-status.md)) o cancelarla (consulte [Cancelación de consultas de Gremlin](gremlin-api-status-cancel.md) o [Cancelación de consultas SPARQL](sparql-api-status-cancel.md)).

Neptune también le permite especificar su propio valor `queryId` para una consulta de Gremlin o SPARQL, ya sea en el encabezado HTTP o para una consulta SPARQL mediante la sugerencia de la consulta `queryId`. La asignación de su propio `queryID` facilita la realización de un seguimiento de una consulta para obtener el estado o cancelarlo.

## Inserción de un valor `queryId` personalizado mediante el encabezado HTTP
<a name="features-query-id-header"></a>

Tanto para Gremlin como para SPARQL, el encabezado HTTP se puede utilizar para insertar su propio valor `queryId` en una consulta.

**Ejemplo de Gremlin**

```
curl -XPOST https://your-neptune-endpoint:port \
    -d "{\"gremlin\": \
        \"g.V().limit(1).count()\" , \
        \"queryId\":\"4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47\"  }"
```

**Ejemplo de SPARQL**

```
curl https://your-neptune-endpoint:port/sparql \
    -d "query=SELECT * WHERE { ?s ?p ?o } " \
       --data-urlencode \
       "queryId=4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47"
```

## Inserción de un valor `queryId` personalizado mediante una sugerencia de consulta SPARQL
<a name="features-query-id-hint"></a>

A continuación se muestra un ejemplo de cómo utilizaría la sugerencia de la consulta `queryId` SPARQL para insertar un valor `queryId` personalizado en una consulta SPARQL:

```
curl https://your-neptune-endpoint:port/sparql \
    -d "query=PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#> \
       SELECT * WHERE { hint:Query hint:queryId \"4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47\" \
       {?s ?p ?o}}"
```

## Uso del valor `queryId` para comprobar el estado de la consulta
<a name="features-query-id-check-status"></a>

**Ejemplo de Gremlin**

```
curl https://your-neptune-endpoint:port/gremlin/status \
    -d "queryId=4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47"
```

**Ejemplo de SPARQL**

```
curl https://your-neptune-endpoint:port/sparql/status \
    -d "queryId=4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47"
```

# Acceso al gráfico de Neptune con Gremlin
<a name="access-graph-gremlin"></a>

Amazon Neptune es compatible con Apache TinkerPop y Gremlin. Esto significa que puede conectarse a una instancia de base de datos de Neptune y utilizar el lenguaje transversal Gremlin para consultar el gráfico (consulte The Graph en [la](https://tinkerpop.apache.org/docs/current/reference/#graph) documentación de Apache). TinkerPop Para conocer las diferencias en la implementación de Gremlin, consulte [Conformidad con los estándares de Gremlin](access-graph-gremlin-differences.md).

 Un *recorrido* en Gremlin es una serie de pasos encadenados. Comienza en un vértice (o borde). Recorre el gráfico siguiendo el borde de salida de cada vértice y, después, los bordes resultantes de todos ellos. Cada paso es una operación del recorrido. Para obtener más información, consulte [The Traversal en la documentación](https://tinkerpop.apache.org/docs/current/reference/#traversal). TinkerPop 

Las diferentes versiones del motor de Neptune admiten diferentes versiones de Gremlin. Consulte la [página de versiones del motor](engine-releases.md) de la versión de Neptuno que esté utilizando para determinar qué versión de Gremlin admite o consulte la siguiente tabla, que enumera las versiones anteriores y más recientes TinkerPop compatibles con las diferentes versiones del motor de Neptuno:


| Versión del motor de Neptune | Versión mínima TinkerPop  |  TinkerPop Versión máxima | 
| --- | --- | --- | 
| `1.3.2.0 and newer` | `3.7.1` | `3.7.3` | 
| `1.3.1.0` | `3.6.2` | `3.6.5` | 
| `1.3.0.0` | `3.6.2` | `3.6.4` | 
| `1.2.1.0 <= 1.2.1.2` | `3.6.2` | `3.6.2` | 
| `1.1.1.0 <= 1.2.0.2` | `3.5.5` | `3.5.6` | 
| `1.1.0.0 and older` | `(deprecated)` | `(deprecated)` | 

TinkerPop Los clientes suelen ser compatibles con versiones anteriores dentro de una serie (`3.6.x`por ejemplo, o`3.7.x`) y, si bien suelen funcionar más allá de esos límites, en la tabla anterior se recomiendan las combinaciones de versiones que se deben utilizar para obtener la mejor experiencia y compatibilidad posibles. A menos que se indique lo contrario, generalmente es mejor seguir estas pautas y actualizar las aplicaciones cliente para que coincidan con la versión TinkerPop que esté utilizando.

Al actualizar TinkerPop las versiones, siempre es importante consultar la [documentación TinkerPop de actualización](http://tinkerpop.apache.org/docs/current/upgrade/), que le ayudará a identificar las nuevas funciones que puede aprovechar, pero también los problemas que quizás deba tener en cuenta a medida que se acerca la actualización. Lo normal es que las consultas y funciones existentes funcionen después de la actualización, a menos que se indique algo en particular como cuestión a tener en cuenta. Por último, es importante tener en cuenta que si una versión que actualice para incluir una nueva función, es posible que no pueda utilizarla si es de una versión posterior a la que admite Neptune.

Existen variantes del lenguaje Gremlin y compatibilidad con el acceso a Gremlin en varios lenguajes de programación. Para obtener más información, consulte [Sobre las variantes del lenguaje Gremlin](https://tinkerpop.apache.org/docs/current/reference/#gremlin-drivers-variants) en la documentación. TinkerPop 

Esta documentación describe cómo acceder a Neptune con las siguientes variantes y lenguajes de programación:
+ [Configure la consola de Gremlin para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-console.md)
+ [Uso del punto de conexión HTTP REST para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-rest.md)
+ [Clientes Gremlin basados en Java que se utilizan con Amazon Neptune](access-graph-gremlin-client.md)
+ [Uso de Python para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-python.md)
+ [Uso de .NET para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-dotnet.md)
+ [Uso de Node.js para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-node-js.md)
+ [Uso de Go para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-go.md)

Como se explica en[Cifrado de conexiones a la base de datos de Amazon Neptune con SSL/HTTPS](security-ssl.md), debe utilizar la capa de Security/Secure sockets de capa de transporte (TLS/SSL) cuando se conecte a Neptune en todas las regiones. AWS 

Antes de comenzar, debe disponer de lo siguiente:
+ Una instancia de base de datos de Neptune. Para obtener información acerca de la creación de una instancia de base de datos de Neptune, consulte [Creación de un clúster de Amazon Neptune](get-started-create-cluster.md).
+ Una instancia de Amazon EC2 en la misma nube privada virtual (VPC) que su instancia de base de datos de Neptune.

Para obtener más información acerca de cómo cargar datos en Neptune, incluidos los requisitos previos, los formatos de carga y los parámetros de carga, consulte [Carga de datos en Amazon Neptune](load-data.md).

**Topics**
+ [Configure la consola de Gremlin para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-console.md)
+ [Uso del punto de conexión HTTP REST para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-rest.md)
+ [Clientes Gremlin basados en Java que se utilizan con Amazon Neptune](access-graph-gremlin-client.md)
+ [Uso de Python para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-python.md)
+ [Uso de .NET para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-dotnet.md)
+ [Uso de Node.js para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-node-js.md)
+ [Uso de Go para conectarse a una instancia de base de datos de Neptune](access-graph-gremlin-go.md)
+ [Uso del AWS SDK para ejecutar consultas de Gremlin](access-graph-gremlin-sdk.md)
+ [Sugerencias de consulta de Gremlin](gremlin-query-hints.md)
+ [API del estado de la consulta de Gremlin](gremlin-api-status.md)
+ [Cancelación de consultas de Gremlin](gremlin-api-status-cancel.md)
+ [Compatibilidad con sesiones basadas en scripts de Gremlin](access-graph-gremlin-sessions.md)
+ [Transacciones de Gremlin en Neptune](access-graph-gremlin-transactions.md)
+ [Uso de la API de Gremlin con Amazon Neptune](gremlin-api-reference.md)
+ [Almacenamiento en caché de los resultados de las consultas en Gremlin de Amazon Neptune](gremlin-results-cache.md)
+ [Realización de actualizaciones o inserciones eficientes con los pasos `mergeV()` y `mergeE()` de Gremlin](gremlin-efficient-upserts.md)
+ [Realización de actualizaciones o inserciones de Gremlin eficientes con `fold()/coalesce()/unfold()`](gremlin-efficient-upserts-pre-3.6.md)
+ [Análisis de la ejecución de las consultas de Neptune con `explain` de Gremlin](gremlin-explain.md)
+ [Uso de Gremlin con el motor de consultas DFE de Neptune](gremlin-with-dfe.md)

# Configure la consola de Gremlin para conectarse a una instancia de base de datos de Neptune
<a name="access-graph-gremlin-console"></a>

La consola Gremlin le permite experimentar con TinkerPop gráficos y consultas en un entorno REPL (bucle). read-eval-print 

## Instalación de la consola de Gremlin y conexión a ella de la forma habitual
<a name="access-graph-gremlin-console-usual-connect"></a>

Puede utilizar dicha consola para conectarse a una base de datos de gráficos remota. En la siguiente sección, se explica la instalación y la configuración de la consola de Gremlin para conectarse a una instancia de base de datos de Neptune de forma remota. Siga estas instrucciones desde una instancia de Amazon EC2 que esté en la misma nube privada virtual (VPC) que su instancia de base de datos de Neptune.

Para obtener ayuda para conectarse a Neptune con SSL/TLS (obligatorio), consulte. [Configuración de SSL/TLS](access-graph-gremlin-java.md#access-graph-gremlin-java-ssl)

**nota**  
Si tiene [habilitada la autenticación de IAM](iam-auth-enable.md) en su clúster de base de datos de Neptune, siga en [Conexión a bases de datos de Amazon Neptune mediante la autenticación de IAM con la consola de Gremlin](iam-auth-connecting-gremlin-console.md) las instrucciones para instalar la consola de Gremlin en lugar de las instrucciones que aparecen aquí.

**Para instalar la consola de Gremlin y conectarse a Neptune**

1. Los archivos binarios de la consola de Gremlin requieren Java 8 o Java 11. En estas instrucciones se presupone el uso de Java 11. Puede instalar Java 11 en su instancia de EC2 de la siguiente manera:
   + Si utilizas [Amazon Linux 2 (AL2)](https://aws.amazon.com/amazon-linux-2):

     ```
     sudo amazon-linux-extras install java-openjdk11
     ```
   + Si utilizas [Amazon Linux 2023 (AL2023)](https://docs.aws.amazon.com/linux/al2023/ug/what-is-amazon-linux.html):

     ```
     sudo yum install java-11-amazon-corretto-devel
     ```
   + Para otras distribuciones, utilice la que sea adecuada de las siguientes opciones:

     ```
     sudo yum install java-11-openjdk-devel
     ```

     o bien:

     ```
     sudo apt-get install openjdk-11-jdk
     ```

1. Escriba lo siguiente para establecer Java 11 como tiempo de ejecución predeterminado en la instancia EC2.

   ```
   sudo /usr/sbin/alternatives --config java
   ```

   Cuando se le solicite, escriba el número para Java 11.

1. Descargue la versión adecuada de la consola Gremlin del sitio web de Apache. Puedes comprobar [Acceso al gráfico de Neptune con Gremlin](access-graph-gremlin.md) qué versión de Gremlin es compatible con tu versión de Neptune. Por ejemplo, si necesita la versión 3.7.2, puede descargar la [consola Gremlin](https://archive.apache.org/dist/tinkerpop/3.7.2/apache-tinkerpop-gremlin-console-3.7.2-bin.zip) del sitio web de [Apache Tinkerpop](https://tinkerpop.apache.org/download.html) a su instancia EC2 de la siguiente manera:

   ```
   wget https://archive.apache.org/dist/tinkerpop/3.7.2/apache-tinkerpop-gremlin-console-3.7.2-bin.zip
   ```

1. Descomprima el archivo .zip de la consola de Gremlin.

   ```
   unzip apache-tinkerpop-gremlin-console-3.7.2-bin.zip
   ```

1. Cambie al directorio del directorio descomprimido.

   ```
   cd apache-tinkerpop-gremlin-console-3.7.2
   ```

1. En el subdirectorio `conf` del directorio extraído, cree un archivo llamado `neptune-remote.yaml` con el texto siguiente. *your-neptune-endpoint*Sustitúyalo por el nombre de host o la dirección IP de la instancia de base de datos de Neptune. Los corchetes (`[ ]`) son obligatorios.
**nota**  
Para obtener información acerca de cómo encontrar el nombre de host de la instancia de base de datos de Neptune, consulte la sección [Conexión a los puntos de conexión de Amazon Neptune](feature-overview-endpoints.md).

   ```
   hosts: [your-neptune-endpoint]
   port: 8182
   connectionPool: { enableSsl: true }
   serializer: { className: org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1,
                 config: { serializeResultToString: true }}
   ```
**nota**  
 Los serializadores se trasladaron del módulo `gremlin-driver` al nuevo módulo `gremlin-util` de la versión 3.7.0. El paquete cambió de org.apache.tinkerpop.gremlin.driver.ser a org.apache.tinkerpop.gremlin.util.ser. 

1. En un terminal, vaya al directorio de la consola de Gremlin (`apache-tinkerpop-gremlin-console-3.7.2`) y, a continuación, escriba el siguiente comando para ejecutarla.

   ```
   bin/gremlin.sh
   ```

   Debería ver los siguientes datos de salida:

   ```
            \,,,/
            (o o)
   -----oOOo-(3)-oOOo-----
   plugin activated: tinkerpop.server
   plugin activated: tinkerpop.utilities
   plugin activated: tinkerpop.tinkergraph
   gremlin>
   ```

   Ahora se encuentra en `gremlin>`. Escriba los pasos restantes en este punto.

1. En el símbolo del sistema `gremlin>`, escriba lo siguiente para conectarse a la instancia de base de datos de Neptune.

   ```
   :remote connect tinkerpop.server conf/neptune-remote.yaml
   ```

1. En la entrada `gremlin>`, escriba lo siguiente para cambiar al modo remoto. Esto envía todas las consultas de Gremlin a la conexión remota.

   ```
   :remote console
   ```

1. Escriba lo siguiente para enviar una consulta al gráfico de Gremlin.

   ```
   g.V().limit(1)
   ```

1. Cuando haya terminado, escriba lo siguiente para salir de la consola de Gremlin.

   ```
   :exit
   ```

**nota**  
Utilice punto y coma (`;`) o un carácter de nueva línea (`\n`) para separar las instrucciones.   
Cada recorrido anterior al final debe terminar en `next()` para ejecutarse. Solo se devuelven los datos del recorrido final.

Para obtener más información sobre la implementación de Gremlin en Neptune, consulte [Conformidad con los estándares de Gremlin en Amazon Neptune](access-graph-gremlin-differences.md).

# Una forma alternativa de conectarse a la consola de Gremlin
<a name="access-graph-gremlin-console-connect"></a>

**Inconvenientes del enfoque de conexión normal**

La forma más común de conectarse a la consola de Gremlin es la que hemos explicado anteriormente, utilizando comandos como estos en el símbolo del sistema de `gremlin>`:

```
gremlin> :remote connect tinkerpop.server conf/(file name).yaml
gremlin> :remote console
```

Esto funciona bien y permite enviar consultas a Neptune. Sin embargo, saca del bucle al motor de scripts Groovy, por lo que Neptune trata todas las consultas como puras de Gremlin. Esto significa que los siguientes formularios de consulta fallan:

```
gremlin> 1 + 1
gremlin> x = g.V().count()
```

Lo más parecido a utilizar una variable cuando se está conectado de esta forma es utilizar la variable `result` que mantiene la consola y enviar la consulta utilizando `:>`, de esta forma:

```
gremlin> :remote console
==>All scripts will now be evaluated locally - type ':remote console' to return to remote mode for Gremlin Server - [krl-1-cluster.cluster-ro-cm9t6tfwbtsr.us-east-1.neptune.amazonaws.com/172.31.19.217:8182]
gremlin> :> g.V().count()
==>4249

gremlin> println(result)
[result{object=4249 class=java.lang.Long}]

gremlin> println(result['object'])
[4249]
```

 

**Una forma diferente de conectarse**

También puede conectarse a la consola de Gremlin de una forma diferente, que tal vez le parezca más agradable, como esta:

```
gremlin> g = traversal().withRemote('conf/neptune.properties')
```

Aquí `neptune.properties` toma esta forma:

```
gremlin.remote.remoteConnectionClass=org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection
gremlin.remote.driver.clusterFile=conf/my-cluster.yaml
gremlin.remote.driver.sourceName=g
```

El archivo `my-cluster.yaml` debería ser similar a esto:

```
hosts: [my-cluster-abcdefghijk.us-east-1.neptune.amazonaws.com]
port: 8182
serializer: { className: org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1,
              config: { serializeResultToString: false } }
connectionPool: { enableSsl: true }
```

**nota**  
 Los serializadores se trasladaron del módulo `gremlin-driver` al nuevo módulo `gremlin-util` de la versión 3.7.0. El paquete cambió de org.apache.tinkerpop.gremlin.driver.ser a org.apache.tinkerpop.gremlin.util.ser. 

Configurar la conexión de la consola de Gremlin de esta manera le permite realizar correctamente los siguientes tipos de consultas:

```
gremlin> 1+1
==>2

gremlin> x=g.V().count().next()
==>4249

gremlin> println("The answer was ${x}")
The answer was 4249
```

Puede evitar que se muestre el resultado, de esta manera:

```
gremlin> x=g.V().count().next();[]
gremlin> println(x)
4249
```

Todas las formas habituales de realizar consultas (sin el paso del terminal) siguen funcionando. Por ejemplo:

```
gremlin> g.V().count()
==>4249
```

Incluso puede usar el paso [https://tinkerpop.apache.org/docs/current/reference/#io-step](https://tinkerpop.apache.org/docs/current/reference/#io-step) para cargar un archivo con este tipo de conexión.

## Autenticación de IAM
<a name="access-graph-gremlin-console-iam"></a>

Neptune admite la [autenticación de IAM](iam-auth-enable.md) para controlar el acceso a su clúster de base de datos. Si tiene habilitada la autenticación de IAM, debe utilizar la firma de la versión 4 de Signature para autenticar sus solicitudes. Para obtener instrucciones detalladas y ejemplos de código para conectarse desde la consola de Gremlin, consulte. [Conexión a bases de datos de Amazon Neptune mediante la autenticación de IAM con la consola de Gremlin](iam-auth-connecting-gremlin-console.md)

# Uso del punto de conexión HTTP REST para conectarse a una instancia de base de datos de Neptune
<a name="access-graph-gremlin-rest"></a>

Amazon Neptune proporciona un punto de conexión HTTP para las consultas de Gremlin. La interfaz REST es compatible con cualquier versión de Gremlin que utilice su clúster de base de datos (consulte la [página de versiones del motor](engine-releases.md) de Neptune que esté utilizando para determinar qué versión de Gremlin admite).

**nota**  
Como se explica en [Cifrado de conexiones a la base de datos de Amazon Neptune con SSL/HTTPS](security-ssl.md), Neptune ahora requiere que se conecte mediante HTTPS en lugar de HTTP. Además, Neptune no admite actualmente HTTP/2 para las solicitudes de la API REST. Los clientes deben usar HTTP/1.1 al conectarse a los puntos finales.

Las siguientes instrucciones le ayudarán a conectar con el punto de enlace de Gremlin mediante el comando `curl` y HTTPS. Siga estas instrucciones desde una instancia de Amazon EC2 que esté en la misma nube privada virtual (VPC) que su instancia de base de datos de Neptune.

El punto de conexión HTTPS para las consultas de Gremlin a una instancia de base de datos de Neptune es `https://your-neptune-endpoint:port/gremlin`.

**nota**  
Para obtener información acerca de cómo encontrar el nombre de host de la instancia de base de datos de Neptune, consulte [Conexión a los puntos de conexión de Amazon Neptune](feature-overview-endpoints.md).

## Para conectarse a Neptune mediante el punto de conexión HTTP REST
<a name="access-graph-gremlin-rest-connect"></a>

En el siguiente ejemplo se utiliza **curl** para enviar una consulta de Gremlin a través de HTTP **POST**. La consulta se envía en formato JSON en el cuerpo de la publicación como propiedad `gremlin`.

```
curl -X POST -d '{"gremlin":"g.V().limit(1)"}' https://your-neptune-endpoint:port/gremlin
```

Este ejemplo devuelve el primer vértice del gráfico utilizando el recorrido `g.V().limit(1)`. Puede consultar otra cosa sustituyéndolo por otro recorrido de Gremlin.

**importante**  
De manera predeterminada, el punto de conexión REST devuelve todos los resultados en un único conjunto de resultados JSON. Si este conjunto de resultados es demasiado grande, se puede producir una excepción `OutOfMemoryError` en la instancia de base de datos de Neptune.  
Para evitarlo, habilite las respuestas fragmentadas (los resultados se devuelven en una serie de respuestas independientes). Consulte [Use encabezados finales HTTP opcionales para habilitar las respuestas de Gremlin compuestas por varias partes](access-graph-gremlin-rest-trailing-headers.md).

Aunque se recomiendan las solicitudes HTTP **POST** para enviar consultas de Gremlin, también es posible utilizar solicitudes HTTP **GET**:

```
curl -G "https://your-neptune-endpoint:port?gremlin=g.V().count()"
```

**nota**  
Neptune no admite la propiedad `bindings`.

# Use encabezados finales HTTP opcionales para habilitar las respuestas de Gremlin compuestas por varias partes
<a name="access-graph-gremlin-rest-trailing-headers"></a>

De forma predeterminada, la respuesta HTTP a las consultas de Gremlin se devuelve en un único conjunto de resultados JSON. En el caso de un conjunto de resultados muy grande, esto puede provocar una excepción `OutOfMemoryError` en la instancia de base de datos.

Sin embargo, puede habilitar las respuestas *fragmentadas* (respuestas que se devuelven en varias partes separadas). Para ello, incluya un encabezado final (`te: trailers`) con codificación de transferencia (TE) en su solicitud. Consulte [la página de MDN sobre los encabezados de las solicitudes TE](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/TE) para obtener más información sobre los encabezados TE.

Cuando se devuelve una respuesta en varias partes, puede resultar difícil diagnosticar un problema que se produce después de recibir la primera parte, ya que la primera parte llega con un código de estado HTTP de `200` (OK). Un error posterior suele provocar que el cuerpo del mensaje contenga una respuesta dañada, al final de la cual Neptune añade un mensaje de error.

Para facilitar la detección y el diagnóstico de este tipo de errores, Neptune también incluye dos nuevos campos de encabezado dentro de los encabezados finales de cada fragmento de respuesta:
+ `X-Neptune-Status`: contiene el código de respuesta seguido de un nombre abreviado. Por ejemplo, en caso de que se realizara correctamente, el encabezado final sería: `X-Neptune-Status: 200 OK`. En caso de fallo, el código de respuesta sería uno de los [códigos de error del motor de Neptune](errors-engine-codes.md), como `X-Neptune-Status: 500 TimeLimitExceededException`.
+ `X-Neptune-Detail`: está vacío si las solicitudes se han realizado correctamente. En caso de errores, contiene el mensaje de error JSON. Como solo se permiten caracteres ASCII en los valores de los encabezados HTTP, la cadena JSON está codificada en URL.

**nota**  
Neptune no admite actualmente la compresión `gzip` de respuestas fragmentadas. Si el cliente solicita codificación y compresión fragmentadas al mismo tiempo, Neptune omite la compresión.

# Clientes Gremlin basados en Java que se utilizan con Amazon Neptune
<a name="access-graph-gremlin-client"></a>

[Puede usar cualquiera de los dos clientes Gremlin de código abierto basados en Java con Amazon Neptune: el cliente [Apache TinkerPop Java Gremlin o el cliente Gremlin](https://search.maven.org/artifact/org.apache.tinkerpop/gremlin-driver) para Amazon Neptune.](https://search.maven.org/artifact/software.amazon.neptune/gremlin-client)

## El cliente Apache Java Gremlin TinkerPop
<a name="access-graph-gremlin-java-driver"></a>

El [controlador gremlin Apache TinkerPop Java es el cliente estándar y oficial de](https://tinkerpop.apache.org/docs/current/reference/#gremlin-java) Gremlin que funciona con cualquier base de datos de gráficos habilitada. TinkerPop Utilice este cliente cuando necesite la máxima compatibilidad con un espacio de TinkerPop desarrollo más amplio, cuando trabaje con varios sistemas de bases de datos de gráficos o cuando no necesite las funciones avanzadas de administración de clústeres y equilibrio de carga específicas de Neptune. Este cliente también es adecuado para aplicaciones sencillas que se conectan a una sola instancia de Neptune o cuando prefiere gestionar el equilibrio de carga a nivel de infraestructura en lugar de hacerlo dentro del cliente.

**importante**  
La elección de la versión correcta del controlador Apache TinkerPop Gremlin es fundamental para la compatibilidad con la versión del motor Neptune. El uso de una versión incompatible puede provocar fallos de conexión o un comportamiento inesperado. Para obtener información detallada sobre la compatibilidad de versiones, consulte[Acceso al gráfico de Neptune con Gremlin](access-graph-gremlin.md).

**nota**  
La tabla que le ayuda a determinar la TinkerPop versión correcta de Apache para usar con Neptune se ha trasladado a. [Acceso al gráfico de Neptune con Gremlin](access-graph-gremlin.md) Esta tabla estuvo ubicada anteriormente en esta página durante muchos años y ahora está más centralizada como referencia para todos los lenguajes de programación TinkerPop compatibles.

## Cliente Java de Gremlin para Amazon Neptune
<a name="access-graph-neptune-gremlin-client"></a>

El cliente Gremlin para Amazon Neptune es [un cliente Gremlin de código abierto basado en Java que sustituye directamente](https://github.com/aws/neptune-gremlin-client) al cliente Java estándar. TinkerPop 

El cliente de Gremlin para Neptune está optimizado para los clústeres de Neptune. Le permite administrar la distribución del tráfico entre varias instancias de un clúster y se adapta a los cambios en la topología del clúster al añadir o eliminar una réplica. Incluso puede configurar el cliente para que distribuya las solicitudes entre un subconjunto de instancias del clúster, en función del rol, el tipo de instancia, la zona de disponibilidad (AZ) o las etiquetas asociadas a las instancias.

La [versión más reciente del cliente Java de Gremlin para Neptune](https://search.maven.org/artifact/software.amazon.neptune/gremlin-client) está disponible en Maven Central.

Para obtener más información acerca del cliente Java de Gremlin para Neptune, consulte esta [publicación del blog](https://aws.amazon.com/blogs/database/load-balance-graph-queries-using-the-amazon-neptune-gremlin-client/). [Para ver ejemplos de código y demostraciones, consulte el proyecto del cliente. GitHub ](https://github.com/aws/neptune-gremlin-client)

Al elegir la versión del cliente Neptune Gremlin, debe tener en cuenta la TinkerPop versión subyacente en relación con la versión del motor Neptune. Consulte la tabla de compatibilidad en [Acceso al gráfico de Neptune con Gremlin](access-graph-gremlin.md) para determinar la TinkerPop versión correcta para su motor Neptuno y, a continuación, utilice la siguiente tabla para seleccionar la versión de cliente Neptune Gremlin adecuada:


**Compatibilidad de las versiones del cliente Neptune Gremlin**  

| Versión del cliente Neptune Gremlin | TinkerPop versión | 
| --- | --- | 
| 3.x | 3.7.x (AWS SDK para Java 2.x/1.x) | 
| 2.1.x | 3.7.x (AWS SDK para Java 1.x) | 
| 2.0.x | 3.6.x | 
| 1.12 | 3.5.x | 

# Uso de un cliente Java para conectarse a una instancia de base de datos de Neptune
<a name="access-graph-gremlin-java"></a>

En la siguiente sección, se explica cómo ejecutar un ejemplo completo de Java que se conecta a una instancia de base de datos de Neptune y realiza un recorrido por Gremlin mediante el cliente Apache Gremlin. TinkerPop

Siga estas instrucciones desde una instancia de Amazon EC2 que esté en la misma nube privada virtual (VPC) que su instancia de base de datos de Neptune.

**Para conectarse a Neptune mediante Java**

1. Instale Apache Maven en la instancia EC2. Si utiliza Amazon Linux 2023 (preferido), utilice:

   ```
   sudo dnf update -y
   sudo dnf install maven -y
   ```

   Si utiliza Amazon Linux 2, descargue el archivo binario más reciente desde [https://maven.apache.org/download.cgi:](https://maven.apache.org/download.cgi:)

   ```
   sudo yum remove maven -y
   wget https://dlcdn.apache.org/maven/maven-3/ <version>/binaries/apache-maven-<version>-bin.tar.gz
   sudo tar -xzf apache-maven-<version>-bin.tar.gz -C /opt/
   sudo ln -sf /opt/apache-maven-<version> /opt/maven
   echo 'export MAVEN_HOME=/opt/maven' >> ~/.bashrc
   echo 'export PATH=$MAVEN_HOME/bin:$PATH' >> ~/.bashrc
   source ~/.bashrc
   ```

1. **Instale Java.** Las bibliotecas de Gremlin necesitan Java 8 u 11. Puede instalar Java 11 como se indica a continuación:
   + Si utilizas [Amazon Linux 2 (AL2)](https://aws.amazon.com/amazon-linux-2):

     ```
     sudo amazon-linux-extras install java-openjdk11
     ```
   + Si utilizas [Amazon Linux 2023 (AL2023)](https://docs.aws.amazon.com/linux/al2023/ug/what-is-amazon-linux.html):

     ```
     sudo yum install java-11-amazon-corretto-devel
     ```
   + Para otras distribuciones, utilice la que sea adecuada de las siguientes opciones:

     ```
     sudo yum install java-11-openjdk-devel
     ```

     o bien:

     ```
     sudo apt-get install openjdk-11-jdk
     ```

1. **Defina Java 11 como tiempo de ejecución predeterminado en su instancia EC2:** introduzca lo siguiente para establecer Java 8 como tiempo de ejecución predeterminado en su instancia EC2:

   ```
   sudo /usr/sbin/alternatives --config java
   ```

   Cuando se le solicite, escriba el número para Java 11.

1. **Cree un nuevo directorio llamado `gremlinjava`:**

   ```
   mkdir gremlinjava
   cd gremlinjava
   ```

1.  En el directorio `gremlinjava`, cree un archivo `pom.xml` y, a continuación, ábralo en un editor de texto:

   ```
   nano pom.xml
   ```

1. Copie lo siguiente en el archivo `pom.xml` y guárdelo:

   ```
   <project xmlns="https://maven.apache.org/POM/4.0.0"
            xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
     <properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.amazonaws</groupId>
     <artifactId>GremlinExample</artifactId>
     <packaging>jar</packaging>
     <version>1.0-SNAPSHOT</version>
     <name>GremlinExample</name>
     <url>https://maven.apache.org</url>
     <dependencies>
       <dependency>
         <groupId>org.apache.tinkerpop</groupId>
         <artifactId>gremlin-driver</artifactId>
         <version>3.7.2</version>
       </dependency>
       <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-jdk14</artifactId>
         <version>1.7.25</version>
       </dependency>
     </dependencies>
     <build>
       <plugins>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <version>2.5.1</version>
           <configuration>
             <source>11</source>
             <target>11</target>
           </configuration>
         </plugin>
           <plugin>
             <groupId>org.codehaus.mojo</groupId>
             <artifactId>exec-maven-plugin</artifactId>
             <version>1.3</version>
             <configuration>
               <executable>java</executable>
               <arguments>
                 <argument>-classpath</argument>
                 <classpath/>
                 <argument>com.amazonaws.App</argument>
               </arguments>
               <mainClass>com.amazonaws.App</mainClass>
               <complianceLevel>1.11</complianceLevel>
               <killAfter>-1</killAfter>
             </configuration>
           </plugin>
       </plugins>
     </build>
   </project>
   ```
**nota**  
Si está modificando un proyecto de Maven ya existente, la dependencia requerida aparece resaltada en el código anterior.

1. Escriba lo siguiente en la línea de comandos a fin de crear subdirectorios para el código fuente de ejemplo (`src/main/java/com/amazonaws/`):

   ```
   mkdir -p src/main/java/com/amazonaws/
   ```

1. En el directorio `src/main/java/com/amazonaws/`, cree un archivo llamado `App.java` y, a continuación, ábralo en un editor de texto.

   ```
   nano src/main/java/com/amazonaws/App.java
   ```

1. Copie lo siguiente en el archivo `App.java`. *your-neptune-endpoint*Sustitúyala por la dirección de la instancia de base de datos de Neptune. *No* incluya el prefijo `https://` en el método `addContactPoint`.
**nota**  
Para obtener información acerca de cómo encontrar el nombre de host de la instancia de base de datos de Neptune, consulte [Conexión a los puntos de conexión de Amazon Neptune](feature-overview-endpoints.md).

   ```
   package com.amazonaws;
   import org.apache.tinkerpop.gremlin.driver.Cluster;
   import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
   import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
   import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal;
   import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection;
   import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
   import org.apache.tinkerpop.gremlin.structure.T;
   
   public class App
   {
     public static void main( String[] args )
     {
       Cluster.Builder builder = Cluster.build();
       builder.addContactPoint("your-neptune-endpoint");
       builder.port(8182);
       builder.enableSsl(true);
   
       Cluster cluster = builder.create();
   
       GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster));
   
       // Add a vertex.
       // Note that a Gremlin terminal step, e.g. iterate(), is required to make a request to the remote server.
       // The full list of Gremlin terminal steps is at https://tinkerpop.apache.org/docs/current/reference/#terminal-steps
       g.addV("Person").property("Name", "Justin").iterate();
   
       // Add a vertex with a user-supplied ID.
       g.addV("Custom Label").property(T.id, "CustomId1").property("name", "Custom id vertex 1").iterate();
       g.addV("Custom Label").property(T.id, "CustomId2").property("name", "Custom id vertex 2").iterate();
   
       g.addE("Edge Label").from(__.V("CustomId1")).to(__.V("CustomId2")).iterate();
   
       // This gets the vertices, only.
       GraphTraversal t = g.V().limit(3).elementMap();
   
       t.forEachRemaining(
         e ->  System.out.println(t.toList())
       );
   
       cluster.close();
     }
   }
   ```

   Para obtener ayuda para conectarse a Neptune con SSL/TLS (obligatorio), consulte. [Configuración de SSL/TLS](#access-graph-gremlin-java-ssl)

1. Compile y ejecute el ejemplo usando el siguiente comando Maven:

   ```
   mvn compile exec:exec
   ```

El ejemplo anterior devuelve un mapa de las claves y los valores de cada propiedad para los dos primeros índices del gráfico utilizando el recorrido `g.V().limit(3).elementMap()`. Para otras consultas, sustitúyalo por otro recorrido de Gremlin con uno de los métodos de finalización adecuados.

**nota**  
La parte final de la consulta de Gremlin, `.toList()`, es necesaria para enviar el recorrido al servidor para su evaluación. Si no incluye ese método u otro equivalente, la consulta no se envía a la instancia de base de datos de Neptune.  
También debe añadir una terminación adecuada al añadir un vértice o borde, como cuando utiliza el paso `addV( )`.

Los siguientes métodos envían la consulta a la instancia de base de datos de Neptune:
+ `toList()`
+ `toSet()`
+ `next()`
+ `nextTraverser()`
+ `iterate()`

## Configuración de SSL/TLS para el cliente Java de Gremlin
<a name="access-graph-gremlin-java-ssl"></a>

Neptune debe estar activado SSL/TLS de forma predeterminada. Normalmente, si el controlador Java está configurado con `enableSsl(true)`, se puede conectar a Neptune sin tener que configurar un `trustStore()` o `keyStore()` con una copia local de un certificado.

Sin embargo, si la instancia con la que se está conectando no tiene una conexión a Internet a través de la cual verificar un certificado público, o si el certificado que está usando no es público, puede seguir estos pasos para configurar una copia del certificado local:

**Configuración de una copia del certificado local para habilitar SSL/TLS**

1. Descargue e instale [keytool](https://docs.oracle.com/javase/9/tools/keytool.htm#JSWOR-GUID-5990A2E4-78E3-47B7-AE75-6D1826259549) de Oracle. Esto facilitará mucho la configuración del almacén de claves local.

1. Descargue el certificado `SFSRootCAG2.pem`CA (el SDK de Java de Gremlin necesita un certificado para verificar el certificado remoto):

   ```
   wget https://www.amazontrust.com/repository/SFSRootCAG2.pem
   ```

1. Cree un almacén de claves en formato JKS o PKCS12 en formato. En este ejemplo se utiliza JKS. Responda las preguntas que aparecen a continuación cuando se le pida. La contraseña que cree aquí la necesitará más adelante:

   ```
   keytool -genkey -alias (host name) -keyalg RSA -keystore server.jks
   ```

1. Importe el archivo `SFSRootCAG2.pem` que descargó al almacén de claves recién creado:

   ```
   keytool -import -keystore server.jks -file .pem
   ```

1. Configure el objeto `Cluster` mediante programación:

   ```
   Cluster cluster = Cluster.build("(your neptune endpoint)")
                            .port(8182)
                            .enableSSL(true)
                            .keyStore(‘server.jks’)
                            .keyStorePassword("(the password from step 2)")
                            .create();
   ```

   Si lo desea, puede hacer lo mismo en un archivo de configuración, y también con la consola de Gremlin:

   ```
   hosts: [(your neptune endpoint)]
   port: 8182
   connectionPool: { enableSsl: true, keyStore: server.jks, keyStorePassword: (the password from step 2) }
   serializer: { className: org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1, config: { serializeResultToString: true }}
   ```

## Autenticación de IAM
<a name="access-graph-gremlin-java-iam"></a>

Neptune admite la [autenticación de IAM](iam-auth-enable.md) para controlar el acceso a su clúster de base de datos. Si tiene habilitada la autenticación de IAM, debe utilizar la firma de la versión 4 de Signature para autenticar sus solicitudes. Para obtener instrucciones detalladas y ejemplos de código para conectarse desde un cliente Java, consulte. [Conexión a bases de datos de Amazon Neptune mediante IAM con Java de Gremlin](iam-auth-connecting-gremlin-java.md)

# Ejemplo en Java de conexión a una instancia de base de datos de Neptune con lógica de reconexión
<a name="access-graph-gremlin-java-reconnect-example"></a>

El siguiente ejemplo de Java muestra cómo conectarse al cliente de Gremlin con una lógica de reconexión para recuperarse de una desconexión inesperada.

Tiene las siguientes dependencias:

```
<dependency>
    <groupId>org.apache.tinkerpop</groupId>
    <artifactId>gremlin-driver</artifactId>
    <version>${gremlin.version}</version>
</dependency>

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>amazon-neptune-sigv4-signer</artifactId>
    <version>${sig4.signer.version}</version>
</dependency>

<dependency>
    <groupId>com.evanlennick</groupId>
    <artifactId>retry4j</artifactId>
    <version>0.15.0</version>
</dependency>
```

Este es el código de ejemplo:

**importante**  
 Es posible que el `CallExecutor` de Retry4j no sea seguro para subprocesos. Considere la posibilidad de que cada subproceso use su propia instancia `CallExecutor` o use una biblioteca de reintentos diferente. 

**nota**  
 El siguiente ejemplo se ha actualizado para incluir el uso de requestInterceptor(). Esto se añadió en la versión TinkerPop 3.6.6. Antes de la TinkerPop versión 3.6.6, el ejemplo de código utilizaba handshakeInterceptor (), que quedó obsoleto en esa versión. 

```
public static void main(String args[]) {
  boolean useIam = true;

  // Create Gremlin cluster and traversal source
  Cluster.Builder builder = Cluster.build()
         .addContactPoint(System.getenv("neptuneEndpoint"))
         .port(Integer.parseInt(System.getenv("neptunePort")))
         .enableSsl(true)
         .minConnectionPoolSize(1)
         .maxConnectionPoolSize(1)
         .serializer(Serializers.GRAPHBINARY_V1D0)
         .reconnectInterval(2000);

  if (useIam) {
      builder.requestInterceptor( r -> {
         try {
            NeptuneNettyHttpSigV4Signer sigV4Signer =
                        new NeptuneNettyHttpSigV4Signer("(your region)", new DefaultAWSCredentialsProviderChain());
            sigV4Signer.signRequest(r);
         } catch (NeptuneSigV4SignerException e) {
            throw new RuntimeException("Exception occurred while signing the request", e);
         }
         return r;
      });
   }

  Cluster cluster = builder.create();

  GraphTraversalSource g = AnonymousTraversalSource
      .traversal()
      .withRemote(DriverRemoteConnection.using(cluster));

  // Configure retries
  RetryConfig retryConfig = new RetryConfigBuilder()
      .retryOnCustomExceptionLogic(getRetryLogic())
      .withDelayBetweenTries(1000, ChronoUnit.MILLIS)
      .withMaxNumberOfTries(5)
      .withFixedBackoff()
      .build();

  @SuppressWarnings("unchecked")
  CallExecutor<Object> retryExecutor = new CallExecutorBuilder<Object>()
      .config(retryConfig)
      .build();

  // Do lots of queries
  for (int i = 0; i < 100; i++){
    String id = String.valueOf(i);

    @SuppressWarnings("unchecked")
    Callable<Object> query = () -> g.V(id)
        .fold()
        .coalesce(
            unfold(),
            addV("Person").property(T.id, id))
        .id().next();

    // Retry query
    // If there are connection failures, the Java Gremlin client will automatically
    // attempt to reconnect in the background, so all we have to do is wait and retry.
    Status<Object> status = retryExecutor.execute(query);

    System.out.println(status.getResult().toString());
  }

  cluster.close();
}

private static Function<Exception, Boolean> getRetryLogic() {

  return e -> {

    Class<? extends Exception> exceptionClass = e.getClass();

    StringWriter stringWriter = new StringWriter();
    String message = stringWriter.toString();


    if (RemoteConnectionException.class.isAssignableFrom(exceptionClass)){
      System.out.println("Retrying because RemoteConnectionException");
      return true;
    }

    // Check for connection issues
    if (message.contains("Timed out while waiting for an available host") ||
        message.contains("Timed-out") && message.contains("waiting for connection on Host") ||
        message.contains("Connection to server is no longer active") ||
        message.contains("Connection reset by peer") ||
        message.contains("SSLEngine closed already") ||
        message.contains("Pool is shutdown") ||
        message.contains("ExtendedClosedChannelException") ||
        message.contains("Broken pipe") ||
        message.contains(System.getenv("neptuneEndpoint")))
    {
      System.out.println("Retrying because connection issue");
      return true;
    };

    // Concurrent writes can sometimes trigger a ConcurrentModificationException.
    // In these circumstances you may want to backoff and retry.
    if (message.contains("ConcurrentModificationException")) {
      System.out.println("Retrying because ConcurrentModificationException");
      return true;
    }

    // If the primary fails over to a new instance, existing connections to the old primary will
    // throw a ReadOnlyViolationException. You may want to back and retry.
    if (message.contains("ReadOnlyViolationException")) {
      System.out.println("Retrying because ReadOnlyViolationException");
      return true;
    }

    System.out.println("Not a retriable error");
    return false;
  };
}
```

# Uso de Python para conectarse a una instancia de base de datos de Neptune
<a name="access-graph-gremlin-python"></a>

**importante**  
La elección de la versión correcta del controlador Apache TinkerPop Gremlin es fundamental para la compatibilidad con la versión del motor Neptune. El uso de una versión incompatible puede provocar fallos de conexión o un comportamiento inesperado. Para obtener información detallada sobre la compatibilidad de versiones, consulte[Acceso al gráfico de Neptune con Gremlin](access-graph-gremlin.md).

En la siguiente sección se indica cómo ejecutar una muestra de Python que se conecta a una instancia de base de datos de Amazon Neptune y realiza un recorrido de Gremlin.

Siga estas instrucciones desde una instancia de Amazon EC2 que esté en la misma nube privada virtual (VPC) que su instancia de base de datos de Neptune.

Antes de comenzar, haga lo siguiente:
+ Descargue e instale Python 3.6 o una versión posterior desde el [sitio web de Python.org](https://www.python.org/downloads/).
+ Compruebe que tiene **pip** instalado. Si no tiene **pip** o no está seguro, consulte [Do I need to install pip?](https://pip.pypa.io/en/stable/installing/#do-i-need-to-install-pip) en la documentación de **pip**.
+ Si su instalación de Python aún no lo tiene, descargue `futures` tal y como se indica a continuación: `pip install futures`



**Para conectarse a Neptune mediante Python**

1. Escriba lo siguiente para instalar el paquete `gremlinpython`:

   ```
   pip install --user gremlinpython
   ```

1. Cree un archivo con el nombre `gremlinexample.py` y, a continuación, ábralo en un editor de texto.

1. Copie lo siguiente en el archivo `gremlinexample.py`. *your-neptune-endpoint*Sustitúyala por la dirección de su clúster de base de datos Neptune y *your-neptune-port* por el puerto de su clúster de base de datos Neptune (predeterminado: 8182). 

   Para obtener información acerca de cómo encontrar la dirección de la instancia de base de datos de Neptune, consulte la sección [Conexión a los puntos de conexión de Amazon Neptune](feature-overview-endpoints.md).

    En el siguiente ejemplo se muestra cómo conectarse con Python de Gremlin. 

   ```
   import boto3
   import os
   from botocore.auth import SigV4Auth
   from botocore.awsrequest import AWSRequest
   from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
   from gremlin_python.process.anonymous_traversal import traversal
   
   database_url = "wss://your-neptune-endpoint:your-neptune-port/gremlin"
   
   remoteConn = DriverRemoteConnection(database_url, "g")
   
   g = traversal().withRemote(remoteConn)
   
   print(g.inject(1).toList())
   remoteConn.close()
   ```

1. Escriba el comando siguiente para ejecutar el ejemplo:

   ```
   python gremlinexample.py
   ```

   La consulta de Gremlin al final de este ejemplo devuelve los vértices (`g.V().limit(2)`) en una lista. A continuación, la lista se imprime con la función `print` de Python estándar.
**nota**  
La parte final de la consulta de Gremlin, `toList()`, es necesaria para enviar el recorrido al servidor para su evaluación. Si no incluye ese método u otro equivalente, la consulta no se envía a la instancia de base de datos de Neptune.

   Los siguientes métodos envían la consulta a la instancia de base de datos de Neptune:
   + `toList()`
   + `toSet()`
   + `next()`
   + `nextTraverser()`
   + `iterate()`

   

   El ejemplo anterior devuelve los dos primeros vértices del gráfico utilizando el recorrido `g.V().limit(2).toList()`. Para otras consultas, sustitúyalo por otro recorrido de Gremlin con uno de los métodos de finalización adecuados.

## Autenticación de IAM
<a name="access-graph-gremlin-python-iam"></a>

Neptune admite la [autenticación de IAM](iam-auth-enable.md) para controlar el acceso a su clúster de base de datos. Si tiene habilitada la autenticación de IAM, debe utilizar la firma de la versión 4 de Signature para autenticar sus solicitudes. Para obtener instrucciones detalladas y ejemplos de código para conectarse desde un cliente Python, consulte[Conexión a bases de datos de Amazon Neptune mediante la autenticación de IAM con Python de Gremlin](gremlin-python-iam-auth.md).

# Uso de .NET para conectarse a una instancia de base de datos de Neptune
<a name="access-graph-gremlin-dotnet"></a>

**importante**  
La elección de la versión correcta del controlador Apache TinkerPop Gremlin es fundamental para la compatibilidad con la versión del motor Neptune. El uso de una versión incompatible puede provocar fallos de conexión o un comportamiento inesperado. Para obtener información detallada sobre la compatibilidad de versiones, consulte[Acceso al gráfico de Neptune con Gremlin](access-graph-gremlin.md).

La siguiente sección contiene un ejemplo de código escrito en C\$1 que se conecta a una instancia de base de datos de Neptune y realiza un recorrido de Gremlin.

La conexiones a Amazon Neptune deben realizarse desde una instancia de Amazon EC2 que esté en la misma nube privada virtual (VPC) que su instancia de base de datos de Neptune. Este código de muestra se ha probado en una instancia de Amazon EC2 que ejecuta Ubuntu.

Antes de comenzar, haga lo siguiente:
+ Instale .NET en la instancia de Amazon EC2. Para obtener instrucciones de instalación de .NET en varios sistemas operativos, incluidos Windows, Linux y macOS, consulte el artículo de [introducción a .NET](https://www.microsoft.com/net/learn/get-started/).
+ Instale Gremlin.NET ejecutando `dotnet add package gremlin.net` para su paquete. Para obtener más información, consulte [Gremlin.net](https://tinkerpop.apache.org/docs/current/reference/#gremlin-DotNet) en la documentación. TinkerPop 



**Para conectarse a Neptune mediante Gremlin.NET**

1. Creación de un nuevo proyecto de .NET.

   ```
   dotnet new console -o gremlinExample
   ```

1. Cambie al directorio nuevo del proyecto.

   ```
   cd gremlinExample
   ```

1. Copie lo siguiente en el archivo `Program.cs`. *your-neptune-endpoint*Sustitúyala por la dirección de la instancia de base de datos de Neptune.

   Para obtener información acerca de cómo encontrar la dirección de la instancia de base de datos de Neptune, consulte la sección [Conexión a los puntos de conexión de Amazon Neptune](feature-overview-endpoints.md).

   ```
   using System;
   using System.Threading.Tasks;
   using System.Collections.Generic;
   using Gremlin.Net;
   using Gremlin.Net.Driver;
   using Gremlin.Net.Driver.Remote;
   using Gremlin.Net.Structure;
   using static Gremlin.Net.Process.Traversal.AnonymousTraversalSource;
   namespace gremlinExample
   {
     class Program
     {
       static void Main(string[] args)
       {
         try
         {
           var endpoint = "your-neptune-endpoint";
           // This uses the default Neptune and Gremlin port, 8182
           var gremlinServer = new GremlinServer(endpoint, 8182, enableSsl: true );
           var gremlinClient = new GremlinClient(gremlinServer);
           var remoteConnection = new DriverRemoteConnection(gremlinClient, "g");
           var g = Traversal().WithRemote(remoteConnection);
           g.AddV("Person").Property("Name", "Justin").Iterate();
           g.AddV("Custom Label").Property("name", "Custom id vertex 1").Iterate();
           g.AddV("Custom Label").Property("name", "Custom id vertex 2").Iterate();
           var output = g.V().Limit<Vertex>(3).ToList();
           foreach(var item in output) {
               Console.WriteLine(item);
           }
         }
         catch (Exception e)
         {
             Console.WriteLine("{0}", e);
         }
       }
     }
   }
   ```

1. Escriba el comando siguiente para ejecutar el ejemplo:

   ```
   dotnet run
   ```

   La consulta de Gremlin al final de este ejemplo devuelve un recuento de un solo vértice con fines de prueba. Después, se imprime en la consola.
**nota**  
La parte final de la consulta de Gremlin, `Next()`, es necesaria para enviar el recorrido al servidor para su evaluación. Si no incluye ese método u otro equivalente, la consulta no se envía a la instancia de base de datos de Neptune.

   Los siguientes métodos envían la consulta a la instancia de base de datos de Neptune:
   + `ToList()`
   + `ToSet()`
   + `Next()`
   + `NextTraverser()`
   + `Iterate()`

   Utilice `Next()` si necesita que los resultados de la consulta se serialicen y devuelvan, o `Iterate()` si no es así.

   En el ejemplo anterior se devuelve una lista utilizando el recorrido `g.V().Limit(3).ToList()`. Para otras consultas, sustitúyalo por otro recorrido de Gremlin con uno de los métodos de finalización adecuados.

## Autenticación de IAM
<a name="access-graph-gremlin-dotnet-iam"></a>

Neptune admite la [autenticación de IAM](iam-auth-enable.md) para controlar el acceso a su clúster de base de datos. Si tiene habilitada la autenticación de IAM, debe utilizar la firma de la versión 4 de Signature para autenticar sus solicitudes. Para obtener instrucciones detalladas y ejemplos de código para conectarse desde un cliente.NET, consulte. [Conexión a bases de datos de Amazon Neptune mediante la autenticación de IAM con .NET de Gremlin](gremlin-dotnet-iam-auth.md)

# Uso de Node.js para conectarse a una instancia de base de datos de Neptune
<a name="access-graph-gremlin-node-js"></a>

**importante**  
La elección de la versión correcta del controlador Apache TinkerPop Gremlin es fundamental para la compatibilidad con la versión del motor Neptune. El uso de una versión incompatible puede provocar fallos de conexión o un comportamiento inesperado. Para obtener información detallada sobre la compatibilidad de versiones, consulte[Acceso al gráfico de Neptune con Gremlin](access-graph-gremlin.md).

En la siguiente sección se indica cómo ejecutar una muestra de Node.js que se conecta a una instancia de base de datos de Amazon Neptune y realiza un recorrido de Gremlin.

Siga estas instrucciones desde una instancia de Amazon EC2 que esté en la misma nube privada virtual (VPC) que su instancia de base de datos de Neptune.

Antes de comenzar, haga lo siguiente:
+ Compruebe que esté instalado Node.js versión 8.11 o superior. Si no lo está, descargue e instale Node.js desde el [sitio web de Nodejs.org](https://nodejs.org).

**Para conectarse a Neptune mediante Node.js**

1. Escriba lo siguiente para instalar el paquete `gremlin-javascript`:

   ```
   npm install gremlin
   ```

1. Cree un archivo con el nombre `gremlinexample.js` y ábralo en un editor de texto.

1. Copie lo siguiente en el archivo `gremlinexample.js`. *your-neptune-endpoint*Sustitúyala por la dirección de la instancia de base de datos de Neptune.

   Para obtener información acerca de cómo encontrar la dirección de la instancia de base de datos de Neptune, consulte la sección [Conexión a los puntos de conexión de Amazon Neptune](feature-overview-endpoints.md).

   ```
   const gremlin = require('gremlin');
   const DriverRemoteConnection = gremlin.driver.DriverRemoteConnection;
   const Graph = gremlin.structure.Graph;
   
   dc = new DriverRemoteConnection('wss://your-neptune-endpoint:8182/gremlin',{});
   
   const graph = new Graph();
   const g = graph.traversal().withRemote(dc);
   
   g.V().limit(1).count().next().
       then(data => {
           console.log(data);
           dc.close();
       }).catch(error => {
           console.log('ERROR', error);
           dc.close();
       });
   ```

1. Escriba el comando siguiente para ejecutar el ejemplo:

   ```
   node gremlinexample.js
   ```

El ejemplo anterior devuelve el recuento de un único vértice en el gráfico utilizando el recorrido `g.V().limit(1).count().next()`. Para otras consultas, sustitúyalo por otro recorrido de Gremlin con uno de los métodos de finalización adecuados.

**nota**  
La parte final de la consulta de Gremlin, `next()`, es necesaria para enviar el recorrido al servidor para su evaluación. Si no incluye ese método u otro equivalente, la consulta no se envía a la instancia de base de datos de Neptune.

Los siguientes métodos envían la consulta a la instancia de base de datos de Neptune:
+ `toList()`
+ `toSet()`
+ `next()`
+ `nextTraverser()`
+ `iterate()`

Utilice `next()` si necesita que los resultados de la consulta se serialicen y devuelvan, o `iterate()` si no es así.

**importante**  
Se trata de un ejemplo aislado de Node.js. Si planea ejecutar código como este en una AWS Lambda función, consulte [Ejemplos de funciones de Lambda](lambda-functions-examples.md) para obtener más información sobre el uso JavaScript eficiente de una función de Neptune Lambda.

## Autenticación de IAM
<a name="access-graph-gremlin-nodejs-iam"></a>

Neptune admite la [autenticación de IAM](iam-auth-enable.md) para controlar el acceso a su clúster de base de datos. Si tiene habilitada la autenticación de IAM, debe utilizar la firma de la versión 4 de Signature para autenticar sus solicitudes. Para obtener instrucciones detalladas y ejemplos de código para conectarse desde un JavaScript cliente, consulte. [Conexión a las bases de datos de Amazon Neptune mediante la autenticación de IAM con Gremlin JavaScript](gremlin-javascript-iam-auth.md)

# Uso de Go para conectarse a una instancia de base de datos de Neptune
<a name="access-graph-gremlin-go"></a>

**importante**  
La elección de la versión correcta del controlador Apache TinkerPop Gremlin es fundamental para la compatibilidad con la versión del motor Neptune. El uso de una versión incompatible puede provocar fallos de conexión o un comportamiento inesperado. Para obtener información detallada sobre la compatibilidad de versiones, consulte[Acceso al gráfico de Neptune con Gremlin](access-graph-gremlin.md).

**nota**  
Las versiones 3.5.x de gremlingo son retrocompatibles con las versiones 3.4.x siempre y cuando solo utilices las funciones de la versión TinkerPop 3.4.x en las consultas de Gremlin que escribas.

En la siguiente sección se indica cómo ejecutar una muestra de Go que se conecta a una instancia de base de datos de Amazon Neptune y realiza un recorrido Gremlin.

Siga estas instrucciones desde una instancia de Amazon EC2 que esté en la misma nube privada virtual (VPC) que su instancia de base de datos de Neptune.

Antes de comenzar, haga lo siguiente:
+ Descargue e instale Go 1.17 o una versión posterior desde el sitio web [go.dev](https://go.dev/dl/).

**Para conectarse a Neptune mediante Go**

1. A partir de un directorio vacío, inicialice un nuevo módulo de Go:

   ```
   go mod init example.com/gremlinExample
   ```

1. Añada gremlin-go como dependencia de su nuevo módulo:

   ```
   go get github.com/apache/tinkerpop/gremlin-go/v3/driver
   ```

1. Cree un archivo con el nombre `gremlinExample.go` y, a continuación, ábralo en un editor de texto.

1. Copie lo siguiente en el archivo `gremlinExample.go` y sustituya *`(your neptune endpoint)`* por la dirección de su instancia de base de datos de Neptune:

   ```
   package main
   
   import (
     "fmt"
     gremlingo "github.com/apache/tinkerpop/gremlin-go/v3/driver"
   )
   
   func main() {
     // Creating the connection to the server.
     driverRemoteConnection, err := gremlingo.NewDriverRemoteConnection("wss://(your neptune endpoint):8182/gremlin",
       func(settings *gremlingo.DriverRemoteConnectionSettings) {
         settings.TraversalSource = "g"
       })
     if err != nil {
       fmt.Println(err)
       return
     }
     // Cleanup
     defer driverRemoteConnection.Close()
   
     // Creating graph traversal
     g := gremlingo.Traversal_().WithRemote(driverRemoteConnection)
   
     // Perform traversal
     results, err := g.V().Limit(2).ToList()
     if err != nil {
       fmt.Println(err)
       return
     }
     // Print results
     for _, r := range results {
       fmt.Println(r.GetString())
     }
   }
   ```
**nota**  
El formato del certificado TLS de Neptune no es compatible actualmente en Go 1.18\$1 con macOS y puede generar un error 509 al intentar iniciar una conexión. En el caso de las pruebas locales, puede omitirlo añadiendo “crypto/tls” a las importaciones y modificando la configuración de `DriverRemoteConnection` de la siguiente manera:  

   ```
   // Creating the connection to the server.
   driverRemoteConnection, err := gremlingo.NewDriverRemoteConnection("wss://your-neptune-endpoint:8182/gremlin",
     func(settings *gremlingo.DriverRemoteConnectionSettings) {
         settings.TraversalSource = "g"
         settings.TlsConfig = &tls.Config{InsecureSkipVerify: true}
     })
   ```

1. Escriba el comando siguiente para ejecutar el ejemplo:

   ```
   go run gremlinExample.go
   ```

La consulta de Gremlin al final de este ejemplo devuelve los vértices `(g.V().Limit(2))` en un sector. A continuación, este segmento se itera y se imprime con la función `fmt.Println` estándar.

**nota**  
La parte final de la consulta de Gremlin, `ToList()`, es necesaria para enviar el recorrido al servidor para su evaluación. Si no incluye ese método u otro equivalente, la consulta no se envía a la instancia de base de datos de Neptune.

Los siguientes métodos envían la consulta a la instancia de base de datos de Neptune:
+ `ToList()`
+ `ToSet()`
+ `Next()`
+ `GetResultSet()`
+ `Iterate()`

El ejemplo anterior devuelve los dos primeros vértices del gráfico utilizando el recorrido `g.V().Limit(2).ToList()`. Para otras consultas, sustitúyalo por otro recorrido de Gremlin con uno de los métodos de finalización adecuados.

## Autenticación de IAM
<a name="access-graph-gremlin-go-iam"></a>

Neptune admite la [autenticación de IAM](iam-auth-enable.md) para controlar el acceso a su clúster de base de datos. Si tiene habilitada la autenticación de IAM, debe utilizar la firma de la versión 4 de Signature para autenticar sus solicitudes. Para obtener instrucciones detalladas y ejemplos de código para conectarse desde un cliente Go, consulte. [Conexión a bases de datos de Amazon Neptune mediante la autenticación de IAM con Go de Gremlin](gremlin-go-iam-auth.md)

# Uso del AWS SDK para ejecutar consultas de Gremlin
<a name="access-graph-gremlin-sdk"></a>

Con el AWS SDK, puede ejecutar consultas de Gremlin en su gráfico de Neptune utilizando el lenguaje de programación que prefiera. El SDK de la API de datos de Neptune (nombre del servicio`neptunedata`) proporciona la [ExecuteGremlinQuery](https://docs.aws.amazon.com/neptune/latest/data-api/API_ExecuteGremlinQuery.html)acción para enviar consultas de Gremlin.

Debe ejecutar estos ejemplos desde una instancia de Amazon EC2 en la misma nube privada virtual (VPC) que el clúster de base de datos de Neptune, o desde una ubicación que tenga conectividad de red con el punto final del clúster.

A continuación, encontrará enlaces directos a la documentación de referencia de la API del `neptunedata` servicio en cada idioma del SDK:


| Lenguaje de programación | Referencia de la API neptunedata | 
| --- | --- | 
| C\$1\$1 | [https://sdk.amazonaws.com/cpp/api/LATEST/aws-cpp-sdk-neptunedata/html/annotated.html](https://sdk.amazonaws.com/cpp/api/LATEST/aws-cpp-sdk-neptunedata/html/annotated.html) | 
| Go | [https://docs.aws.amazon.com/sdk-for-go/api/service/neptunedata/](https://docs.aws.amazon.com/sdk-for-go/api/service/neptunedata/) | 
| Java | [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/neptunedata/package-summary.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/neptunedata/package-summary.html) | 
| JavaScript | [https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-neptunedata/](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-neptunedata/) | 
| Kotlin | [https://sdk.amazonaws.com/kotlin/api/latest/neptunedata/index.html](https://sdk.amazonaws.com/kotlin/api/latest/neptunedata/index.html) | 
| .NET | [https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Neptunedata/NNeptunedata.html](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Neptunedata/NNeptunedata.html) | 
| PHP | [https://docs.aws.amazon.com/aws-sdk-php/v3/api/namespace-Aws.Neptunedata.html](https://docs.aws.amazon.com/aws-sdk-php/v3/api/namespace-Aws.Neptunedata.html) | 
| Python | [https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/neptunedata.html](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/neptunedata.html) | 
| Ruby | [https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Neptunedata.html](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Neptunedata.html) | 
| Rust | [https://crates.io/crates/aws-sdk-neptunedata](https://crates.io/crates/aws-sdk-neptunedata) | 
| CLI | [https://docs.aws.amazon.com/cli/latest/reference/neptunedata/](https://docs.aws.amazon.com/cli/latest/reference/neptunedata/) | 

## Ejemplos del SDK de Gremlin AWS
<a name="access-graph-gremlin-sdk-examples"></a>

Los siguientes ejemplos muestran cómo configurar un `neptunedata` cliente, ejecutar una consulta de Gremlin e imprimir los resultados. Sustituya *YOUR\$1NEPTUNE\$1HOST* y *YOUR\$1NEPTUNE\$1PORT* por el punto final y el puerto de su clúster de base de datos Neptune.

**Configuración de tiempo de espera y reintento del lado del cliente**  
El tiempo de espera del cliente del SDK controla cuánto tiempo espera el *cliente* para recibir una respuesta. No controla cuánto tiempo se ejecuta la consulta en el servidor. Si se agota el tiempo de espera del cliente antes de que finalice el servidor, es posible que la consulta continúe ejecutándose en Neptune mientras el cliente no tenga forma de recuperar los resultados.  
[Recomendamos establecer el tiempo de espera de lectura del lado del cliente en `0` (sin tiempo de espera) o en un valor que sea al menos unos segundos más largo que la configuración neptune\$1query\$1timeout del lado del servidor en su clúster de base de datos Neptune.](parameters.md#parameters-db-cluster-parameters-neptune_query_timeout) Esto permite a Neptune controlar cuándo se agota el tiempo de espera de las consultas.  
También recomendamos establecer el número máximo de reintentos en `1` (sin reintentos). Si el SDK vuelve a intentar una consulta que aún se está ejecutando en el servidor, es posible que se dupliquen las operaciones. Esto es especialmente importante en el caso de las consultas de mutación, en las que un reintento podría provocar escrituras duplicadas no deseadas.

------
#### [ Python ]

1. Siga las [instrucciones de instalación](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html) para instalar Boto3.

1. Cree un archivo con el nombre `gremlinExample.py` y pegue el siguiente código:

   ```
   import boto3
   import json
   from botocore.config import Config
   
   # Disable the client-side read timeout and retries so that
   # Neptune's server-side neptune_query_timeout controls query duration.
   client = boto3.client(
       'neptunedata',
       endpoint_url=f'https://YOUR_NEPTUNE_HOST:YOUR_NEPTUNE_PORT',
       config=Config(read_timeout=None, retries={'total_max_attempts': 1})
   )
   
   # Use the untyped GraphSON v3 serializer for a cleaner JSON response.
   response = client.execute_gremlin_query(
       gremlinQuery='g.V().limit(1)',
       serializer='application/vnd.gremlin-v3.0+json;types=false'
   )
   
   print(json.dumps(response['result'], indent=2))
   ```

1. Ejecute el ejemplo: `python gremlinExample.py`

------
#### [ Java ]

1. Siga las [instrucciones de instalación](https://docs.aws.amazon.com//sdk-for-java/latest/developer-guide/setup.html) para configurar el AWS SDK para Java.

1. Usa el siguiente código para configurar una consulta de Gremlin`NeptunedataClient`, ejecutar una e imprimir el resultado:

   ```
   import java.net.URI;
   import java.time.Duration;
   import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
   import software.amazon.awssdk.core.retry.RetryPolicy;
   import software.amazon.awssdk.services.neptunedata.NeptunedataClient;
   import software.amazon.awssdk.services.neptunedata.model.ExecuteGremlinQueryRequest;
   import software.amazon.awssdk.services.neptunedata.model.ExecuteGremlinQueryResponse;
   
   // Disable the client-side timeout and retries so that
   // Neptune's server-side neptune_query_timeout controls query duration.
   NeptunedataClient client = NeptunedataClient.builder()
       .endpointOverride(URI.create("https://YOUR_NEPTUNE_HOST:YOUR_NEPTUNE_PORT"))
       .overrideConfiguration(ClientOverrideConfiguration.builder()
           .apiCallTimeout(Duration.ZERO)
           .retryPolicy(RetryPolicy.none())
           .build())
       .build();
   
   // Use the untyped GraphSON v3 serializer for a cleaner JSON response.
   ExecuteGremlinQueryRequest request = ExecuteGremlinQueryRequest.builder()
       .gremlinQuery("g.V().limit(1)")
       .serializer("application/vnd.gremlin-v3.0+json;types=false")
       .build();
   
   ExecuteGremlinQueryResponse response = client.executeGremlinQuery(request);
   
   System.out.println(response.result().toString());
   ```

------
#### [ JavaScript ]

1. Siga las [instrucciones de instalación](https://docs.aws.amazon.com//sdk-for-javascript/v3/developer-guide/getting-started-nodejs.html) para configurar el AWS SDK. JavaScript Instale el paquete de cliente neptunedata:. `npm install @aws-sdk/client-neptunedata`

1. Cree un archivo con el nombre `gremlinExample.js` y pegue el siguiente código:

   ```
   import { NeptunedataClient, ExecuteGremlinQueryCommand } from "@aws-sdk/client-neptunedata";
   import { NodeHttpHandler } from "@smithy/node-http-handler";
   
   const config = {
       endpoint: "https://YOUR_NEPTUNE_HOST:YOUR_NEPTUNE_PORT",
       // Disable the client-side request timeout so that
       // Neptune's server-side neptune_query_timeout controls query duration.
       requestHandler: new NodeHttpHandler({
           requestTimeout: 0
       }),
       maxAttempts: 1
   };
   
   const client = new NeptunedataClient(config);
   
   // Use the untyped GraphSON v3 serializer for a cleaner JSON response.
   const input = {
       gremlinQuery: "g.V().limit(1)",
       serializer: "application/vnd.gremlin-v3.0+json;types=false"
   };
   
   const command = new ExecuteGremlinQueryCommand(input);
   const response = await client.send(command);
   
   console.log(JSON.stringify(response, null, 2));
   ```

1. Ejecute el ejemplo: `node gremlinExample.js`

------

# Sugerencias de consulta de Gremlin
<a name="gremlin-query-hints"></a>

Puede utilizar sugerencias de consulta para especificar estrategias de optimización y evaluación para una consulta de Gremlin concreta en Amazon Neptune. 

Las sugerencias de consulta se especifican añadiendo un paso `withSideEffect` a la consulta con la siguiente sintaxis.

```
g.withSideEffect(hint, value)
```
+ *hint*: identifica el tipo de sugerencia que se va a aplicar.
+ *value*: determina el comportamiento del aspecto del sistema considerado.

Por ejemplo, a continuación se muestra cómo incluir una sugerencia `repeatMode` en un recorrido Gremlin.

**nota**  
Todos los efectos secundarios de las sugerencias de consulta Gremlin están precedidos por `Neptune#`.

```
g.withSideEffect('Neptune#repeatMode', 'DFS').V("3").repeat(out()).times(10).limit(1).path()
```

La consulta anterior indica al motor de Neptune que recorra el gráfico *Primero por profundidad* (`DFS`) en vez del gráfico predeterminado de Neptune *Primero por amplitud* (`BFS`).

En las siguientes secciones se proporciona más información sobre las sugerencias de consulta disponibles y su uso.

**Topics**
+ [Sugerencia de consulta repeatMode de Gremlin](gremlin-query-hints-repeatMode.md)
+ [Sugerencia de consulta noReordering de Gremlin](gremlin-query-hints-noReordering.md)
+ [Sugerencia de consulta typePromotion de Gremlin](gremlin-query-hints-typePromotion.md)
+ [Sugerencia de consulta useDFE de Gremlin](gremlin-query-hints-useDFE.md)
+ [Sugerencias de consulta de Gremlin para usar la caché de resultados](gremlin-query-hints-results-cache.md)

# Sugerencia de consulta repeatMode de Gremlin
<a name="gremlin-query-hints-repeatMode"></a>

La sugerencia de consulta `repeatMode` de Neptune muestra cómo el motor de Neptune evalúa el paso `repeat()` en un recorrido de Gremlin: primero por amplitud, primero por profundidad o primero por profundidad fragmentada.

El modo de evaluación del paso `repeat()` es importante cuando se utiliza para encontrar o seguir una ruta, en lugar de simplemente repetir un paso un número limitado de veces.

## Sintaxis
<a name="gremlin-query-hints-repeatMode-syntax"></a>

La sugerencia de consulta `repeatMode` se especifica mediante la incorporación de un paso `withSideEffect` a la consulta.

```
g.withSideEffect('Neptune#repeatMode', 'mode').gremlin-traversal
```

**nota**  
Todos los efectos secundarios de las sugerencias de consulta Gremlin están precedidos por `Neptune#`.

**Modos disponibles**
+ `BFS`

  Búsqueda primero por amplitud.

  Modo de ejecución predeterminado para el paso `repeat()`. Obtiene todos los nodos del mismo nivel antes de profundizar en la ruta.

  Esta versión tiene un uso intensivo de la memoria y los límites pueden ser muy grandes. Existe un mayor riesgo de que la consulta se quede sin memoria y la cancele el motor de Neptune. Es lo que más se acerca a otras implementaciones de Gremlin.
+ `DFS`

  Búsqueda primero por profundidad.

  Sigue cada ruta hasta la profundidad máxima antes de pasar a la siguiente solución.

  Utiliza menos memoria. Puede proporcionar mejor rendimiento en situaciones como la búsqueda de una sola ruta desde un punto de partida con múltiples saltos.
+ `CHUNKED_DFS`

  Búsqueda primero por profundidad fragmentada.

  Un planteamiento híbrido que explora primero la profundidad del gráfico en fragmentos de 1.000 nodos, en lugar de 1 nodo (`DFS`) o todos los nodos (`BFS)`.

  El motor de Neptune obtendrá hasta 1000 nodos en cada nivel antes de profundizar más en la ruta.

  Se trata de un enfoque equilibrado entre la velocidad y el uso de la memoria. 

  También es útil si desea usar `BFS`, pero la consulta usa demasiada memoria.



## Ejemplo
<a name="gremlin-query-hints-repeatMode-example"></a>

En la siguiente sección se describe el efecto del modo de repetición en un recorrido Gremlin.

En Neptune, el modo predeterminado para el paso `repeat()` es realizar una estrategia de ejecución primero por amplitud (`BFS`) para todos los recorridos. 

En la mayoría de los casos, la TinkerGraph implementación usa la misma estrategia de ejecución, pero en algunos casos altera la ejecución de un recorrido. 

Por ejemplo, la TinkerGraph implementación modifica la siguiente consulta.

```
g.V("3").repeat(out()).times(10).limit(1).path()
```

El paso `repeat()` de este recorrido se "despliega" en el siguiente recorrido, lo que resulta una estrategia de primero por profundidad (`DFS`).

```
g.V(<id>).out().out().out().out().out().out().out().out().out().out().limit(1).path()
```

**importante**  
El motor de consulta de Neptune no lo hace automáticamente.

Breadth-first (`BFS`) es la estrategia de ejecución predeterminada y es similar a la que se utiliza TinkerGraph en la mayoría de los casos. Sin embargo, existen ciertos casos donde se prefieren las estrategias primero por profundidad (`DFS`).

 

**BFS (predeterminado)**  
La estrategia de ejecución predeterminada es primero por amplitud (BFS) para el operador de `repeat()`.

```
g.V("3").repeat(out()).times(10).limit(1).path()
```

El motor de Neptune explora a fondo las primeras fronteras de nueve saltos antes de buscar una solución de diez saltos. Esto es efectivo en muchos casos, como la consulta de la ruta más corta.

Sin embargo, en el ejemplo anterior, el recorrido sería mucho más rápido utilizando el modo de primero por profundidad (`DFS`) para el operador (`repeat()`).

**DFS**  
La siguiente consulta utiliza el modo de primero por profundidad (`DFS`) para el operador `repeat()`.

```
g.withSideEffect("Neptune#repeatMode", "DFS").V("3").repeat(out()).times(10).limit(1)
```

Sigue cada solución individual hasta la máxima profundidad antes de explorar la siguiente solución. 

# Sugerencia de consulta noReordering de Gremlin
<a name="gremlin-query-hints-noReordering"></a>

Cuando envía un recorrido de Gremlin, el motor de consulta de Neptune investiga la estructura del recorrido y reordena las partes de la consulta, intentando minimizar la cantidad de trabajo necesaria para la evaluación y el tiempo de respuesta de la consulta. Por ejemplo, un recorrido con múltiples restricciones, como múltiples pasos `has()`, no suele evaluarse en un orden determinado. En su lugar, se reordena después de que la consulta se compruebe con el análisis estático.

El motor de consulta de Neptune intenta identificar qué restricción es más selectiva y la ejecuta primero. Esto a menudo produce un mejor rendimiento, pero el orden en el que Neptune decide evaluar la consulta podría no ser siempre el óptimo.

Si conoce las características exactas de los datos y desea dictar manualmente el orden de ejecución de la consulta, puede utilizar la sugerencia de consulta `noReordering` de Neptune para especificar que el recorrido se evalúe en el orden indicado.

## Sintaxis
<a name="gremlin-query-hints-noReordering-syntax"></a>

La sugerencia de consulta `noReordering` se especifica mediante la incorporación de un paso `withSideEffect` a la consulta.

```
g.withSideEffect('Neptune#noReordering', true or false).gremlin-traversal
```

**nota**  
Todos los efectos secundarios de las sugerencias de consulta Gremlin están precedidos por `Neptune#`.

**Valores disponibles**
+ `true`
+ `false`

# Sugerencia de consulta typePromotion de Gremlin
<a name="gremlin-query-hints-typePromotion"></a>

Cuando envía un recorrido de Gremlin que filtra por un valor o rango numérico, el motor de consultas de Neptune debe usar normalmente la promoción de tipos cuando ejecuta la consulta. Esto significa que tiene que examinar los valores de todos los tipos que puedan contener el valor por el que se está filtrando.

Por ejemplo, si está filtrando valores iguales a 55, el motor debe buscar números enteros iguales a 55, enteros largos iguales a 55L, flotantes iguales a 55,0, etc. Cada promoción de tipo requiere una búsqueda adicional en el almacenamiento, lo que puede provocar que una consulta aparentemente simple tarde un tiempo inesperadamente largo en completarse.

Supongamos que está buscando todos los vértices con una propiedad de edad del cliente superior a 5:

```
g.V().has('customerAge', gt(5))
```

Para ejecutar ese recorrido bien, Neptune debe expandir la consulta para examinar todos los tipos numéricos a los que se pueda promover el valor que está consultando. En este caso, el filtro `gt` debe aplicarse a cualquier número entero superior a 5, a cualquier largo superior a 5L, a cualquier flotante superior a 5,0 y a cualquier doble superior a 5,0. Como cada una de estas promociones de tipos requiere una búsqueda adicional en el almacenamiento, verá varios filtros por filtro numérico cuando ejecute la consulta [API `profile` de Gremlin](gremlin-profile-api.md) y tardará mucho más en completarse de lo que cabría esperar.

A menudo, la promoción de tipos no es necesaria porque ya sabe de antemano que solo necesita encontrar valores de un tipo específico. Cuando sea así, puede acelerar considerablemente sus consultas utilizando la sugerencia de consulta `typePromotion` para desactivar la promoción de tipos.

## Sintaxis
<a name="gremlin-query-hints-typePromotion-syntax"></a>

La sugerencia de consulta `typePromotion` se especifica mediante la incorporación de un paso `withSideEffect` a la consulta.

```
g.withSideEffect('Neptune#typePromotion', true or false).gremlin-traversal
```

**nota**  
Todos los efectos secundarios de las sugerencias de consulta Gremlin están precedidos por `Neptune#`.

**Valores disponibles**
+ `true`
+ `false`

Para desactivar la promoción de tipos en la consulta anterior, utilizaría lo sigiuiente:

```
g.withSideEffect('Neptune#typePromotion', false).V().has('customerAge', gt(5))
```

# Sugerencia de consulta useDFE de Gremlin
<a name="gremlin-query-hints-useDFE"></a>

Utilice esta sugerencia de consulta para permitir el uso del DFE para ejecutar la consulta. De forma predeterminada, Neptune no usa el DFE sin que esta sugerencia de consulta esté establecida en `true`, ya que el parámetro de instancia [neptune\$1dfe\$1query\$1engine](parameters.md#parameters-instance-parameters-neptune_dfe_query_engine) tiene el valor predeterminado `viaQueryHint`. Si establece ese parámetro de instancia en `enabled`, el motor DFE se utiliza para todas las consultas excepto para las que la sugerencia de consulta `useDFE` está establecida en `false`.

Ejemplo de cómo habilitar el DFE para una consulta:

```
g.withSideEffect('Neptune#useDFE', true).V().out()
```

# Sugerencias de consulta de Gremlin para usar la caché de resultados
<a name="gremlin-query-hints-results-cache"></a>

Las siguientes sugerencias de consulta se pueden utilizar cuando la [caché de resultados de consultas](gremlin-results-cache.md) está habilitada.

## Sugerencia de consulta `enableResultCache` de Gremlin
<a name="gremlin-query-hints-results-cache-enableResultCache"></a>

La sugerencia de consulta `enableResultCache` con un valor de `true` hace que los resultados de la consulta se devuelvan desde la caché si ya se han almacenado en caché. De lo contrario, devuelve nuevos resultados y los almacena en caché hasta que se borren de la caché. Por ejemplo:

```
g.with('Neptune#enableResultCache', true)
 .V().has('genre','drama').in('likes')
```

Luego, puede acceder a los resultados en caché emitiendo exactamente la misma consulta de nuevo:

Si el valor de esta sugerencia de consulta es `false`, o si no está presente, los resultados de la consulta no se almacenan en caché. Sin embargo, si se establece en `false`, no se borran los resultados en caché existentes. Para borrar los resultados en caché, use la sugerencia `invalidateResultCache` o `invalidateResultCachekey`.

## Sugerencia de consulta `enableResultCacheWithTTL` de Gremlin
<a name="gremlin-query-hints-results-cache-enableResultCacheWithTTL"></a>

La sugerencia de consulta `enableResultCacheWithTTL` también devuelve los resultados en caché, si los hay, sin que ello afecte al TTL de los resultados que ya están en la caché. Si actualmente no hay resultados en caché, la consulta devuelve nuevos resultados y los guarda en caché durante el tiempo de vida (TTL) especificado en la sugerencia de consulta `enableResultCacheWithTTL`. Ese tiempo de vida se especifica en segundos. La consulta siguiente especifica un tiempo de vida de sesenta segundos:

```
g.with('Neptune#enableResultCacheWithTTL', 60)
 .V().has('genre','drama').in('likes')
```

Antes de que pasen los 60 segundos time-to-live, puedes usar la misma consulta (aquí,`g.V().has('genre','drama').in('likes')`) con la sugerencia de consulta `enableResultCache` o con la sugerencia de `enableResultCacheWithTTL` consulta para acceder a los resultados almacenados en caché.

**nota**  
El tiempo de vida especificado con `enableResultCacheWithTTL` no afecta a los resultados que ya se hayan almacenado en caché.  
Si los resultados se almacenaron previamente en caché utilizando `enableResultCache`, primero se debe borrar explícitamente la caché antes de que `enableResultCacheWithTTL` genere nuevos resultados y los almacene en caché durante el TTL que especifique.
Si los resultados se almacenaron previamente en caché utilizando `enableResultCachewithTTL`, ese TTL anterior debe vencer primero antes de que `enableResultCacheWithTTL` genere nuevos resultados y los almacene en caché durante el TTL que especifique.

Una vez transcurrido el tiempo de vida, se borran los resultados de la consulta en caché y, a continuación, una instancia posterior de la misma consulta devuelve nuevos resultados. Si `enableResultCacheWithTTL` se asocia a la consulta posterior, los nuevos resultados se almacenan en caché con el TTL que especifique.

## Sugerencia de consulta `invalidateResultCacheKey` de Gremlin
<a name="gremlin-query-hints-results-cache-invalidateResultCacheKey"></a>

La sugerencia de consulta `invalidateResultCacheKey` puede tomar un valor `true` o `false`. Un valor `true` hace que se borren los resultados en caché de la consulta a la que `invalidateResultCacheKey` está asociada. Por ejemplo, el siguiente ejemplo hace que se borren los resultados almacenados en caché para la clave de consulta `g.V().has('genre','drama').in('likes')`:

```
g.with('Neptune#invalidateResultCacheKey', true)
 .V().has('genre','drama').in('likes')
```

El ejemplo de consulta anterior no hace que sus nuevos resultados se almacenen en caché. Puede incluir `enableResultCache` (o`enableResultCacheWithTTL`) en la misma consulta si desea almacenar en caché los nuevos resultados después de borrar los existentes:

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#invalidateResultCacheKey', true)
 .V().has('genre','drama').in('likes')
```

## Sugerencia de consulta `invalidateResultCache` de Gremlin
<a name="gremlin-query-hints-results-cache-invalidateResultCache"></a>

La sugerencia de consulta `invalidateResultCache` puede tomar un valor `true` o `false`. Un valor `true` hace que se borren todos los resultados de la caché de resultados. Por ejemplo:

```
g.with('Neptune#invalidateResultCache', true)
 .V().has('genre','drama').in('likes')
```

El ejemplo de consulta anterior no hace que sus nuevos resultados se almacenen en caché. Puede incluir `enableResultCache` (o`enableResultCacheWithTTL`) en la misma consulta si desea almacenar en caché los nuevos resultados después de borrar por completo la caché existente:

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#invalidateResultCache', true)
 .V().has('genre','drama').in('likes')
```

## Sugerencia de consulta `numResultsCached` de Gremlin
<a name="gremlin-query-hints-results-cache-numResultsCached"></a>

La sugerencia de consulta `numResultsCached` solo se puede usar con consultas que contengan `iterate()` y especifica el número máximo de resultados que se guardarán en caché para la consulta a la que está asociada. Tenga en cuenta que los resultados almacenados en caché cuando `numResultsCached` está presente no se devuelven, solo se almacenan en caché.

Por ejemplo, la siguiente consulta especifica que se deben almacenar en caché hasta 100 de sus resultados, pero no se debe devolver ninguno de esos resultados en caché:

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#numResultsCached', 100)
 .V().has('genre','drama').in('likes').iterate()
```

A continuación, puede utilizar una consulta como la siguiente para recuperar un rango de los resultados en caché (en este caso, los diez primeros):

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#numResultsCached', 100)
 .V().has('genre','drama').in('likes').range(0, 10)
```

## Sugerencia de consulta `noCacheExceptions` de Gremlin
<a name="gremlin-query-hints-results-cache-noCacheExceptions"></a>

La sugerencia de consulta `noCacheExceptions` puede tomar un valor `true` o `false`. Un valor `true` hace que se suprima cualquier excepción relacionada con la caché de resultados. Por ejemplo:

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#noCacheExceptions', true)
 .V().has('genre','drama').in('likes')
```

En concreto, esto suprime la `QueryLimitExceededException`, que se activa si los resultados de una consulta son demasiado grandes para la caché de resultados.

# API del estado de la consulta de Gremlin
<a name="gremlin-api-status"></a>

Para obtener el estado de las consultas Gremlin, utilice HTTP `GET` o `POST` para realizar una solicitud al punto de enlace `https://your-neptune-endpoint:port/gremlin/status`. 

## Parámetros de solicitud de estado de consulta de Gremlin
<a name="gremlin-api-status-get-request"></a>
+ **queryID** (*opcional*): el identificador de una consulta de Gremlin en ejecución. Solo muestra el estado de la consulta especificada.
+ **includeWaiting** (*opcional*): devuelve el estado de todas las consultas en espera.

  Normalmente, en la respuesta solo se incluyen las consultas en ejecución, pero cuando se especifica el parámetro `includeWaiting`, también se devuelve el estado de todas las consultas en espera.

## Sintaxis de respuesta de estado de la consulta de Gremlin
<a name="gremlin-api-status-get-response-syntax"></a>

```
{
  "acceptedQueryCount": integer,
  "runningQueryCount": integer,
  "queries": [
    {
      "queryId":"guid",
      "queryEvalStats":
        {
          "waited": integer,
          "elapsed": integer,
          "cancelled": boolean
        },
      "queryString": "string"
    }
  ]
}
```

## Valores de respuesta de estado de la consulta de Gremlin
<a name="gremlin-api-status-get-response-values"></a>
+ **acceptedQueryCount**— El número de consultas que se han aceptado pero que aún no se han completado, incluidas las consultas de la cola.
+ **runningQueryCount**— El número de consultas de Gremlin que se están ejecutando actualmente.
+ **queries**: una lista de las consultas de Gremlin actuales.
+ **queryId**: un identificador GUID de la consulta. Neptune asigna automáticamente este valor de identificador a cada consulta o también puede asignar su propio identificador (consulte [Inserte un identificador personalizado en una consulta de Neptune Gremlin o SPARQL](features-query-id.md)).
+ **queryEvalStats**— Estadísticas de esta consulta.
+ **subqueries**: el número de subconsultas de esta consulta.
+ **elapsed**: el número de milisegundos que la consulta lleva en ejecución.
+ **cancelled**: true indica que se canceló la consulta.
+ **queryString**: la consulta enviada. Esta se trunca en 1024 caracteres, si supera este número.
+ **waited**: indica cuánto tiempo esperó la consulta, en milisegundos.

## Ejemplo de estado de la consulta de Gremlin
<a name="gremlin-api-status-get-example"></a>

A continuación se muestra un ejemplo de comando de estado que utiliza `curl` y HTTP `GET`.

```
curl https://your-neptune-endpoint:port/gremlin/status
```

Esta salida muestra una única consulta en ejecución.

```
{
  "acceptedQueryCount":9,
  "runningQueryCount":1,
  "queries": [
    {
      "queryId":"fb34cd3e-f37c-4d12-9cf2-03bb741bf54f",
      "queryEvalStats":
        {
          "waited": 0,
          "elapsed": 23,
          "cancelled": false
        },
      "queryString": "g.V().out().count()"
    }
  ]
}
```

# Cancelación de consultas de Gremlin
<a name="gremlin-api-status-cancel"></a>

Para obtener el estado de las consultas Gremlin, utilice HTTP `GET` o `POST` para realizar una solicitud al punto de enlace `https://your-neptune-endpoint:port/gremlin/status`.

## Parámetros de solicitud de cancelación de la consulta de Gremlin
<a name="gremlin-api-status-cancel-request"></a>
+ **cancelQuery**: obligatorio para la cancelación. Este parámetro no tiene un valor correspondiente.
+ **queryId**: el identificador de la consulta de Gremlin en ejecución que se va a cancelar.

## Ejemplo de cancelación de la consulta de Gremlin
<a name="gremlin-api-status-cancel-example"></a>

A continuación se muestra un ejemplo del comando que utiliza `curl` para cancelar una consulta.

```
curl https://your-neptune-endpoint:port/gremlin/status \
  --data-urlencode "cancelQuery" \
  --data-urlencode "queryId=fb34cd3e-f37c-4d12-9cf2-03bb741bf54f"
```

Si la cancelación se lleva a cabo correctamente, se devuelve HTTP `200` OK.

# Compatibilidad con sesiones basadas en scripts de Gremlin
<a name="access-graph-gremlin-sessions"></a>

Puede utilizar sesiones de Gremlin con transacciones implícitas en Amazon Neptune. Para obtener información sobre las sesiones de Gremlin, consulte [Considerar las sesiones](http://tinkerpop.apache.org/docs/current/reference/#sessions) en la documentación de Apache TinkerPop . En las siguientes secciones, se describe cómo utilizar las sesiones de Gremlin con Java.

**importante**  
Actualmente, el tiempo máximo durante el que Neptune puede mantener una sesión abierta es de 10 minutos. Si no cierra una sesión antes de que se agote el tiempo, la sesión expirará y todo lo que se haya hecho volverá a su estado inicial.

**Topics**
+ [Sesiones de Gremlin en la consola de Gremlin](#access-graph-gremlin-sessions-console)
+ [Sesiones de Gremlin en la variante de lenguaje Gremlin](#access-graph-gremlin-sessions-glv)

## Sesiones de Gremlin en la consola de Gremlin
<a name="access-graph-gremlin-sessions-console"></a>

Si crea una conexión remota en la consola de Gremlin sin el parámetro `session`, la conexión remota se crea en el modo *sin sesión* . En este modo, cada solicitud que se envía al servidor se trata como una transacción completa en sí misma y no se guarda ningún estado entre las solicitudes. Si se produce un error en la solicitud, solo se revierte esta.

Si crea una conexión remota que *emplea* el parámetro `session`, crea una sesión basada en script que dura hasta que se cierre la conexión remota. Cada sesión se identifica mediante un UUID único, el cual crea la consola y le devuelve.

A continuación se muestra un ejemplo de una llamada a la consola que crea una sesión. Una vez enviadas las consultas, otra llamada cierra la sesión y confirma las consultas.

**nota**  
El cliente de Gremlin siempre debe estar cerrado para liberar los recursos del servidor.

```
gremlin> :remote connect tinkerpop.server conf/neptune-remote.yaml session
  . . .
  . . .
gremlin> :remote close
```

Para obtener más información y ejemplos, consulte [las sesiones](http://tinkerpop.apache.org/docs/current/reference/#console-sessions) en la TinkerPop documentación.

Todas las consultas que ejecuta durante una sesión forman una única transacción que no se confirma hasta que todas las consultas se completan correctamente y se cierra la conexión remota. Si se produce un error con una solicitud o si no cierra la conexión en el plazo máximo de duración de una sesión que Neptune admite, la transacción de la sesión no se confirmará y todas las solicitudes de la misma se revertirán.

## Sesiones de Gremlin en la variante de lenguaje Gremlin
<a name="access-graph-gremlin-sessions-glv"></a>

En la variante de lenguaje Gremlin (GLV), tiene que crear un objeto `SessionedClient` para emitir varias consultas en una única transacción como se muestra en el siguiente ejemplo.

```
try {                              // line 1
  Cluster cluster = Cluster.open();                    // line 2
  Client client = cluster.connect("sessionName");      // line 3
   ...
   ...
} finally {
  // Always close. If there are no errors, the transaction is committed; otherwise, it's rolled back.
  client.close();
}
```

La línea 3 del ejemplo anterior crea el objeto `SessionedClient` en función de las opciones de configuración establecidas para el clúster en cuestión. La *sessionName* cadena que se pasa al método connect pasa a ser el nombre exclusivo de la sesión. Para evitar las colisiones, utilice un UUID para el nombre.

El cliente inicia una transacción de sesión cuando se inicializa. Todas las consultas que ejecuta durante el formulario de sesión se confirman solo cuando llama a `client.close( )`. Si se produce un error de nuevo con una única solicitud o si no cierra la conexión en el plazo máximo de duración de una sesión que Neptune admite, la transacción de la sesión no se confirmará y todas las solicitudes de la misma se revertirán.

**nota**  
El cliente de Gremlin siempre debe estar cerrado para liberar los recursos del servidor.

```
GraphTraversalSource g = traversal().withRemote(conn);

Transaction tx = g.tx();

// Spawn a GraphTraversalSource from the Transaction.
// Traversals spawned from gtx are executed within a single transaction.
GraphTraversalSource gtx = tx.begin();
try {
  gtx.addV('person').iterate();
  gtx.addV('software').iterate();

  tx.commit();
} finally {
    if (tx.isOpen()) {
        tx.rollback();
    }
}
```

# Transacciones de Gremlin en Neptune
<a name="access-graph-gremlin-transactions"></a>

Hay varios contextos en los que se ejecutan las [transacciones](transactions.md) de Gremlin. Al trabajar con Gremlin, es importante entender el contexto en el que se trabaja y cuáles son sus implicaciones:
+ **`Script-based`**: las solicitudes se realizan mediante cadenas de Gremlin basadas en texto, como esta:
  + Con el controlador Java y `Client.submit(string)`.
  + Con la consola de Gremlin y `:remote connect`.
  + Con la API HTTP.
+ **`Bytecode-based`**: las solicitudes se realizan utilizando el código de bytes de Gremlin serializado típico de las [variantes del lenguaje Gremlin](https://tinkerpop.apache.org/docs/current/reference/#gremlin-drivers-variants) (GLV).

  Por ejemplo, utilizando el controlador de Java, `g = traversal().withRemote(...)`.

Para cualquiera de los contextos anteriores, existe el contexto adicional de la solicitud que se envía sin sesión o vinculada a una sesión.

**nota**  
 Las transacciones de Gremlin siempre deben confirmarse o revertirse, de modo que se puedan liberar los recursos del lado del servidor. En caso de que se produzca un error durante la transacción, es importante volver a intentar toda la transacción y no solo la solicitud concreta que ha fallado. 

## Solicitudes sin sesión
<a name="access-graph-gremlin-transactions-sessionless"></a>

 Cuando no hay sesión, una solicitud equivale a una sola transacción.

En el caso de los scripts, esto implica que una o más instrucciones de Gremlin enviadas en una sola solicitud se confirmarán o anularán como una sola transacción. Por ejemplo:

```
Cluster cluster = Cluster.open();
Client client = cluster.connect(); // sessionless
// 3 vertex additions in one request/transaction:
client.submit("g.addV();g.addV();g.addV()").all().get();
```

En el caso del código de bytes, se realiza una solicitud sin sesión para cada recorrido que se genera y ejecuta desde `g`:

```
GraphTraversalSource g = traversal().withRemote(...);

// 3 vertex additions in three individual requests/transactions:
g.addV().iterate();
g.addV().iterate();
g.addV().iterate();

// 3 vertex additions in one single request/transaction:
g.addV().addV().addV().iterate();
```

## Solicitudes vinculadas a una sesión
<a name="access-graph-gremlin-transactions-session-bound"></a>

Cuando están vinculadas a una sesión, se pueden aplicar varias solicitudes en el contexto de una sola transacción.

En el caso de los scripts, esto implica que no es necesario concatenar todas las operaciones gráficas en un único valor de cadena integrado:

```
Cluster cluster = Cluster.open();
Client client = cluster.connect(sessionName); // session
try {
    // 3 vertex additions in one request/transaction:
    client.submit("g.addV();g.addV();g.addV()").all().get();
} finally {
    client.close();
}

try {
    // 3 vertex additions in three requests, but one transaction:
    client.submit("g.addV()").all().get(); // starts a new transaction with the same sessionName
    client.submit("g.addV()").all().get();
    client.submit("g.addV()").all().get();
} finally {
    client.close();
}
```

En el caso del código de bytes, después TinkerPop `3.5.x`, la transacción se puede controlar de forma explícita y la sesión se puede gestionar de forma transparente. Las variantes del lenguaje Gremlin (GLV) admiten la sintaxis `tx()` de Gremlin para utilizar `commit()` o `rollback()` en una transacción de la siguiente manera:

```
GraphTraversalSource g = traversal().withRemote(conn);

Transaction tx = g.tx();

// Spawn a GraphTraversalSource from the Transaction.
// Traversals spawned from gtx are executed within a single transaction.
GraphTraversalSource gtx = tx.begin();
try {
    gtx.addV('person').iterate();
    gtx.addV('software').iterate();

    tx.commit();
} finally {
    if (tx.isOpen()) {
        tx.rollback();
    }
}
```

Aunque el ejemplo anterior está escrito en Java, también puede utilizar esta sintaxis de `tx()` en Python, Javascript y .NET.

**aviso**  
Las consultas de solo lectura sin sesión se ejecutan bajo el aislamiento [SNAPSHOT](transactions-isolation-levels.md), pero las consultas de solo lectura que se ejecutan dentro de una transacción explícita se ejecutan bajo el aislamiento [SERIALIZABLE](transactions-isolation-levels.md). Las consultas de solo lectura que se ejecutan bajo el aislamiento `SERIALIZABLE` generan una mayor sobrecarga y pueden bloquearse o quedar bloqueadas por escrituras simultáneas, a diferencia de las que se ejecutan bajo el aislamiento `SNAPSHOT`.

# Uso de la API de Gremlin con Amazon Neptune
<a name="gremlin-api-reference"></a>

**nota**  
Amazon Neptune no admite la propiedad `bindings`.

Todas las solicitudes HTTPS de Gremlin utilizan un único punto de enlace: `https://your-neptune-endpoint:port/gremlin`. Todas las conexiones de Neptune deben usar HTTPS.

Puedes conectar la consola Gremlin a un gráfico de Neptuno directamente a través de ella. WebSockets

Para obtener más información acerca de la conexión al punto de enlace de Gremlin, consulte [Acceso al gráfico de Neptune con Gremlin](access-graph-gremlin.md).

La implementación de Gremlin en Amazon Neptune presenta detalles y diferencias específicas que debe tener en cuenta. Para obtener más información, consulte [Conformidad con los estándares de Gremlin en Amazon Neptune](access-graph-gremlin-differences.md).

[Para obtener información sobre el lenguaje Gremlin y los recorridos, consulte The Traversal en la documentación de Apache.](https://tinkerpop.apache.org/docs/current/reference/#traversal) TinkerPop 

# Almacenamiento en caché de los resultados de las consultas en Gremlin de Amazon Neptune
<a name="gremlin-results-cache"></a>

Amazon Neptune admite una caché de resultados para las consultas de Gremlin.

Puede habilitar la caché de resultados de las consultas y, a continuación, utilizar una sugerencia de consulta para almacenar en caché los resultados de una consulta de solo lectura de Gremlin.

Al volver a ejecutar la consulta, se recuperan los resultados en caché con baja latencia y sin I/O coste alguno, siempre y cuando permanezcan en la memoria caché. Esto funciona para las consultas que se envían tanto en un punto de conexión HTTP como mediante Websockets, ya sea como código de bytes o en forma de cadena.

**nota**  
Las consultas que se envían al punto de conexión del perfil no se almacenan en caché ni siquiera cuando la caché de consultas está habilitada.

Hay varias maneras de controlar el comportamiento de la caché de resultados de consultas de Neptune. Por ejemplo:
+ Puede paginar los resultados almacenados en caché en bloques.
+ Puede especificar el time-to-live (TTL) para consultas específicas.
+ Puede borrar la caché para consultas específicas.
+ Puede borrar toda la caché.
+ Puede configurarlo para que se le notifique si los resultados superan el tamaño de la caché.

La caché se mantiene mediante una política least-recently-used (LRU), lo que significa que, una vez que se llena el espacio asignado a la caché, se eliminan los least-recently-used resultados para dejar espacio cuando se almacenen nuevos resultados en la memoria caché.

**importante**  
La caché de resultados de consultas no está disponible en los tipos de instancias `t3.medium` y `t4.medium`.

## Habilitación de la caché de resultados de consultas en Neptune
<a name="gremlin-results-cache-enabling"></a>

 La caché de resultados de consultas se puede habilitar en todas las instancias de un clúster o por instancia. Para habilitar la caché de resultados en todas las instancias de un clúster, establezca el parámetro `neptune_result_cache` del `cluster-parameter-group` del clúster en `1`. Para habilitarlo en una instancia específica, establezca el parámetro `neptune_result_cache` del `instance-parameter-group` de la instancia en `1`. La configuración del grupo de parámetros del clúster sustituirá el valor del grupo de parámetros de la instancia. 

 Es necesario reiniciar todas las instancias afectadas para poder aplicar la configuración de los parámetros de la caché de resultados. Aunque puede habilitar la caché de resultados en todas las instancias de un clúster mediante el `cluster-parameter-group`, cada instancia mantiene su propia caché. La característica de caché de resultados de consultas no es una caché que abarque todo el clúster. 

Una vez que se habilita la caché de resultados, Neptune reserva una parte de la memoria actual para almacenar en caché los resultados de las consultas. Cuanto más grande sea el tipo de instancia que utilice y más memoria haya disponible, más memoria reservará Neptune para la caché.

Si la memoria caché de resultados se llena, Neptune descarta automáticamente los resultados en caché least-recently-used (LRU) para dar paso a otros nuevos.

Puede comprobar el estado actual de la caché de resultados mediante el comando [Estado de la instancia](access-graph-status.md).

## Uso de sugerencias para almacenar en caché los resultados de las consultas
<a name="gremlin-results-cache-using"></a>

Una vez que se habilita la caché de resultados de consultas, las sugerencias de consulta se utilizan para controlar el almacenamiento en caché de las consultas. Todos los ejemplos siguientes se aplican al mismo recorrido de consultas:

```
g.V().has('genre','drama').in('likes')
```

### Uso de `enableResultCache`
<a name="using-enableResultCache"></a>

Con la caché de resultados de consultas habilitada, puede almacenar en caché los resultados de una consulta de Gremlin utilizando la sugerencia de consulta `enableResultCache`, de la siguiente manera:

```
g.with('Neptune#enableResultCache', true)
 .V().has('genre','drama').in('likes')
```

A continuación, Neptune le devuelve los resultados de la consulta y también los almacena en caché. Luego, puede acceder a los resultados en caché emitiendo exactamente la misma consulta de nuevo:

```
g.with('Neptune#enableResultCache', true)
 .V().has('genre','drama').in('likes')
```

La clave de caché que identifica los resultados en caché es la propia cadena de consulta:

```
g.V().has('genre','drama').in('likes')
```

### Uso de `enableResultCacheWithTTL`
<a name="using-enableResultCacheWithTTL"></a>

Puede especificar durante cuánto tiempo se deben almacenar en caché los resultados de la consulta mediante la sugerencia de consulta `enableResultCacheWithTTL`. Por ejemplo, la siguiente consulta especifica que los resultados de la consulta deben caducar después de 120 segundos:

```
g.with('Neptune#enableResultCacheWithTTL', 120)
 .V().has('genre','drama').in('likes')
```

De nuevo, la clave de caché que identifica los resultados almacenados en caché es la cadena de consulta base:

```
g.V().has('genre','drama').in('likes')
```

Y, de nuevo, puede acceder a los resultados en caché utilizando esa cadena de consulta con la sugerencia de consulta `enableResultCache`:

```
g.with('Neptune#enableResultCache', true)
 .V().has('genre','drama').in('likes')
```

Si han transcurrido 120 segundos o más desde que los resultados se almacenaron en caché, la consulta devolverá nuevos resultados y los guardará en la caché, sin ninguno. time-to-live

También puede acceder a los resultados almacenados en caché si vuelve a ejecutar la misma consulta con la sugerencia de consulta `enableResultCacheWithTTL`. Por ejemplo:

```
g.with('Neptune#enableResultCacheWithTTL', 140)
 .V().has('genre','drama').in('likes')
```

Hasta que hayan transcurrido 120 segundos (es decir, el TTL actualmente en vigor), esta nueva consulta que utilice la sugerencia de consulta `enableResultCacheWithTTL` devolverá los resultados almacenados en caché. Transcurridos 120 segundos, devolverá nuevos resultados y los almacenará en la memoria caché durante 140 segundos. time-to-live

**nota**  
Si los resultados de una clave de consulta ya están en caché, la misma clave de consulta `enableResultCacheWithTTL` no genera nuevos resultados y no afecta a time-to-live los resultados actualmente almacenados en caché.  
Si los resultados se almacenaron previamente en caché utilizando `enableResultCache`, primero se debe borrar la caché antes de que `enableResultCacheWithTTL` genere nuevos resultados y los almacene en caché durante el TTL que especifique.
Si los resultados se almacenaron previamente en caché utilizando `enableResultCachewithTTL`, ese TTL anterior debe vencer primero antes de que `enableResultCacheWithTTL` genere nuevos resultados y los almacene en caché durante el TTL que especifique.

### Uso de `invalidateResultCacheKey`
<a name="using-invalidateResultCacheKey"></a>

Puede utilizar la sugerencia de consulta `invalidateResultCacheKey` para borrar los resultados en caché de una consulta concreta. Por ejemplo:

```
g.with('Neptune#invalidateResultCacheKey', true)
 .V().has('genre','drama').in('likes')
```

Esa consulta borra la caché de la clave de consulta, `g.V().has('genre','drama').in('likes')`, y devuelve nuevos resultados para esa consulta.

También se puede combinar `invalidateResultCacheKey` con `enableResultCache` o `enableResultCacheWithTTL`. Por ejemplo, la siguiente consulta borra los resultados actuales almacenados en caché, almacena en caché los nuevos resultados y los devuelve:

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#invalidateResultCacheKey', true)
 .V().has('genre','drama').in('likes')
```

### Uso de `invalidateResultCache`
<a name="using-invalidateResultCache"></a>

Puede usar la sugerencia de consulta `invalidateResultCache` para borrar todos los resultados en caché de la caché de resultados de consultas. Por ejemplo:

```
g.with('Neptune#invalidateResultCache', true)
 .V().has('genre','drama').in('likes')
```

Esa consulta borra la caché de resultados completa y devuelve nuevos resultados para la consulta.

También se puede combinar `invalidateResultCache` con `enableResultCache` o `enableResultCacheWithTTL`. Por ejemplo, la siguiente consulta borra toda la caché de resultados, almacena en caché los nuevos resultados de esta consulta y los devuelve:

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#invalidateResultCache', true)
 .V().has('genre','drama').in('likes')
```

## Paginación de los resultados de consultas en caché
<a name="gremlin-results-cache-paginating"></a>

Supongamos que ya ha almacenado en caché un gran número de resultados como este:

```
g.with('Neptune#enableResultCache', true)
 .V().has('genre','drama').in('likes')
```

Ahora supongamos que ejecuta la siguiente consulta de rango:

```
g.with('Neptune#enableResultCache', true)
 .V().has('genre','drama').in('likes').range(0,10)
```

Neptune busca primero la clave de caché completa, es decir, `g.V().has('genre','drama').in('likes').range(0,10)`. Si esa clave no existe, Neptune busca si hay una clave para esa cadena de consulta sin el rango (es decir, `g.V().has('genre','drama').in('likes')`). Cuando encuentra esa clave, Neptune obtiene los diez primeros resultados de su caché, según lo que especifique el rango.

**nota**  
Si usa la sugerencia de consulta `invalidateResultCacheKey` con una consulta que tiene un rango al final, Neptune borra la caché de una consulta sin el rango si no encuentra una coincidencia exacta para la consulta con el rango.

### Uso de `numResultsCached` con `.iterate()`
<a name="gremlin-results-cache-paginating-numResultsCached"></a>

Con la sugerencia de consulta `numResultsCached`, puede rellenar la caché de resultados sin devolver todos los resultados almacenados en la caché, lo que puede resultar útil cuando prefiere paginar un gran número de resultados.

La sugerencia de consulta `numResultsCached` solo funciona con las consultas que terminan en `iterate()`.

Por ejemplo, si quiere almacenar en caché los primeros 50 resultados de la consulta de ejemplo:

```
g.with("Neptune#enableResultCache", true)
 .with("Neptune#numResultsCached", 50)
 .V().has('genre','drama').in('likes').iterate()
```

En este caso, la clave de consulta de la caché es `g.with("Neptune#numResultsCached", 50).V().has('genre','drama').in('likes')`. Ahora puede recuperar los diez primeros resultados en caché con esta consulta:

```
g.with("Neptune#enableResultCache", true)
 .with("Neptune#numResultsCached", 50)
 .V().has('genre','drama').in('likes').range(0, 10)
```

Además, puede recuperar los diez resultados siguientes de la consulta de la siguiente manera:

```
g.with("Neptune#enableResultCache", true)
 .with("Neptune#numResultsCached", 50)
 .V().has('genre','drama').in('likes').range(10, 20)
```

¡No se olvide incluir la sugerencia `numResultsCached`\$1 Es una parte esencial de la clave de consulta y, por lo tanto, debe estar presente para poder acceder a los resultados almacenados en caché.

**Algunas cosas a tener en cuenta al usar `numResultsCached`**
+ **El número que proporcione con `numResultsCached` se aplica al final de la consulta.**   Esto significa, por ejemplo, que la siguiente consulta almacena en caché los resultados del rango`(1000, 1500)`:

  ```
  g.with("Neptune#enableResultCache", true)
   .with("Neptune#numResultsCached", 500)
   .V().range(1000, 2000).iterate()
  ```
+ **El número que proporcione con `numResultsCached` especifica el número máximo de resultados que se van a almacenar en caché.**   Esto significa, por ejemplo, que la siguiente consulta almacena en caché los resultados del rango`(1000, 2000)`:

  ```
  g.with("Neptune#enableResultCache", true)
   .with("Neptune#numResultsCached", 100000)
   .V().range(1000, 2000).iterate()
  ```
+ **Los resultados que almacenan en caché las consultas que terminan con `.range().iterate()` tienen su propio rango.**   Por ejemplo, supongamos que se almacenan en caché los resultados mediante una consulta como esta:

  ```
  g.with("Neptune#enableResultCache", true)
   .with("Neptune#numResultsCached", 500)
   .V().range(1000, 2000).iterate()
  ```

  Para recuperar los primeros 100 resultados de la caché, escribiría una consulta como esta:

  ```
  g.with("Neptune#enableResultCache", true)
   .with("Neptune#numResultsCached", 500)
   .V().range(1000, 2000).range(0, 100)
  ```

  Esos cien resultados equivaldrían a los resultados de la consulta base del rango `(1000, 1100)`.

## Las claves de caché de consultas que se utilizan para localizar los resultados en caché
<a name="gremlin-results-cache-query-keys"></a>

Una vez guardados en caché los resultados de una consulta, las consultas posteriores con la misma *clave de caché de consultas* recuperan los resultados de la caché en lugar de generar otros nuevos. La clave de caché de consultas de una consulta se evalúa de la siguiente manera:

1. Se omiten todas las sugerencias de consulta relacionadas con la caché, excepto `numResultsCached`.

1. Se omite un último paso `iterate()`.

1. El resto de la consulta se ordena según su representación en código de bytes.

La cadena resultante se compara con un índice de los resultados de la consulta que ya están en la caché para determinar si hay un acierto de caché para la consulta.

Por ejemplo, vamos a tomar como ejemplo esta consulta:

```
g.withSideEffect('Neptune#typePromotion', false).with("Neptune#enableResultCache", true)
 .with("Neptune#numResultsCached", 50)
 .V().has('genre','drama').in('likes').iterate()
```

Se almacenará como la versión de código de bytes de esto:

```
g.withSideEffect('Neptune#typePromotion', false)
 .with("Neptune#numResultsCached", 50)
 .V().has('genre','drama').in('likes')
```

## Excepciones relacionadas con la caché de resultados
<a name="gremlin-results-cache-exceptions"></a>

Si los resultados de una consulta que está intentando almacenar en caché son demasiado grandes para caber en la memoria caché incluso después de eliminar todo lo que estaba almacenado en caché anteriormente, Neptune genera un error `QueryLimitExceededException`. No se devuelve ningún resultado y la excepción genera el siguiente mensaje de error:

```
The result size is larger than the allocated cache,
      please refer to results cache best practices for options to rerun the query.
```

Puede suprimir este mensaje mediante la sugerencia de consulta `noCacheExceptions`, de la siguiente manera:

```
g.with('Neptune#enableResultCache', true)
 .with('Neptune#noCacheExceptions', true)
 .V().has('genre','drama').in('likes')
```

# Realización de actualizaciones o inserciones eficientes con los pasos `mergeV()` y `mergeE()` de Gremlin
<a name="gremlin-efficient-upserts"></a>

Una actualización o inserción (o inserción condicional) reutiliza un vértice o un borde si ya existe, o lo crea si no existe. Las actualizaciones o inserciones eficientes pueden marcar una diferencia significativa en el rendimiento de las consultas de Gremlin.

Las actualizaciones o inserciones permiten escribir operaciones de inserción de idempotencia; independientemente del número de veces que se ejecute una operación de este tipo, el resultado general es el mismo. Esto resulta útil en situaciones de escritura muy simultáneas, en las que las modificaciones simultáneas en la misma parte del gráfico pueden obligar a una o más transacciones a revertirse con una `ConcurrentModificationException`, por lo que es necesario volver a intentarlas.

Por ejemplo, la siguiente consulta desplaza realiza actualizaciones o inserciones en un vértice utilizando el `Map` proporcionado para intentar buscar primero un vértice con un `T.id` de `"v-1"`. Si se encuentra ese vértice, se devuelve. Si no se encuentra, se crea un vértice con ese `id` y una propiedad a través de la cláusula `onCreate`.

```
g.mergeV([(id):'v-1']).
  option(onCreate, [(label): 'PERSON', 'email': 'person-1@example.org'])
```

## Realización de actualizaciones o inserciones por lotes para mejorar el rendimiento
<a name="gremlin-upserts-batching"></a>

En escenarios de escritura de alto rendimiento, puede encadenar los pasos `mergeV()` y `mergeE()` para realizar actualizaciones o inserciones en vértices y bordes por lotes. El procesamiento por lotes reduce la sobrecarga transaccional que supone realizar actualizaciones o inserciones en un gran número de vértices y bordes. A continuación, puede mejorar aún más el rendimiento realizando actualizaciones o inserciones en solicitudes por lotes en paralelo con varios clientes.

Como regla general, recomendamos modificar aproximadamente 200 registros por solicitud en lote. Un registro es una propiedad o etiqueta de un solo vértice o borde. Un vértice con una sola etiqueta y 4 propiedades, por ejemplo, crea 5 registros. Un borde con una etiqueta y una sola propiedad crea 2 registros. Si desea realizar actualizaciones o inserciones en lotes de vértices, cada uno con una sola etiqueta y 4 propiedades, debería empezar con un tamaño de lote de 40, ya que `200 / (1 + 4) = 40`.

Puede experimentar con el tamaño del lote. Un buen punto de partida son 200 registros por lote, pero el tamaño de lote ideal puede ser mayor o menor en función de la carga de trabajo. Sin embargo, tenga en cuenta que Neptune puede limitar el número total de pasos de Gremlin por solicitud. Este límite no está documentado, pero por si acaso, intente asegurarse de que sus solicitudes no contengan más de 1500 pasos de Gremlin. Neptune puede rechazar solicitudes por lotes grandes con más de 1500 pasos.

Para aumentar el rendimiento, puede realizar actualizaciones o inserciones en los lotes en paralelo utilizando varios clientes (consulte [Creación de escrituras de Gremlin eficientes de múltiples subprocesos](best-practices-gremlin-multithreaded-writes.md)). El número de clientes debe ser el mismo que el número de subprocesos de trabajo de la instancia de Neptune Writer, que normalmente es 2 veces el número de v del CPUs servidor. Por ejemplo, una `r5.8xlarge` instancia tiene 32 subprocesos de trabajo CPUs y 64 subprocesos de trabajo. En escenarios de escritura de alto rendimiento en los que se usa un `r5.8xlarge`, utilizaría 64 clientes que escriben actualizaciones o inserciones por lotes en Neptune en paralelo.

Cada cliente debe enviar una solicitud por lotes y esperar a que se complete antes de enviar otra. Aunque los múltiples clientes se ejecutan en paralelo, cada cliente individual envía las solicitudes en serie. Esto garantiza que el servidor reciba un flujo constante de solicitudes que ocupen todos los subprocesos de trabajadores sin saturar la cola de solicitudes del lado del servidor (consulte [Dimensionamiento de las instancias de base de datos en un clúster de base de datos de Neptune](feature-overview-db-clusters.md#feature-overview-sizing-instances)).

## Intente evitar pasos que generen múltiples recorridos
<a name="gremlin-upserts-single-traverser"></a>

Cuando se ejecuta un paso de Gremlin, toma un recorrido entrante y emite uno o más recorridos de salida. El número de entrante que emite un paso determina el número de veces que se ejecuta el siguiente paso.

Por lo general, cuando se realizan operaciones por lotes, se desea que cada operación, como el vértice de actualización o inserción A, se ejecute una vez, de modo que la secuencia de operaciones tenga el siguiente aspecto: vértice de actualización o inserción A, vértice de actualización o inserción B, vértice de actualización o inserción C, etc. Mientras que un paso cree o modifique solo un elemento, solo emite un recorrido y los pasos que representan la siguiente operación se ejecutan solo una vez. Si, por otro lado, una operación crea o modifica más de un elemento, emite varios recorridos, lo que a su vez provoca que los pasos siguientes se ejecuten varias veces, una vez por recorrido emitido. Esto puede provocar que la base de datos realice un trabajo adicional innecesario y, en algunos casos, que se creen vértices, bordes o valores de propiedad adicionales no deseados.

Un ejemplo de cómo esto puede ir mal es con una consulta como `g.V().addV()`. Esta sencilla consulta añade un vértice por cada vértice que se encuentre en el gráfico, ya que `V()` emite un recorrido para cada vértice del gráfico y cada uno de esos recorridos activa una llamada a `addV()`.

Consulte en [Combinación de actualizaciones o inserciones e inserciones](#gremlin-upserts-and-inserts) las formas de gestionar las operaciones que pueden emitir varios recorridos.

## Actualizaciones o inserciones en vértices
<a name="gremlin-upserts-vertices"></a>

El paso `mergeV()` está diseñado específicamente para realizar opciones actualizaciones o inserciones en vértices. Toma como argumento un `Map` que representa los elementos que deben coincidir con los vértices existentes en el gráfico y, si no se encuentra un elemento, usa ese `Map` para crear un nuevo vértice. El paso también le permite alterar el comportamiento en caso de una creación o una coincidencia, donde el modulador `option()` se puede aplicar con los tokens `Merge.onCreate` y `Merge.onMatch` para controlar esos comportamientos respectivos. Consulte la [documentación de TinkerPop referencia](https://tinkerpop.apache.org/docs/current/reference/#mergevertex-step) para obtener más información sobre cómo utilizar este paso.

Puede usar un identificador de vértice para determinar si existe un vértice específico. Este es el enfoque preferido, ya que Neptune optimiza los upserts para casos de uso altamente concurrentes. IDs Por ejemplo, la siguiente consulta crea un vértice con un identificador de vértice determinado si aún no existe, o lo reutiliza si ya existe:

```
g.mergeV([(T.id): 'v-1']).
    option(onCreate, [(T.label): 'PERSON', email: 'person-1@example.org', age: 21]).
    option(onMatch, [age: 22]).
  id()
```

Tenga en cuenta que esta consulta termina con un paso `id()`. Si bien no es estrictamente necesario para realizar una actualización o inserción en el vértice, un paso `id()` hasta el final de una consulta de actualización o inserción garantiza que el servidor no vuelva a serializar todas las propiedades del vértice para el cliente, lo que ayuda a reducir el costo de bloqueo de la consulta.

También puede utilizar una propiedad de vértice para identificar un vértice:

```
g.mergeV([email: 'person-1@example.org']).
    option(onCreate, [(T.label): 'PERSON', age: 21]).
    option(onMatch, [age: 22]).
  id()
```

Si es posible, utilice los vértices proporcionados por el usuario IDs para crear vértices y utilícelos para determinar si existe un vértice durante una IDs operación de upsert. Esto permite a Neptune optimizar las actualizaciones o inserciones. Una actualización o inserción basada en identificador puede ser considerablemente más eficiente que una actualización o inserción basada en propiedades cuando las modificaciones simultáneas son frecuentes.

### Encadenamiento de actualizaciones o inserciones de vértices
<a name="gremlin-upserts-vertices-chaining"></a>

Puede encadenar actualizaciones o inserciones de vértices para insertarlos en un lote:

```
g.V('v-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-1')
                         .property('email', 'person-1@example.org'))
 .V('v-2')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-2')
                         .property('email', 'person-2@example.org'))
 .V('v-3')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-3')
                         .property('email', 'person-3@example.org'))
 .id()
```

También puede utilizar esta sintaxis de `mergeV()`:

```
g.mergeV([(T.id): 'v-1', (T.label): 'PERSON', email: 'person-1@example.org']).
  mergeV([(T.id): 'v-2', (T.label): 'PERSON', email: 'person-2@example.org']).
  mergeV([(T.id): 'v-3', (T.label): 'PERSON', email: 'person-3@example.org'])
```

Sin embargo, dado que esta forma de consulta incluye elementos en los criterios de búsqueda que son superfluos para la búsqueda básica de `id`, no es tan eficaz como la consulta anterior.

## Actualizaciones o inserciones de bordes
<a name="gremlin-upserts-edges"></a>

El paso `mergeE()` está diseñado específicamente para realizar actualizaciones o inserciones de bordes. Toma como argumento un `Map` que representa los elementos que deben coincidir con los bordes existentes en el gráfico y, si no se encuentra un elemento, usa ese `Map` para crear un nuevo borde. El paso también le permite alterar el comportamiento en caso de una creación o una coincidencia, donde el modulador `option()` se puede aplicar con los tokens `Merge.onCreate` y `Merge.onMatch` para controlar esos comportamientos respectivos. Consulte la [documentación de TinkerPop referencia](https://tinkerpop.apache.org/docs/current/reference/#mergeedge-step) para obtener más información sobre cómo utilizar este paso.

Puede utilizar la arista IDs para realzar las aristas de la misma manera que se moldean los vértices con un vértice personalizado. IDs De nuevo, este es el enfoque preferido porque permite a Neptune optimizar la consulta. Por ejemplo, la siguiente consulta crea un borde en función de su identificador de borde si aún no existe, o lo reutiliza si ya existe. La consulta también utiliza los IDs `Direction.to` vértices `Direction.from` y si necesita crear una arista nueva:

```
g.mergeE([(T.id): 'e-1']).
    option(onCreate, [(from): 'v-1', (to): 'v-2', weight: 1.0]).
    option(onMatch, [weight: 0.5]).
  id()
```

Tenga en cuenta que esta consulta termina con un paso `id()`. Si bien no es estrictamente necesario para realizar una actualización o inserción del borde, un paso `id()` hasta el final de una consulta de actualización o inserción garantiza que el servidor no vuelva a serializar todas las propiedades del borde para el cliente, lo que ayuda a reducir el costo de bloqueo de la consulta.

Muchas aplicaciones utilizan un vértice personalizado IDs, pero dejan que Neptune genere el borde. IDs Si no conoces el identificador de una arista, pero sí el `to` vértice `from` y IDs, puedes usar este tipo de consulta para cambiar una arista:

```
g.mergeE([(from): 'v-1', (to): 'v-2', (T.label): 'KNOWS']).
  id()
```

Todos los vértices a los que hace referencia `mergeE()` deben existir para que el paso cree el borde.

### Encadenamiento de actualizaciones o inserciones de bordes
<a name="gremlin-upserts-edges-chaining"></a>

Al igual que con las actualizaciones o inserciones de vértices, es sencillo encadenar los pasos `mergeE()` para las solicitudes por lotes:

```
g.mergeE([(from): 'v-1', (to): 'v-2', (T.label): 'KNOWS']).
  mergeE([(from): 'v-2', (to): 'v-3', (T.label): 'KNOWS']).
  mergeE([(from): 'v-3', (to): 'v-4', (T.label): 'KNOWS']).
  id()
```

## Combinación de actualizaciones o inserciones de vértices y bordes
<a name="gremlin-upserts-vertexes-and-edges"></a>

A veces, es posible que desee realizar actualizaciones o inserciones en ambos vértices y los bordes que los conectan. Puede combinar los ejemplos de lotes que se presentan aquí. En el siguiente ejemplo, se realizan actualizaciones o inserciones de 3 vértices y 2 bordes:

```
g.mergeV([(id):'v-1']).
    option(onCreate, [(label): 'PERSON', 'email': 'person-1@example.org']).
  mergeV([(id):'v-2']).
    option(onCreate, [(label): 'PERSON', 'email': 'person-2@example.org']).
  mergeV([(id):'v-3']).
    option(onCreate, [(label): 'PERSON', 'email': 'person-3@example.org']).
  mergeE([(from): 'v-1', (to): 'v-2', (T.label): 'KNOWS']).
  mergeE([(from): 'v-2', (to): 'v-3', (T.label): 'KNOWS']).
 id()
```

## Combinación de actualizaciones o inserciones e inserciones
<a name="gremlin-upserts-and-inserts"></a>

A veces, es posible que desee realizar actualizaciones o inserciones en ambos vértices y los bordes que los conectan. Puede combinar los ejemplos de lotes que se presentan aquí. En el siguiente ejemplo, se realizan actualizaciones o inserciones de 3 vértices y 2 bordes:

Por lo general, las actualizaciones o inserciones se realizan con un elemento a la vez. Si sigue los patrones de actualización o inserción que se presentan aquí, cada operación de actualización o inserción emite un único recorrido, lo que hace que la siguiente operación se ejecute solo una vez.

Sin embargo, a veces es posible que desee mezclar actualizaciones o inserciones con inserciones. Podría ser así, por ejemplo, si utiliza bordes para representar instancias de acciones o eventos. Una solicitud puede usar actualizaciones o inserciones para garantizar que existan todos los vértices necesarios y, a continuación, usar inserciones para añadir bordes. En el caso de solicitudes de este tipo, preste atención a la cantidad potencial de recorridos que emite cada operación.

Examine el siguiente ejemplo, en el que se combinan actualizaciones o inserciones e inserciones para añadir bordes que representen eventos en el gráfico:

```
// Fully optimized, but inserts too many edges
g.mergeV([(id):'v-1']).
    option(onCreate, [(label): 'PERSON', 'email': 'person-1@example.org']).
  mergeV([(id):'v-2']).
    option(onCreate, [(label): 'PERSON', 'email': 'person-2@example.org']).
  mergeV([(id):'v-3']).
    option(onCreate, [(label): 'PERSON', 'email': 'person-3@example.org']).
  mergeV([(T.id): 'c-1', (T.label): 'CITY', name: 'city-1']).
  V('p-1', 'p-2').
  addE('FOLLOWED').to(V('p-1')).
  V('p-1', 'p-2', 'p-3').
  addE('VISITED').to(V('c-1')).
  id()
```

La consulta debe insertar 5 bordes: 2 bordes FOLLOWED y 3 bordes VISITED. Sin embargo, la consulta tal como está escrita inserta 8 bordes: 2 FOLLOWED y 6 VISITED. Esto se debe a que la operación que inserta los 2 bordes FOLLOWED emite 2 recorridos, lo que provoca que la siguiente operación de inserción, que inserta 3 bordes, se ejecute dos veces.

La solución consiste en añadir un paso `fold()` después de cada operación que pueda emitir más de un recorrido:

```
g.mergeV([(T.id): 'v-1', (T.label): 'PERSON', email: 'person-1@example.org']).
  mergeV([(T.id): 'v-2', (T.label): 'PERSON', email: 'person-2@example.org']).
  mergeV([(T.id): 'v-3', (T.label): 'PERSON', email: 'person-3@example.org']).
  mergeV([(T.id): 'c-1', (T.label): 'CITY', name: 'city-1']).
  V('p-1', 'p-2').
  addE('FOLLOWED').
    to(V('p-1')).
  fold().
  V('p-1', 'p-2', 'p-3').
  addE('VISITED').
    to(V('c-1')).
  id()
```

Aquí hemos insertado un paso `fold()` después de la operación que inserta los bordes FOLLOWED. Esto da como resultado un único recorrido, lo que hace que la siguiente operación se ejecute solo una vez.

La desventaja de este enfoque es que la consulta ahora no se optimiza por completo, porque `fold()` no se optimiza. La siguiente operación de inserción que sigue `fold()` ahora tampoco se optimizará.

Si necesita usar `fold()` para reducir el número de recorridos para los pasos posteriores, intente ordenar las operaciones de manera que las menos costosas ocupen la parte no optimizada de la consulta.

## Configuración de la cardinalidad
<a name="gremlin-upserts-setting-cardinality"></a>

 Las propiedades de los vértices en Neptune tienen una cardinalidad predeterminada, lo que significa que al usar mergeV(), todos los valores proporcionados en el mapa tendrán esa cardinalidad. Para usar una cardinalidad única, debe indicarlo de manera explícita. A partir de la TinkerPop versión 3.7.0, hay una nueva sintaxis que permite proporcionar la cardinalidad como parte del mapa, como se muestra en el siguiente ejemplo: 

```
g.mergeV([(T.id): '1234']).
  option(onMatch, ['age': single(20), 'name': single('alice'), 'city': set('miami')])
```

 Como alternativa, puede configurar la cardinalidad como predeterminada para esa `option` de la siguiente manera: 

```
// age and name are set to single cardinality by default
g.mergeV([(T.id): '1234']).
  option(onMatch, ['age': 22, 'name': 'alice', 'city': set('boston')], single)
```

 Hay menos opciones para establecer la cardinalidad en `mergeV()` antes de la versión 3.7.0. El enfoque general consiste en recurrir al paso `property()` de la siguiente manera: 

```
g.mergeV([(T.id): '1234']). 
  option(onMatch, sideEffect(property(single,'age', 20).
  property(set,'city','miami')).constant([:]))
```

**nota**  
 Este enfoque solo funcionará cuando `mergeV()` se utilice con un paso de inicio. Por lo tanto, no será posible encadenar `mergeV()` dentro de un único recorrido, ya que el primer `mergeV()` después del paso inicial que utilice esta sintaxis producirá un error si el recorrido entrante es un elemento del gráfico. En este caso, conviene dividir las llamadas a `mergeV()` en varias solicitudes, cada una de las cuales puede ser un paso inicial. 

# Realización de actualizaciones o inserciones de Gremlin eficientes con `fold()/coalesce()/unfold()`
<a name="gremlin-efficient-upserts-pre-3.6"></a>

Una actualización o inserción (o inserción condicional) reutiliza un vértice o un borde si ya existe, o lo crea si no existe. Las actualizaciones o inserciones eficientes pueden marcar una diferencia significativa en el rendimiento de las consultas de Gremlin.

En esta página se muestra cómo usar el patrón de Gremlin `fold()/coalesce()/unfold()` para crear actualizaciones o inserciones eficientes. Sin embargo, con el lanzamiento de la TinkerPop versión 3.6.x introducido en Neptune en la versión [1.2.1.0](engine-releases-1.2.1.0.md) del motor, en la mayoría de los casos son preferibles lo nuevo `mergeV()` y los `mergeE()` escalones. El patrón `fold()/coalesce()/unfold()` descrito aquí podría seguir siendo útil en algunas situaciones complejas, pero en general, utilice `mergeV()` y `mergeE()` si es posible, como se describe en [Realización de actualizaciones o inserciones eficientes con los pasos `mergeV()` y `mergeE()` de Gremlin](gremlin-efficient-upserts.md).

Las actualizaciones o inserciones permiten escribir operaciones de inserción de idempotencia; independientemente del número de veces que se ejecute una operación de este tipo, el resultado general es el mismo. Esto resulta útil en situaciones de escritura muy simultáneas, en las que las modificaciones simultáneas en la misma parte del gráfico pueden obligar a una o más transacciones a revertirse con una `ConcurrentModificationException`, por lo que es necesario volver a intentarlas.

Por ejemplo, la siguiente consulta realiza actualizaciones o inserciones en un vértice. Para ello, busca primero el vértice especificado en el conjunto de datos y, a continuación, pliega los resultados en una lista. En el primer recorrido proporcionado al paso `coalesce()`, la consulta despliega esta lista. Si la lista desplegada no está vacía, los resultados se emiten desde `coalesce()`. Sin embargo, si `unfold()` devuelve una colección vacía porque el vértice no existe actualmente, `coalesce()` pasa a evaluar el segundo recorrido con el que se ha proporcionado y, en este segundo recorrido , la consulta crea el vértice que falta.

```
g.V('v-1').fold()
          .coalesce(
             unfold(),
             addV('Person').property(id, 'v-1')
                           .property('email', 'person-1@example.org')
           )
```

## Utilice una forma optimizada de `coalesce()` para las actualizaciones o inserciones
<a name="gremlin-upserts-pre-3.6-coalesce"></a>

Neptune puede optimizar la expresión `fold().coalesce(unfold(), ...)` para realizar actualizaciones de alto rendimiento, pero esta optimización solo funciona si ambas partes de `coalesce()` devuelven un vértice o un borde, pero nada más. Si intenta devolver algo diferente, como una propiedad, desde cualquier parte del `coalesce()`, no se produce la optimización de Neptune. Es posible que la consulta se realice correctamente, pero no funcionará tan bien como en una versión optimizada, especialmente en conjuntos de datos de gran tamaño.

Dado que las consultas de actualización o inserción no optimizadas aumentan los tiempos de ejecución y reducen el rendimiento, vale la pena utilizar el punto de conexión `explain` de Gremlin para determinar si una consulta de actualización o inserción está totalmente optimizada. Al revisar los planes `explain`, busque líneas que comiencen por `+ not converted into Neptune steps` y `WARNING: >>`. Por ejemplo:

```
+ not converted into Neptune steps: [FoldStep, CoalesceStep([[UnfoldStep], [AddEdgeSte...
WARNING: >> FoldStep << is not supported natively yet
```

Estas advertencias pueden ayudarle a identificar las partes de una consulta que impiden que se optimice por completo.

A veces, no es posible optimizar una consulta por completo. En estas situaciones, debe intentar colocar los pasos que no se pueden optimizar al final de la consulta, lo que permitirá que el motor optimice tantos pasos como sea posible. Esta técnica se utiliza en algunos de los ejemplos de actualizaciones o inserciones por lotes, en los que todas las actualizaciones o inserciones optimizadas para un conjunto de vértices o bordes se realizan antes de aplicar cualquier modificación adicional, potencialmente no optimizada, a los mismos vértices o bordes.

## Realización de actualizaciones o inserciones por lotes para mejorar el rendimiento
<a name="gremlin-upserts-pre-3.6-batching"></a>

En escenarios de escritura de alto rendimiento, puede encadenar pasos de actualizaciones o inserciones para realizar actualizaciones o inserciones en vértices y bordes por lotes. El procesamiento por lotes reduce la sobrecarga transaccional que supone realizar actualizaciones o inserciones en un gran número de vértices y bordes. A continuación, puede mejorar aún más el rendimiento realizando actualizaciones o inserciones en solicitudes por lotes en paralelo con varios clientes.

Como regla general, recomendamos modificar aproximadamente 200 registros por solicitud en lote. Un registro es una propiedad o etiqueta de un solo vértice o borde. Un vértice con una sola etiqueta y 4 propiedades, por ejemplo, crea 5 registros. Un borde con una etiqueta y una sola propiedad crea 2 registros. Si desea realizar actualizaciones o inserciones en lotes de vértices, cada uno con una sola etiqueta y 4 propiedades, debería empezar con un tamaño de lote de 40, ya que `200 / (1 + 4) = 40`.

Puede experimentar con el tamaño del lote. Un buen punto de partida son 200 registros por lote, pero el tamaño de lote ideal puede ser mayor o menor en función de la carga de trabajo. Sin embargo, tenga en cuenta que Neptune puede limitar el número total de pasos de Gremlin por solicitud. Este límite no está documentado, pero por si acaso, intente asegurarse de que sus solicitudes no contengan más de 1500 pasos de Gremlin. Neptune puede rechazar solicitudes por lotes grandes con más de 1500 pasos.

Para aumentar el rendimiento, puede realizar actualizaciones o inserciones en los lotes en paralelo utilizando varios clientes (consulte [Creación de escrituras de Gremlin eficientes de múltiples subprocesos](best-practices-gremlin-multithreaded-writes.md)). El número de clientes debe ser el mismo que el número de subprocesos de trabajo de la instancia de Neptune Writer, que normalmente es 2 veces el número de v del CPUs servidor. Por ejemplo, una `r5.8xlarge` instancia tiene 32 subprocesos de trabajo CPUs y 64 subprocesos de trabajo. En escenarios de escritura de alto rendimiento en los que se usa un `r5.8xlarge`, utilizaría 64 clientes que escriben actualizaciones o inserciones por lotes en Neptune en paralelo.

Cada cliente debe enviar una solicitud por lotes y esperar a que se complete antes de enviar otra. Aunque los múltiples clientes se ejecutan en paralelo, cada cliente individual envía las solicitudes en serie. Esto garantiza que el servidor reciba un flujo constante de solicitudes que ocupen todos los subprocesos de trabajadores sin saturar la cola de solicitudes del lado del servidor (consulte [Dimensionamiento de las instancias de base de datos en un clúster de base de datos de Neptune](feature-overview-db-clusters.md#feature-overview-sizing-instances)).

## Intente evitar pasos que generen múltiples recorridos
<a name="gremlin-upserts-pre-3.6-single-traverser"></a>

Cuando se ejecuta un paso de Gremlin, toma un recorrido entrante y emite uno o más recorridos de salida. El número de entrante que emite un paso determina el número de veces que se ejecuta el siguiente paso.

Por lo general, cuando se realizan operaciones por lotes, se desea que cada operación, como el vértice de actualización o inserción A, se ejecute una vez, de modo que la secuencia de operaciones tenga el siguiente aspecto: vértice de actualización o inserción A, vértice de actualización o inserción B, vértice de actualización o inserción C, etc. Mientras que un paso cree o modifique solo un elemento, solo emite un recorrido y los pasos que representan la siguiente operación se ejecutan solo una vez. Si, por otro lado, una operación crea o modifica más de un elemento, emite varios recorridos, lo que a su vez provoca que los pasos siguientes se ejecuten varias veces, una vez por recorrido emitido. Esto puede provocar que la base de datos realice un trabajo adicional innecesario y, en algunos casos, que se creen vértices, bordes o valores de propiedad adicionales no deseados.

Un ejemplo de cómo esto puede ir mal es con una consulta como `g.V().addV()`. Esta sencilla consulta añade un vértice por cada vértice que se encuentre en el gráfico, ya que `V()` emite un recorrido para cada vértice del gráfico y cada uno de esos recorridos activa una llamada a `addV()`.

Consulte en [Combinación de actualizaciones o inserciones e inserciones](#gremlin-upserts-pre-3.6-and-inserts) las formas de gestionar las operaciones que pueden emitir varios recorridos.

## Actualizaciones o inserciones en vértices
<a name="gremlin-upserts-pre-3.6-vertices"></a>

Puede usar un identificador de vértice para determinar si existe un vértice correspondiente. Este es el enfoque preferido, ya que Neptune optimiza los upserts para casos de uso altamente concurrentes. IDs Por ejemplo, la siguiente consulta crea un vértice con un identificador de vértice determinado si aún no existe, o lo reutiliza si ya existe:

```
g.V('v-1')
 .fold()
  .coalesce(unfold(),
            addV('Person').property(id, 'v-1')
                          .property('email', 'person-1@example.org'))
  .id()
```

Tenga en cuenta que esta consulta termina con un paso `id()`. Si bien no es estrictamente necesario para realizar una actualización o inserción en el vértice, añadir un paso `id()` hasta el final de una consulta de actualización o inserción garantiza que el servidor no vuelva a serializar todas las propiedades del vértice para el cliente, lo que ayuda a reducir el costo de bloqueo de la consulta.

También puede utilizar una propiedad de vértice para determinar si el vértice existe:

```
g.V()
 .hasLabel('Person')
 .has('email', 'person-1@example.org')
 .fold()
 .coalesce(unfold(),
           addV('Person').property('email', 'person-1@example.org'))
 .id()
```

Si es posible, utilice los vértices proporcionados por el usuario IDs para crear vértices y utilícelos para determinar si existe un vértice durante una IDs operación de upsert. Esto permite a Neptune optimizar las perturbaciones alrededor del. IDs Una actualización o inserción basada en identificador puede ser considerablemente más eficiente que una actualización o inserción basada en propiedades en escenarios con una gran simultaneidad en las modificaciones.

### Encadenamiento de actualizaciones o inserciones de vértices
<a name="gremlin-upserts-pre-3.6-vertices-chaining"></a>

Puede encadenar actualizaciones o inserciones de vértices para insertarlos en un lote:

```
g.V('v-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-1')
                         .property('email', 'person-1@example.org'))
 .V('v-2')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-2')
                         .property('email', 'person-2@example.org'))
 .V('v-3')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-3')
                         .property('email', 'person-3@example.org'))
 .id()
```

## Actualizaciones o inserciones de bordes
<a name="gremlin-upserts-pre-3.6-edges"></a>

Puede usar el borde IDs para alterar los bordes de la misma manera que para desviar los vértices con un vértice personalizado. IDs De nuevo, este es el enfoque preferido porque permite a Neptune optimizar la consulta. Por ejemplo, la siguiente consulta crea un borde en función de su identificador de borde si aún no existe, o lo reutiliza si ya existe. La consulta también utiliza los IDs `to` vértices `from` y si necesita crear una arista nueva.

```
g.E('e-1')
 .fold()
 .coalesce(unfold(),
           addE('KNOWS').from(V('v-1'))
                        .to(V('v-2'))
                        .property(id, 'e-1'))
 .id()
```

Muchas aplicaciones utilizan un vértice personalizado IDs, pero dejan que Neptune genere el borde. IDs Si no conoce el identificador de una arista, pero sí conoce el `to` vértice `from` y IDs, puede utilizar esta formulación para descomponer una arista:

```
g.V('v-1')
 .outE('KNOWS')
 .where(inV().hasId('v-2'))
 .fold()
 .coalesce(unfold(),
           addE('KNOWS').from(V('v-1'))
                        .to(V('v-2')))
 .id()
```

Tenga en cuenta que el paso de vértice de la cláusula `where()` debería ser `inV()` (o `outV()` si ha utilizado `inE()` para buscar el borde), no `otherV()`. No utilice `otherV()` aquí o la consulta no se optimizará y el rendimiento se verá afectado. Por ejemplo, Neptune no optimizaría la siguiente consulta:

```
// Unoptimized upsert, because of otherV()
g.V('v-1')
 .outE('KNOWS')
 .where(otherV().hasId('v-2'))
 .fold()
 .coalesce(unfold(),
           addE('KNOWS').from(V('v-1'))
                        .to(V('v-2')))
 .id()
```

Si no conoce la arista o el vértice de la parte delantera, puede colocarlos IDs en posición vertical utilizando las propiedades del vértice:

```
g.V()
 .hasLabel('Person')
 .has('name', 'person-1')
 .outE('LIVES_IN')
 .where(inV().hasLabel('City').has('name', 'city-1'))
 .fold()
 .coalesce(unfold(),
           addE('LIVES_IN').from(V().hasLabel('Person')
                                    .has('name', 'person-1'))
                           .to(V().hasLabel('City')
                                  .has('name', 'city-1')))
 .id()
```

Al igual que con los upserts de vértices, es preferible utilizar los upserts de borde basados en ID utilizando un ID de borde o `from` un `to` vértice IDs, en lugar de los upserts basados en propiedades, de modo que Neptune pueda optimizar completamente el upsert.

### Comprobación de la existencia de vértices `from` y `to`
<a name="gremlin-upserts-pre-3.6-edges-checking"></a>

Observe la construcción de los pasos que permiten crear un nuevo borde: `addE().from().to()`. Esta construcción garantiza que la consulta compruebe la existencia tanto del vértice `from` como `to`. Si alguna de estas opciones no existe, la consulta devuelve el siguiente error:

```
{
  "detailedMessage": "Encountered a traverser that does not map to a value for child...
  "code": "IllegalArgumentException",
  "requestId": "..."
}
```

Si es posible que el vértice `from` o `to` no existan, debería intentar realizar actualizaciones o inserciones en ellos antes de realizar actualizaciones o inserciones en el borde entre ellos. Consulte [Combinación de actualizaciones o inserciones de vértices y bordes](#gremlin-upserts-pre-3.6-vertexes-and-edges).

Existe una construcción alternativa para crear un borde que no debería usar: `V().addE().to()`. Solo añade un borde si existe el vértice `from`. Si el vértice `to` no existe, la consulta genera un error, como se ha descrito anteriormente, pero si el vértice `from` no existe, falla de forma silenciosa al insertar un borde, sin generar ningún error. Por ejemplo, si el vértice `from` no existe, la siguiente actualización o inserción se completa sin realizar actualizaciones o inserciones en un borde:

```
// Will not insert edge if from vertex does not exist
g.V('v-1')
 .outE('KNOWS')
 .where(inV().hasId('v-2'))
 .fold()
 .coalesce(unfold(),
           V('v-1').addE('KNOWS')
                   .to(V('v-2')))
 .id()
```

### Encadenamiento de actualizaciones o inserciones de bordes
<a name="gremlin-upserts-pre-3.6-edges-chaining"></a>

Si desea encadenar las direcciones verticales de arista para crear una solicitud por lotes, debe comenzar cada posición vertical con una búsqueda de vértices, incluso si ya conoce la arista. IDs

Si ya sabe cuáles son IDs las aristas que desea intercalar y los `to` vértices `from` y, puede IDs utilizar esta formulación:

```
g.V('v-1')
 .outE('KNOWS')
 .hasId('e-1')
 .fold()
 .coalesce(unfold(),
           V('v-1').addE('KNOWS')
                   .to(V('v-2'))
                   .property(id, 'e-1'))
 .V('v-3')
 .outE('KNOWS')
 .hasId('e-2').fold()
 .coalesce(unfold(),
           V('v-3').addE('KNOWS')
                   .to(V('v-4'))
                   .property(id, 'e-2'))
 .V('v-5')
 .outE('KNOWS')
 .hasId('e-3')
 .fold()
 .coalesce(unfold(),
           V('v-5').addE('KNOWS')
                   .to(V('v-6'))
                   .property(id, 'e-3'))
 .id()
```

Quizás el escenario más común de rotación de aristas por lotes sea que conozca el `to` vértice `from` y IDs, pero no sepa cuáles son las aristas que desea IDs intercalar. En ese caso, utilice la siguiente fórmula:

```
g.V('v-1')
 .outE('KNOWS')
 .where(inV().hasId('v-2'))
 .fold()
 .coalesce(unfold(),
           V('v-1').addE('KNOWS')
                   .to(V('v-2')))

 .V('v-3')
 .outE('KNOWS')
 .where(inV().hasId('v-4'))
 .fold()
 .coalesce(unfold(),
           V('v-3').addE('KNOWS')
                   .to(V('v-4')))
 .V('v-5')
 .outE('KNOWS')
 .where(inV().hasId('v-6'))
 .fold()
 .coalesce(unfold(),
           V('v-5').addE('KNOWS').to(V('v-6')))
 .id()
```

Si sabes cuáles son IDs las aristas que quieres moldear, pero no conoces los IDs `to` vértices `from` y (esto es inusual), puedes usar esta fórmula:

```
g.V()
 .hasLabel('Person')
 .has('email', 'person-1@example.org')
 .outE('KNOWS')
 .hasId('e-1')
 .fold()
 .coalesce(unfold(),
           V().hasLabel('Person')
              .has('email', 'person-1@example.org')
              .addE('KNOWS')
              .to(V().hasLabel('Person')
                     .has('email', 'person-2@example.org'))
                     .property(id, 'e-1'))
 .V()
 .hasLabel('Person')
 .has('email', 'person-3@example.org')
 .outE('KNOWS')
 .hasId('e-2')
 .fold()
 .coalesce(unfold(),
           V().hasLabel('Person')
              .has('email', 'person-3@example.org')
              .addE('KNOWS')
              .to(V().hasLabel('Person')
                     .has('email', 'person-4@example.org'))
              .property(id, 'e-2'))
 .V()
 .hasLabel('Person')
 .has('email', 'person-5@example.org')
 .outE('KNOWS')
 .hasId('e-1')
 .fold()
 .coalesce(unfold(),
           V().hasLabel('Person')
              .has('email', 'person-5@example.org')
              .addE('KNOWS')
              .to(V().hasLabel('Person')
                     .has('email', 'person-6@example.org'))
                     .property(id, 'e-3'))
 .id()
```

## Combinación de actualizaciones o inserciones de vértices y bordes
<a name="gremlin-upserts-pre-3.6-vertexes-and-edges"></a>

A veces, es posible que desee realizar actualizaciones o inserciones en ambos vértices y los bordes que los conectan. Puede combinar los ejemplos de lotes que se presentan aquí. En el siguiente ejemplo, se realizan actualizaciones o inserciones de 3 vértices y 2 bordes:

```
g.V('p-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-1')
                         .property('email', 'person-1@example.org'))
 .V('p-2')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-2')
                         .property('name', 'person-2@example.org'))
 .V('c-1')
 .fold()
 .coalesce(unfold(),
           addV('City').property(id, 'c-1')
                       .property('name', 'city-1'))
 .V('p-1')
 .outE('LIVES_IN')
 .where(inV().hasId('c-1'))
 .fold()
 .coalesce(unfold(),
           V('p-1').addE('LIVES_IN')
                   .to(V('c-1')))
 .V('p-2')
 .outE('LIVES_IN')
 .where(inV().hasId('c-1'))
 .fold()
 .coalesce(unfold(),
           V('p-2').addE('LIVES_IN')
                   .to(V('c-1')))
 .id()
```

## Combinación de actualizaciones o inserciones e inserciones
<a name="gremlin-upserts-pre-3.6-and-inserts"></a>

A veces, es posible que desee realizar actualizaciones o inserciones en ambos vértices y los bordes que los conectan. Puede combinar los ejemplos de lotes que se presentan aquí. En el siguiente ejemplo, se realizan actualizaciones o inserciones de 3 vértices y 2 bordes:

Por lo general, las actualizaciones o inserciones se realizan con un elemento a la vez. Si sigue los patrones de actualización o inserción que se presentan aquí, cada operación de actualización o inserción emite un único recorrido, lo que hace que la siguiente operación se ejecute solo una vez.

Sin embargo, a veces es posible que desee mezclar actualizaciones o inserciones con inserciones. Podría ser así, por ejemplo, si utiliza bordes para representar instancias de acciones o eventos. Una solicitud puede usar actualizaciones o inserciones para garantizar que existan todos los vértices necesarios y, a continuación, usar inserciones para añadir bordes. En el caso de solicitudes de este tipo, preste atención a la cantidad potencial de recorridos que emite cada operación.

Examine el siguiente ejemplo, en el que se combinan actualizaciones o inserciones e inserciones para añadir bordes que representen eventos en el gráfico:

```
// Fully optimized, but inserts too many edges
g.V('p-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-1')
                         .property('email', 'person-1@example.org'))
 .V('p-2')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-2')
                         .property('name', 'person-2@example.org'))
 .V('p-3')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-3')
                         .property('name', 'person-3@example.org'))
 .V('c-1')
 .fold()
 .coalesce(unfold(),
           addV('City').property(id, 'c-1')
                       .property('name', 'city-1'))
 .V('p-1', 'p-2')
 .addE('FOLLOWED')
 .to(V('p-1'))
 .V('p-1', 'p-2', 'p-3')
 .addE('VISITED')
 .to(V('c-1'))
 .id()
```

La consulta debe insertar 5 bordes: 2 bordes FOLLOWED y 3 bordes VISITED. Sin embargo, la consulta tal como está escrita inserta 8 bordes: 2 FOLLOWED y 6 VISITED. Esto se debe a que la operación que inserta los 2 bordes FOLLOWED emite 2 recorridos, lo que provoca que la siguiente operación de inserción, que inserta 3 bordes, se ejecute dos veces.

La solución consiste en añadir un paso `fold()` después de cada operación que pueda emitir más de un recorrido:

```
g.V('p-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-1')
                         .property('email', 'person-1@example.org'))
 .V('p-2')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-2').
                         .property('name', 'person-2@example.org'))
 .V('p-3')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'p-3').
                         .property('name', 'person-3@example.org'))
 .V('c-1')
 .fold().
 .coalesce(unfold(),
            addV('City').property(id, 'c-1').
                        .property('name', 'city-1'))
 .V('p-1', 'p-2')
 .addE('FOLLOWED')
 .to(V('p-1'))
 .fold()
 .V('p-1', 'p-2', 'p-3')
 .addE('VISITED')
 .to(V('c-1')).
 .id()
```

Aquí hemos insertado un paso `fold()` después de la operación que inserta los bordes FOLLOWED. Esto da como resultado un único recorrido, lo que hace que la siguiente operación se ejecute solo una vez.

La desventaja de este enfoque es que la consulta ahora no se optimiza por completo, porque `fold()` no se optimiza. La operación de inserción que sigue a `fold()` no estará optimizada.

Si necesita usar `fold()` para reducir el número de recorridos para los pasos posteriores, intente ordenar las operaciones de manera que las menos costosas ocupen la parte no optimizada de la consulta.

## Actualizaciones o inserciones que modifican vértices y bordes existentes
<a name="gremlin-upserts-pre-3.6-that-modify"></a>

A veces, desea crear un vértice o un borde si no existe y, a continuación, añadir o actualizar una propiedad, independientemente de si se trata de un vértice o un borde nuevo o existente.

Para añadir o modificar una propiedad, utilice el paso `property()`. Utilice este paso fuera del paso `coalesce()`. Si intenta modificar la propiedad de un vértice o borde existente dentro del paso `coalesce()`, es posible que el motor de consultas de Neptune no optimice la consulta.

La siguiente consulta añade o actualiza una propiedad de contador en cada vértice en el que se ha realizado una actualización o inserción. Cada paso `property()` tiene una cardinalidad única para garantizar que los nuevos valores sustituyan a los valores existentes, en lugar de añadirlos a un conjunto de valores existentes.

```
g.V('v-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-1')
                         .property('email', 'person-1@example.org'))
 .property(single, 'counter', 1)
 .V('v-2')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-2')
                         .property('email', 'person-2@example.org'))
 .property(single, 'counter', 2)
 .V('v-3')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-3')
                         .property('email', 'person-3@example.org'))
 .property(single, 'counter', 3)
 .id()
```

Si tiene un valor de propiedad, como un valor de marca temporal `lastUpdated`, que se aplica a todos los elementos en los que se ha realizado una actualización o inserción, puede añadirlo o actualizarlo al final de la consulta:

```
g.V('v-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-1')
                         .property('email', 'person-1@example.org'))
 .V('v-2').
 .fold().
 .coalesce(unfold(),
           addV('Person').property(id, 'v-2')
                         .property('email', 'person-2@example.org'))
 .V('v-3')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-3')
                         .property('email', 'person-3@example.org'))
 .V('v-1', 'v-2', 'v-3')
 .property(single, 'lastUpdated', datetime('2020-02-08'))
 .id()
```

Si hay condiciones adicionales que determinan si se debe seguir modificando un vértice o un borde, puede utilizar un paso `has()` para filtrar los elementos a los que se aplicará la modificación. En el siguiente ejemplo, se utiliza un paso `has()` para filtrar los vértices en los que se han realizado actualizaciones o inserciones en función del valor de su propiedad `version`. A continuación, la consulta actualiza a 3 la `version` de cualquier vértice cuyo `version` es menor que 3:

```
g.V('v-1')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-1')
                         .property('email', 'person-1@example.org')
                         .property('version', 3))
 .V('v-2')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-2')
                         .property('email', 'person-2@example.org')
                         .property('version', 3))
 .V('v-3')
 .fold()
 .coalesce(unfold(),
           addV('Person').property(id, 'v-3')
                         .property('email', 'person-3@example.org')
                         .property('version', 3))
 .V('v-1', 'v-2', 'v-3')
 .has('version', lt(3))
 .property(single, 'version', 3)
 .id()
```

# Análisis de la ejecución de las consultas de Neptune con `explain` de Gremlin
<a name="gremlin-explain"></a>

Amazon Neptune ha añadido una característica de Gremlin denominada *explain*. Esta característica es una herramienta de autoservicio para comprender el enfoque de ejecución adoptado por el motor de Neptune. Puede invocarla añadiendo un parámetro `explain` a una llamada HTTP que envíe una consulta de Gremlin.

La característica `explain` proporciona información sobre la estructura lógica de los planes de ejecución de consultas. Puede utilizar esta información para identificar posibles cuellos de botella de evaluación y ejecución y ajustar la consulta, tal y como se explica en [Ajuste de las consultas de Gremlin](gremlin-traversal-tuning.md). También puede usar [sugerencias de consulta](gremlin-query-hints.md) para mejorar los planes de ejecución de consultas.

**Topics**
+ [Descripción de cómo funcionan las consultas de Gremlin en Neptune](gremlin-explain-background.md)
+ [Uso de la API `explain` de Gremlin en Neptune](gremlin-explain-api.md)
+ [API `profile` de Gremlin en Neptune](gremlin-profile-api.md)
+ [Ajuste de las consultas de Gremlin mediante `explain` y `profile`](gremlin-traversal-tuning.md)
+ [Compatibilidad nativa con pasos de Gremlin en Amazon Neptune](gremlin-step-support.md)

# Descripción de cómo funcionan las consultas de Gremlin en Neptune
<a name="gremlin-explain-background"></a>

Para sacar el máximo partido de los informes `explain` y `profile` de Gremlin en Amazon Neptune, es útil tener información general sobre las consultas de Gremlin.

**Topics**
+ [Instrucciones de Gremlin en Neptune](gremlin-explain-background-statements.md)
+ [Cómo procesa Neptune las consultas de Gremlin mediante índices de instrucción](gremlin-explain-background-indexing-examples.md)
+ [Cómo se procesan las consultas de Gremlin en Neptune](gremlin-explain-background-querying.md)

# Instrucciones de Gremlin en Neptune
<a name="gremlin-explain-background-statements"></a>

Los datos de gráficos de propiedades en Amazon Neptune se componen de instrucciones de cuatro posiciones (cuádruples). Cada una de estas instrucciones representa una unidad atómica individual de datos de gráficos de propiedades. Para obtener más información, consulte [Modelo de datos de gráficos de Neptune.](feature-overview-data-model.md). Al igual que el modelo de datos del marco de descripción de recursos (RDF), estas cuatro posiciones son las siguientes:
+ `subject (S)`
+ `predicate (P)`
+ `object (O)`
+ `graph (G)`

Cada instrucción es una afirmación sobre uno o varios recursos. Por ejemplo, una instrucción puede afirmar la existencia de una relación entre dos recursos o puede asociar una propiedad (par clave/valor) a algún recurso.

Puede considerar el predicado como el verbo de la instrucción, que describe el tipo de relación o propiedad. El objeto es el objetivo de la relación o el valor de la propiedad. La posición del gráfico es opcional y se puede utilizar de muchas maneras diferentes. En el caso de los datos de gráficos-propiedades (PG) de Neptune, o bien no se usan (gráfico nulo) o se utilizan para representar el identificador de un borde. Un conjunto de instrucciones con identificadores de recursos compartidos crea un gráfico.

Existen tres clases de instrucciones en el modelo de datos de gráficos de propiedades de Neptune:

**Topics**
+ [Instrucciones de etiqueta de vértice](#gremlin-explain-background-vertex-labels)
+ [Instrucciones de borde](#gremlin-explain-background-edge-statements)
+ [Instrucciones de propiedades](#gremlin-explain-background-property-statements)

## Instrucciones de etiqueta de vértice de Gremlin
<a name="gremlin-explain-background-vertex-labels"></a>

Las instrucciones de etiqueta de vértice en Neptune sirven para dos fines:
+ Realizan un seguimiento de las etiquetas de un vértice.
+ La presencia de al menos una de estas instrucciones es lo que implica la existencia de un vértice determinado en el gráfico.

El asunto de estas instrucciones es un identificador de vértice y el objeto es una etiqueta que especifica el usuario. Puede utilizar un predicado fijo especial para estas instrucciones, que se muestra como `<~label>`, y un identificador de gráfico predeterminado (el gráfico nulo), que se muestra como `<~>`.

Por ejemplo, fíjese en el siguiente recorrido de `addV`.

```
g.addV("Person").property(id, "v1")
```

Este recorrido hace que se añada la siguiente instrucción al gráfico.

```
StatementEvent[Added(<v1> <~label> <Person> <~>) .]
```

## Instrucciones de borde de Gremlin
<a name="gremlin-explain-background-edge-statements"></a>

Una instrucción de borde de Gremlin es lo que implica la existencia de un límite entre dos vértices en un gráfico en Neptune. El asunto (S) de una instrucción de borde es el vértice `from` de origen. El predicado (P) es una etiqueta de borde proporcionada por el usuario. El objeto (O) es el vértice `to` de destino. El gráfico (G) es un identificador de borde proporcionado por el usuario.

Por ejemplo, fíjese en el siguiente recorrido de `addE`.

```
g.addE("knows").from(V("v1")).to(V("v2")).property(id, "e1")
```

El recorrido hace que se añada la siguiente instrucción al gráfico.

```
StatementEvent[Added(<v1> <knows> <v2> <e1>) .]
```

## Instrucciones de propiedades de Gremlin
<a name="gremlin-explain-background-property-statements"></a>

Una instrucción de propiedad de Gremlin en Neptune confirma un valor de propiedad individual para un vértice o borde. El asunto es un vértice o un identificador de borde proporcionado por el usuario. El predicado es el nombre de la propiedad (clave) y el objeto es el valor de la propiedad individual. El gráfico (G) es de nuevo el identificador de gráfico predeterminado, el gráfico nulo, que se muestra como `<~>`.

Considere el siguiente ejemplo de propiedad de vértice.

```
g.V("v1").property("name", "John")
```

Esta instrucción da como resultado lo siguiente.

```
StatementEvent[Added(<v1> <name> "John" <~>) .]
```

Las instrucciones de propiedad difieren de otras en que su objeto es un valor primitivo (`string`, `date`, `byte`, `short`, `int`, `long`, `float` o `double`). Su objeto no es un identificador de recursos que pueda utilizarse como sujeto de otra afirmación.

Para varias propiedades, cada valor de propiedad individual del conjunto recibe su propia instrucción.

```
g.V("v1").property(set, "phone", "956-424-2563").property(set, "phone", "956-354-3692 (tel:9563543692)")
```

Se obtiene el siguiente resultado:

```
StatementEvent[Added(<v1> <phone> "956-424-2563" <~>) .]
StatementEvent[Added(<v1> <phone> "956-354-3692" <~>) .]
```

Las propiedades de arista se gestionan de forma similar a las de los vértices, pero utilizan el identificador de arista en la posición (S). Por ejemplo, añadir una propiedad a un borde:

```
g.E("e1").property("weight", 0.8)
```

Esto da como resultado que se añada la siguiente declaración al gráfico.

```
StatementEvent[Added(<e1> <weight> 0.8 <~>) .]
```

# Cómo procesa Neptune las consultas de Gremlin mediante índices de instrucción
<a name="gremlin-explain-background-indexing-examples"></a>

Para acceder a las instrucciones en Amazon Neptune, se utilizan tres índices de instrucción, tal y como se detalla en [Cómo se indexan las instrucciones en Neptune](feature-overview-storage-indexing.md). Neptune extrae un *patrón* de instrucciones de una consulta de Gremlin en el que se conocen algunas posiciones y el resto se deja para que se descubran mediante una búsqueda de índices.

Neptune asume que el tamaño del esquema de gráfico de propiedades no es grande. Esto significa que el número de etiquetas de borde y nombres de propiedades distintos es bastante bajo, lo que genera un número total bajo de predicados distintos. Neptune realiza un seguimiento de los predicados distintos en un índice independiente. Utiliza esta caché de predicados para realizar un análisis de unión de `{ all P x POGS }` en lugar de utilizar un índice de OSGP. Evitar la necesidad de un índice de OSGP de recorrido inverso ahorra espacio de almacenamiento y rendimiento de carga.

La Explain/Profile API Neptune Gremlin le permite obtener el recuento de predicados en su gráfico. A continuación, puede determinar si la aplicación invalida la suposición de Neptune de que el esquema de gráficos de propiedades es pequeño.

Los siguientes ejemplos ayudan a demostrar cómo utiliza Neptune los índices para procesar consultas de Gremlin.

**Pregunta: ¿Cuáles son las etiquetas del vértice `v1`?**

```
  Gremlin code:      g.V('v1').label()
  Pattern:           (<v1>, <~label>, ?, ?)
  Known positions:   SP
  Lookup positions:  OG
  Index:             SPOG
  Key range:         <v1>:<~label>:*
```

**Pregunta: ¿Cuáles son los bordes de tipo "knows" (conoce) del vértice `v1`?**

```
  Gremlin code:      g.V('v1').out('knows')
  Pattern:           (<v1>, <knows>, ?, ?)
  Known positions:   SP
  Lookup positions:  OG
  Index:             SPOG
  Key range:         <v1>:<knows>:*
```

**Pregunta: ¿Qué vértices tienen una etiqueta de vértice de `Person`?**

```
  Gremlin code:      g.V().hasLabel('Person')
  Pattern:           (?, <~label>, <Person>, <~>)
  Known positions:   POG
  Lookup positions:  S
  Index:             POGS
  Key range:         <~label>:<Person>:<~>:*
```

**Pregunta: ¿Cuáles son los from/to vértices de una arista determinada? `e1`**

```
  Gremlin code:      g.E('e1').bothV()
  Pattern:           (?, ?, ?, <e1>)
  Known positions:   G
  Lookup positions:  SPO
  Index:             GPSO
  Key range:         <e1>:*
```

Un índice de instrucción que **no** tiene Neptune es un índice OSGP de recorrido inverso. Este índice podría utilizarse para reunir todos los bordes entrantes de todas las etiquetas de borde, como en el siguiente ejemplo.

**Pregunta: ¿Cuáles son los vértices `v1` adyacentes entrantes?**

```
  Gremlin code:      g.V('v1').in()
  Pattern:           (?, ?, <v1>, ?)
  Known positions:   O
  Lookup positions:  SPG
  Index:             OSGP  // <-- Index does not exist
```

# Cómo se procesan las consultas de Gremlin en Neptune
<a name="gremlin-explain-background-querying"></a>

En Amazon Neptune, los recorridos más complejos se pueden representar mediante una serie de patrones que crean una relación basada en la definición de variables con nombre que se pueden compartir entre patrones para crear uniones. Esto se muestra en el siguiente ejemplo.

**Pregunta: ¿Cuál es el vecindario de dos saltos del vértice `v1`?**

```
  Gremlin code:      g.V(‘v1’).out('knows').out('knows').path()
  Pattern:           (?1=<v1>, <knows>, ?2, ?) X Pattern(?2, <knows>, ?3, ?)

  The pattern produces a three-column relation (?1, ?2, ?3) like this:
                     ?1     ?2     ?3
                     ================
                     v1     v2     v3
                     v1     v2     v4
                     v1     v5     v6
```

Cuando se comparte la variable `?2` entre los dos patrones (en la posición O en el primer patrón y en la posición S del segundo patrón), se crea una combinación entre los vecinos del primer salto y los vecinos del segundo salto. Cada solución de Neptune tiene enlaces para las tres variables nombradas, que se pueden usar para recrear un [TinkerPopTraverser](http://tinkerpop.apache.org/docs/current/reference/#_the_traverser) (incluida la información de la ruta).

```
```

[El primer paso en el procesamiento de consultas de Gremlin consiste en analizar la consulta para convertirla en un objeto TinkerPop [transversal, compuesto por una serie](http://tinkerpop.apache.org/docs/current/reference/#traversal) de pasos. TinkerPop ](http://tinkerpop.apache.org/docs/current/reference/#graph-traversal-steps) Estos pasos, que forman parte del [ TinkerPop proyecto Apache](http://tinkerpop.apache.org/) de código abierto, son los operadores lógicos y físicos que componen un recorrido de Gremlin en la implementación de referencia. Ambos se utilizan para representar el modelo de la consulta. Son operadores ejecutables que pueden producir soluciones de acuerdo con la semántica del operador que representan. Por ejemplo, `.V()` está representado y ejecutado a la vez por. TinkerPop [GraphStep](http://tinkerpop.apache.org/docs/current/reference/#graph-step)

Como estos off-the-shelf TinkerPop pasos son ejecutables, un TinkerPop Traversal de este tipo puede ejecutar cualquier consulta de Gremlin y producir la respuesta correcta. Sin embargo, cuando se ejecuta con un gráfico grande, TinkerPop los pasos a veces pueden ser muy ineficientes y lentos. En lugar de utilizarlos, Neptune intenta convertir el recorrido en una forma declarativa compuesta por grupos de patrones, tal y como se ha descrito anteriormente.

Neptune no admite actualmente todos los operadores de Gremlin (pasos) en su motor de consultas nativo. Por lo tanto, intenta contraer tantos pasos como sea posible en un único `NeptuneGraphQueryStep`, que contiene el plan de consulta lógica declarativa para todos los pasos que se han convertido. Idealmente, se convierten todos los pasos. Sin embargo, cuando se encuentra un paso que no se puede convertir, Neptune interrumpe la ejecución nativa y aplaza toda la ejecución de consultas desde ese punto hasta los pasos. TinkerPop No intenta entrelazar y salir de la ejecución nativa.

Después de convertir los pasos en un plan de consulta lógica, Neptune ejecuta una serie de optimizadores de consultas que reescriben el plan de consulta en función del análisis estático y las cardinalidades estimadas. Esos optimizadores realizan tareas como reordenar operadores en función del recuento de rangos, eliminar operadores innecesarios o redundantes, reorganizar filtros, insertar operadores en diferentes grupos, etc.

Después de generar un plan de consulta optimizado, Neptune crea una canalización de operadores físicos que realizan el trabajo de ejecución de la consulta. Esto incluye la lectura de datos de los índices de instrucción, la realización de combinaciones de varios tipos, el filtrado, la ordenación, etc. La canalización produce un flujo de soluciones que luego se convierte de nuevo en un flujo de objetos de TinkerPop Traverser.

## Serialización de los resultados de las consultas
<a name="gremlin-explain-background-querying-serialization"></a>

Actualmente, Amazon Neptune utiliza los serializadores de mensajes de TinkerPop respuesta para convertir los resultados de las consultas (TinkerPop Traversers) en datos serializados que se enviarán por cable al cliente. Estos formatos de serialización suelen ser bastante específicos.

Por ejemplo, para serializar el resultado de una consulta de vértice como `g.V().limit(1)`, el motor de consultas de Neptune debe realizar una única búsqueda para producir el resultado de la consulta. Sin embargo, el serializador de `GraphSON` realizaría un gran número de búsquedas adicionales para empaquetar el vértice en el formato de serialización. Tendría que realizar una búsqueda para obtener la etiqueta, una para obtener las claves de propiedad y una búsqueda por clave de propiedad para el vértice para obtener todos los valores de cada clave.

Algunos de los formatos de serialización son más eficientes, pero todos requieren búsquedas adicionales. Además, los TinkerPop serializadores no intentan evitar la duplicación de búsquedas, lo que suele provocar que muchas búsquedas se repitan innecesariamente.

Esto hace que sea muy importante escribir sus consultas para que soliciten específicamente solo la información que necesitan. Por ejemplo, `g.V().limit(1).id()` devolvería solo el ID de vértice y eliminaría todas las búsquedas de serializador adicionales. [API `profile` de Gremlin en Neptune](gremlin-profile-api.md) le permite ver cuántas llamadas de búsqueda se realizan durante la ejecución de consultas y durante la serialización.

# Uso de la API `explain` de Gremlin en Neptune
<a name="gremlin-explain-api"></a>

La API de `explain` de Gremlin de Amazon Neptune devuelve el plan de consulta que se ejecutaría si se realizara una consulta especificada. Puesto que la API no ejecuta realmente la consulta, el plan se devuelve casi instantáneamente.

Se diferencia del paso TinkerPop .explain () para poder reportar información específica del motor de Neptune.

## Información contenida en un informe `explain` de Gremlin
<a name="gremlin-explain-api-results"></a>

Un informe `explain` contiene la siguiente información:
+ La cadena de consulta según se solicita.
+ **El recorrido original.** Este es el objeto TinkerPop Traversal que se produce al analizar la cadena de consulta en pasos. TinkerPop Es equivalente a la consulta original que se produce al ejecutar `.explain()` la consulta contra el. TinkerPop TinkerGraph
+ **El recorrido convertido.** Este es el recorrido de Neptuno producido al convertir el TinkerPop recorrido en la representación del plan de consulta lógico de Neptuno. En muchos casos, todo el TinkerPop recorrido se convierte en dos pasos de Neptuno: uno que ejecuta toda la consulta `NeptuneGraphQueryStep` () y otro que convierte la salida del motor de consultas de Neptuno en Traversers (). TinkerPop `NeptuneTraverserConverterStep`
+ **El recorrido optimizado.** Esta es la versión optimizada del plan de consultas de Neptune después de que se haya ejecutado a través de una serie de optimizadores estáticos de reducción de trabajo que reescriben la consulta en función del análisis estático y las cardinalidades estimadas. Esos optimizadores realizan tareas como reordenar operadores en función del recuento de rangos, eliminar operadores innecesarios o redundantes, reorganizar filtros, insertar operadores en diferentes grupos, etc.
+ **El recuento de predicados.** Debido a la estrategia de indexación de Neptune descrita anteriormente, disponer de un gran número de predicados diferentes puede provocar problemas de rendimiento. Esto es especialmente cierto para las consultas que utilizan operadores de recorrido inverso sin etiqueta de borde (`.in` o `.both`). Si se utilizan estos operadores y el recuento de predicados es lo suficientemente alto, el informe `explain` muestra un mensaje de advertencia.
+ **Información de DFE** Cuando el motor alternativo de DFE está habilitado, es posible que los siguientes componentes de recorrido aparezcan en el recorrido optimizado:
  + **`DFEStep`**: un paso de DFE optimizado para Neptune en el recorrido que contiene un `DFENode`secundario. `DFEStep` representa la parte del plan de consultas que se ejecuta en el motor DFE.
  + **`DFENode`**: contiene la representación intermedia como uno o más `DFEJoinGroupNodes` secundarios.
  + **`DFEJoinGroupNode`**: representa una unión de uno o más elementos `DFENode` o `DFEJoinGroupNode`.
  + **`NeptuneInterleavingStep`**: un paso de DFE optimizado para Neptune en el recorrido que contiene un `DFEStep`secundario.

    También contiene un elemento `stepInfo` que contiene información sobre el recorrido, como el elemento fronterizo, los elementos de la ruta utilizados, etc. Esta información se usa para procesar el `DFEStep` secundario.

  Una forma sencilla de averiguar si el DFE está evaluando su consulta consiste en comprobar si el resultado de `explain` contiene un `DFEStep`. Cualquier parte del recorrido que no forme parte del recorrido no será ejecutada por el DFE y `DFEStep` será ejecutada por el motor. TinkerPop 

  Consulte [Ejemplo con DFE habilitado](#gremlin-explain-dfe) para ver un informe de ejemplo.

## Sintaxis de `explain` de Gremlin
<a name="gremlin-explain-api-syntax"></a>

La sintaxis de la API `explain` es la misma que la de la API HTTP para consultas, salvo que utiliza `/gremlin/explain` como punto de enlace en lugar de `/gremlin`, como en el siguiente ejemplo.

```
curl -X POST https://your-neptune-endpoint:port/gremlin/explain -d '{"gremlin":"g.V().limit(1)"}'
```

La consulta anterior produciría el siguiente resultado.

```
*******************************************************
                Neptune Gremlin Explain
*******************************************************

Query String
============
g.V().limit(1)

Original Traversal
==================
[GraphStep(vertex,[]), RangeGlobalStep(0,1)]

Converted Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(Vertex) {
        JoinGroupNode {
            PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .]
        }, finishers=[limit(1)], annotations={path=[Vertex(?1):GraphStep], maxVarId=3}
    },
    NeptuneTraverserConverterStep
]

Optimized Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(Vertex) {
        JoinGroupNode {
            PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .], {estimatedCardinality=INFINITY}
        }, finishers=[limit(1)], annotations={path=[Vertex(?1):GraphStep], maxVarId=3}
    },
    NeptuneTraverserConverterStep
]

Predicates
==========
# of predicates: 18
```

## Pasos no convertidos TinkerPop
<a name="gremlin-explain-unconverted-steps"></a>

Lo ideal es que todos los TinkerPop pasos de un recorrido cuenten con la cobertura nativa del operador de Neptune. Cuando este no es el caso, Neptune recurre a la ejecución TinkerPop escalonada para cubrir las brechas en la cobertura de sus operadores. Si un recorrido utiliza un paso para el que Neptune todavía no tiene cobertura nativa, el informe `explain` muestra una advertencia que indica dónde se produjo la brecha.

Cuando se encuentra un paso sin un operador nativo de Neptuno correspondiente, todo el recorrido desde ese punto en adelante se ejecuta mediante TinkerPop pasos, incluso si los pasos posteriores tienen operadores nativos de Neptuno.

La excepción a esto se produce cuando se invoca la búsqueda de texto completo de Neptune. NeptuneSearchStep Implementa los pasos sin equivalentes nativos como pasos de búsqueda de texto completo.

## Ejemplo de resultado de `explain` donde todos los pasos de una consulta tienen equivalentes nativos
<a name="gremlin-explain-all-steps-converted"></a>

A continuación se muestra un ejemplo de informe `explain` para una consulta en la que todos los pasos tienen equivalentes nativos:

```
*******************************************************
                Neptune Gremlin Explain
*******************************************************

Query String
============
g.V().out()

Original Traversal
==================
[GraphStep(vertex,[]), VertexStep(OUT,vertex)]

Converted Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(Vertex) {
        JoinGroupNode {
            PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .]
            PatternNode[(?1, ?5, ?3, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .]
            PatternNode[(?3, <~label>, ?4, <~>) . project ask .]
        }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep], maxVarId=7}
    },
    NeptuneTraverserConverterStep
]

Optimized Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(Vertex) {
        JoinGroupNode {
            PatternNode[(?1, ?5, ?3, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .], {estimatedCardinality=INFINITY}
        }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep], maxVarId=7}
    },
    NeptuneTraverserConverterStep
]

Predicates
==========
# of predicates: 18
```

## Ejemplo en el que algunos pasos de una consulta no tienen equivalentes nativos
<a name="gremlin-explain-not-all-steps-converted"></a>

Neptune gestiona `GraphStep` y `VertexStep` de forma nativa, pero si introduce un `FoldStep` y `UnfoldStep`, el `explain` resultante es diferente.

```
*******************************************************
                Neptune Gremlin Explain
*******************************************************

Query String
============
g.V().fold().unfold().out()

Original Traversal
==================
[GraphStep(vertex,[]), FoldStep, UnfoldStep, VertexStep(OUT,vertex)]

Converted Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(Vertex) {
        JoinGroupNode {
            PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .]
        }, annotations={path=[Vertex(?1):GraphStep], maxVarId=3}
    },
    NeptuneTraverserConverterStep
]
+ not converted into Neptune steps: [FoldStep, UnfoldStep, VertexStep(OUT,vertex)]

Optimized Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(Vertex) {
        JoinGroupNode {
            PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .], {estimatedCardinality=INFINITY}
        }, annotations={path=[Vertex(?1):GraphStep], maxVarId=3}
    },
    NeptuneTraverserConverterStep,
    NeptuneMemoryTrackerStep
]
+ not converted into Neptune steps: [FoldStep, UnfoldStep, VertexStep(OUT,vertex)]

WARNING: >> FoldStep << is not supported natively yet
```

En este caso, `FoldStep` elimina la ejecución nativa. Sin embargo, incluso el `VertexStep` siguiente ya no se gestiona de forma nativa, ya que aparece después de los pasos `Fold/Unfold`.

Para ahorrar rendimiento y costes, es importante que intente formular los recorridos de forma que la mayor cantidad de trabajo posible se realice de forma nativa dentro del motor de consultas de Neptune, en lugar de hacerlo paso a paso. TinkerPop 

## Ejemplo de consulta que usa Neptune full-text-search
<a name="gremlin-explain-full-text-search-steps"></a>

En la siguiente consulta se utiliza la búsqueda de texto completo de Neptune:

```
g.withSideEffect("Neptune#fts.endpoint", "some_endpoint")
  .V()
  .tail(100)
  .has("Neptune#fts mark*")
  -------
  .has("name", "Neptune#fts mark*")
  .has("Person", "name", "Neptune#fts mark*")
```

La parte `.has("name", "Neptune#fts mark*")` limita la búsqueda a los vértices con `name`, mientras que `.has("Person", "name", "Neptune#fts mark*")` limita la búsqueda a los vértices con `name` y la etiqueta `Person`. Esto da como resultado el siguiente recorrido en el informe `explain`:

```
Final Traversal
[NeptuneGraphQueryStep(Vertex) {
    JoinGroupNode {
        PatternNode[(?1, termid(1,URI), ?2, termid(0,URI)) . project distinct ?1 .], {estimatedCardinality=INFINITY}
    }, annotations={path=[Vertex(?1):GraphStep], maxVarId=4}
}, NeptuneTraverserConverterStep, NeptuneTailGlobalStep(10), NeptuneTinkerpopTraverserConverterStep, NeptuneSearchStep {
    JoinGroupNode {
        SearchNode[(idVar=?3, query=mark*, field=name) . project ask .], {endpoint=some_endpoint}
    }
    JoinGroupNode {
        SearchNode[(idVar=?3, query=mark*, field=name) . project ask .], {endpoint=some_endpoint}
    }
}]
```

## Ejemplo de uso de `explain` cuando DFE está habilitado
<a name="gremlin-explain-dfe"></a>

El siguiente es un ejemplo de un informe `explain` cuando el motor de consultas alternativo DFE está habilitado:

```
*******************************************************
                Neptune Gremlin Explain
*******************************************************

Query String
============

g.V().as("a").out().has("name", "josh").out().in().where(eq("a"))


Original Traversal
==================
[GraphStep(vertex,[])@[a], VertexStep(OUT,vertex), HasStep([name.eq(josh)]), VertexStep(OUT,vertex), VertexStep(IN,vertex), WherePredicateStep(eq(a))]

Converted Traversal
===================
Neptune steps:
[
    DFEStep(Vertex) {
      DFENode {
        DFEJoinGroupNode[ children={
          DFEPatternNode[(?1, <http://www.w3.org/1999/02/22-rdf-syntax-ns#type>, ?2, <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph>) . project DISTINCT[?1] {rangeCountEstimate=unknown}],
          DFEPatternNode[(?1, ?3, ?4, ?5) . project ALL[?1, ?4] graphFilters=(!= <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph> . ), {rangeCountEstimate=unknown}]
        }, {rangeCountEstimate=unknown}
        ]
      } [Vertex(?1):GraphStep@[a], Vertex(?4):VertexStep]
    } ,
    NeptuneTraverserConverterDFEStep
]
+ not converted into Neptune steps: HasStep([name.eq(josh)]),
Neptune steps:
[
    NeptuneInterleavingStep {
      StepInfo[joinVars=[?7, ?1], frontierElement=Vertex(?7):HasStep, pathElements={a=(last,Vertex(?1):GraphStep@[a])}, listPathElement={}, indexTime=0ms],
      DFEStep(Vertex) {
        DFENode {
          DFEJoinGroupNode[ children={
            DFEPatternNode[(?7, ?8, ?9, ?10) . project ALL[?7, ?9] graphFilters=(!= <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph> . ), {rangeCountEstimate=unknown}],
            DFEPatternNode[(?12, ?11, ?9, ?13) . project ALL[?9, ?12] graphFilters=(!= <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph> . ), {rangeCountEstimate=unknown}]
          }, {rangeCountEstimate=unknown}
          ]
        } [Vertex(?9):VertexStep, Vertex(?12):VertexStep]
      } 
    }
]
+ not converted into Neptune steps: WherePredicateStep(eq(a)),
Neptune steps:
[
    DFECleanupStep
]


Optimized Traversal
===================
Neptune steps:
[
    DFEStep(Vertex) {
      DFENode {
        DFEJoinGroupNode[ children={
          DFEPatternNode[(?1, ?3, ?4, ?5) . project ALL[?1, ?4] graphFilters=(!= defaultGraph[526] . ), {rangeCountEstimate=9223372036854775807}]
        }, {rangeCountEstimate=unknown}
        ]
      } [Vertex(?1):GraphStep@[a], Vertex(?4):VertexStep]
    } ,
    NeptuneTraverserConverterDFEStep
]
+ not converted into Neptune steps: NeptuneHasStep([name.eq(josh)]),
Neptune steps:
[
    NeptuneMemoryTrackerStep,
    NeptuneInterleavingStep {
      StepInfo[joinVars=[?7, ?1], frontierElement=Vertex(?7):HasStep, pathElements={a=(last,Vertex(?1):GraphStep@[a])}, listPathElement={}, indexTime=0ms],
      DFEStep(Vertex) {
        DFENode {
          DFEJoinGroupNode[ children={
            DFEPatternNode[(?7, ?8, ?9, ?10) . project ALL[?7, ?9] graphFilters=(!= defaultGraph[526] . ), {rangeCountEstimate=9223372036854775807}],
            DFEPatternNode[(?12, ?11, ?9, ?13) . project ALL[?9, ?12] graphFilters=(!= defaultGraph[526] . ), {rangeCountEstimate=9223372036854775807}]
          }, {rangeCountEstimate=unknown}
          ]
        } [Vertex(?9):VertexStep, Vertex(?12):VertexStep]
      } 
    }
]
+ not converted into Neptune steps: WherePredicateStep(eq(a)),
Neptune steps:
[
    DFECleanupStep
]


WARNING: >> [NeptuneHasStep([name.eq(josh)]), WherePredicateStep(eq(a))] << (or one of the children for each step) is not supported natively yet

Predicates
==========
# of predicates: 8
```

Consulte [Información en `explain`](#gremlin-explain-api-results) para obtener una descripción de las secciones específicas de DFE en el informe.

# API `profile` de Gremlin en Neptune
<a name="gremlin-profile-api"></a>

La API `profile` de Gremlin de Neptune ejecuta un recorrido de Gremlin especificado, recopila varias métricas sobre la ejecución y produce un informe de perfil como salida.

Se diferencia del paso TinkerPop .profile () para poder reportar información específica del motor de Neptune.

El informe de perfil incluye la siguiente información sobre el plan de consulta:
+ La canalización del operador físico.
+ Las operaciones de índice para la ejecución y serialización de consultas.
+ El tamaño del resultado.

La API de `profile` utiliza una versión ampliada de la sintaxis de la API HTTP para las consultas, con `/gremlin/profile` como punto de enlace en lugar de `/gremlin`.

## Parámetros específicos de `profile` de Gremlin en Neptune
<a name="gremlin-profile-api-parameters"></a>
+ **profile.results**: `boolean`, valores permitidos: `TRUE` y `FALSE`, valor predeterminado: `TRUE`.

  Si es true, los resultados de la consulta se recopilan y se muestran como parte del informe de `profile`. Si es false, solo se muestra el recuento de resultados.
+ **profile.chop**: `int`, valor predeterminado: 250.

  Si es distinto de cero, hace que la cadena de resultados se trunque con ese número de caracteres. Esto no impide que se capturen todos los resultados. Simplemente limita el tamaño de la cadena en el informe de perfil. Si se establece en cero, la cadena contiene todos los resultados.
+ **profile.serializer**: `string`, valor predeterminado: `<null>`.

  Si no es nulo, los resultados recopilados se devuelven en un mensaje de respuesta serializado en el formato especificado por este parámetro. El número de operaciones de índice necesarias para producir ese mensaje de respuesta se notifica junto con el tamaño en bytes que se va a enviar al cliente.

  Los valores permitidos son `<null>` o cualquiera de los valores de enumeración de los «serializadores» válidos del tipo MIME o TinkerPop del controlador.

  ```
  "application/json" or "GRAPHSON"
  "application/vnd.gremlin-v1.0+json" or "GRAPHSON_V1"
  "application/vnd.gremlin-v1.0+json;types=false" or "GRAPHSON_V1_UNTYPED"
  "application/vnd.gremlin-v2.0+json" or "GRAPHSON_V2"
  "application/vnd.gremlin-v2.0+json;types=false" or "GRAPHSON_V2_UNTYPED"
  "application/vnd.gremlin-v3.0+json" or "GRAPHSON_V3"
  "application/vnd.gremlin-v3.0+json;types=false" or "GRAPHSON_V3_UNTYPED"
  "application/vnd.graphbinary-v1.0" or "GRAPHBINARY_V1"
  ```
+ **profile.indexOps**: `boolean`, valores permitidos: `TRUE` y `FALSE`, valor predeterminado: `FALSE`.

  Si es verdadero, muestra un informe detallado de todas las operaciones de índice que se realizaron durante la ejecución y serialización de consultas. Advertencia: este informe puede ser detallado.



## Salida de ejemplo de `profile` de Gremlin en Neptune
<a name="gremlin-profile-sample-output"></a>

A continuación, se muestra una consulta `profile` de ejemplo.

```
curl -k -X POST https://your-neptune-endpoint:port/gremlin/profile \
     -d '{"gremlin":"g.V().hasLabel(\"airport\").has(\"code\", \"AUS\").emit().repeat(in().simplePath()).times(2).limit(100)", "profile.serializer":"application/vnd.gremlin-v3.0+json"}'
```

Esta consulta genera el siguiente informe `profile` cuando se ejecuta en el gráfico de muestra de rutas aéreas de la entrada de blog, [Let Me Graph That For You - Part 1 - Air Routes](https://aws.amazon.com/blogs/database/let-me-graph-that-for-you-part-1-air-routes/).

```
*******************************************************
                Neptune Gremlin Profile
*******************************************************

Query String
==================
g.V().hasLabel("airport").has("code", "AUS").emit().repeat(in().simplePath()).times(2).limit(100)

Original Traversal
==================
[GraphStep(vertex,[]), HasStep([~label.eq(airport), code.eq(AUS)]), RepeatStep(emit(true),[VertexStep(IN,vertex), PathFilterStep(simple), RepeatEndStep],until(loops(2))), RangeGlobalStep(0,100)]

Optimized Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(Vertex) {
        JoinGroupNode {
            PatternNode[(?1, <code>, "AUS", ?) . project ?1 .], {estimatedCardinality=1, indexTime=84, hashJoin=true, joinTime=3, actualTotalOutput=1}
            PatternNode[(?1, <~label>, ?2=<airport>, <~>) . project ask .], {estimatedCardinality=3374, indexTime=29, hashJoin=true, joinTime=0, actualTotalOutput=61}
            RepeatNode {
                Repeat {
                    PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) . SimplePathFilter(?1, ?3)) .], {hashJoin=true, estimatedCardinality=50148, indexTime=0, joinTime=3}
                }
                Emit {
                    Filter(true)
                }
                LoopsCondition {
                    LoopsFilter([?1, ?3],eq(2))
                }
            }, annotations={repeatMode=BFS, emitFirst=true, untilFirst=false, leftVar=?1, rightVar=?3}
        }, finishers=[limit(100)], annotations={path=[Vertex(?1):GraphStep, Repeat[Vertex(?3):VertexStep]], joinStats=true, optimizationTime=495, maxVarId=7, executionTime=323}
    },
    NeptuneTraverserConverterStep
]

Physical Pipeline
=================
NeptuneGraphQueryStep
    |-- StartOp
    |-- JoinGroupOp
        |-- SpoolerOp(100)
        |-- DynamicJoinOp(PatternNode[(?1, <code>, "AUS", ?) . project ?1 .], {estimatedCardinality=1, indexTime=84, hashJoin=true})
        |-- SpoolerOp(100)
        |-- DynamicJoinOp(PatternNode[(?1, <~label>, ?2=<airport>, <~>) . project ask .], {estimatedCardinality=3374, indexTime=29, hashJoin=true})
        |-- RepeatOp
            |-- <upstream input> (Iteration 0) [visited=1, output=1 (until=0, emit=1), next=1]
            |-- BindingSetQueue (Iteration 1) [visited=61, output=61 (until=0, emit=61), next=61]
                |-- SpoolerOp(100)
                |-- DynamicJoinOp(PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) . SimplePathFilter(?1, ?3)) .], {hashJoin=true, estimatedCardinality=50148, indexTime=0})
            |-- BindingSetQueue (Iteration 2) [visited=38, output=38 (until=38, emit=0), next=0]
                |-- SpoolerOp(100)
                |-- DynamicJoinOp(PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) . SimplePathFilter(?1, ?3)) .], {hashJoin=true, estimatedCardinality=50148, indexTime=0})
        |-- LimitOp(100)

Runtime (ms)
============
Query Execution:  392.686
Serialization:   2636.380

Traversal Metrics
=================
Step                                                               Count  Traversers       Time (ms)    % Dur
-------------------------------------------------------------------------------------------------------------
NeptuneGraphQueryStep(Vertex)                                        100         100         314.162    82.78
NeptuneTraverserConverterStep                                        100         100          65.333    17.22
                                            >TOTAL                     -           -         379.495        -

Repeat Metrics
==============
Iteration  Visited   Output    Until     Emit     Next
------------------------------------------------------
        0        1        1        0        1        1
        1       61       61        0       61       61
        2       38       38       38        0        0
------------------------------------------------------
               100      100       38       62       62

Predicates
==========
# of predicates: 16

WARNING: reverse traversal with no edge label(s) - .in() / .both() may impact query performance

Results
=======
Count: 100
Output: [v[3], v[3600], v[3614], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[47], v[49], v[136], v[13], v[15], v[16], v[17], v[18], v[389], v[20], v[21], v[22], v[23], v[24], v[25], v[26], v[27], v[28], v[416], v[29], v[30], v[430], v[31], v[9...
Response serializer: GRYO_V3D0
Response size (bytes): 23566

Index Operations
================
Query execution:
    # of statement index ops: 3
    # of unique statement index ops: 3
    Duplication ratio: 1.0
    # of terms materialized: 0
Serialization:
    # of statement index ops: 200
    # of unique statement index ops: 140
    Duplication ratio: 1.43
    # of terms materialized: 393
```

Además de los planes de consulta devueltos por una llamada a `explain` de Neptune, los resultados de `profile` incluyen estadísticas de tiempo de ejecución sobre la ejecución de consultas. Cada operación Join se etiqueta con el tiempo que se tarda en realizar su combinación, así como el número real de soluciones que ha pasado a través de ella.

La salida `profile` incluye el tiempo necesario durante la fase de ejecución de consultas principales, así como la fase de serialización si se especificó la opción `profile.serializer`.

El desglose de las operaciones de índice realizadas durante cada fase también se incluye en la parte inferior de la salida `profile`.

Tenga en cuenta que las ejecuciones consecutivas de la misma consulta pueden mostrar resultados diferentes en términos de tiempo de ejecución e índice debido al almacenamiento en caché.

Para las consultas que utilizan el paso `repeat()`, hay disponible un desglose de la frontera en cada iteración si el paso `repeat()` se bajó como parte de `NeptuneGraphQueryStep`.

## Diferencias en los informes de `profile` cuando DFE está habilitado
<a name="gremlin-profile-dfe-output"></a>

Cuando el motor de consultas alternativo DFE de Neptune está habilitado, la salida de `profile` es ligeramente diferente:

**Recorrido optimizado:** esta sección es similar a la de la salida de `explain`, pero contiene información adicional. Esto incluye el tipo de operadores de DFE que se han tenido en cuenta en la planificación y las estimaciones de los costos correspondientes en el peor y el mejor de los casos.

**Canalización física:** en esta sección se muestran los operadores que se utilizan para ejecutar la consulta. Los elementos `DFESubQuery` resumen el plan físico que utiliza DFE para ejecutar la parte del plan de la que es responsable. Los elementos `DFESubQuery` se detallan en la siguiente sección, donde se enumeran las estadísticas de DFE.

**DFEQueryEstadísticas del motor:** esta sección solo aparece cuando el DFE ejecuta al menos una parte de la consulta. En ella se describen varias estadísticas de tiempo de ejecución que son específicas de DFE y contiene un desglose detallado del tiempo empleado en las distintas partes de la ejecución de la consulta, por `DFESubQuery`.

En esta sección, las subconsultas anidadas en distintos elementos `DFESubQuery` se aplanan y los identificadores únicos se marcan con un encabezado que comienza con `subQuery=`.

**Métricas transversales**: en esta sección se muestran las métricas transversales a nivel de pasos y, cuando el motor DFE ejecuta toda la consulta o parte de ella, se muestran métricas para `DFEStep` y/o `NeptuneInterleavingStep`. Consulte [Ajuste de las consultas de Gremlin mediante `explain` y `profile`](gremlin-traversal-tuning.md).

**nota**  
El DFE es una característica experimental publicada en el modo de laboratorio, por lo que el formato exacto de la salida de `profile` aún está sujeto a cambios.

## Salida de `profile` de ejemplo cuando el motor de flujo de datos de Neptune (DFE) está habilitado
<a name="gremlin-profile-sample-dfe-output"></a>

Cuando se utiliza el motor DFE para ejecutar consultas de Gremlin, la salida de [API `profile` de Gremlin](#gremlin-profile-api) tiene el formato que se muestra en el siguiente ejemplo.

Consulta:

```
curl https://your-neptune-endpoint:port/gremlin/profile \
  -d "{\"gremlin\": \"g.withSideEffect('Neptune#useDFE', true).V().has('code', 'ATL').out()\"}"
```

```
*******************************************************
                    Neptune Gremlin Profile
    *******************************************************

    Query String
    ==================
    g.withSideEffect('Neptune#useDFE', true).V().has('code', 'ATL').out()

    Original Traversal
    ==================
    [GraphStep(vertex,[]), HasStep([code.eq(ATL)]), VertexStep(OUT,vertex)]

    Optimized Traversal
    ===================
    Neptune steps:
    [
        DFEStep(Vertex) {
          DFENode {
            DFEJoinGroupNode[null](
              children=[
                DFEPatternNode((?1, vp://code[419430926], ?4, defaultGraph[526]) . project DISTINCT[?1] objectFilters=(in(ATL[452987149]) . ), {rangeCountEstimate=1},
                  opInfo=(type=PipelineJoin, cost=(exp=(in=1.00,out=1.00,io=0.00,comp=0.00,mem=0.00),wc=(in=1.00,out=1.00,io=0.00,comp=0.00,mem=0.00)),
                    disc=(type=PipelineScan, cost=(exp=(in=1.00,out=1.00,io=0.00,comp=0.00,mem=34.00),wc=(in=1.00,out=1.00,io=0.00,comp=0.00,mem=34.00))))),
                DFEPatternNode((?1, ?5, ?6, ?7) . project ALL[?1, ?6] graphFilters=(!= defaultGraph[526] . ), {rangeCountEstimate=9223372036854775807})],
              opInfo=[
                OperatorInfoWithAlternative[
                  rec=(type=PipelineJoin, cost=(exp=(in=1.00,out=27.76,io=0.00,comp=0.00,mem=0.00),wc=(in=1.00,out=27.76,io=0.00,comp=0.00,mem=0.00)),
                    disc=(type=PipelineScan, cost=(exp=(in=1.00,out=27.76,io=Infinity,comp=0.00,mem=295147905179352830000.00),wc=(in=1.00,out=27.76,io=Infinity,comp=0.00,mem=295147905179352830000.00)))),
                  alt=(type=PipelineScan, cost=(exp=(in=1.00,out=27.76,io=Infinity,comp=0.00,mem=295147905179352830000.00),wc=(in=1.00,out=27.76,io=Infinity,comp=0.00,mem=295147905179352830000.00)))]])
          } [Vertex(?1):GraphStep, Vertex(?6):VertexStep]
        } ,
        NeptuneTraverserConverterDFEStep,
        DFECleanupStep
    ]


    Physical Pipeline
    =================
    DFEStep
        |-- DFESubQuery1

    DFEQueryEngine Statistics
    =================
    DFESubQuery1
    ╔════╤════════╤════════╤═══════════════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤════════╤═══════════╗
    ║ ID │ Out #1 │ Out #2 │ Name                  │ Arguments                                                                                                    │ Mode │ Units In │ Units Out │ Ratio  │ Time (ms) ║
    ╠════╪════════╪════════╪═══════════════════════╪══════════════════════════════════════════════════════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪════════╪═══════════╣
    ║ 0  │ 1      │ -      │ DFESolutionInjection  │ solutions=[]                                                                                                 │ -    │ 0        │ 1         │ 0.00   │ 0.01      ║
    ║    │        │        │                       │ outSchema=[]                                                                                                 │      │          │           │        │           ║
    ╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 1  │ 2      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#089f43e3-4d71-4259-8d19-254ff63cee04/graph_1 │ -    │ 1        │ 1         │ 1.00   │ 0.02      ║
    ╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 2  │ 3      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#089f43e3-4d71-4259-8d19-254ff63cee04/graph_2 │ -    │ 1        │ 242       │ 242.00 │ 0.02      ║
    ╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 3  │ 4      │ -      │ DFEMergeChunks        │ -                                                                                                            │ -    │ 242      │ 242       │ 1.00   │ 0.01      ║
    ╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 4  │ -      │ -      │ DFEDrain              │ -                                                                                                            │ -    │ 242      │ 0         │ 0.00   │ 0.01      ║
    ╚════╧════════╧════════╧═══════════════════════╧══════════════════════════════════════════════════════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧════════╧═══════════╝


    subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#089f43e3-4d71-4259-8d19-254ff63cee04/graph_1
    ╔════╤════════╤════════╤══════════════════════╤═════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
    ║ ID │ Out #1 │ Out #2 │ Name                 │ Arguments                                                   │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
    ╠════╪════════╪════════╪══════════════════════╪═════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
    ║ 0  │ 1      │ -      │ DFEPipelineScan      │ pattern=Node(?1) with property 'code' as ?4 and label 'ALL' │ -    │ 0        │ 1         │ 0.00  │ 0.22      ║
    ║    │        │        │                      │ inlineFilters=[(?4 IN ["ATL"])]                             │      │          │           │       │           ║
    ║    │        │        │                      │ patternEstimate=1                                           │      │          │           │       │           ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
    ║ 1  │ 2      │ -      │ DFEMergeChunks       │ -                                                           │ -    │ 1        │ 1         │ 1.00  │ 0.02      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
    ║ 2  │ 4      │ -      │ DFERelationalJoin    │ joinVars=[]                                                 │ -    │ 2        │ 1         │ 0.50  │ 0.09      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
    ║ 3  │ 2      │ -      │ DFESolutionInjection │ solutions=[]                                                │ -    │ 0        │ 1         │ 0.00  │ 0.01      ║
    ║    │        │        │                      │ outSchema=[]                                                │      │          │           │       │           ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
    ║ 4  │ -      │ -      │ DFEDrain             │ -                                                           │ -    │ 1        │ 0         │ 0.00  │ 0.01      ║
    ╚════╧════════╧════════╧══════════════════════╧═════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝


    subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#089f43e3-4d71-4259-8d19-254ff63cee04/graph_2
    ╔════╤════════╤════════╤══════════════════════╤═════════════════════════════════════╤══════╤══════════╤═══════════╤════════╤═══════════╗
    ║ ID │ Out #1 │ Out #2 │ Name                 │ Arguments                           │ Mode │ Units In │ Units Out │ Ratio  │ Time (ms) ║
    ╠════╪════════╪════════╪══════════════════════╪═════════════════════════════════════╪══════╪══════════╪═══════════╪════════╪═══════════╣
    ║ 0  │ 1      │ -      │ DFESolutionInjection │ solutions=[]                        │ -    │ 0        │ 1         │ 0.00   │ 0.01      ║
    ║    │        │        │                      │ outSchema=[?1]                      │      │          │           │        │           ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 1  │ 2      │ 3      │ DFETee               │ -                                   │ -    │ 1        │ 2         │ 2.00   │ 0.01      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 2  │ 4      │ -      │ DFEDistinctColumn    │ column=?1                           │ -    │ 1        │ 1         │ 1.00   │ 0.21      ║
    ║    │        │        │                      │ ordered=false                       │      │          │           │        │           ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 3  │ 5      │ -      │ DFEHashIndexBuild    │ vars=[?1]                           │ -    │ 1        │ 1         │ 1.00   │ 0.03      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 4  │ 5      │ -      │ DFEPipelineJoin      │ pattern=Edge((?1)-[?7:?5]->(?6))    │ -    │ 1        │ 242       │ 242.00 │ 0.51      ║
    ║    │        │        │                      │ constraints=[]                      │      │          │           │        │           ║
    ║    │        │        │                      │ patternEstimate=9223372036854775807 │      │          │           │        │           ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 5  │ 6      │ 7      │ DFESync              │ -                                   │ -    │ 243      │ 243       │ 1.00   │ 0.02      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 6  │ 8      │ -      │ DFEForwardValue      │ -                                   │ -    │ 1        │ 1         │ 1.00   │ 0.01      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 7  │ 8      │ -      │ DFEForwardValue      │ -                                   │ -    │ 242      │ 242       │ 1.00   │ 0.02      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 8  │ 9      │ -      │ DFEHashIndexJoin     │ -                                   │ -    │ 243      │ 242       │ 1.00   │ 0.31      ║
    ╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼────────┼───────────╢
    ║ 9  │ -      │ -      │ DFEDrain             │ -                                   │ -    │ 242      │ 0         │ 0.00   │ 0.01      ║
    ╚════╧════════╧════════╧══════════════════════╧═════════════════════════════════════╧══════╧══════════╧═══════════╧════════╧═══════════╝


    Runtime (ms)
    ============
    Query Execution: 11.744

    Traversal Metrics
    =================
    Step                                                               Count  Traversers       Time (ms)    % Dur
    -------------------------------------------------------------------------------------------------------------
    DFEStep(Vertex)                                                      242         242          10.849    95.48
    NeptuneTraverserConverterDFEStep                                     242         242           0.514     4.52
                                                >TOTAL                     -           -          11.363        -

    Predicates
    ==========
    # of predicates: 18

    Results
    =======
    Count: 242


    Index Operations
    ================
    Query execution:
        # of statement index ops: 0
        # of terms materialized: 0
```

**nota**  
Dado que el DFE es una característica experimental publicada en el modo de laboratorio, el formato exacto de la salida de `profile` está sujeto a cambios.

# Ajuste de las consultas de Gremlin mediante `explain` y `profile`
<a name="gremlin-traversal-tuning"></a>

[[Con frecuencia, puede ajustar sus consultas de Gremlin en Amazon Neptune para obtener un mejor rendimiento, utilizando la información que tiene a su disposición en los informes que obtiene de la explicación y el perfil de Neptune.](gremlin-profile-api.md)](gremlin-explain-api.md) APIs Para ello, es útil entender cómo Neptune procesa los recorridos de Gremlin.

**importante**  
En la TinkerPop versión 3.4.11 se realizó un cambio que mejora la exactitud del procesamiento de las consultas, pero por el momento a veces puede afectar gravemente al rendimiento de las consultas.  
Por ejemplo, una consulta de este tipo podría ejecutarse de una forma mucho más lenta:  

```
g.V().hasLabel('airport').
  order().
    by(out().count(),desc).
  limit(10).
  out()
```
Los vértices situados tras el paso límite ahora se obtienen de forma no óptima debido al cambio de la versión 3.4.11. TinkerPop Para evitarlo, puede modificar la consulta añadiendo el paso barrier() en cualquier punto después de `order().by()`. Por ejemplo:  

```
g.V().hasLabel('airport').
  order().
    by(out().count(),desc).
  limit(10).
  barrier().
  out()
```
TinkerPop [La versión 3.4.11 estaba habilitada en la versión 1.0.5.0 del motor de Neptune.](engine-releases-1.0.5.0.md)

## Descripción del procesamiento de recorridos de Gremlin en Neptune
<a name="gremlin-traversal-processing"></a>

Cuando se envía un recorrido de Gremlin a Neptune, hay tres procesos principales que transforman el recorrido en un plan de ejecución subyacente para que el motor lo ejecute. Estos procesos son el análisis, la conversión y la optimización,

![\[y transforman una consulta de Gremlin en un plan de ejecución.\]](http://docs.aws.amazon.com/es_es/neptune/latest/userguide/images/Gremlin_traversal_processing.png)


### El proceso de análisis de recorridos
<a name="gremlin-traversal-processing-parsing"></a>

El primer paso para procesar un recorrido es analizarlo en un lenguaje común. [En Neptune, ese lenguaje común es el conjunto de TinkerPop pasos que forman parte de la TinkerPop API.](http://tinkerpop.apache.org/javadocs/3.4.8/full/org/apache/tinkerpop/gremlin/process/traversal/Step.html) Cada uno de estos pasos representa una unidad de cálculo dentro del recorrido.

Puede enviar un recorrido de Gremlin a Neptune como cadena o como código de bytes. El punto de conexión REST y el método `submit()` del controlador del cliente Java envían los recorridos como cadenas, como en este ejemplo:

```
client.submit("g.V()")
```

Las aplicaciones y los controladores de lenguaje que utilizan [variantes del lenguaje Gremlin (GLV)](https://tinkerpop.apache.org/docs/current/tutorials/gremlin-language-variants/) envían los recorridos en código de bits.

### El proceso de conversión del recorrido
<a name="gremlin-traversal-processing-conversion"></a>

El segundo paso para procesar un recorrido es convertir sus TinkerPop pasos en un conjunto de pasos de Neptune convertidos y no convertidos. La mayoría de los pasos del lenguaje de consulta Apache TinkerPop Gremlin se convierten en pasos específicos de Neptuno que están optimizados para ejecutarse en el motor Neptune subyacente. Cuando se encuentra un TinkerPop paso sin un equivalente a Neptuno en un recorrido, el motor de consulta procesa ese paso y todos los pasos subsiguientes del recorrido. TinkerPop 

Para obtener más información sobre qué pasos se pueden convertir y en qué circunstancias, consulte [Compatibilidad con pasos de Gremlin](gremlin-step-support.md).

### El proceso de optimización de recorridos
<a name="gremlin-traversal-processing-optimization"></a>

El último paso del procesamiento de recorridos consiste en ejecutar la serie de pasos convertidos y no convertidos a través del optimizador para tratar de determinar el mejor plan de ejecución. El resultado de esta optimización es el plan de ejecución que procesa el motor de Neptune.

## Uso de la API `explain` en un recorrido de Gremlin de Neptune para ajustar las consultas
<a name="gremlin-traversal-tuning-explain"></a>

La API de explain de Neptune no es lo mismo que el paso `explain()` de Gremlin. Devuelve el plan de ejecución final que el motor de Neptune procesaría al ejecutar la consulta. Dado que no realiza ningún procesamiento, devuelve el mismo plan independientemente de los parámetros utilizados y su resultado no contiene estadísticas sobre la ejecución real.

Considere el siguiente recorrido simple que encuentra todos los vértices de los aeropuertos de Anchorage:

```
g.V().has('code','ANC')
```

Hay dos formas de ejecutar este recorrido a través de la API `explain` de Neptune. La primera es hacer una llamada REST al punto de conexión de explain de la siguiente manera:

```
curl -X POST https://your-neptune-endpoint:port/gremlin/explain -d '{"gremlin":"g.V().has('code','ANC')"}'
```

La segunda forma es usar la magia de celda [%%gremlin](notebooks-magics.md#notebooks-cell-magics-gremlin) del entorno de trabajo de Neptune con el parámetro `explain`. Esto pasa el recorrido contenido en el cuerpo de la celda a la API `explain` de Neptune y, a continuación, muestra el resultado al ejecutar la celda:

```
%%gremlin explain

g.V().has('code','ANC')
```

El resultado de la API `explain` resultante describe el plan de ejecución de Neptune para el recorrido. Como puede ver en la imagen siguiente, el plan incluye cada uno de los tres pasos de la canalización de procesamiento:

![\[Salida de la API de explain para un recorrido simple de Gremlin.\]](http://docs.aws.amazon.com/es_es/neptune/latest/userguide/images/Gremlin_explain_output_1.png)


### Ajuste de un recorrido observando los pasos que no se convierten
<a name="gremlin-traversal-tuning-explain-non-converted-steps"></a>

Una de las primeras cosas que hay que buscar en el resultado de la API `explain` de Neptune son los pasos de Gremlin que no se convierten en pasos nativos de Neptune. En un plan de consulta, cuando se encuentra un paso que no se puede convertir en un paso nativo de Neptune, el servidor de Gremlin procesa este paso y todos los pasos subsiguientes del plan.

En el ejemplo anterior, se convirtieron todos los pasos del recorrido. Examinemos el resultado de la API de `explain` para este recorrido:

```
g.V().has('code','ANC').out().choose(hasLabel('airport'), values('code'), constant('Not an airport'))
```

Como puede ver en la imagen de abajo, Neptune no pudo convertir el paso `choose()`:

![\[Salida de la API de explain en la que no se pueden convertir todos los pasos.\]](http://docs.aws.amazon.com/es_es/neptune/latest/userguide/images/Gremlin_explain_output_2.png)


Hay varias cosas que puede hacer para ajustar el rendimiento del recorrido. La primera sería reescribirlo de tal manera que se elimine el paso que no se pudo convertir. Otra sería mover el paso al final del recorrido para que todos los demás pasos se puedan convertir en pasos nativos.

No siempre es necesario ajustar un plan de consultas con pasos que no estén convertidos. Si los pasos que no se pueden convertir se encuentran al final del recorrido y están relacionados con el formato de la salida y no con la forma en que se recorre el gráfico, es posible que no afecten mucho al rendimiento.

### 
<a name="gremlin-traversal-tuning-explain-unindexed-lookups"></a>

Otra cosa que hay que tener en cuenta al examinar los resultados de la API de `explain` de Neptune son los pasos que no utilizan índices. En el siguiente recorrido, se muestran todos los aeropuertos con vuelos que aterrizan en Anchorage:

```
g.V().has('code','ANC').in().values('code')
```

La salida de la API de explain para este recorrido es:

```
*******************************************************
                Neptune Gremlin Explain
*******************************************************

Query String
============

g.V().has('code','ANC').in().values('code')

Original Traversal
==================
[GraphStep(vertex,[]), HasStep([code.eq(ANC)]), VertexStep(IN,vertex), PropertiesStep([code],value)]

Converted Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(PropertyValue) {
        JoinGroupNode {
            PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .]
            PatternNode[(?1, <code>, "ANC", ?) . project ask .]
            PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .]
            PatternNode[(?3, <~label>, ?4, <~>) . project ask .]
            PatternNode[(?3, ?7, ?8, <~>) . project ?3,?8 . ContainsFilter(?7 in (<code>)) .]
        }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9}
    },
    NeptuneTraverserConverterStep
]

Optimized Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(PropertyValue) {
        JoinGroupNode {
            PatternNode[(?1, <code>, "ANC", ?) . project ?1 .], {estimatedCardinality=1}
            PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .], {estimatedCardinality=INFINITY}
            PatternNode[(?3, ?7=<code>, ?8, <~>) . project ?3,?8 .], {estimatedCardinality=7564}
        }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9}
    },
    NeptuneTraverserConverterStep
]

Predicates
==========
# of predicates: 26

WARNING: reverse traversal with no edge label(s) - .in() / .both() may impact query performance
```

El mensaje `WARNING` en la parte inferior de la salida se produce porque el paso `in()` del recorrido no se puede gestionar con uno de los 3 índices que mantiene Neptune (consulte [Cómo se indexan las instrucciones en Neptune](feature-overview-storage-indexing.md) y [Instrucciones de Gremlin en Neptune](gremlin-explain-background-statements.md)). Como el paso `in()` no contiene ningún filtro de bordes, no se puede resolver mediante el índice `SPOG`, `POGS` o `GPSO`. En cambio, Neptune debe realizar un escaneo de unión para encontrar los vértices solicitados, lo cual es mucho menos eficiente.

Hay dos formas de ajustar el recorrido en esta situación. La primera consiste en añadir uno o más criterios de filtrado al paso `in()` para poder utilizar una búsqueda indexada para resolver la consulta. Para el ejemplo anterior, esto podría ser:

```
g.V().has('code','ANC').in('route').values('code')
```

La salida de la API `explain` de Neptune para el recorrido revisado ya no contiene el mensaje `WARNING`:

```
*******************************************************
                Neptune Gremlin Explain
*******************************************************

Query String
============

g.V().has('code','ANC').in('route').values('code')

Original Traversal
==================
[GraphStep(vertex,[]), HasStep([code.eq(ANC)]), VertexStep(IN,[route],vertex), PropertiesStep([code],value)]

Converted Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(PropertyValue) {
        JoinGroupNode {
            PatternNode[(?1, <~label>, ?2, <~>) . project distinct ?1 .]
            PatternNode[(?1, <code>, "ANC", ?) . project ask .]
            PatternNode[(?3, ?5, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) . ContainsFilter(?5 in (<route>)) .]
            PatternNode[(?3, <~label>, ?4, <~>) . project ask .]
            PatternNode[(?3, ?7, ?8, <~>) . project ?3,?8 . ContainsFilter(?7 in (<code>)) .]
        }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9}
    },
    NeptuneTraverserConverterStep
]

Optimized Traversal
===================
Neptune steps:
[
    NeptuneGraphQueryStep(PropertyValue) {
        JoinGroupNode {
            PatternNode[(?1, <code>, "ANC", ?) . project ?1 .], {estimatedCardinality=1}
            PatternNode[(?3, ?5=<route>, ?1, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .], {estimatedCardinality=32042}
            PatternNode[(?3, ?7=<code>, ?8, <~>) . project ?3,?8 .], {estimatedCardinality=7564}
        }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep, PropertyValue(?8):PropertiesStep], maxVarId=9}
    },
    NeptuneTraverserConverterStep
]

Predicates
==========
# of predicates: 26
```

Otra opción si ejecuta muchos recorridos de este tipo es ejecutarlos en un clúster de base de datos de Neptune que tenga habilitado el índice `OSGP` opcional (consulte [Habilitación de un índice OSGP](feature-overview-storage-indexing.md#feature-overview-storage-indexing-osgp)). Habilitar un índice `OSGP` tiene sus inconvenientes:
+ Debe habilitarse en un clúster de base de datos antes de cargar cualquier dato.
+ Las tasas de inserción de vértices y bordes pueden disminuir hasta un 23 %.
+ El uso del almacenamiento aumentará en torno a un 20 %.
+ Es posible que en las consultas de lectura que dispersan las solicitudes en todos los índices aumente la latencia.

Tener un índice `OSGP` tiene mucho sentido en un conjunto restringido de patrones de consulta, pero a menos que los utilice con frecuencia, suele ser preferible asegurarse de que los recorridos que escribe se puedan resolver utilizando los tres índices principales.

### Uso de una gran cantidad de predicados
<a name="gremlin-traversal-tuning-explain-many-predicates"></a>

Neptune trata cada etiqueta de borde y cada nombre de propiedad de vértice o borde distinto del gráfico como un predicado y está diseñado de forma predeterminada para funcionar con un número relativamente bajo de predicados distintos. Cuando hay más de unos pocos miles de predicados en los datos de gráficos, el rendimiento podría degradarse.

La salida de `explain` de Neptune le avisará si es así:

```
Predicates
==========
# of predicates: 9549
WARNING: high predicate count (# of distinct property names and edge labels)
```

Si no es conveniente reelaborar el modelo de datos para reducir el número de etiquetas y propiedades y, por lo tanto, el número de predicados, la mejor manera de ajustar los recorridos es ejecutarlos en un clúster de base de datos que tenga el índice `OSGP` habilitado, como se ha explicado anteriormente.

## Uso de la API de `profile` de Gremlin de Neptune para ajustar los recorridos
<a name="gremlin-traversal-tuning-profile"></a>

La API `profile` de Neptune es muy diferente del paso `profile()` de Gremlin. Al igual que la API de `explain`, su salida incluye el plan de consultas que utiliza el motor de Neptune al ejecutar el recorrido. Además, la salida de `profile` incluye estadísticas de ejecución reales del recorrido, teniendo en cuenta cómo están configurados sus parámetros.

De nuevo, tomemos como ejemplo el recorrido simple que encuentra todos los vértices del aeropuerto de Anchorage:

```
g.V().has('code','ANC')
```

Al igual que con la API de `explain`, puede invocar a la API de `profile` mediante una llamada REST:

```
curl -X POST https://your-neptune-endpoint:port/gremlin/profile -d '{"gremlin":"g.V().has('code','ANC')"}'
```

También se puede usar la magia de celda [%%gremlin](notebooks-magics.md#notebooks-cell-magics-gremlin) del entorno de trabajo de Neptune con el parámetro `profile`. Esto pasa el recorrido contenido en el cuerpo de la celda a la API de `profile` de Neptune y, a continuación, muestra el resultado al ejecutar la celda:

```
%%gremlin profile

g.V().has('code','ANC')
```

La salida de la API de `profile` resultante contiene tanto el plan de ejecución de Neptune para el recorrido como las estadísticas sobre la ejecución del plan, como puede ver en esta imagen:

![\[Un ejemplo de la salida de la API de profile de Neptune.\]](http://docs.aws.amazon.com/es_es/neptune/latest/userguide/images/Gremlin_profile_output_1.png)


En la salida de `profile`, la sección del plan de ejecución solo contiene el plan de ejecución final para el recorrido, no los pasos intermedios. La sección de canalización contiene las operaciones físicas de la canalización que se realizaron, así como el tiempo real (en milisegundos) que ha tardado la ejecución del recorrido. La métrica del tiempo de ejecución es extremadamente útil para comparar los tiempos que tardan dos versiones diferentes de un recorrido a medida que se optimizan.

**nota**  
El tiempo de ejecución inicial de un recorrido suele ser mayor que el de los tiempos de ejecución posteriores, ya que el primero hace que los datos relevantes se almacenen en caché.

La tercera sección de la salida de `profile` contiene las estadísticas de ejecución y los resultados del recorrido. Para ver cómo esta información puede resultar útil para ajustar un recorrido, considere el siguiente recorrido, en el que se encuentran todos los aeropuertos cuyo nombre comienza por "Anchora" y todos los aeropuertos a los que se puede llegar en dos saltos desde esos aeropuertos, y se devuelven los códigos del aeropuerto de vuelta, las rutas de los vuelos y las distancias:

```
%%gremlin profile

g.withSideEffect("Neptune#fts.endpoint", "{your-OpenSearch-endpoint-URL").
    V().has("city", "Neptune#fts Anchora~").
    repeat(outE('route').inV().simplePath()).times(2).
    project('Destination', 'Route').
        by('code').
        by(path().by('code').by('dist'))
```

### Métricas de recorrido en la salida de la API de `profile` de Neptune
<a name="gremlin-traversal-tuning-profile-traversal-metrics"></a>

El primer conjunto de métricas que está disponible en todas las salidas de `profile` es el de las métricas de recorrido. Son similares a las métricas de pasos de `profile()` de Gremlin, con algunas diferencias:

```
Traversal Metrics
=================
Step                                                               Count  Traversers       Time (ms)    % Dur
-------------------------------------------------------------------------------------------------------------
NeptuneGraphQueryStep(Vertex)                                       3856        3856          91.701     9.09
NeptuneTraverserConverterStep                                       3856        3856          38.787     3.84
ProjectStep([Destination, Route],[value(code), ...                  3856        3856         878.786    87.07
  PathStep([value(code), value(dist)])                              3856        3856         601.359
                                            >TOTAL                     -           -        1009.274        -
```

La primera columna de la tabla de métricas de recorrido muestra los pasos que ha ejecutado el recorrido. Los dos primeros pasos son generalmente los pasos específicos de Neptune, `NeptuneGraphQueryStep` y `NeptuneTraverserConverterStep`.

`NeptuneGraphQueryStep` representa el tiempo de ejecución de toda la parte del recorrido que el motor de Neptune podría convertir y ejecutar de forma nativa.

`NeptuneTraverserConverterStep`representa el proceso de convertir la salida de esos pasos convertidos en TinkerPop travesaños que permiten procesar los pasos que no se pudieron convertir, si los hubiera, o devolver los resultados en un formato compatible. TinkerPop

En el ejemplo anterior, tenemos varios pasos no convertidos, por lo que vemos que cada uno de estos TinkerPop pasos (`ProjectStep`,`PathStep`) aparece entonces como una fila en la tabla.

[La segunda columna de la tabla,`Count`, indica el número de travesaños *representados* que han pasado por el paso, mientras que la tercera columna,`Traversers`, indica el número de travesaños que han pasado por ese paso, tal y como se explica en la documentación del paso del perfil. TinkerPop](https://tinkerpop.apache.org/docs/current/reference/#profile-step)

En nuestro ejemplo, hay 3856 vértices y 3856 recorridos que devuelve el `NeptuneGraphQueryStep`, y estos números permanecen iguales durante el resto del procesamiento, porque `ProjectStep` y `PathStep` están formateando los resultados, no filtrándolos.

**nota**  
A diferencia TinkerPop, el motor Neptune no optimiza el rendimiento aumentando el *volumen en sus `NeptuneGraphQueryStep` escalones*. `NeptuneTraverserConverterStep` El aumento de volumen es la TinkerPop operación que combina los travesaños en el mismo vértice para reducir la sobrecarga operativa, y eso es lo que hace que los números y los números difieran. `Count` `Traversers` Como el aumento de volumen solo ocurre en los pasos en los que Neptuno delega TinkerPop y no en los pasos que Neptuno maneja de forma nativa, `Count` las columnas y rara vez difieren. `Traverser`

La columna de Tiempo indica el número de milisegundos que tardó el paso y la columna `% Dur` indica el porcentaje del tiempo total de procesamiento que tardó el paso. Estas son las métricas que indican dónde centrar el trabajo de ajuste, ya que muestran los pasos que han tardado más tiempo.

### Métricas de operaciones de índice en la salida de la API de `profile` de Neptune
<a name="gremlin-traversal-tuning-profile-index-operations"></a>

Otro conjunto de métricas en la salida de la API de profile de Neptune son las operaciones de índice:

```
Index Operations
================
Query execution:
    # of statement index ops: 23191
    # of unique statement index ops: 5960
    Duplication ratio: 3.89
    # of terms materialized: 0
```

Estas métricas informan de lo siguiente:
+ El número total de búsquedas de índices.
+ El número de búsquedas de índices únicas realizadas.
+ La relación entre el total de búsquedas en el índice y las búsquedas únicas. Una relación más baja indica menos redundancia.
+ El número de términos materializados a partir del diccionario de términos.

### Métricas de repetición en la salida de la API de `profile` de Neptune
<a name="gremlin-traversal-tuning-profile-repeat-metrics"></a>

Si el recorrido utiliza un paso `repeat()` como en el ejemplo anterior, en la salida de `profile` aparecerá una sección que contiene las métricas de repetición:

```
Repeat Metrics
==============
Iteration  Visited   Output    Until     Emit     Next
------------------------------------------------------
        0        2        0        0        0        2
        1       53        0        0        0       53
        2     3856     3856     3856        0        0
------------------------------------------------------
              3911     3856     3856        0       55
```

Estas métricas informan de lo siguiente:
+ El recuento de bucles de una fila (la columna `Iteration`).
+ El número de elementos visitados por el bucle (la columna `Visited`).
+ El número de elementos generados por el bucle (la columna `Output`).
+ El último elemento generado por el bucle (la columna `Until`).
+ El número de elementos emitidos por el bucle (la columna `Emit`).
+ El número de elementos que se pasan del bucle al bucle siguiente (la columna `Next`).

Estas métricas de repetición son muy útiles para comprender el factor de ramificación del recorrido y así tener una idea del trabajo que está realizando la base de datos. Puede utilizar estos números para diagnosticar problemas de rendimiento, especialmente cuando el mismo recorrido funciona de forma muy diferente con parámetros diferentes.

### Métricas de búsqueda de texto completo en la salida de la API `profile` de Neptune
<a name="gremlin-traversal-tuning-profile-fts-metrics"></a>

Cuando un recorrido utiliza una [búsqueda de texto completo](full-text-search.md), como en el ejemplo anterior, en la salida de `profile` aparece una sección que contiene las métricas de búsqueda de texto completo (FTS):

```
FTS Metrics
==============
SearchNode[(idVar=?1, query=Anchora~, field=city) . project ?1 .],
    {endpoint=your-OpenSearch-endpoint-URL, incomingSolutionsThreshold=1000, estimatedCardinality=INFINITY,
    remoteCallTimeSummary=[total=65, avg=32.500000, max=37, min=28],
    remoteCallTime=65, remoteCalls=2, joinTime=0, indexTime=0, remoteResults=2}

    2 result(s) produced from SearchNode above
```

Aquí se muestra la consulta enviada al clúster ElasticSearch (ES) y se muestran varias métricas sobre la interacción ElasticSearch que pueden ayudarle a identificar los problemas de rendimiento relacionados con la búsqueda de texto completo:
+ Información resumida sobre las llamadas al ElasticSearch índice:
  + El número total de milisegundos que necesitan todas las remoteCalls para satisfacer la consulta (`total`).
  + El número medio de milisegundos empleados en una remoteCall (`avg`).
  + El número mínimo de milisegundos empleados en una remoteCall (`min`).
  + El número máximo de milisegundos empleados en una remoteCall (`max`).
+ Tiempo total consumido por RemoteCalls to ElasticSearch ()`remoteCallTime`.
+ El número de llamadas remotas realizadas a (). ElasticSearch `remoteCalls`
+ El número de milisegundos utilizados en las uniones de ElasticSearch los resultados ()`joinTime`.
+ El número de milisegundos empleados en las búsquedas de índices (`indexTime`).
+ El número total de resultados devueltos por ElasticSearch (`remoteResults`).

# Compatibilidad nativa con pasos de Gremlin en Amazon Neptune
<a name="gremlin-step-support"></a>

El motor de Amazon Neptune actualmente no es totalmente compatible de forma nativa con todos los pasos de Gremlin, como se explica en [Ajuste de las consultas de Gremlin](gremlin-traversal-tuning.md). La compatibilidad actual se divide en cuatro categorías:
+ [Los pasos de Gremlin que siempre se pueden convertir en operaciones nativas del motor de Neptune](#gremlin-steps-always)
+ [Pasos de Gremlin que se pueden convertir en operaciones nativas del motor de Neptune en algunos casos](#gremlin-steps-sometimes) 
+ [Pasos de Gremlin que nunca se convierten en operaciones nativas del motor de Neptune](#gremlin-steps-never) 
+ [Pasos de Gremlin que Neptune no admite en absoluto](#neptune-gremlin-steps-unsupported) 

## Los pasos de Gremlin que siempre se pueden convertir en operaciones nativas del motor de Neptune
<a name="gremlin-steps-always"></a>

Muchos pasos de Gremlin se pueden convertir en operaciones nativas del motor de Neptune siempre que cumplan las siguientes condiciones:
+ En la consulta no van precedidos de ningún paso que no se pueda convertir.
+ Su paso principal, si lo hay, se puede convertir.
+ Todos sus recorridos secundarios, si los hay, se pueden convertir.

Los siguientes pasos de Gremlin siempre se convierten en operaciones nativas del motor de Neptune si cumplen esas condiciones:
+ [and( )](http://tinkerpop.apache.org/docs/current/reference/#and-step)
+ [as( )](http://tinkerpop.apache.org/docs/current/reference/#as-step)
+ [count( )](http://tinkerpop.apache.org/docs/current/reference/#count-step)
+ [E( )](http://tinkerpop.apache.org/docs/current/reference/#graph-step)
+ [emit( )](http://tinkerpop.apache.org/docs/current/reference/#emit-step)
+ [explain( )](http://tinkerpop.apache.org/docs/current/reference/#explain-step)
+ [group( )](http://tinkerpop.apache.org/docs/current/reference/#group-step)
+ [groupCount( )](http://tinkerpop.apache.org/docs/current/reference/#groupcount-step)
+ [identity( )](http://tinkerpop.apache.org/docs/current/reference/#identity-step)
+ [is( )](http://tinkerpop.apache.org/docs/current/reference/#is-step)
+ [key( )](http://tinkerpop.apache.org/docs/current/reference/#key-step)
+ [label( )](http://tinkerpop.apache.org/docs/current/reference/#label-step)
+ [limit( )](http://tinkerpop.apache.org/docs/current/reference/#limit-step)
+ [local( )](http://tinkerpop.apache.org/docs/current/reference/#local-step)
+ [loops( )](http://tinkerpop.apache.org/docs/current/reference/#loops-step)
+ [not( )](http://tinkerpop.apache.org/docs/current/reference/#not-step)
+ [or( )](http://tinkerpop.apache.org/docs/current/reference/#or-step)
+ [profile( )](http://tinkerpop.apache.org/docs/current/reference/#profile-step)
+ [properties( )](http://tinkerpop.apache.org/docs/current/reference/#properties-step)
+ [subgraph( )](http://tinkerpop.apache.org/docs/current/reference/#subgraph-step)
+ [until( )](http://tinkerpop.apache.org/docs/current/reference/#until-step)
+ [V( )](http://tinkerpop.apache.org/docs/current/reference/#graph-step)
+ [value( )](http://tinkerpop.apache.org/docs/current/reference/#value-step)
+ [valueMap( )](http://tinkerpop.apache.org/docs/current/reference/#valuemap-step)
+ [values( )](http://tinkerpop.apache.org/docs/current/reference/#values-step)

## Pasos de Gremlin que se pueden convertir en operaciones nativas del motor de Neptune en algunos casos
<a name="gremlin-steps-sometimes"></a>

Algunos pasos de Gremlin se pueden convertir en operaciones nativas del motor de Neptune en algunas situaciones, pero no en otras:
+ [addE( )](http://tinkerpop.apache.org/docs/current/reference/#addedge-step): el paso `addE()` generalmente se puede convertir en una operación nativa del motor de Neptune, a menos que vaya seguido inmediatamente de un paso `property()` que contenga un recorrido como clave.
+ [addV( )](http://tinkerpop.apache.org/docs/current/reference/#addvertex-step): el paso `addV()` generalmente se puede convertir en una operación nativa del motor de Neptune, a menos que vaya seguido inmediatamente de un paso `property()` que contenga un recorrido como clave, o a menos que se asignen varias etiquetas.
+ [aggregate( )](http://tinkerpop.apache.org/docs/current/reference/#store-step): el paso `aggregate()` generalmente se puede convertir en una operación nativa del motor de Neptune, a menos que el paso se utilice en un recorrido secundario o en un subrecorrido, o a menos que el valor que se esté almacenando no sea un vértice, un borde, un identificador, una etiqueta o un valor de propiedad.

  En el ejemplo siguiente, `aggregate()` no se convierte porque se utiliza en un recorrido secundario:

  ```
  g.V().has('code','ANC').as('a')
       .project('flights').by(select('a')
       .outE().aggregate('x'))
  ```

  En este ejemplo, aggregate( ) no se convierte porque lo que se almacena es el `min()` de un valor:

  ```
  g.V().has('code','ANC').outE().aggregate('x').by(values('dist').min())
  ```
+ [barrier( )](http://tinkerpop.apache.org/docs/current/reference/#barrier-step): el paso `barrier()` generalmente se puede convertir en una operación nativa del motor de Neptune, a menos que el paso siguiente no se convierta.
+ [cap( )](http://tinkerpop.apache.org/docs/current/reference/#cap-step): el único caso en el que el paso `cap()` se convierte es cuando se combina con el paso `unfold()` para devolver una versión desplegada de un agregado de valores de vértice, borde, identificador o propiedad. En este ejemplo, `cap()` se convertirá porque va seguido de `.unfold()`.

  ```
  g.V().has('airport','country','IE').aggregate('airport').limit(2)
       .cap('airport').unfold()
  ```

  Sin embargo, si elimina `.unfold()`, `cap()` no se convertirá:

  ```
  g.V().has('airport','country','IE').aggregate('airport').limit(2)
       .cap('airport')
  ```
+ [coalesce ()](http://tinkerpop.apache.org/docs/current/reference/#coalesce-step)[: el único caso en el que el `coalesce()` paso se convierte es cuando sigue el [patrón Upsert](http://tinkerpop.apache.org/docs/current/recipes/#element-existence) recomendado en la página de recetas. TinkerPop ](http://tinkerpop.apache.org/docs/current/recipes/) No se permiten otros patrones de coalesce(). La conversión se limita al caso en el que todos los recorridos secundarios se pueden convertir, todos producen el mismo tipo que la salida (vértice, borde, identificador, valor, clave o etiqueta), todos se desplazan hacia un elemento nuevo y no contienen el paso `repeat()`.
+ [constant( )](http://tinkerpop.apache.org/docs/current/reference/#constant-step): actualmente, el paso constant() solo se convierte si se usa dentro de una parte de `sack().by()` de un recorrido para asignar un valor constante, como este:

  ```
  g.V().has('code','ANC').sack(assign).by(constant(10)).out().limit(2)
  ```
+ [CyclicPath( )](http://tinkerpop.apache.org/docs/current/reference/#cyclicpath-step): el paso `cyclicPath()` generalmente se puede convertir en una operación nativa del motor de Neptune, a menos que el paso se utilice con moduladores `by()`, `from()` o `to()`. En las siguientes consultas, por ejemplo, `cyclicPath()` no se convierte:

  ```
  g.V().has('code','ANC').as('a').out().out().cyclicPath().by('code')
  g.V().has('code','ANC').as('a').out().out().cyclicPath().from('a')
  g.V().has('code','ANC').as('a').out().out().cyclicPath().to('a')
  ```
+ [drop( )](http://tinkerpop.apache.org/docs/current/reference/#drop-step): el paso `drop()` generalmente se puede convertir en una operación nativa del motor de Neptune, a menos que el paso se utilice dentro de un paso `sideEffect(` o `optional()`.
+ [fold ()](http://tinkerpop.apache.org/docs/current/reference/#fold-step): solo hay dos situaciones en las que se puede convertir el paso fold (), a saber, cuando se usa con el [patrón Upsert](http://tinkerpop.apache.org/docs/current/recipes/#element-existence) recomendado en la [página de TinkerPop recetas](http://tinkerpop.apache.org/docs/current/recipes/) y cuando se usa en un contexto como este: `group().by()`

  ```
  g.V().has('code','ANC').out().group().by().by(values('code', 'city').fold())
  ```
+  [has ()](http://tinkerpop.apache.org/docs/current/reference/#has-step): el paso “has ()” generalmente se puede convertir en una operación nativa del motor de Neptune siempre que las consultas con “T” usen el predicado “P.eq”, “P.neq” o “P.contains”. Es de esperar que las variaciones de “has()” que implican que esas instancias de “P” también se conviertan en nativas, como “hasId (‘id1234’)”, que equivale a “has(eq, T.ID, ‘id1234’)”. 
+ [id( )](http://tinkerpop.apache.org/docs/current/reference/#id-step): el paso `id()` se convierte a menos que se utilice en una propiedad, como esta:

  ```
  g.V().has('code','ANC').properties('code').id()
  ```
+  [mergeE()](https://tinkerpop.apache.org/docs/current/reference/#mergeedge-step): el paso `mergeE()` se puede convertir en una operación nativa del motor de Neptune si los parámetros (la condición merge, `onCreate` y `onMatch`) son constantes (ya sea `null`, un `Map` constante o `select()` de un `Map`). Todos los ejemplos de [bordes de actualización e inserción](https://docs.aws.amazon.com//neptune/latest/userguide/gremlin-efficient-upserts.html#gremlin-upserts-edges) se pueden convertir. 
+  [mergeV()](https://tinkerpop.apache.org/docs/current/reference/#mergevertex-step): el paso mergeV () se puede convertir en una operación nativa del motor de Neptune si los parámetros (la condición merge, `onCreate` y `onMatch`) son constantes (ya sea `null`, un `Map` constante o `select()` de un `Map`). Todos los ejemplos de [bordes de actualización e inserción](https://docs.aws.amazon.com//neptune/latest/userguide/gremlin-efficient-upserts.html#gremlin-upserts-vertices) se pueden convertir. 
+ [order( )](http://tinkerpop.apache.org/docs/current/reference/#order-step): el paso `order()` generalmente se puede convertir en una operación nativa del motor de Neptune, a menos que se produzca algo de lo siguiente:
  + El paso `order()` se encuentra dentro de un recorrido secundario anidado, como este:

    ```
    g.V().has('code','ANC').where(V().out().order().by(id))
    ```
  + Se utilizan los pedidos locales, como por ejemplo con `order(local)`.
  + En la modulación `by()` se utiliza un comparador personalizado por el que ordenar. Un ejemplo es el uso de `sack()`:

    ```
    g.withSack(0).
      V().has('code','ANC').
          repeat(outE().sack(sum).by('dist').inV()).times(2).limit(10).
          order().by(sack())
    ```
  + Hay varias ordenaciones en el mismo elemento.
+ [project( )](http://tinkerpop.apache.org/docs/current/reference/#project-step): el paso `project()` generalmente se puede convertir en una operación nativa del motor de Neptune, a menos que el número de instrucciones de `by()` que siguen a `project()` no coincida con el número de etiquetas especificado, como aquí:

  ```
  g.V().has('code','ANC').project('x', 'y').by(id)
  ```
+ [range( )](http://tinkerpop.apache.org/docs/current/reference/#range-step): el paso `range()` solo se convierte cuando el extremo inferior del rango en cuestión es cero (por ejemplo, `range(0,3)`).
+ [repeat( )](http://tinkerpop.apache.org/docs/current/reference/#repeat-step): el paso `repeat()` generalmente se puede convertir en una operación nativa del motor de Neptune, a menos que esté anidado dentro de otro paso `repeat()`, como este:

  ```
  g.V().has('code','ANC').repeat(out().repeat(out()).times(2)).times(2)
  ```
+ [sack()](http://tinkerpop.apache.org/docs/current/reference/#sack-step): el paso `sack()` generalmente se puede convertir en una operación nativa del motor de Neptune, excepto en los siguientes casos:
  + Si se utiliza un operador sack no numérico.
  + Si se utiliza un operador sack numérico que no sea `+`, `-`, `mult`, `div`, `min` ni `max`.
  + Si `sack()` se usa dentro de un paso `where()` para filtrar en función del valor de sack, como se muestra a continuación:

    ```
    g.V().has('code','ANC').sack(assign).by(values('code')).where(sack().is('ANC'))
    ```
+ [sum( )](http://tinkerpop.apache.org/docs/current/reference/#sum-step): el paso `sum()` generalmente se puede convertir en una operación nativa del motor de Neptune, pero no cuando se usa para calcular una suma global, como esta:

  ```
  g.V().has('code','ANC').outE('routes').values('dist').sum()
  ```
+ [union( )](http://tinkerpop.apache.org/docs/current/reference/#union-step): el paso `union()` se puede convertir en una operación nativa del motor de Neptune siempre que sea el último paso de la consulta, aparte del paso terminal.
+ [unfold ()](http://tinkerpop.apache.org/docs/current/reference/#unfold-step): el `unfold()` paso solo se puede convertir en una operación nativa del motor de Neptuno cuando se usa con [el patrón Upsert](http://tinkerpop.apache.org/docs/current/recipes/#element-existence) recomendado en [TinkerPopla página de recetas](http://tinkerpop.apache.org/docs/current/recipes/) y cuando se usa junto con esto: `cap()`

  ```
  g.V().has('airport','country','IE').aggregate('airport').limit(2)
       .cap('airport').unfold()
  ```
+ [where( )](http://tinkerpop.apache.org/docs/current/reference/#where-step): el paso `where()` generalmente se puede convertir en una operación nativa del motor de Neptune, excepto en los siguientes casos:
  + Cuando se utilizan modulaciones by(), como esta:

    ```
    g.V().hasLabel('airport').as('a')
         .where(gt('a')).by('runways')
    ```
  + Cuando se utilizan operadores de comparación que no sean `eq`, `neq`, `within` y `without`.
  + Cuando se utilizan agregaciones proporcionadas por el usuario.

## Pasos de Gremlin que nunca se convierten en operaciones nativas del motor de Neptune
<a name="gremlin-steps-never"></a>

Los siguientes pasos de Gremlin son compatibles con Neptune, pero nunca se convierten en operaciones nativas del motor de Neptune. En su lugar, los ejecuta el servidor de Gremlin.
+ [choose( )](http://tinkerpop.apache.org/docs/current/reference/#choose-step)
+ [coin( )](http://tinkerpop.apache.org/docs/current/reference/#coin-step)
+ [inject( )](http://tinkerpop.apache.org/docs/current/reference/#inject-step)
+ [match( )](http://tinkerpop.apache.org/docs/current/reference/#match-step)
+ [math( )](http://tinkerpop.apache.org/docs/current/reference/#math-step)
+ [max( )](http://tinkerpop.apache.org/docs/current/reference/#max-step)
+ [mean( )](http://tinkerpop.apache.org/docs/current/reference/#mean-step)
+ [min( )](http://tinkerpop.apache.org/docs/current/reference/#min-step)
+ [option( )](http://tinkerpop.apache.org/docs/current/reference/#option-step)
+ [optional( )](http://tinkerpop.apache.org/docs/current/reference/#optional-step)
+ [path( )](http://tinkerpop.apache.org/docs/current/reference/#path-step)
+ [propertyMap( )](http://tinkerpop.apache.org/docs/current/reference/#propertymap-step)
+ [sample( )](http://tinkerpop.apache.org/docs/current/reference/#sample-step)
+ [skip( )](http://tinkerpop.apache.org/docs/current/reference/#skip-step)
+ [tail( )](http://tinkerpop.apache.org/docs/current/reference/#tail-step)
+ [timeLimit( )](http://tinkerpop.apache.org/docs/current/reference/#timelimit-step)
+ [tree( )](http://tinkerpop.apache.org/docs/current/reference/#tree-step)

## Pasos de Gremlin que Neptune no admite en absoluto
<a name="neptune-gremlin-steps-unsupported"></a>

Los siguientes pasos de Gremlin no se admiten en absoluto en Neptune. En la mayoría de los casos, esto se debe a que requieren un `GraphComputer`, que Neptune no admite actualmente.
+ [connectedComponent( )](http://tinkerpop.apache.org/docs/current/reference/#connectedcomponent-step)
+ [io( )](http://tinkerpop.apache.org/docs/current/reference/#io-step)
+ [shortestPath( )](http://tinkerpop.apache.org/docs/current/reference/#shortestpath-step)
+ [withComputer( )](http://tinkerpop.apache.org/docs/current/reference/#with-step)
+ [pageRank( )](http://tinkerpop.apache.org/docs/current/reference/#pagerank-step)
+ [peerPressure( )](http://tinkerpop.apache.org/docs/current/reference/#peerpressure-step)
+ [program( )](http://tinkerpop.apache.org/docs/current/reference/#program-step)

En realidad, el paso `io()` se admite parcialmente, ya que se puede usar `read()` desde una URL, pero no para `write()`.

# Uso de Gremlin con el motor de consultas DFE de Neptune
<a name="gremlin-with-dfe"></a>

Si habilita el [motor de consultas alternativo](neptune-dfe-engine.md) de Neptune, conocido como DFE, en [modo laboratorio](features-lab-mode.md) (configurando el parámetro del `neptune_lab_mode` clúster de base de datos en`DFEQueryEngine=enabled`), Neptune traduce Gremlin de solo lectura queries/traversals en una representación lógica intermedia y los ejecuta en el motor DFE siempre que sea posible.

Sin embargo, el DFE aún no admite todos los pasos de Gremlin. Cuando un paso no se puede ejecutar de forma nativa en el DFE, Neptune recurre a él TinkerPop para ejecutar el paso. Los informes `explain` y `profile` incluyen advertencias cuando esto ocurre.

# Cobertura de pasos de Gremlin en el DFE
<a name="gremlin-step-coverage-in-DFE"></a>

 El DFE de Gremlin es una característica de modo laboratorio y se puede utilizar habilitando el parámetro de clúster o mediante la sugerencia de consulta `Neptune#useDFE`. Para obtener más información, consulte [Uso de Gremlin con el motor de consultas DFE de Neptune](https://docs.aws.amazon.com//neptune/latest/userguide/gremlin-with-dfe.html). 

 Los siguientes pasos están disponibles para su uso en el DFE de Gremlin. 

## Pasos de ruta y recorrido:
<a name="DFE-path-and-traversal"></a>

 [asDate ()](https://tinkerpop.apache.org/docs/current/reference/#asDate-step), [barrier ()](https://tinkerpop.apache.org/docs/current/reference/#barrier-step), [call ()](https://tinkerpop.apache.org/docs/current/reference/#call-step), [cap ()](https://tinkerpop.apache.org/docs/current/reference/#cap-step), [dateAdd ()](https://tinkerpop.apache.org/docs/current/reference/#dateadd-step), [dateDiff ()](https://tinkerpop.apache.org/docs/current/reference/#datediff-step), [disjunct ()](https://tinkerpop.apache.org/docs/current/reference/#disjunct-step)[, drop](https://tinkerpop.apache.org/docs/current/reference/#drop-step) [(), fail](https://tinkerpop.apache.org/docs/current/reference/#fail-step) [(), filter](https://tinkerpop.apache.org/docs/current/reference/#filter-step) [(), flatMap](https://tinkerpop.apache.org/docs/current/reference/#flatmap-step) [(), id](https://tinkerpop.apache.org/docs/current/reference/#id-step) [(), identity](https://tinkerpop.apache.org/docs/current/reference/#identity-step) [(), index ()](https://tinkerpop.apache.org/docs/current/reference/#index-step), [intersect ()](https://tinkerpop.apache.org/docs/current/reference/#intersect-step), [inject ()](https://tinkerpop.apache.org/docs/current/reference/#inject-step)[, label](https://tinkerpop.apache.org/docs/current/reference/#label-step) [(), length](https://tinkerpop.apache.org/docs/current/reference/#length-step) [(), loops](https://tinkerpop.apache.org/docs/current/reference/#loops-step) [(), map](https://tinkerpop.apache.org/docs/current/reference/#map-step) [()](https://tinkerpop.apache.org/docs/current/reference/#order-step), [order () (local)](https://tinkerpop.apache.org/docs/current/reference/#order-step), [ruta ()](https://tinkerpop.apache.org/docs/current/reference/#path-step), [project ()](https://tinkerpop.apache.org/docs/current/reference/#project-step), [range ()](https://tinkerpop.apache.org/docs/current/reference/#range-step), [repeat ()](https://tinkerpop.apache.org/docs/current/reference/#repeat-step), [reverse ()](https://tinkerpop.apache.org/docs/current/reference/#reverse-step), [sack ()](https://tinkerpop.apache.org/docs/current/reference/#sack-step), [sample ()](https://tinkerpop.apache.org/docs/current/reference/#sample-step), [select ()](https://tinkerpop.apache.org/docs/current/reference/#select-step), [sideEffect ()](https://tinkerpop.apache.org/docs/current/reference/#sideeffect-step), [split ()](https://tinkerpop.apache.org/docs/current/reference/#split-step), [unfold ()](https://tinkerpop.apache.org/docs/current/reference/#unfold-step), [union ()](https://tinkerpop.apache.org/docs/current/reference/#union-step) 

## Pasos de agregación y recopilación:
<a name="DFE-aggregate-and-collection"></a>

 [aggregate (global)](https://tinkerpop.apache.org/docs/current/reference/#aggregate-step)[, [combine ()](https://tinkerpop.apache.org/docs/current/reference/#combine-step), [count (), dedup ()](https://tinkerpop.apache.org/docs/current/reference/#count-step)[, dedup (local)](https://tinkerpop.apache.org/docs/current/reference/#dedup-step)[, fold ()](https://tinkerpop.apache.org/docs/current/reference/#dedup-step)[, group ()](https://tinkerpop.apache.org/docs/current/reference/#fold-step)[, groupCount ()](https://tinkerpop.apache.org/docs/current/reference/#group-step),](https://tinkerpop.apache.org/docs/current/reference/#groupcount-step) 

## Pasos matemáticos:
<a name="DFE-mathematical"></a>

 [max()](https://tinkerpop.apache.org/docs/current/reference/#max-step), [mean()](https://tinkerpop.apache.org/docs/current/reference/#mean-step), [min()](https://tinkerpop.apache.org/docs/current/reference/#min-step), [sum()](https://tinkerpop.apache.org/docs/current/reference/#sum-step) 

## Pasos de elementos:
<a name="DFE-element"></a>

 [otherV()](https://tinkerpop.apache.org/docs/current/reference/#otherv-step), [elementMap()](https://tinkerpop.apache.org/docs/current/reference/#elementmap-step), [element()](https://tinkerpop.apache.org/docs/current/reference/#element-step), [v()](https://tinkerpop.apache.org/docs/current/reference/#graph-step), [ out(), in(), both(), outE(), inE(), bothE(), outV(), inV(), bothV(), otherV()](https://tinkerpop.apache.org/docs/current/reference/#vertex-step) 

## Pasos de propiedad:
<a name="DFE-property"></a>

 [properties()](https://tinkerpop.apache.org/docs/current/reference/#properties-step), [key()](https://tinkerpop.apache.org/docs/current/reference/#key-step), [valueMap()](https://tinkerpop.apache.org/docs/current/reference/#propertymap-step), [value()](https://tinkerpop.apache.org/docs/current/reference/#value-step) 

## Pasos de filtrado:
<a name="DFE-filter"></a>

 [and()](https://tinkerpop.apache.org/docs/current/reference/#and-step), [coalesce()](https://tinkerpop.apache.org/docs/current/reference/#coalesce-step), [coin()](https://tinkerpop.apache.org/docs/current/reference/#coin-step), [has()](https://tinkerpop.apache.org/docs/current/reference/#has-step), [is()](https://tinkerpop.apache.org/docs/current/reference/#is-step), [local()](https://tinkerpop.apache.org/docs/current/reference/#local-step), [none()](https://tinkerpop.apache.org/docs/current/reference/#none-step), [not()](https://tinkerpop.apache.org/docs/current/reference/#not-step), [or()](https://tinkerpop.apache.org/docs/current/reference/#or-step), [where()](https://tinkerpop.apache.org/docs/current/reference/#where-step) 

## Pasos para la manipulación de cadenas:
<a name="DFE-string-manipulation"></a>

 [concat()](https://tinkerpop.apache.org/docs/current/reference/#concat-step), [lTrim()](https://tinkerpop.apache.org/docs/current/reference/#lTrim-step), [rTrim()](https://tinkerpop.apache.org/docs/current/reference/#rtrim-step), [substring()](https://tinkerpop.apache.org/docs/current/reference/#substring-step), [toLower()](https://tinkerpop.apache.org/docs/current/reference/#toLower-step), [toUpper()](https://tinkerpop.apache.org/docs/current/reference/#toUpper-step), [trim()](https://tinkerpop.apache.org/docs/current/reference/#trim-step) 

## Predicados:
<a name="DFE-predicates"></a>
+  [ Compare: eq, neq, lt, lte, gt, gte](https://tinkerpop.apache.org/docs/current/reference/#a-note-on-predicates) 
+  [Contains: within, without](https://tinkerpop.apache.org/docs/current/reference/#a-note-on-predicates) 
+  [textoP: termina con, contiene, no contiene notStartingWith notEndingWith](https://tinkerpop.apache.org/docs/current/reference/#a-note-on-predicates) 
+  [ P: and, or, between, outside, inside](https://tinkerpop.apache.org/docs/current/reference/#a-note-on-predicates) 

## Limitaciones
<a name="gremlin-with-dfe-limitations"></a>

 Aún no se admite la repetición con límite, las etiquetas dentro del recorrido de repetición y la deduplicación en el DFE. 

```
// With Limit inside the repeat traversal
  g.V().has('code','AGR').repeat(out().limit(5)).until(has('code','FRA'))
  
  // With Labels inside the repeat traversal
  g.V().has('code','AGR').repeat(out().as('a')).until(has('code','FRA'))
  
  // With Dedup inside the repeat traversal
  g.V().has('code','AGR').repeat(out().dedup()).until(has('code','FRA'))
```

 Aún no se admiten rutas con repeticiones anidadas ni pasos ramificados. 

```
// Path with branching steps
  g.V().has('code','AGR').union(identity, outE().inV()).path().by('code')
  
  
  // With nested repeat
  g.V().has('code','AGR').repeat(out().union(identity(), out())).path().by('code')
```

## Intercalación de planificación de consultas
<a name="gremlin-with-dfe-interleaving"></a>

Cuando el proceso de conversión encuentra un paso de Gremlin que no tiene un operador de DFE nativo correspondiente, antes de volver a utilizar Tinkerpop, intenta buscar otras partes de la consulta intermedias que se puedan ejecutar de forma nativa en el motor DFE. Para ello, aplica la lógica de intercalado al recorrido de nivel superior. El resultado es que, siempre que es posible, se utilizan pasos compatibles.

Cualquier conversión de consultas intermedias sin prefijo se representa mediante `NeptuneInterleavingStep` en las salidas `explain` y `profile`.

Para comparar el rendimiento, es posible que desee desactivar el intercalado en una consulta y, al mismo tiempo, seguir utilizando el motor DFE para ejecutar la parte del prefijo. O bien, es posible que desee utilizar solo el TinkerPop motor para la ejecución de consultas sin prefijo. Puede hacerlo mediante la sugerencia de consulta `disableInterleaving`.

Del mismo modo que la sugerencia de consulta [useDFE](gremlin-query-hints-useDFE.md) con un valor de `false` impide que una consulta se ejecute en el DFE, la sugerencia de consulta `disableInterleaving` con un valor de `true` desactiva el intercalado del DFE para la conversión de una consulta. Por ejemplo:

```
g.with('Neptune#disableInterleaving', true)
 .V().has('genre','drama').in('likes')
```

## Se ha actualizado la salida `explain` y `profile` de Gremlin
<a name="gremlin-with-dfe-explain-update"></a>

[explain](gremlin-explain.md) de Gremlin proporciona detalles sobre el recorrido optimizado que Neptune utiliza para ejecutar una consulta. Consulte la [salida de `explain` del DFE de ejemplo](gremlin-explain-api.md#gremlin-explain-dfe) para ver un ejemplo del aspecto que tiene la salida de `explain` cuando el motor DFE está habilitado.

[API `profile` de Gremlin](gremlin-profile-api.md) ejecuta un recorrido de Gremlin específico, recopila varias métricas sobre la ejecución y produce un informe de profile que contiene detalles sobre el plan de consultas optimizado y las estadísticas de tiempo de ejecución de varios operadores. Consulte la [salida de `profile` del DFE de ejemplo](gremlin-profile-api.md#gremlin-profile-sample-dfe-output) para ver un ejemplo del aspecto que tiene la salida de `profile` cuando el motor DFE está habilitado.

**nota**  
Dado que el DFE es una característica experimental publicada en el modo de laboratorio, el formato exacto de la salida de `explain` y `profile` está sujeto a cambios.

# Acceso al gráfico de Neptune con openCypher
<a name="access-graph-opencypher"></a>

Neptune permite crear aplicaciones de gráficos mediante openCypher, que actualmente es uno de los lenguajes de consulta más populares para los desarrolladores que trabajan con bases de datos de gráficos. A los desarrolladores, analistas de negocio y científicos de datos les gusta la sintaxis de openCypher, que está inspirada en SQL, porque proporciona una estructura familiar para redactar consultas para aplicaciones gráficas.

**openCypher** es un lenguaje de consulta declarativo para gráficos de propiedades que desarrolló originalmente Neo4j, luego de código abierto en 2015, y que contribuyó al proyecto [openCypher](http://www.opencypher.org/) en virtud de una licencia de código abierto Apache 2. Su sintaxis está documentada en [Cypher Query Language Reference, versión 9](https://s3.amazonaws.com/artifacts.opencypher.org/openCypher9.pdf).

Para conocer las limitaciones y diferencias en la compatibilidad con Neptune de la especificación de openCypher, consulte [Conformidad con la especificación de openCypher en Amazon Neptune](feature-opencypher-compliance.md).

**nota**  
La implementación actual de Neo4j del lenguaje de consulta Cypher se diferencia en algunos aspectos de la especificación de openCypher. Si está migrando el código de Cypher actual de Neo4j a Neptune, consulte [Compatibilidad de Neptune con Neo4j](migration-compatibility.md) y [Reescritura de consultas de Cypher para ejecutarlas en openCypher en Neptune](migration-opencypher-rewrites.md) para obtener ayuda.

A partir de la versión 1.1.1.0 del motor, openCypher está disponible para su uso en producción en Neptune.

## Gremlin frente a openCypher: similitudes y diferencias
<a name="access-graph-opencypher-overview-with-gremlin"></a>

Gremlin y openCypher son lenguajes de consulta basados en gráficos de propiedades y se complementan en muchos aspectos.

Gremlin se diseñó para atraer a los programadores y adaptarse perfectamente al código. Como resultado, Gremlin es imperativo por diseño, mientras que la sintaxis declarativa de openCypher puede resultarle más familiar a las personas con experiencia en SQL o SPARQL. Gremlin puede parecer más natural para un científico de datos que usa Python en un cuaderno de Jupyter, mientras que openCypher puede parecer más intuitivo para un usuario empresarial con experiencia en SQL.

Lo bueno es que **no tiene que elegir** entre Gremlin y openCypher en Neptune. Las consultas en cualquiera de los dos lenguajes pueden funcionar en el mismo gráfico, independientemente de cuál de los dos se haya utilizado para introducir esos datos. Puede que le resulte más cómodo usar Gremlin para algunas cosas y openCypher para otras, dependiendo de lo que esté haciendo.

Gremlin utiliza una sintaxis imperativa que le permite controlar cómo se mueve por el gráfico en una serie de pasos, cada uno de los cuales incluye un flujo de datos, realiza alguna acción sobre él (mediante un filtro, un mapa, etc.) y, a continuación, envía los resultados al siguiente paso. Por lo general, una consulta de Gremlin adopta la forma `g.V()` y va seguida de pasos adicionales.

En openCypher, se utiliza una sintaxis declarativa, inspirada en SQL, que especifica un patrón de nodos y relaciones que se pueden encontrar en el gráfico mediante una sintaxis de motivos (como `()-[]->()`). Una consulta de openCypher suele empezar con una cláusula `MATCH`, seguida de otras cláusulas como `WHERE`, `WITH` y `RETURN`.

# Introducción al uso de openCypher
<a name="access-graph-opencypher-overview-getting-started"></a>

Puede consultar datos de gráficos de propiedades en Neptune con openCypher independientemente de cómo se hayan cargado, pero no puede usar openCypher para consultar datos cargados como RDF.

El [programa de carga masiva de Neptune](bulk-load.md) acepta datos de gráficos de propiedades en un [formato CSV para Gremlin](bulk-load-tutorial-format-gremlin.md) y en un [formato CSV para openCypher](bulk-load-tutorial-format-opencypher.md). Por supuesto, también puede añadir datos de propiedades a su gráfico mediante las consultas OpenCypher de Gremlin and/or .

Hay disponibles muchos tutoriales en línea para aprender el lenguaje de consultas Cypher. Aquí se ofrecen algunos ejemplos rápidos de consultas de openCypher que pueden ayudarle a hacerse una idea del lenguaje, pero la mejor y más sencilla forma de empezar a utilizar openCypher para consultar el gráfico de Neptune es utilizar los cuadernos de openCypher del [entorno de trabajo de Neptune](graph-notebooks.md). El banco de trabajo es de código abierto y está alojado en. GitHub [https://github.com/aws-samples/amazon-neptune-samples](https://github.com/aws-samples/amazon-neptune-samples/)

[Encontrará los cuadernos OpenCypher en el repositorio de cuadernos gráficos de Neptune GitHub .](https://github.com/aws/graph-notebook/tree/main/src/graph_notebook/notebooks) En concreto, consulte la [visualización de las rutas aéreas](https://github.com/aws/graph-notebook/blob/main/src/graph_notebook/notebooks/02-Visualization/Air-Routes-openCypher.ipynb) y los cuadernos de openCypher de los [equipos de la liga inglesa](https://github.com/aws/graph-notebook/blob/main/src/graph_notebook/notebooks/02-Visualization/EPL-openCypher.ipynb).

Los datos procesados por OpenCypher adoptan la forma de una serie desordenada de mapas. key/value La forma principal de refinar, manipular y aumentar estos mapas es usar cláusulas que realicen tareas como la coincidencia de patrones, la inserción, la actualización y la eliminación de los pares. key/value 

Existen varias cláusulas en openCypher para buscar patrones de datos en el gráfico, de las cuales `MATCH` es la más común. `MATCH` le permite especificar el patrón de nodos, relaciones y filtros que desea buscar en el gráfico. Por ejemplo:
+ **Obtener todos los nodos**

  ```
  MATCH (n) RETURN n
  ```
+ **Encontrar los nodos conectados**

  ```
  MATCH (n)-[r]->(d) RETURN n, r, d
  ```
+ **Encontrar una ruta**

  ```
  MATCH p=(n)-[r]->(d) RETURN p
  ```
+ **Obtener todos los nodos con una etiqueta**

  ```
  MATCH (n:airport) RETURN n
  ```

Tenga en cuenta que la primera consulta anterior devuelve todos los nodos del gráfico y las dos siguientes muestran todos los nodos que tienen una relación. Esto no suele ser recomendable. En casi todos los casos, es necesario restringir los datos que se devuelven, lo que puede hacer especificando las etiquetas y propiedades de los nodos o relaciones, como en el cuarto ejemplo.

Encontrará una hoja de referencia muy útil sobre la sintaxis de openCypher en el [repositorio de ejemplos de GitHub de Neptune](https://github.com/aws-samples/amazon-neptune-samples/tree/master/opencypher/Cheatsheet.md).

# Servlet de estado de openCypher de Neptune y punto de conexión de estado
<a name="access-graph-opencypher-status"></a>

El punto de conexión de estado de openCypher proporciona acceso a la información sobre las consultas que se están ejecutando actualmente en el servidor o están en espera de ejecutarse. También le permite cancelar esas consultas. El punto de conexión es:

```
https://(the server):(the port number)/openCypher/status
```

Puede utilizar los métodos HTTP `GET` y `POST` para obtener el estado actual del servidor o para cancelar una consulta. También puede usar el método `DELETE` para cancelar una consulta en curso o en espera.

## Parámetros de las solicitudes de estado
<a name="access-graph-opencypher-status-parameters"></a>

**Parámetros de la consulta de estado**
+ **`includeWaiting`** (`true` o `false`): cuando se establece en `true` y otros parámetros no están presentes, se devuelve la información de estado tanto para las consultas en espera como para las consultas en ejecución.
+ **`cancelQuery`**: se utiliza únicamente con los métodos `GET` y `POST` para indicar que se trata de una solicitud de cancelación. El método `DELETE` no necesita este parámetro.

  El valor del parámetro `cancelQuery` no se utiliza, pero cuando `cancelQuery` está presente, el parámetro `queryId` es obligatorio para identificar qué consulta se va a cancelar.
+ **`queryId`**: contiene el identificador de una consulta específica.

  Cuando se usa con el método `GET` o `POST` y el parámetro `cancelQuery` no está presente, `queryId` hace que se devuelva la información de estado de la consulta específica que identifica. Si el parámetro `cancelQuery` está presente, se cancela la consulta específica que `queryId` identifica.

  Cuando se usa con el método `DELETE`, `queryId` siempre indica que se debe cancelar una consulta específica.
+ **`silent`**: solo se usa al cancelar una consulta. Si se establece en `true`, hace que la cancelación se produzca de forma silenciosa.

## Campos de respuesta a la solicitud de estado
<a name="access-graph-opencypher-status-response-fields"></a>

**Campos de respuesta de estado si no se proporciona el identificador de una consulta específica**
+ **acceptedQueryCount**— El número de consultas que se han aceptado pero que aún no se han completado, incluidas las consultas de la cola.
+ **runningQueryCount**— El número de consultas de OpenCypher que se están ejecutando actualmente.
+ **queries**: una lista de las consultas de openCypher actuales.

**Campos de respuesta de estado para una consulta específica**
+ **queryId**: un identificador GUID de la consulta. Neptune asigna automáticamente este valor de identificador a cada consulta o también puede asignar su propio identificador (consulte [Inserte un identificador personalizado en una consulta de Neptune Gremlin o SPARQL](features-query-id.md)).
+ **queryString**: la consulta enviada. Esta se trunca en 1024 caracteres, si supera este número.
+ **queryEvalStats**— Estadísticas de esta consulta:
  + **waited**: indica cuánto tiempo esperó la consulta, en milisegundos.
  + **elapsed**: el número de milisegundos que la consulta lleva en ejecución.
  + **cancelled**: `True` indica que la consulta se ha cancelado o `False` que no se ha cancelado.

## Ejemplos de solicitudes de estado y respuestas
<a name="access-graph-opencypher-status-samples"></a>
+ **Solicitud del estado de todas las consultas, incluidas las que están en espera:**

  ```
  curl https://server:port/openCypher/status \
    --data-urlencode "includeWaiting=true"
  ```

  *Respuesta:*

  ```
  {
    "acceptedQueryCount" : 0,
    "runningQueryCount" : 0,
    "queries" : [ ]
  }
  ```
+ **Solicitud del estado de todas las consultas, **sin** incluir las que están en espera:**

  ```
  curl https://server:port/openCypher/status
  ```

  *Respuesta*:

  ```
  {
    "acceptedQueryCount" : 0,
    "runningQueryCount" : 0,
    "queries" : [ ]
  }
  ```
+ **Solicitud del estado de una sola consulta:**

  ```
  curl https://server:port/openCypher/status \
   --data-urlencode "queryId=eadc6eea-698b-4a2f-8554-5270ab17ebee"
  ```

  *Respuesta*:

  ```
  {
    "queryId" : "eadc6eea-698b-4a2f-8554-5270ab17ebee",
    "queryString" : "MATCH (n1)-[:knows]->(n2), (n2)-[:knows]->(n3), (n3)-[:knows]->(n4), (n4)-[:knows]->(n5), (n5)-[:knows]->(n6), (n6)-[:knows]->(n7), (n7)-[:knows]->(n8), (n8)-[:knows]->(n9), (n9)-[:knows]->(n10) RETURN COUNT(n1);",
    "queryEvalStats" : {
      "waited" : 0,
      "elapsed" : 23463,
      "cancelled" : false
    }
  }
  ```
+ **Solicitudes para cancelar una consulta**

  1. Uso de `POST`:

  ```
  curl -X POST https://server:port/openCypher/status \
    --data-urlencode "cancelQuery" \
    --data-urlencode "queryId=f43ce17b-db01-4d37-a074-c76d1c26d7a9"
  ```

  *Respuesta*:

  ```
  {
    "status" : "200 OK",
    "payload" : true
  }
  ```

  2. Uso de `GET`:

  ```
  curl -X GET https://server:port/openCypher/status \
    --data-urlencode "cancelQuery" \
    --data-urlencode "queryId=588af350-cfde-4222-bee6-b9cedc87180d"
  ```

  *Respuesta*:

  ```
  {
    "status" : "200 OK",
    "payload" : true
  }
  ```

  3. Uso de `DELETE`:

  ```
  curl -X DELETE \
    -s "https://server:port/openCypher/status?queryId=b9a516d1-d25c-4301-bb80-10b2743ecf0e"
  ```

  *Respuesta*:

  ```
  {
    "status" : "200 OK",
    "payload" : true
  }
  ```

# El punto de conexión HTTPS de Amazon Neptune OpenCypher
<a name="access-graph-opencypher-queries"></a>

**Topics**
+ [OpenCypher consultas de lectura y escritura en el punto final HTTPS](#access-graph-opencypher-queries-read-write)
+ [El formato de resultados OpenCypher JSON predeterminado](#access-graph-opencypher-queries-results-simple-JSON)
+ [Cabeceras finales HTTP opcionales para respuestas de varias partes OpenCypher](#optional-http-trailing-headers)

**nota**  
Actualmente, Neptune no admite HTTP/2 para las solicitudes de la API REST. Los clientes deben usar HTTP/1.1 al conectarse a los puntos finales.

## OpenCypher consultas de lectura y escritura en el punto final HTTPS
<a name="access-graph-opencypher-queries-read-write"></a>

El punto final OpenCypher HTTPS admite consultas de lectura y actualización mediante el método `GET` y el `POST` método. No se admiten los métodos `DELETE` y `PUT`.

En las siguientes instrucciones, se explica cómo conectarse al OpenCypher punto final mediante el `curl` comando y HTTPS. Siga estas instrucciones desde una instancia de Amazon EC2 que esté en la misma nube privada virtual (VPC) que su instancia de base de datos de Neptune.

La sintaxis es la siguiente:

```
HTTPS://(the server):(the port number)/openCypher
```

Estos son ejemplos de consultas de lectura, una que usa `POST` y otra que usa `GET`.

1. Uso de `POST`:

```
curl HTTPS://server:port/openCypher \
  -d "query=MATCH (n1) RETURN n1;"
```

2. Uso de `GET` (la cadena de consulta está codificada en una URL):

```
curl -X GET \
  "HTTPS://server:port/openCypher?query=MATCH%20(n1)%20RETURN%20n1"
```

Estos son ejemplos de write/update consultas, una que usa `POST` y otra que usa`GET`:

1. Uso de `POST`:

```
curl HTTPS://server:port/openCypher \
  -d "query=CREATE (n:Person { age: 25 })"
```

2. Uso de `GET` (la cadena de consulta está codificada en una URL):

```
curl -X GET \
  "HTTPS://server:port/openCypher?query=CREATE%20(n%3APerson%20%7B%20age%3A%2025%20%7D)"
```

## El formato de resultados OpenCypher JSON predeterminado
<a name="access-graph-opencypher-queries-results-simple-JSON"></a>

El siguiente formato JSON se devuelve de forma predeterminada o si se establece explícitamente el encabezado de la solicitud en `Accept: application/json`. Este formato está diseñado para poder analizarse fácilmente en objetos utilizando las características del lenguaje nativo de la mayoría de las bibliotecas.

El documento JSON que se devuelve contiene un campo, `results`, que contiene los valores devueltos por la consulta. Los ejemplos siguientes muestran el formato JSON para valores comunes.

**Ejemplo de respuesta de valor:**

```
{
  "results": [
    {
      "count(a)": 121
    }
  ]
}
```

**Ejemplo de respuesta de nodo:**

```
{
  "results": [
    {
      "a": {
        "~id": "22",
        "~entityType": "node",
        "~labels": [
          "airport"
        ],
        "~properties": {
          "desc": "Seattle-Tacoma",
          "lon": -122.30899810791,
          "runways": 3,
          "type": "airport",
          "country": "US",
          "region": "US-WA",
          "lat": 47.4490013122559,
          "elev": 432,
          "city": "Seattle",
          "icao": "KSEA",
          "code": "SEA",
          "longest": 11901
        }
      }
    }
  ]
}
```

**Ejemplo de respuesta de relación:**

```
{
  "results": [
    {
      "r": {
        "~id": "7389",
        "~entityType": "relationship",
        "~start": "22",
        "~end": "151",
        "~type": "route",
        "~properties": {
          "dist": 956
        }
      }
    }
  ]
}
```

**Ejemplo de respuesta de ruta:**

```
{
  "results": [
    {
      "p": [
        {
          "~id": "22",
          "~entityType": "node",
          "~labels": [
            "airport"
          ],
          "~properties": {
            "desc": "Seattle-Tacoma",
            "lon": -122.30899810791,
            "runways": 3,
            "type": "airport",
            "country": "US",
            "region": "US-WA",
            "lat": 47.4490013122559,
            "elev": 432,
            "city": "Seattle",
            "icao": "KSEA",
            "code": "SEA",
            "longest": 11901
          }
        },
        {
          "~id": "7389",
          "~entityType": "relationship",
          "~start": "22",
          "~end": "151",
          "~type": "route",
          "~properties": {
            "dist": 956
          }
        },
        {
          "~id": "151",
          "~entityType": "node",
          "~labels": [
            "airport"
          ],
          "~properties": {
            "desc": "Ontario International Airport",
            "lon": -117.600997924805,
            "runways": 2,
            "type": "airport",
            "country": "US",
            "region": "US-CA",
            "lat": 34.0559997558594,
            "elev": 944,
            "city": "Ontario",
            "icao": "KONT",
            "code": "ONT",
            "longest": 12198
          }
        }
      ]
    }
  ]
}
```

## Cabeceras finales HTTP opcionales para respuestas de varias partes OpenCypher
<a name="optional-http-trailing-headers"></a>

 Esta característica está disponible a partir de la versión [1.4.5.0](https://docs.aws.amazon.com/releases/release-1.4.5.0.xml) del motor de Neptune. 

 La respuesta HTTP a OpenCypher las consultas y actualizaciones suele devolverse en varios fragmentos. Cuando se producen errores después de enviar los fragmentos de respuesta iniciales (con un código de estado HTTP de 200), puede resultar difícil diagnosticar el problema. De forma predeterminada, Neptune informa de dichos errores añadiendo un mensaje de error al cuerpo del mensaje, que puede estar dañado debido a la naturaleza de transmisión de la respuesta. 

**Uso de encabezados finales**  
 Para mejorar la detección y el diagnóstico de errores, puede habilitar los encabezados finales incluyendo un encabezado final (te: trailers) con codificación de transferencia (TE) en su solicitud. Si lo hace, Neptune incluirá dos nuevos campos de encabezados dentro de los encabezados finales de los fragmentos de respuesta: 
+  `X-Neptune-Status`: contiene el código de respuesta seguido de un nombre abreviado. Por ejemplo, en caso de que se realizara correctamente, el encabezado final sería: `X-Neptune-Status: 200 OK`. En caso de fallo, el código de respuesta sería uno de los códigos de error del motor de Neptune, como `X-Neptune-Status: 500 TimeLimitExceededException`. 
+  `X-Neptune-Detail`: está vacío si las solicitudes se han realizado correctamente. En caso de errores, contiene el mensaje de error JSON. Como solo se permiten caracteres ASCII en los valores de los encabezados HTTP, la cadena JSON está codificada en URL. El mensaje de error también se sigue adjuntando al cuerpo del mensaje de respuesta. 

 Consulte la [página de MDN sobre los encabezados de las solicitudes TE](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/TE) para obtener más información. 

**OpenCypher ejemplo de uso de encabezados finales**  
 En este ejemplo se muestra cómo los encabezados finales ayudan a diagnosticar una consulta que supera su límite de tiempo: 

```
curl --raw 'https://your-neptune-endpoint:port/openCypher' \
-H 'TE: trailers' \
-d 'query=MATCH(n) RETURN n.firstName'
 
 
Output:
< HTTP/1.1 200 OK
< transfer-encoding: chunked
< trailer: X-Neptune-Status, X-Neptune-Detail
< content-type: application/json;charset=UTF-8
< 
< 
{
  "results": [{
      "n.firstName": "Hossein"
    }, {
      "n.firstName": "Jan"
    }, {
      "n.firstName": "Miguel"
    }, {
      "n.firstName": "Eric"
    }, 
{"detailedMessage":"Operation terminated (deadline exceeded)",
"code":"TimeLimitExceededException",
"requestId":"a7e9d2aa-fbb7-486e-8447-2ef2a8544080",
"message":"Operation terminated (deadline exceeded)"}
0
X-Neptune-Status: 500 TimeLimitExceededException
X-Neptune-Detail: %7B%22detailedMessage%22%3A%22Operation+terminated+%28deadline+exceeded%29%22%2C%22code%22%3A%22TimeLimitExceededException%22%2C%22requestId%22%3A%22a7e9d2aa-fbb7-486e-8447-2ef2a8544080%22%2C%22message%22%3A%22Operation+terminated+%28deadline+exceeded%29%22%7D
```

**Desglose de la respuesta:**  
 El ejemplo anterior muestra cómo una OpenCypher respuesta con encabezados finales puede ayudar a diagnosticar errores en las consultas. Aquí vemos cuatro partes secuenciales: (1) los encabezados iniciales con un estado 200 OK que indica que la transmisión ha comenzado, (2) los resultados JSON parciales (rotos) que se han transmitido correctamente antes del error, (3) el mensaje de error adjunto que muestra el tiempo de espera y (4) los encabezados finales que contienen el estado final (500) e información de error detallada. TimeLimitExceededException 

# Uso del SDK para ejecutar consultas de OpenCypher AWS
<a name="access-graph-opencypher-sdk"></a>

Con el AWS SDK, puede ejecutar consultas de OpenCypher en su gráfico de Neptune mediante el lenguaje de programación que prefiera. El SDK de la API de datos de Neptune (nombre del servicio`neptunedata`) proporciona la [ExecuteOpenCypherQuery](https://docs.aws.amazon.com/neptune/latest/data-api/API_ExecuteOpenCypherQuery.html)acción para enviar consultas de OpenCypher.

Debe ejecutar estos ejemplos desde una instancia de Amazon EC2 en la misma nube privada virtual (VPC) que el clúster de base de datos de Neptune, o desde una ubicación que tenga conectividad de red con el punto final del clúster.

A continuación, encontrará enlaces directos a la documentación de referencia de la API del `neptunedata` servicio en cada idioma del SDK:


| Lenguaje de programación | Referencia de la API neptunedata | 
| --- | --- | 
| C\$1\$1 | [https://sdk.amazonaws.com/cpp/api/LATEST/aws-cpp-sdk-neptunedata/html/annotated.html](https://sdk.amazonaws.com/cpp/api/LATEST/aws-cpp-sdk-neptunedata/html/annotated.html) | 
| Go | [https://docs.aws.amazon.com/sdk-for-go/api/service/neptunedata/](https://docs.aws.amazon.com/sdk-for-go/api/service/neptunedata/) | 
| Java | [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/neptunedata/package-summary.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/neptunedata/package-summary.html) | 
| JavaScript | [https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-neptunedata/](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-neptunedata/) | 
| Kotlin | [https://sdk.amazonaws.com/kotlin/api/latest/neptunedata/index.html](https://sdk.amazonaws.com/kotlin/api/latest/neptunedata/index.html) | 
| .NET | [https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Neptunedata/NNeptunedata.html](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/Neptunedata/NNeptunedata.html) | 
| PHP | [https://docs.aws.amazon.com/aws-sdk-php/v3/api/namespace-Aws.Neptunedata.html](https://docs.aws.amazon.com/aws-sdk-php/v3/api/namespace-Aws.Neptunedata.html) | 
| Python | [https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/neptunedata.html](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/neptunedata.html) | 
| Ruby | [https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Neptunedata.html](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Neptunedata.html) | 
| Rust | [https://crates.io/crates/aws-sdk-neptunedata](https://crates.io/crates/aws-sdk-neptunedata) | 
| CLI | [https://docs.aws.amazon.com/cli/latest/reference/neptunedata/](https://docs.aws.amazon.com/cli/latest/reference/neptunedata/) | 

## Ejemplos del SDK de OpenCypher AWS
<a name="access-graph-opencypher-sdk-examples"></a>

Los siguientes ejemplos muestran cómo configurar un `neptunedata` cliente, ejecutar una consulta de OpenCypher e imprimir los resultados. Sustituya *YOUR\$1NEPTUNE\$1HOST* y *YOUR\$1NEPTUNE\$1PORT* por el punto final y el puerto de su clúster de base de datos Neptune.

**Configuración de tiempo de espera y reintento del lado del cliente**  
El tiempo de espera del cliente del SDK controla cuánto tiempo espera el *cliente* para recibir una respuesta. No controla cuánto tiempo se ejecuta la consulta en el servidor. Si se agota el tiempo de espera del cliente antes de que finalice el servidor, es posible que la consulta continúe ejecutándose en Neptune mientras el cliente no tenga forma de recuperar los resultados.  
[Recomendamos establecer el tiempo de espera de lectura del lado del cliente en `0` (sin tiempo de espera) o en un valor que sea al menos unos segundos más largo que la configuración neptune\$1query\$1timeout del lado del servidor en su clúster de base de datos Neptune.](parameters.md#parameters-db-cluster-parameters-neptune_query_timeout) Esto permite a Neptune controlar cuándo se agota el tiempo de espera de las consultas.  
También recomendamos establecer el número máximo de reintentos en `1` (sin reintentos). Si el SDK vuelve a intentar una consulta que aún se está ejecutando en el servidor, es posible que se dupliquen las operaciones. Esto es especialmente importante en el caso de las consultas de mutación, en las que un reintento podría provocar escrituras duplicadas no deseadas.

------
#### [ Python ]

1. Siga las [instrucciones de instalación](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html) para instalar Boto3.

1. Cree un archivo con el nombre `openCypherExample.py` y pegue el siguiente código:

   ```
   import boto3
   import json
   from botocore.config import Config
   
   # Disable the client-side read timeout and retries so that
   # Neptune's server-side neptune_query_timeout controls query duration.
   client = boto3.client(
       'neptunedata',
       endpoint_url=f'https://YOUR_NEPTUNE_HOST:YOUR_NEPTUNE_PORT',
       config=Config(read_timeout=None, retries={'total_max_attempts': 1})
   )
   
   response = client.execute_open_cypher_query(
       openCypherQuery='MATCH (n) RETURN n LIMIT 1'
   )
   
   print(json.dumps(response['results'], indent=2))
   ```

1. Ejecute el ejemplo: `python openCypherExample.py`

------
#### [ Java ]

1. Siga las [instrucciones de instalación](https://docs.aws.amazon.com//sdk-for-java/latest/developer-guide/setup.html) para configurar el AWS SDK para Java.

1. Utilice el siguiente código para configurar una consulta de OpenCypher`NeptunedataClient`, ejecutar una e imprimir el resultado:

   ```
   import java.net.URI;
   import java.time.Duration;
   import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
   import software.amazon.awssdk.core.retry.RetryPolicy;
   import software.amazon.awssdk.services.neptunedata.NeptunedataClient;
   import software.amazon.awssdk.services.neptunedata.model.ExecuteOpenCypherQueryRequest;
   import software.amazon.awssdk.services.neptunedata.model.ExecuteOpenCypherQueryResponse;
   
   // Disable the client-side timeout and retries so that
   // Neptune's server-side neptune_query_timeout controls query duration.
   NeptunedataClient client = NeptunedataClient.builder()
       .endpointOverride(URI.create("https://YOUR_NEPTUNE_HOST:YOUR_NEPTUNE_PORT"))
       .overrideConfiguration(ClientOverrideConfiguration.builder()
           .apiCallTimeout(Duration.ZERO)
           .retryPolicy(RetryPolicy.none())
           .build())
       .build();
   
   ExecuteOpenCypherQueryRequest request = ExecuteOpenCypherQueryRequest.builder()
       .openCypherQuery("MATCH (n) RETURN n LIMIT 1")
       .build();
   
   ExecuteOpenCypherQueryResponse response = client.executeOpenCypherQuery(request);
   
   System.out.println(response.results().toString());
   ```

------
#### [ JavaScript ]

1. Siga las [instrucciones de instalación](https://docs.aws.amazon.com//sdk-for-javascript/v3/developer-guide/getting-started-nodejs.html) para configurar el AWS SDK. JavaScript Instale el paquete de cliente neptunedata:. `npm install @aws-sdk/client-neptunedata`

1. Cree un archivo con el nombre `openCypherExample.js` y pegue el siguiente código:

   ```
   import { NeptunedataClient, ExecuteOpenCypherQueryCommand } from "@aws-sdk/client-neptunedata";
   import { NodeHttpHandler } from "@smithy/node-http-handler";
   
   const config = {
       endpoint: "https://YOUR_NEPTUNE_HOST:YOUR_NEPTUNE_PORT",
       // Disable the client-side request timeout so that
       // Neptune's server-side neptune_query_timeout controls query duration.
       requestHandler: new NodeHttpHandler({
           requestTimeout: 0
       }),
       maxAttempts: 1
   };
   
   const client = new NeptunedataClient(config);
   
   const input = {
       openCypherQuery: "MATCH (n) RETURN n LIMIT 1"
   };
   
   const command = new ExecuteOpenCypherQueryCommand(input);
   const response = await client.send(command);
   
   console.log(JSON.stringify(response, null, 2));
   ```

1. Ejecute el ejemplo: `node openCypherExample.js`

------

# Uso del protocolo Bolt para realizar consultas de openCypher a Neptune
<a name="access-graph-opencypher-bolt"></a>

[Bolt](https://boltprotocol.org/) [es un client/server protocolo orientado a declaraciones desarrollado inicialmente por Neo4j y licenciado bajo la licencia Creative Commons 3.0 Attribution. ShareAlike](https://creativecommons.org/licenses/by-sa/3.0/) Está orientado al cliente, lo que significa que el cliente siempre inicia el intercambio de mensajes.

Para conectarse a Neptune mediante los controladores Bolt de Neo4j, simplemente sustituya la URL y el número de puerto por los puntos de conexión de su clúster mediante el esquema URI `bolt`. Si tiene una sola instancia de Neptune en ejecución, utilice el punto de conexión read\$1write. Si se están ejecutando varias instancias, se recomiendan dos controladores, uno para el escritor y otro para todas las réplicas de lectura. Si solo tiene los dos puntos de conexión predeterminados, basta con un controlador read\$1write y un controlador de solo lectura, pero si también tiene puntos de conexión personalizados, considere la posibilidad de crear una instancia de controlador para cada uno de ellos.

**nota**  
Aunque la especificación de Bolt indica que Bolt puede conectarse mediante TCP o, WebSockets Neptune solo admite conexiones TCP para Bolt.

Neptune permite hasta 1000 conexiones Bolt simultáneas en todos los tamaños de instancia, excepto en t3.medium y t4g.medium. En las instancias t3.medium y t4g.medium, solo se permiten 512 conexiones.

Para ver ejemplos de consultas de openCypher en varios lenguajes que utilizan los controladores de Bolt, consulte la documentación de las [guías de lenguajes y controladores](https://neo4j.com/developer/language-guides/) de Neo4j.

**importante**  
Los controladores Neo4j Bolt para Python JavaScript, .NET y Golang inicialmente no admitían la renovación automática de los tokens de autenticación AWS Signature v4. Esto significa que, una vez caducada la firma (normalmente en 5 minutos), el controlador no pudo autenticarse y las solicitudes posteriores fallaron. Todos los ejemplos de Python JavaScript, .NET y Go que aparecen a continuación se vieron afectados por este problema.  
Consulte el [problema \$1834 del controlador Python de Neo4j, el problema](https://github.com/neo4j/neo4j-python-driver/issues/834) [\$1664 de Neo4j.NET, el problema](https://github.com/neo4j/neo4j-dotnet-driver/issues/664) [\$1993 del controlador Neo4j y el problema \$1429 JavaScript del controlador](https://github.com/neo4j/neo4j-javascript-driver/issues/993) [Neo4j GoLang, para obtener más información](https://github.com/neo4j/neo4j-go-driver/issues/429).  
A partir de la versión 5.8.0 del controlador, se lanzó una nueva versión preliminar de la API de reautenticación del controlador de Go (consulte [v5.8.0 - Feedback wanted on re-authentication](https://github.com/neo4j/neo4j-go-driver/discussions/482)).

## Uso de Bolt con Java para conectarse a Neptune
<a name="access-graph-opencypher-bolt-java"></a>

Puede descargar un controlador para cualquier versión que desee usar desde el [repositorio MVN](https://mvnrepository.com/artifact/org.neo4j.driver/neo4j-java-driver) de Maven o puede añadir esta dependencia a su proyecto:

```
<dependency>
  <groupId>org.neo4j.driver</groupId>
  <artifactId>neo4j-java-driver</artifactId>
  <version>4.3.3</version>
</dependency>
```

A continuación, para conectarse a Neptune en Java mediante uno de estos controladores Bolt, cree una instancia de controlador para la primary/writer instancia del clúster mediante un código como el siguiente:

```
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;

final Driver driver =
  GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)",
    AuthTokens.none(),
    Config.builder().withEncryption()
                    .withTrustStrategy(TrustStrategy.trustSystemCertificates())
                    .build());
```

Si tiene una o más réplicas de lector, también puede crear una instancia de controlador para ellas utilizando un código como el siguiente:

```
final Driver read_only_driver =              // (without connection timeout)
  GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)",
      Config.builder().withEncryption()
                      .withTrustStrategy(TrustStrategy.trustSystemCertificates())
                      .build());
```

O bien, con un tiempo de espera:

```
final Driver read_only_timeout_driver =      // (with connection timeout)
  GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)",
    Config.builder().withConnectionTimeout(30, TimeUnit.SECONDS)
                    .withEncryption()
                    .withTrustStrategy(TrustStrategy.trustSystemCertificates())
                    .build());
```

Si tiene puntos de conexión personalizados, puede que también valga la pena crear una instancia de controlador para cada uno de ellos.

## Ejemplo de consulta de openCypher en Python con Bolt
<a name="access-graph-opencypher-bolt-python"></a>

Aquí se explica cómo hacer una consulta de openCypher en Python usando Bolt:

```
python -m pip install neo4j
```

```
from neo4j import GraphDatabase
uri = "bolt://(your cluster endpoint URL):(your cluster port)"
driver = GraphDatabase.driver(uri, auth=("username", "password"), encrypted=True)
```

Tenga en cuenta que los parámetros `auth` se omiten.

## Ejemplo de consulta de openCypher en .NET con Bolt
<a name="access-graph-opencypher-bolt-dotnet"></a>

Para realizar una consulta de OpenCypher en .NET usando Bolt, el primer paso es instalar el controlador Neo4j usando. NuHet Para realizar llamadas síncronas, use la versión `.Simple`, de esta manera:

```
Install-Package Neo4j.Driver.Simple-4.3.0
```

```
using Neo4j.Driver;

namespace hello
{
  // This example creates a node and reads a node in a Neptune
  // Cluster where IAM Authentication is not enabled.
  public class HelloWorldExample : IDisposable
  {
    private bool _disposed = false;
    private readonly IDriver _driver;
    private static string url = "bolt://(your cluster endpoint URL):(your cluster port)";
    private static string createNodeQuery = "CREATE (a:Greeting) SET a.message = 'HelloWorldExample'";
    private static string readNodeQuery = "MATCH(n:Greeting) RETURN n.message";

    ~HelloWorldExample() => Dispose(false);

    public HelloWorldExample(string uri)
    {
      _driver = GraphDatabase.Driver(uri, AuthTokens.None, o => o.WithEncryptionLevel(EncryptionLevel.Encrypted));
    }

    public void createNode()
    {
      // Open a session
      using (var session = _driver.Session())
      {
         // Run the query in a write transaction
        var greeting = session.WriteTransaction(tx =>
        {
          var result = tx.Run(createNodeQuery);
          // Consume the result
          return result.Consume();
        });

        // The output will look like this:
        //   ResultSummary{Query=`CREATE (a:Greeting) SET a.message = 'HelloWorldExample".....
        Console.WriteLine(greeting);
      }
    }

    public void retrieveNode()
    {
      // Open a session
      using (var session = _driver.Session())
      {
        // Run the query in a read transaction
        var greeting = session.ReadTransaction(tx =>
        {
          var result = tx.Run(readNodeQuery);
          // Consume the result. Read the single node
          // created in a previous step.
          return result.Single()[0].As<string>();
        });
        // The output will look like this:
        //   HelloWorldExample
        Console.WriteLine(greeting);
      }
    }

    public void Dispose()
    {
      Dispose(true);
      GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
      if (_disposed)
        return;
      if (disposing)
      {
        _driver?.Dispose();
      }
      _disposed = true;
    }

    public static void Main()
    {
      using (var apiCaller = new HelloWorldExample(url))
      {
        apiCaller.createNode();
        apiCaller.retrieveNode();
      }
    }
  }
}
```

## Ejemplo de consulta de openCypher en Java que utiliza Bolt con autenticación de IAM
<a name="access-graph-opencypher-bolt-java-iam-auth"></a>

El siguiente código de Java muestra cómo realizar consultas de openCypher en Java mediante Bolt con autenticación de IAM. El JavaDoc comentario describe su uso. Una vez que haya una instancia de controlador disponible, podrá utilizarla para realizar varias solicitudes autenticadas.

```
package software.amazon.neptune.bolt;

import com.amazonaws.DefaultRequest;
import com.amazonaws.Request;
import com.amazonaws.auth.AWS4Signer;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.http.HttpMethodName;
import com.google.gson.Gson;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import org.neo4j.driver.Value;
import org.neo4j.driver.Values;
import org.neo4j.driver.internal.security.InternalAuthToken;
import org.neo4j.driver.internal.value.StringValue;

import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static com.amazonaws.auth.internal.SignerConstants.AUTHORIZATION;
import static com.amazonaws.auth.internal.SignerConstants.HOST;
import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_DATE;
import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_SECURITY_TOKEN;

/**
 * Use this class instead of `AuthTokens.basic` when working with an IAM
 * auth-enabled server. It works the same as `AuthTokens.basic` when using
 * static credentials, and avoids making requests with an expired signature
 * when using temporary credentials. Internally, it generates a new signature
 * on every invocation (this may change in a future implementation).
 *
 * Note that authentication happens only the first time for a pooled connection.
 *
 * Typical usage:
 *
 * NeptuneAuthToken authToken = NeptuneAuthToken.builder()
 *     .credentialsProvider(credentialsProvider)
 *     .region("aws region")
 *     .url("cluster endpoint url")
 *     .build();
 *
 * Driver driver = GraphDatabase.driver(
 *     authToken.getUrl(),
 *     authToken,
 *     config
 * );
 */

public class NeptuneAuthToken extends InternalAuthToken {
  private static final String SCHEME = "basic";
  private static final String REALM = "realm";
  private static final String SERVICE_NAME = "neptune-db";
  private static final String HTTP_METHOD_HDR = "HttpMethod";
  private static final String DUMMY_USERNAME = "username";
  @NonNull
  private final String region;
  @NonNull
  @Getter
  private final String url;
  @NonNull
  private final AWSCredentialsProvider credentialsProvider;
  private final Gson gson = new Gson();

  @Builder
  private NeptuneAuthToken(
      @NonNull final String region,
      @NonNull final String url,
      @NonNull final AWSCredentialsProvider credentialsProvider
  ) {
      // The superclass caches the result of toMap(), which we don't want
      super(Collections.emptyMap());
      this.region = region;
      this.url = url;
      this.credentialsProvider = credentialsProvider;
  }

  @Override
  public Map<String, Value> toMap() {
    final Map<String, Value> map = new HashMap<>();
    map.put(SCHEME_KEY, Values.value(SCHEME));
    map.put(PRINCIPAL_KEY, Values.value(DUMMY_USERNAME));
    map.put(CREDENTIALS_KEY, new StringValue(getSignedHeader()));
    map.put(REALM_KEY, Values.value(REALM));

    return map;
  }

  private String getSignedHeader() {
    final Request<Void> request = new DefaultRequest<>(SERVICE_NAME);
    request.setHttpMethod(HttpMethodName.GET);
    request.setEndpoint(URI.create(url));
    // Comment out the following line if you're using an engine version older than 1.2.0.0
    request.setResourcePath("/opencypher");

    final AWS4Signer signer = new AWS4Signer();
    signer.setRegionName(region);
    signer.setServiceName(request.getServiceName());
    signer.sign(request, credentialsProvider.getCredentials());

    return getAuthInfoJson(request);
  }

  private String getAuthInfoJson(final Request<Void> request) {
    final Map<String, Object> obj = new HashMap<>();
    obj.put(AUTHORIZATION, request.getHeaders().get(AUTHORIZATION));
    obj.put(HTTP_METHOD_HDR, request.getHttpMethod());
    obj.put(X_AMZ_DATE, request.getHeaders().get(X_AMZ_DATE));
    obj.put(HOST, request.getHeaders().get(HOST));
    obj.put(X_AMZ_SECURITY_TOKEN, request.getHeaders().get(X_AMZ_SECURITY_TOKEN));

    return gson.toJson(obj);
  }
}
```

## Ejemplo de consulta de openCypher en Python que utiliza Bolt con autenticación de IAM
<a name="access-graph-opencypher-bolt-python-iam-auth"></a>

La siguiente clase de Python le permite realizar consultas de openCypher en Python mediante Bolt con autenticación de IAM:

```
import json

from neo4j import Auth
from botocore.awsrequest import AWSRequest
from botocore.credentials import Credentials
from botocore.auth import (
  SigV4Auth,
  _host_from_url,
)

SCHEME = "basic"
REALM = "realm"
SERVICE_NAME = "neptune-db"
DUMMY_USERNAME = "username"
HTTP_METHOD_HDR = "HttpMethod"
HTTP_METHOD = "GET"
AUTHORIZATION = "Authorization"
X_AMZ_DATE = "X-Amz-Date"
X_AMZ_SECURITY_TOKEN = "X-Amz-Security-Token"
HOST = "Host"


class NeptuneAuthToken(Auth):
  def __init__(
    self,
    credentials: Credentials,
    region: str,
    url: str,
    **parameters
  ):
    # Do NOT add "/opencypher" in the line below if you're using an engine version older than 1.2.0.0
    request = AWSRequest(method=HTTP_METHOD, url=url + "/opencypher")
    request.headers.add_header("Host", _host_from_url(request.url))
    sigv4 = SigV4Auth(credentials, SERVICE_NAME, region)
    sigv4.add_auth(request)

    auth_obj = {
      hdr: request.headers[hdr]
      for hdr in [AUTHORIZATION, X_AMZ_DATE, X_AMZ_SECURITY_TOKEN, HOST]
    }
    auth_obj[HTTP_METHOD_HDR] = request.method
    creds: str = json.dumps(auth_obj)
    super().__init__(SCHEME, DUMMY_USERNAME, creds, REALM, **parameters)
```

Esta clase se utiliza para crear un controlador de la siguiente manera:

```
  authToken = NeptuneAuthToken(creds, REGION, URL)
  driver = GraphDatabase.driver(URL, auth=authToken, encrypted=True)
```

## Ejemplo de Node.js con la autenticación de IAM y Bolt
<a name="access-graph-opencypher-bolt-nodejs-iam-auth"></a>

El siguiente código de Node.js usa el AWS SDK de la JavaScript versión 3 y la ES6 sintaxis para crear un controlador que autentique las solicitudes:

```
import neo4j from "neo4j-driver";
import { HttpRequest }  from "@smithy/protocol-http";
import { defaultProvider } from "@aws-sdk/credential-provider-node";
import { SignatureV4 } from "@smithy/signature-v4";
import crypto from "@aws-crypto/sha256-js";
const { Sha256 } = crypto;
import assert from "node:assert";

const region = "us-west-2";
const serviceName = "neptune-db";
const host = "(your cluster endpoint URL)";
const port = 8182;
const protocol = "bolt";
const hostPort = host + ":" + port;
const url = protocol + "://" + hostPort;
const createQuery = "CREATE (n:Greeting {message: 'Hello'}) RETURN ID(n)";
const readQuery = "MATCH(n:Greeting) WHERE ID(n) = $id RETURN n.message";

async function signedHeader() {
  const req = new HttpRequest({
    method: "GET",
    protocol: protocol,
    hostname: host,
    port: port,
    // Comment out the following line if you're using an engine version older than 1.2.0.0
    path: "/opencypher",
    headers: {
      host: hostPort
    }
  });

  const signer = new SignatureV4({
    credentials: defaultProvider(),
    region: region,
    service: serviceName,
    sha256: Sha256
  });

  return signer.sign(req, { unsignableHeaders: new Set(["x-amz-content-sha256"]) })
    .then((signedRequest) => {
      const authInfo = {
        "Authorization": signedRequest.headers["authorization"],
        "HttpMethod": signedRequest.method,
        "X-Amz-Date": signedRequest.headers["x-amz-date"],
        "Host": signedRequest.headers["host"],
        "X-Amz-Security-Token": signedRequest.headers["x-amz-security-token"]
      };
      return JSON.stringify(authInfo);
    });
}

async function createDriver() {
  let authToken = { scheme: "basic", realm: "realm", principal: "username", credentials: await signedHeader() };

  return neo4j.driver(url, authToken, {
      encrypted: "ENCRYPTION_ON",
      trust: "TRUST_SYSTEM_CA_SIGNED_CERTIFICATES",
      maxConnectionPoolSize: 1,
      // logging: neo4j.logging.console("debug")
    }
  );
}

async function unmanagedTxn(driver) {
  const session = driver.session();
  const tx = session.beginTransaction();
  try {
    const created = await tx.run(createQuery);
    const matched = await tx.run(readQuery, { id: created.records[0].get(0) });
    const msg = matched.records[0].get("n.message");
    assert.equal(msg, "Hello");
    await tx.commit();
  } catch (err) {
    // The transaction will be rolled back, now handle the error.
    console.log(err);
  } finally {
    await session.close();
  }
}

const driver = await createDriver();
try {
  await unmanagedTxn(driver);
} catch (err) {
  console.log(err);
} finally {
  await driver.close();
}
```

## Ejemplo de consulta de openCypher en .NET que utiliza Bolt con autenticación de IAM
<a name="access-graph-opencypher-bolt-dotnet-iam-auth"></a>

Para habilitar la autenticación de IAM en .NET, debe firmar una solicitud al establecer la conexión. En el siguiente ejemplo, se muestra cómo crear un ayudante de `NeptuneAuthToken` para generar un token de autenticación:

```
using Amazon.Runtime;
using Amazon.Util;
using Neo4j.Driver;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Web;

namespace Hello
{
  /*
   * Use this class instead of `AuthTokens.None` when working with an IAM-auth-enabled server.
   *
   * Note that authentication happens only the first time for a pooled connection.
   *
   * Typical usage:
   *
   * var authToken = new NeptuneAuthToken(AccessKey, SecretKey, Region).GetAuthToken(Host);
   * _driver = GraphDatabase.Driver(Url, authToken, o => o.WithEncryptionLevel(EncryptionLevel.Encrypted));
   */

  public class NeptuneAuthToken
  {
    private const string ServiceName = "neptune-db";
    private const string Scheme = "basic";
    private const string Realm = "realm";
    private const string DummyUserName = "username";
    private const string Algorithm = "AWS4-HMAC-SHA256";
    private const string AWSRequest = "aws4_request";

    private readonly string _accessKey;
    private readonly string _secretKey;
    private readonly string _region;

    private readonly string _emptyPayloadHash;

    private readonly SHA256 _sha256;


    public NeptuneAuthToken(string awsKey = null, string secretKey = null, string region = null)
    {
      var awsCredentials = awsKey == null || secretKey == null
        ? FallbackCredentialsFactory.GetCredentials().GetCredentials()
        : null;

      _accessKey = awsKey ?? awsCredentials.AccessKey;
      _secretKey = secretKey ?? awsCredentials.SecretKey;
      _region = region ?? FallbackRegionFactory.GetRegionEndpoint().SystemName; //ex: us-east-1

      _sha256 = SHA256.Create();
      _emptyPayloadHash = Hash(Array.Empty<byte>());
    }

    public IAuthToken GetAuthToken(string url)
    {
      return AuthTokens.Custom(DummyUserName, GetCredentials(url), Realm, Scheme);
    }

    /******************** AWS SIGNING FUNCTIONS *********************/
    private string Hash(byte[] bytesToHash)
    {
      return ToHexString(_sha256.ComputeHash(bytesToHash));
    }

    private static byte[] HmacSHA256(byte[] key, string data)
    {
      return new HMACSHA256(key).ComputeHash(Encoding.UTF8.GetBytes(data));
    }

    private byte[] GetSignatureKey(string dateStamp)
    {
      var kSecret = Encoding.UTF8.GetBytes($"AWS4{_secretKey}");
      var kDate = HmacSHA256(kSecret, dateStamp);
      var kRegion = HmacSHA256(kDate, _region);
      var kService = HmacSHA256(kRegion, ServiceName);
      return HmacSHA256(kService, AWSRequest);
    }

    private static string ToHexString(byte[] array)
    {
      return Convert.ToHexString(array).ToLowerInvariant();
    }

    private string GetCredentials(string url)
    {
      var request = new HttpRequestMessage
      {
        Method = HttpMethod.Get,
        RequestUri = new Uri($"https://{url}/opencypher")
      };

      var signedrequest = Sign(request);

      var headers = new Dictionary<string, object>
      {
        [HeaderKeys.AuthorizationHeader] = signedrequest.Headers.GetValues(HeaderKeys.AuthorizationHeader).FirstOrDefault(),
        ["HttpMethod"] = HttpMethod.Get.ToString(),
        [HeaderKeys.XAmzDateHeader] = signedrequest.Headers.GetValues(HeaderKeys.XAmzDateHeader).FirstOrDefault(),
        // Host should be capitalized, not like in Amazon.Util.HeaderKeys.HostHeader
        ["Host"] = signedrequest.Headers.GetValues(HeaderKeys.HostHeader).FirstOrDefault(),
      };

      return JsonSerializer.Serialize(headers);
    }

    private HttpRequestMessage Sign(HttpRequestMessage request)
    {
      var now = DateTimeOffset.UtcNow;
      var amzdate = now.ToString("yyyyMMddTHHmmssZ");
      var datestamp = now.ToString("yyyyMMdd");

      if (request.Headers.Host == null)
      {
        request.Headers.Host = $"{request.RequestUri.Host}:{request.RequestUri.Port}";
      }

      request.Headers.Add(HeaderKeys.XAmzDateHeader, amzdate);

      var canonicalQueryParams = GetCanonicalQueryParams(request);

      var canonicalRequest = new StringBuilder();
      canonicalRequest.Append(request.Method + "\n");
      canonicalRequest.Append(request.RequestUri.AbsolutePath + "\n");
      canonicalRequest.Append(canonicalQueryParams + "\n");

      var signedHeadersList = new List<string>();
      foreach (var header in request.Headers.OrderBy(a => a.Key.ToLowerInvariant()))
      {
        canonicalRequest.Append(header.Key.ToLowerInvariant());
        canonicalRequest.Append(':');
        canonicalRequest.Append(string.Join(",", header.Value.Select(s => s.Trim())));
        canonicalRequest.Append('\n');
        signedHeadersList.Add(header.Key.ToLowerInvariant());
      }
      canonicalRequest.Append('\n');

      var signedHeaders = string.Join(";", signedHeadersList);
      canonicalRequest.Append(signedHeaders + "\n");
      canonicalRequest.Append(_emptyPayloadHash);

      var credentialScope = $"{datestamp}/{_region}/{ServiceName}/{AWSRequest}";
      var stringToSign = $"{Algorithm}\n{amzdate}\n{credentialScope}\n"
        + Hash(Encoding.UTF8.GetBytes(canonicalRequest.ToString()));

      var signing_key = GetSignatureKey(datestamp);
      var signature = ToHexString(HmacSHA256(signing_key, stringToSign));

      request.Headers.TryAddWithoutValidation(HeaderKeys.AuthorizationHeader,
        $"{Algorithm} Credential={_accessKey}/{credentialScope}, SignedHeaders={signedHeaders}, Signature={signature}");

      return request;
    }

    private static string GetCanonicalQueryParams(HttpRequestMessage request)
    {
      var querystring = HttpUtility.ParseQueryString(request.RequestUri.Query);

      // Query params must be escaped in upper case (i.e. "%2C", not "%2c").
      var queryParams = querystring.AllKeys.OrderBy(a => a)
        .Select(key => $"{key}={Uri.EscapeDataString(querystring[key])}");
      return string.Join("&", queryParams);
    }
  }
}
```

A continuación, se explica cómo realizar una consulta de openCypher en .NET utilizando Bolt con autenticación de IAM. En el siguiente ejemplo se usa el ayudante `NeptuneAuthToken`.

```
using Neo4j.Driver;

namespace Hello
{
  public class HelloWorldExample
  {
    private const string Host = "(your hostname):8182";
    private const string Url = $"bolt://{Host}";
    private const string CreateNodeQuery = "CREATE (a:Greeting) SET a.message = 'HelloWorldExample'";
    private const string ReadNodeQuery = "MATCH(n:Greeting) RETURN n.message";

    private const string AccessKey = "(your access key)";
    private const string SecretKey = "(your secret key)";
    private const string Region = "(your AWS region)"; // e.g. "us-west-2"

    private readonly IDriver _driver;

    public HelloWorldExample()
    {
      var authToken = new NeptuneAuthToken(AccessKey, SecretKey, Region).GetAuthToken(Host);

      // Note that when the connection is reinitialized after max connection lifetime
      // has been reached, the signature token could have already been expired (usually 5 min)
      // You can face exceptions like:
      //   `Unexpected server exception 'Signature expired: XXXX is now earlier than YYYY (ZZZZ - 5 min.)`
      _driver = GraphDatabase.Driver(Url, authToken, o =>
                o.WithMaxConnectionLifetime(TimeSpan.FromMinutes(60)).WithEncryptionLevel(EncryptionLevel.Encrypted));
    }

    public async Task CreateNode()
    {
      // Open a session
      using (var session = _driver.AsyncSession())
      {
        // Run the query in a write transaction
        var greeting = await session.WriteTransactionAsync(async tx =>
        {
          var result = await tx.RunAsync(CreateNodeQuery);
          // Consume the result
          return await result.ConsumeAsync();
        });

        // The output will look like this:
        //   ResultSummary{Query=`CREATE (a:Greeting) SET a.message = 'HelloWorldExample".....
        Console.WriteLine(greeting.Query);
      }
    }

    public async Task RetrieveNode()
    {
      // Open a session
      using (var session = _driver.AsyncSession())
      {
        // Run the query in a read transaction
        var greeting = await session.ReadTransactionAsync(async tx =>
        {
          var result = await tx.RunAsync(ReadNodeQuery);
          var records = await result.ToListAsync();

          // Consume the result. Read the single node
          // created in a previous step.
          return records[0].Values.First().Value;
        });
        // The output will look like this:
        //   HelloWorldExample
        Console.WriteLine(greeting);
      }
    }
  }
}
```

Este ejemplo se puede iniciar ejecutando el siguiente código en `.NET 6` o `.NET 7` con los siguientes paquetes:
+ **`Neo4j`**`.Driver=4.3.0`
+ **`AWSSDK`**`.Core=3.7.102.1`

```
namespace Hello
{
  class Program
  {
    static async Task Main()
    {
      var apiCaller = new HelloWorldExample();

      await apiCaller.CreateNode();
      await apiCaller.RetrieveNode();
    }
  }
}
```

## Ejemplo de consulta de openCypher en Golang que utiliza Bolt con autenticación de IAM
<a name="access-graph-opencypher-bolt-golang-iam-auth"></a>

En el siguiente paquete de Golang, se muestra cómo realizar consultas de openCypher en el lenguaje Go utilizando Bolt con autenticación de IAM:

```
package main

import (
  "context"
  "encoding/json"
  "fmt"
  "github.com/aws/aws-sdk-go/aws/credentials"
  "github.com/aws/aws-sdk-go/aws/signer/v4"
  "github.com/neo4j/neo4j-go-driver/v5/neo4j"
  "log"
  "net/http"
  "os"
  "time"
)

const (
  ServiceName   = "neptune-db"
  DummyUsername = "username"
)

// Find node by id using Go driver
func findNode(ctx context.Context, region string, hostAndPort string, nodeId string) (string, error) {
  req, err := http.NewRequest(http.MethodGet, "https://"+hostAndPort+"/opencypher", nil)

  if err != nil {
    return "", fmt.Errorf("error creating request, %v", err)
  }

  // credentials must have been exported as environment variables
  signer := v4.NewSigner(credentials.NewEnvCredentials())
  _, err = signer.Sign(req, nil, ServiceName, region, time.Now())

  if err != nil {
    return "", fmt.Errorf("error signing request: %v", err)
  }

  hdrs := []string{"Authorization", "X-Amz-Date", "X-Amz-Security-Token"}
  hdrMap := make(map[string]string)
  for _, h := range hdrs {
    hdrMap[h] = req.Header.Get(h)
  }

  hdrMap["Host"] = req.Host
  hdrMap["HttpMethod"] = req.Method

  password, err := json.Marshal(hdrMap)
  if err != nil {
    return "", fmt.Errorf("error creating JSON, %v", err)
  }
  authToken := neo4j.BasicAuth(DummyUsername, string(password), "")
  // +s enables encryption with a full certificate check
  // Use +ssc to disable client side TLS verification
  driver, err := neo4j.NewDriverWithContext("bolt+s://"+hostAndPort+"/opencypher", authToken)
  if err != nil {
    return "", fmt.Errorf("error creating driver, %v", err)
  }

  defer driver.Close(ctx)

  if err := driver.VerifyConnectivity(ctx); err != nil {
    log.Fatalf("failed to verify connection, %v", err)
  }

  config := neo4j.SessionConfig{}

  session := driver.NewSession(ctx, config)
  defer session.Close(ctx)

  result, err := session.Run(
    ctx,
    fmt.Sprintf("MATCH (n) WHERE ID(n) = '%s' RETURN n", nodeId),
    map[string]any{},
  )
  if err != nil {
    return "", fmt.Errorf("error running query, %v", err)
  }

  if !result.Next(ctx) {
    return "", fmt.Errorf("node not found")
  }

  n, found := result.Record().Get("n")
  if !found {
    return "", fmt.Errorf("node not found")
  }

  return fmt.Sprintf("+%v\n", n), nil
}

func main() {
  if len(os.Args) < 3 {
    log.Fatal("Usage: go main.go (region) (host and port)")
  }
  region := os.Args[1]
  hostAndPort := os.Args[2]
  ctx := context.Background()

  res, err := findNode(ctx, region, hostAndPort, "72c2e8c1-7d5f-5f30-10ca-9d2bb8c4afbc")
  if err != nil {
    log.Fatal(err)
  }
  fmt.Println(res)
}
```

## Comportamiento de conexión de Bolt en Neptune
<a name="access-graph-opencypher-bolt-connections"></a>

Aquí se incluyen algunos aspectos que debe tener en cuenta sobre las conexiones de Bolt de Neptune:
+ Como las conexiones Bolt se crean en la capa TCP, no se puede usar un [equilibrador de carga de aplicación](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html) delante de ellas, como sí puede hacer con un punto de conexión HTTP.
+ El puerto que Neptune utiliza para las conexiones de Bolt es el puerto de su clúster de base de datos.
+ Según el preámbulo de Bolt que se le haya pasado, el servidor de Neptune selecciona la versión de Bolt más alta adecuada (1, 2, 3 o 4.0).
+ El número máximo de conexiones al servidor de Neptune que un cliente puede tener abiertas en cualquier momento es de 1000.
+ Si el cliente no cierra una conexión después de una consulta, esa conexión se puede usar para ejecutar la siguiente consulta.
+ Sin embargo, si una conexión está inactiva durante 20 minutos, el servidor la cierra automáticamente.
+ Si la autenticación de IAM no está habilitada, puede utilizar `AuthTokens.none()` en lugar de proporcionar un nombre de usuario y una contraseña ficticios. Por ejemplo, en Java:

  ```
  GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)", AuthTokens.none(),
      Config.builder().withEncryption().withTrustStrategy(TrustStrategy.trustSystemCertificates()).build());
  ```
+ Cuando la autenticación de IAM está habilitada, una conexión de Bolt siempre se desconecta unos minutos más de 10 días después de su establecimiento si no se ha cerrado aún por algún otro motivo.
+ Si el cliente envía una consulta para su ejecución a través de una conexión sin haber consumido los resultados de una consulta anterior, la nueva consulta se descarta. Para descartar los resultados anteriores, el cliente debe enviar un mensaje de restablecimiento a través de la conexión.
+ Solo se puede crear una transacción a la vez en una conexión determinada.
+ Si se produce una excepción durante una transacción, el servidor de Neptune revierte la transacción y cierra la conexión. En este caso, el controlador crea una nueva conexión para la siguiente consulta.
+ Tenga en cuenta que las sesiones no son seguras para los subprocesos. Las operaciones paralelas múltiples deben utilizar varias sesiones independientes.

# Ejemplos de consultas parametrizadas de openCypher
<a name="opencypher-parameterized-queries"></a>

Neptune admite consultas de openCypher parametrizadas. Esto le permite usar la misma estructura de consulta varias veces con argumentos diferentes. Como la estructura de la consulta no cambia, Neptune puede almacenar en caché su árbol de sintaxis abstracto (AST) en lugar de tener que analizarlo varias veces.

## Ejemplo de una consulta parametrizada de openCypher con el punto de conexión HTTPS
<a name="opencypher-http-parameterized-queries"></a>

A continuación se muestra un ejemplo de uso de una consulta parametrizada con el punto de conexión HTTPS de openCypher en Neptune. La consulta es:

```
MATCH (n {name: $name, age: $age})
RETURN n
```

Los parámetros se definen como sigue:

```
parameters={"name": "john", "age": 20}
```

Con `GET`, puede enviar la consulta parametrizada de la siguiente manera:

```
curl -k \
  "https://localhost:8182/openCypher?query=MATCH%20%28n%20%7Bname:\$name,age:\$age%7D%29%20RETURN%20n&parameters=%7B%22name%22:%22john%22,%22age%22:20%7D"
```

Como alternativa también puede utilizar `POST`.

```
curl -k \
  https://localhost:8182/openCypher \
  -d "query=MATCH (n {name: \$name, age: \$age}) RETURN n" \
  -d "parameters={\"name\": \"john\", \"age\": 20}"
```

O con `DIRECT POST`:

```
curl -k \
   -H "Content-Type: application/opencypher" \
  "https://localhost:8182/openCypher?parameters=%7B%22name%22:%22john%22,%22age%22:20%7D" \
  -d "MATCH (n {name: \$name, age: \$age}) RETURN n"
```

## Ejemplos de consultas parametrizadas de openCypher con Bolt
<a name="opencypher-bolt-parameterized-queries"></a>

A continuación, se muestra un ejemplo de Python de una consulta parametrizada de openCypher que utiliza el protocolo Bolt:

```
from neo4j import GraphDatabase
uri = "bolt://[neptune-endpoint-url]:8182"
driver = GraphDatabase.driver(uri, auth=("", ""))

def match_name_and_age(tx, name, age):
  # Parameterized Query
  tx.run("MATCH (n {name: $name, age: $age}) RETURN n", name=name, age=age)

with driver.session() as session:
  # Parameters
  session.read_transaction(match_name_and_age, "john", 20)

driver.close()
```

A continuación, se muestra un ejemplo de Java de una consulta parametrizada de openCypher que utiliza el protocolo Bolt:

```
Driver driver = GraphDatabase.driver("bolt+s://(your cluster endpoint URL):8182");
HashMap<String, Object> parameters = new HashMap<>();
parameters.put("name", "john");
parameters.put("age", 20);
String queryString = "MATCH (n {name: $name, age: $age}) RETURN n";
Result result = driver.session().run(queryString, parameters);
```

# Modelo de datos de openCypher
<a name="access-graph-opencypher-data-model"></a>

El motor de openCypher de Neptune se basa en el mismo modelo de gráficos de propiedades que Gremlin. En particular:
+ Cada nodo tiene una o varias etiquetas. Si inserta un nodo sin etiquetas, se adjunta una etiqueta predeterminada denominada `vertex`. Si intenta eliminar todas las etiquetas de un nodo, se produce un error.
+ Una relación es una entidad que tiene exactamente un tipo de relación y que forma una conexión unidireccional entre dos nodos (es decir, *desde* uno de los nodos *al* otro).
+ Tanto los nodos como las relaciones pueden tener propiedades, pero no es necesario que las tengan. Neptune admite nodos y relaciones con propiedades cero.
+ Neptune no admite metapropiedades, que tampoco están incluidas en la especificación de openCypher.
+ Las propiedades del gráfico pueden tener varios valores si se crearon con Gremlin. Es decir, una propiedad de nodo o relación puede tener un conjunto de valores diferentes en lugar de solo uno. Neptune ha ampliado la semántica de openCypher para gestionar correctamente las propiedades con varios valores.

Los tipos de datos compatibles se documentan en [Formato de datos de openCypher](bulk-load-tutorial-format-opencypher.md). Sin embargo, por el momento no recomendamos insertar valores de propiedades `Array` en un gráfico de openCypher. Aunque es posible insertar un valor de propiedad de matriz mediante el programa de carga masiva, la versión actual de openCypher de Neptune lo trata como un conjunto de propiedades con varios valores en lugar de como un valor de lista único.

A continuación, se muestra la lista de tipos de datos compatibles con esta versión:
+ `Bool`
+ `Byte`
+ `Short`
+ `Int` 
+ `Long`
+ `Float` (Incluye más y menos Infinity y NaN, pero no INF)
+ `Double` (Incluye más y menos Infinity y NaN, pero no INF)
+ `DateTime` 
+ `String`

# La característica `explain` de openCypher
<a name="access-graph-opencypher-explain"></a>

La característica `explain` de openCypher es una herramienta de autoservicio de Amazon Neptune que le ayuda a entender el enfoque de ejecución adoptado por el motor de Neptune. Para invocar a explain, se pasa un parámetro a una solicitud [HTTPS](access-graph-opencypher-queries.md) de openCypher con`explain=mode`, donde el valor de `mode` puede ser uno de los siguientes:

****
+ **`static`**: en el modo `static`, `explain` solo imprime la estructura estática del plan de consulta. En realidad, no ejecuta la consulta.
+ **`dynamic`**: en el modo `dynamic`, `explain` también ejecuta la consulta e incluye los aspectos dinámicos del plan de consulta. Estos ellos, se puede incluir el número de enlaces intermedios que fluyen a través de los operadores, la proporción entre los enlaces entrantes y los enlaces salientes y el tiempo total que necesita cada operador.
+ **`details`**: en el modo de `details`, `explain` imprime la información mostrada en el modo dinámico más detalles adicionales como la cadena de consulta de openCypher real y el recuento de intervalo estimado para el patrón subyacente de un operador de unión.

  

Por ejemplo, con `POST`:

```
curl HTTPS://server:port/openCypher \
  -d "query=MATCH (n) RETURN n LIMIT 1;" \
  -d "explain=dynamic"
```

O con `GET`:

```
curl -X GET \
  "HTTPS://server:port/openCypher?query=MATCH%20(n)%20RETURN%20n%20LIMIT%201&explain=dynamic"
```

## Limitaciones de `explain` de openCypher en Neptune
<a name="access-graph-opencypher-explain-limitations"></a>

La versión actual de explain de openCypher tiene las siguientes limitaciones:
+ Actualmente, los planes de explain solo están disponibles para consultas que realizan operaciones de solo lectura. No se admiten consultas que realicen algún tipo de mutación, como `CREATE`, `DELETE`, `MERGE`, `SET`, etc.
+ Los operadores y la salida de un plan específico pueden cambiar en futuras versiones.

## Operadores de DFE en la salida de `explain` de openCypher
<a name="access-graph-opencypher-dfe-operators"></a>

Para utilizar la información que proporciona la característica `explain` de openCypher, debe comprender algunos detalles sobre el funcionamiento del [motor de consultas DFE](neptune-dfe-engine.md) (DFE es el motor que utiliza Neptune para procesar las consultas de openCypher).

El motor DFE traduce cada consulta en una canalización de operadores. Partiendo del primer operador, las soluciones intermedias fluyen de un operador al siguiente a través de esta canalización de operadores. Cada fila de la tabla explain representa un resultado, hasta el punto de evaluación.

Los operadores que pueden aparecer en un plan de consulta de DFE son los siguientes:

**DFEApply**— Ejecuta la función especificada en la sección de argumentos, en el valor almacenado en la variable especificada

**DFEBindRelación**: une variables con los nombres especificados

**DFEChunkLocalSubQuery**— Se trata de una operación sin bloqueo que actúa como un contenedor alrededor de las subconsultas que se están realizando.

**DFEDistinctColumna**: devuelve el subconjunto distinto de los valores de entrada en función de la variable especificada.

**DFEDistinctRelación**: devuelve el subconjunto distinto de las soluciones de entrada en función de la variable especificada.

**DFEDrain**— Aparece al final de una subconsulta para actuar como paso de finalización de esa subconsulta. El número de soluciones se registra como `Units In`. `Units Out` siempre es cero.

**DFEForwardValor**: copia todos los fragmentos de entrada directamente como fragmentos de salida para pasarlos a su operador descendente.

**DFEGroupByHashIndex**— Realiza una operación de agrupamiento sobre las soluciones de entrada en función de un índice hash previamente calculado (mediante la operación). `DFEHashIndexBuild` Como salida, la entrada especificada se amplía con una columna que contiene una clave de grupo para cada solución de entrada.

**DFEHashIndexBuild**— Crea un índice hash sobre un conjunto de variables como efecto secundario. Este índice hash se suele reutilizar en operaciones posteriores. Consulte `DFEHashIndexJoin` o `DFEGroupByHashIndex` para ver dónde podría usarse este índice hash.

**DFEHashIndexJoin**— Realiza una combinación de las soluciones entrantes con respecto a un índice hash creado previamente. Consulte `DFEHashIndexBuild` para ver dónde podría crear este índice hash.

**DFEJoinExiste**: toma una relación de entrada izquierda y derecha y conserva los valores de la relación izquierda que tienen un valor correspondiente en la relación derecha, tal como se define en las variables de unión dadas. 

****: se trata de una operación sin bloqueo que actúa como encapsulador de una subconsulta, lo que permite que se ejecute repetidamente para su uso en bucles.

**DFEMergeFragmentos**: se trata de una operación de bloqueo que combina fragmentos de su operador ascendente en un único bloque de soluciones para pasarlos a su operador descendente (a la inversa de). `DFESplitChunks`

**DFEMinus**— Toma una relación de entrada entre la izquierda y la derecha y conserva los valores de la relación izquierda que no tienen un valor correspondiente en la relación derecha, tal como se define en las variables de unión dadas. Si no hay superposición de variables en ambas relaciones, este operador simplemente devuelve la relación de entrada de la izquierda.

**DFENotExiste**: toma una relación de entrada entre la izquierda y la derecha y conserva los valores de la relación izquierda que no tienen un valor correspondiente en la relación derecha, tal como se define en las variables de combinación dadas. Si no hay superposición de variables en ambas relaciones, este operador devuelve una relación vacía.

**DFEOptionalUnir**: realiza una unión externa por la izquierda (también denominada unión OPCIONAL): las soluciones del lado izquierdo que tienen al menos un compañero de unión en el lado derecho se unen, y las soluciones del lado izquierdo sin un compañero de unión en el lado derecho se reenvían tal cual. Es una operación de bloqueo.

**DFEPipelineUnir**: une la entrada siguiendo el patrón de tuplas definido por el argumento. `pattern`

**DFEPipelineRangeCount**— Cuenta el número de soluciones que coinciden con un patrón dado y devuelve una única solución uniaria que contiene el valor de recuento.

**DFEPipelineEscanear**: escanea la base de datos en busca del `pattern` argumento dado, con o sin un filtro determinado en las columnas.

**DFEProject**— Toma varias columnas de entrada y proyecta solo las columnas deseadas.

**DFEReduce**— Realiza la función de agregación especificada en variables especificadas.

**DFERelationalUnir**: une la entrada del operador anterior en función de las claves de patrón especificadas mediante una combinación de fusión. Es una operación de bloqueo.

**DFERouteFragmentos**: toma los fragmentos de entrada de su único borde de entrada y los enruta a lo largo de sus múltiples bordes de salida.

**DFESelectFilas**: este operador toma selectivamente filas de sus soluciones de relaciones de entrada de la izquierda y las reenvía a su operador descendente. Las filas se seleccionan en función de los identificadores de línea proporcionados en la relación de entrada derecha del operador.

**DFESerialize**— Serializa los resultados finales de una consulta en una serialización de cadenas JSON, asignando cada solución de entrada al nombre de variable correspondiente. En el caso de resultados de nodos y bordes, estos resultados se serializan en un mapa de propiedades y metadatos de la entidad.

**DFESort**— Toma una relación de entrada y produce una relación ordenada en función de la clave de clasificación proporcionada.

**DFESplitByGroup**— Divide cada fragmento de entrada de un borde de entrada en fragmentos de salida más pequeños que corresponden a los grupos de filas identificados por fila IDs del fragmento de entrada correspondiente del otro borde de entrada.

**DFESplitFragmentos: divide cada fragmento de entrada individual en fragmentos** de salida más pequeños (a la inversa de). `DFEMergeChunks`

**DFEStreamingHashIndexBuild**— Versión en streaming de. `DFEHashIndexBuild`

**DFEStreamingGroupByHashIndex**— Versión en streaming de`DFEGroupByHashIndex`.

**DFESubquery**— Este operador aparece al principio de todos los planes y resume las partes del plan que se ejecutan en el [motor DFE](neptune-dfe-engine.md), que es el plan completo de OpenCypher.

**DFESymmetricHashJoin**— Une la entrada del operador anterior en función de las claves de patrón especificadas mediante una combinación hash. Es una operación sin bloqueo.

**DFESync**— Este operador es un operador de sincronización que admite planes sin bloqueo. Toma las soluciones de dos periferias entrantes y las envía a los periferias posteriores apropiadas. Para fines de sincronización, las entradas a lo largo de uno de estas periferias pueden almacenarse internamente en búfer. 

**DFETee**— Se trata de un operador de sucursal que envía el mismo conjunto de soluciones a varios operadores.

**DFETermResolución**: realiza una operación de localización o globalización en sus entradas, lo que da como resultado columnas de identificadores localizados o globalizados, respectivamente.

****: despliega listas de valores de una columna de entrada a la columna de salida como elementos individuales.

**DFEUnion**— Toma dos o más relaciones de entrada y produce una unión de esas relaciones utilizando el esquema de salida deseado.

**SolutionInjection**— Aparece antes que todo lo demás en el resultado explicativo, con un valor de 1 en la columna Unidades de salida. Sin embargo, no sirve para nada y en realidad no inyecta ninguna solución en el motor DFE.

**TermResolution**— Aparece al final de los planos y convierte los objetos del motor de Neptune en objetos de OpenCypher.

## Columnas en la salida de `explain` de openCypher
<a name="access-graph-opencypher-explain-columns"></a>

La información del plan de consultas que Neptune genera como resultado de explain de openCypher contiene tablas con un operador por fila. Esta tabla tiene las siguientes columnas:

**ID**: el identificador numérico de este operador del plan.

**Salida 1** (y **Salida 2**): los identificadores de los operadores que se encuentran más abajo de este operador. Puede haber como máximo dos operadores más abajo.

**Nombre**: el nombre de este operador.

**Argumentos**: cualquier detalle relevante para el operador. Incluye elementos como el esquema de entrada, el esquema de salida, el patrón (para `PipelineScan` y `PipelineJoin`), etc.

**Modo**: etiqueta que describe el comportamiento fundamental del operador. La mayor parte de esta columna está en blanco (`-`). Una excepción es `TermResolution`, donde el modo puede ser `id2value_opencypher`, que indica una resolución desde el identificador hasta el valor de openCypher.

**Unidades de entrada**: el número de soluciones que se pasan como entrada a este operador. Los operadores sin operadores por encima, como `DFEPipelineScan`, `SolutionInjections` y `DFESubquery`, sin un valor estático inyectado, tendrían un valor cero.

**Unidades de salida**: la cantidad de soluciones producidas como salida de este operador. `DFEDrain` es un caso especial en el que el número de soluciones que se están drenando se registra en `Units In` y `Units Out` siempre es cero.

**Relación**: la relación de `Units Out` a `Units In`.

**Tiempo (ms)**: el tiempo de CPU que consume este operador, en milisegundos.

## Salida de ejemplo básico de explain de openCypher
<a name="access-graph-opencypher-explain-basic-example"></a>

A continuación, se muestra un ejemplo básico de una salida de `explain` de openCypher: La consulta es una búsqueda de un solo nodo en el conjunto de datos de rutas aéreas para un nodo con el código de aeropuerto `ATL` que invoca a `explain` utilizando el modo `details` en el formato de salida ASCII predeterminado:

```
curl -d "query=MATCH (n {code: 'ATL'}) RETURN n" -k https://localhost:8182/openCypher -d "explain=details"                                                                                                      ~
Query:
MATCH (n {code: 'ATL'}) RETURN n

╔════╤════════╤════════╤═══════════════════╤════════════════════╤═════════════════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments          │ Mode                │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════╪════════════════════╪═════════════════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]     │ -                   │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFESubquery       │ subQuery=subQuery1 │ -                   │ 0        │ 1         │ 0.00  │ 4.00      ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ -      │ -      │ TermResolution    │ vars=[?n]          │ id2value_opencypher │ 1        │ 1         │ 1.00  │ 2.00      ║
╚════╧════════╧════════╧═══════════════════╧════════════════════╧═════════════════════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery1
╔════╤════════╤════════╤═══════════════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                  │ Arguments                                                                                                    │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════════╪══════════════════════════════════════════════════════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFEPipelineScan       │ pattern=Node(?n) with property 'code' as ?n_code2 and label 'ALL'                                            │ -    │ 0        │ 1         │ 0.00  │ 0.21      ║
║    │        │        │                       │ inlineFilters=[(?n_code2 IN ["ATL"^^xsd:string])]                                                            │      │          │           │       │           ║
║    │        │        │                       │ patternEstimate=1                                                                                            │      │          │           │       │           ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#9d84f97c-c3b0-459a-98d5-955a8726b159/graph_1 │ -    │ 1        │ 1         │ 1.00  │ 0.04      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 3      │ -      │ DFEProject            │ columns=[?n]                                                                                                 │ -    │ 1        │ 1         │ 1.00  │ 0.04      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ -      │ -      │ DFEDrain              │ -                                                                                                            │ -    │ 1        │ 0         │ 0.00  │ 0.03      ║
╚════╧════════╧════════╧═══════════════════════╧══════════════════════════════════════════════════════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#9d84f97c-c3b0-459a-98d5-955a8726b159/graph_1
╔════╤════════╤════════╤══════════════════════╤════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                 │ Arguments                                                  │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪══════════════════════╪════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFESolutionInjection │ outSchema=[?n, ?n_code2]                                   │ -    │ 0        │ 1         │ 0.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ 3      │ DFETee               │ -                                                          │ -    │ 1        │ 2         │ 2.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 4      │ -      │ DFEDistinctColumn    │ column=?n                                                  │ -    │ 1        │ 1         │ 1.00  │ 0.20      ║
║    │        │        │                      │ ordered=false                                              │      │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 5      │ -      │ DFEHashIndexBuild    │ vars=[?n]                                                  │ -    │ 1        │ 1         │ 1.00  │ 0.04      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ 5      │ -      │ DFEPipelineJoin      │ pattern=Node(?n) with property 'ALL' and label '?n_label1' │ -    │ 1        │ 1         │ 1.00  │ 0.25      ║
║    │        │        │                      │ patternEstimate=3506                                       │      │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 5  │ 6      │ 7      │ DFESync              │ -                                                          │ -    │ 2        │ 2         │ 1.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 6  │ 8      │ -      │ DFEForwardValue      │ -                                                          │ -    │ 1        │ 1         │ 1.00  │ 0.01      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 7  │ 8      │ -      │ DFEForwardValue      │ -                                                          │ -    │ 1        │ 1         │ 1.00  │ 0.01      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 8  │ 9      │ -      │ DFEHashIndexJoin     │ -                                                          │ -    │ 2        │ 1         │ 0.50  │ 0.35      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 9  │ -      │ -      │ DFEDrain             │ -                                                          │ -    │ 1        │ 0         │ 0.00  │ 0.02      ║
╚════╧════════╧════════╧══════════════════════╧════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝
```

En el nivel superior, `SolutionInjection` aparece antes que todo lo demás, con 1 unidad de salida. Tenga en cuenta que, en realidad, no inyecta ninguna solución. Puede ver que el siguiente operador, `DFESubquery`, tiene 0 unidades.

Después de `SolutionInjection` en el nivel superior, están los operadores `DFESubquery` y `TermResolution`. `DFESubquery` encapsula las partes del plan de ejecución de consultas que se envían al [motor DFE](neptune-dfe-engine.md) (en el caso de las consultas de openCypher, el DFE ejecuta todo el plan de consultas). Todos los operadores del plan de consultas están anidados dentro de `subQuery1`, al que hace referencia `DFESubquery`. La única excepción es `TermResolution` que se materializa internamente IDs en objetos OpenCypher completamente serializados.

Todos los operadores que se desplazan hacia abajo hasta el motor DFE tienen nombres que comienzan con un prefijo `DFE`. Como se ha mencionado anteriormente, el DFE ejecuta todo el plan de consultas de openCypher, por lo que todos los operadores, excepto el operador `TermResolution` final, comienzan con `DFE`.

En el interior de `subQuery1`, puede haber cero o más operadores `DFEChunkLocalSubQuery` o `DFELoopSubQuery` que encapsulen una parte del plan de ejecución desplazado que se ejecuta en un mecanismo limitado por la memoria. `DFEChunkLocalSubQuery`contiene aquí un `SolutionInjection` que se utiliza como entrada para la subconsulta. Para buscar la tabla de esa subconsulta en la salida, busque la `subQuery=graph URI` especificada en la columna `Arguments` correspondiente al operador `DFEChunkLocalSubQuery` o `DFELoopSubQuery`.

En `subQuery1`, `DFEPipelineScan` con el `ID` 0, escanea la base de datos en busca de un `pattern` especificado. El patrón busca una entidad con la propiedad `code` guardada como variable `?n_code2` en todas las etiquetas (puede filtrar una etiqueta específica agregando `airport` a `n:airport`). El argumento `inlineFilters` muestra el filtrado de la propiedad `code`, que equivale a `ATL`.

A continuación, el operador `DFEChunkLocalSubQuery` une los resultados intermedios de una subconsulta que contiene `DFEPipelineJoin`. Esto asegura que en realidad `?n` sea un nodo, ya que el `DFEPipelineScan` anterior busca cualquier entidad con la propiedad `code`.

# Ejemplo de salida de `explain` para una búsqueda de relaciones con un límite
<a name="access-graph-opencypher-explain-example-2"></a>

Esta consulta busca relaciones entre dos nodos anónimos con el tipo `route` y devuelve como máximo 10. De nuevo, el modo `explain` es `details` y el formato de salida es el formato ASCII predeterminado. Esta es la salida de `explain`.

Aquí, `DFEPipelineScan` busca bordes que comiencen en un nodo anónimo `?anon_node7` y terminen en otro nodo anónimo`?anon_node21`, con un tipo de relación guardado como `?p_type1`. Hay un filtro para `?p_type1` que es `el://route` (donde `el` significa la etiqueta de borde), que corresponde a `[p:route]` en la cadena de consulta.

`DFEDrain` recopila la solución de salida con un límite de 10, como se muestra en su columna `Arguments`. `DFEDrain`termina una vez que se alcanza el límite o se producen todas las soluciones, lo que ocurra primero.

```
curl -d "query=MATCH ()-[p:route]->() RETURN p LIMIT 10" -k https://localhost:8182/openCypher -d "explain=details"                                                                                              ~
Query:
MATCH ()-[p:route]->() RETURN p LIMIT 10

╔════╤════════╤════════╤═══════════════════╤════════════════════╤═════════════════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments          │ Mode                │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════╪════════════════════╪═════════════════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]     │ -                   │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFESubquery       │ subQuery=subQuery1 │ -                   │ 0        │ 10        │ 0.00  │ 5.00      ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ -      │ -      │ TermResolution    │ vars=[?p]          │ id2value_opencypher │ 10       │ 10        │ 1.00  │ 1.00      ║
╚════╧════════╧════════╧═══════════════════╧════════════════════╧═════════════════════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery1
╔════╤════════╤════════╤═════════════════╤═══════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name            │ Arguments                                                 │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═════════════════╪═══════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFEPipelineScan │ pattern=Edge((?anon_node7)-[?p:?p_type1]->(?anon_node21)) │ -    │ 0        │ 1000      │ 0.00  │ 0.66      ║
║    │        │        │                 │ inlineFilters=[(?p_type1 IN [<el://route>])]              │      │          │           │       │           ║
║    │        │        │                 │ patternEstimate=26219                                     │      │          │           │       │           ║
╟────┼────────┼────────┼─────────────────┼───────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFEProject      │ columns=[?p]                                              │ -    │ 1000     │ 1000      │ 1.00  │ 0.14      ║
╟────┼────────┼────────┼─────────────────┼───────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ -      │ -      │ DFEDrain        │ limit=10                                                  │ -    │ 1000     │ 0         │ 0.00  │ 0.11      ║
╚════╧════════╧════════╧═════════════════╧═══════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝
```

# Ejemplo de salida de `explain` de una función de expresión de valores
<a name="access-graph-opencypher-explain-example-3"></a>

La función es:

```
MATCH (a) RETURN DISTINCT labels(a)
```

En la siguiente salida de `explain`, `DFEPipelineScan` (ID 0) busca todas las etiquetas de los nodos. Esto se corresponde a `MATCH (a`).

`DFEChunkLocalSubquery` (ID 1) agrega la etiqueta de `?a` por cada `?a`. Esto se corresponde a `labels(a)`. Puede ver esto en `DFEApply` y `DFEReduce`.

`BindRelation` (ID 2) se usa para cambiar el nombre de la columna genérica `?__gen_labelsOfa2` por `?labels(a)`.

`DFEDistinctRelation` (ID 4) recupera solo las etiquetas distintas (múltiple: los nodos de aeropuertos darían lugar a etiquetas duplicadas (a): ["airport"]). Esto se corresponde a `DISTINCT labels(a)`.

```
curl -d "query=MATCH (a) RETURN DISTINCT labels(a)" -k https://localhost:8182/openCypher -d "explain=details"                                                                                                    ~
Query:
MATCH (a) RETURN DISTINCT labels(a)

╔════╤════════╤════════╤═══════════════════╤════════════════════╤═════════════════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments          │ Mode                │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════╪════════════════════╪═════════════════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]     │ -                   │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFESubquery       │ subQuery=subQuery1 │ -                   │ 0        │ 5         │ 0.00  │ 81.00     ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ -      │ -      │ TermResolution    │ vars=[?labels(a)]  │ id2value_opencypher │ 5        │ 5         │ 1.00  │ 1.00      ║
╚════╧════════╧════════╧═══════════════════╧════════════════════╧═════════════════════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery1
╔════╤════════╤════════╤═══════════════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                  │ Arguments                                                                                                    │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════════╪══════════════════════════════════════════════════════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFEPipelineScan       │ pattern=Node(?a) with property 'ALL' and label '?a_label1'                                                   │ -    │ 0        │ 3750      │ 0.00  │ 26.77     ║
║    │        │        │                       │ patternEstimate=3506                                                                                         │      │          │           │       │           ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#8b314f55-2cc7-456a-a48a-c76a0465cfab/graph_1 │ -    │ 3750     │ 3750      │ 1.00  │ 0.04      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 3      │ -      │ DFEBindRelation       │ inputVars=[?a, ?__gen_labelsOfa2, ?__gen_labelsOfa2]                                                         │ -    │ 3750     │ 3750      │ 1.00  │ 0.08      ║
║    │        │        │                       │ outputVars=[?a, ?__gen_labelsOfa2, ?labels(a)]                                                               │      │          │           │       │           ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 4      │ -      │ DFEProject            │ columns=[?labels(a)]                                                                                         │ -    │ 3750     │ 3750      │ 1.00  │ 0.05      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ 5      │ -      │ DFEDistinctRelation   │ -                                                                                                            │ -    │ 3750     │ 5         │ 0.00  │ 2.78      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 5  │ -      │ -      │ DFEDrain              │ -                                                                                                            │ -    │ 5        │ 0         │ 0.00  │ 0.03      ║
╚════╧════════╧════════╧═══════════════════════╧══════════════════════════════════════════════════════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#8b314f55-2cc7-456a-a48a-c76a0465cfab/graph_1
╔════╤════════╤════════╤══════════════════════╤════════════════════════════════════════════════════════════╤══════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                 │ Arguments                                                  │ Mode     │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪══════════════════════╪════════════════════════════════════════════════════════════╪══════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFESolutionInjection │ outSchema=[?a]                                             │ -        │ 0        │ 3750      │ 0.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ 3      │ DFETee               │ -                                                          │ -        │ 3750     │ 7500      │ 2.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 4      │ -      │ DFEProject           │ columns=[?a]                                               │ -        │ 3750     │ 3750      │ 1.00  │ 0.04      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 17     │ -      │ DFEOptionalJoin      │ -                                                          │ -        │ 7500     │ 3750      │ 0.50  │ 0.44      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ 5      │ -      │ DFEDistinctRelation  │ -                                                          │ -        │ 3750     │ 3750      │ 1.00  │ 2.23      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 5  │ 6      │ -      │ DFEDistinctColumn    │ column=?a                                                  │ -        │ 3750     │ 3750      │ 1.00  │ 1.50      ║
║    │        │        │                      │ ordered=false                                              │          │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 6  │ 7      │ -      │ DFEPipelineJoin      │ pattern=Node(?a) with property 'ALL' and label '?a_label3' │ -        │ 3750     │ 3750      │ 1.00  │ 10.58     ║
║    │        │        │                      │ patternEstimate=3506                                       │          │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 7  │ 8      │ 9      │ DFETee               │ -                                                          │ -        │ 3750     │ 7500      │ 2.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 8  │ 10     │ -      │ DFEBindRelation      │ inputVars=[?a_label3]                                      │ -        │ 3750     │ 3750      │ 1.00  │ 0.04      ║
║    │        │        │                      │ outputVars=[?100]                                          │          │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 9  │ 11     │ -      │ DFEBindRelation      │ inputVars=[?a, ?a_label3, ?100]                            │ -        │ 7500     │ 3750      │ 0.50  │ 0.07      ║
║    │        │        │                      │ outputVars=[?a, ?a_label3, ?100]                           │          │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 10 │ 9      │ -      │ DFETermResolution    │ column=?100                                                │ id2value │ 3750     │ 3750      │ 1.00  │ 7.60      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 11 │ 12     │ -      │ DFEBindRelation      │ inputVars=[?a, ?a_label3, ?100]                            │ -        │ 3750     │ 3750      │ 1.00  │ 0.06      ║
║    │        │        │                      │ outputVars=[?a, ?100, ?a_label3]                           │          │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 12 │ 13     │ -      │ DFEApply             │ functor=nodeLabel(?a_label3)                               │ -        │ 3750     │ 3750      │ 1.00  │ 0.55      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 13 │ 14     │ -      │ DFEProject           │ columns=[?a, ?a_label3_alias4]                             │ -        │ 3750     │ 3750      │ 1.00  │ 0.05      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 14 │ 15     │ -      │ DFEMergeChunks       │ -                                                          │ -        │ 3750     │ 3750      │ 1.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 15 │ 16     │ -      │ DFEReduce            │ functor=collect(?a_label3_alias4)                          │ -        │ 3750     │ 3750      │ 1.00  │ 6.37      ║
║    │        │        │                      │ segmentationKey=[?a]                                       │          │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 16 │ 3      │ -      │ DFEMergeChunks       │ -                                                          │ -        │ 3750     │ 3750      │ 1.00  │ 0.03      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 17 │ -      │ -      │ DFEDrain             │ -                                                          │ -        │ 3750     │ 0         │ 0.00  │ 0.02      ║
╚════╧════════╧════════╧══════════════════════╧════════════════════════════════════════════════════════════╧══════════╧══════════╧═══════════╧═══════╧═══════════╝
```

# Ejemplo de salida de `explain` de una función de expresión de valores matemática
<a name="access-graph-opencypher-explain-example-4"></a>

En este ejemplo, `RETURN abs(-10)` realiza una evaluación sencilla, tomando el valor absoluto de una constante,`-10`.

`DFEChunkLocalSubQuery` (ID 1) realiza una inyección de solución para el valor estático `-10`, que se almacena en la variable `?100`.

`DFEApply` (ID 2) es el operador que ejecuta la función de valor absoluto `abs()` en el valor estático almacenado en la variable `?100`.

Esta es la consulta y la salida de `explain` resultante:

```
curl -d "query=RETURN abs(-10)" -k https://localhost:8182/openCypher  -d "explain=details"                                                                                                                       ~
Query:
RETURN abs(-10)

╔════╤════════╤════════╤═══════════════════╤═══════════════════════╤═════════════════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments             │ Mode                │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════╪═══════════════════════╪═════════════════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]        │ -                   │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼───────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFESubquery       │ subQuery=subQuery1    │ -                   │ 0        │ 1         │ 0.00  │ 4.00      ║
╟────┼────────┼────────┼───────────────────┼───────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ -      │ -      │ TermResolution    │ vars=[?_internalVar1] │ id2value_opencypher │ 1        │ 1         │ 1.00  │ 1.00      ║
╚════╧════════╧════════╧═══════════════════╧═══════════════════════╧═════════════════════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery1
╔════╤════════╤════════╤═══════════════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                  │ Arguments                                                                                                    │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════════╪══════════════════════════════════════════════════════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFESolutionInjection  │ outSchema=[]                                                                                                 │ -    │ 0        │ 1         │ 0.00  │ 0.01      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#c4cc6148-cce3-4561-93c0-deb91f257356/graph_1 │ -    │ 1        │ 1         │ 1.00  │ 0.03      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 3      │ -      │ DFEApply              │ functor=abs(?100)                                                                                            │ -    │ 1        │ 1         │ 1.00  │ 0.26      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 4      │ -      │ DFEBindRelation       │ inputVars=[?_internalVar2, ?_internalVar2]                                                                   │ -    │ 1        │ 1         │ 1.00  │ 0.04      ║
║    │        │        │                       │ outputVars=[?_internalVar2, ?_internalVar1]                                                                  │      │          │           │       │           ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ 5      │ -      │ DFEProject            │ columns=[?_internalVar1]                                                                                     │ -    │ 1        │ 1         │ 1.00  │ 0.06      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 5  │ -      │ -      │ DFEDrain              │ -                                                                                                            │ -    │ 1        │ 0         │ 0.00  │ 0.05      ║
╚════╧════════╧════════╧═══════════════════════╧══════════════════════════════════════════════════════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝

subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#c4cc6148-cce3-4561-93c0-deb91f257356/graph_1
╔════╤════════╤════════╤══════════════════════╤═════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                 │ Arguments                           │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪══════════════════════╪═════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFESolutionInjection │ solutions=[?100 -> [-10^^<LONG>]]   │ -    │ 0        │ 1         │ 0.00  │ 0.01      ║
║    │        │        │                      │ outSchema=[?100]                    │      │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 3      │ -      │ DFERelationalJoin    │ joinVars=[]                         │ -    │ 2        │ 1         │ 0.50  │ 0.18      ║
╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 1      │ -      │ DFESolutionInjection │ outSchema=[]                        │ -    │ 0        │ 1         │ 0.00  │ 0.01      ║
╟────┼────────┼────────┼──────────────────────┼─────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ -      │ -      │ DFEDrain             │ -                                   │ -    │ 1        │ 0         │ 0.00  │ 0.02      ║
╚════╧════════╧════════╧══════════════════════╧═════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝
```

# Ejemplo de la salida de `explain` de una consulta de ruta de longitud variable (VLP)
<a name="access-graph-opencypher-explain-example-5"></a>

Este es un ejemplo de un plan de consultas más complejo para gestionar una consulta de ruta de longitud variable. Para mayor claridad, en este ejemplo solo se muestra una parte de la salida de `explain`.

En `subQuery1`, `DFEPipelineScan` (ID 0) y `DFEChunkLocalSubQuery` (ID 1), que inyectan la subconsulta `...graph_1`, se encargan de buscar un nodo con el código `YPO`.

En `subQuery1`, `DFEChunkLocalSubQuery` (ID 2), que inyecta la subconsulta `...graph_2`, se encarga de buscar un nodo con el código `LAX`.

En `subQuery1`, `DFEChunkLocalSubQuery` (ID 3) inyecta la subconsulta `...graph3`, que contiene `DFELoopSubQuery` (ID 17), que a su vez inyecta la subconsulta `...graph5`. Esta operación es responsable de resolver el patrón de longitud variable `-[*2]->` en la cadena de consulta entre dos nodos.

```
curl -d "query=MATCH p=(a {code: 'YPO'})-[*2]->(b{code: 'LAX'}) return p" -k https://localhost:8182/openCypher -d "explain=details"                                                                                                                                                                                ~
Query:
MATCH p=(a {code: 'YPO'})-[*2]->(b{code: 'LAX'}) return p

╔════╤════════╤════════╤═══════════════════╤════════════════════╤═════════════════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments          │ Mode                │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════╪════════════════════╪═════════════════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]     │ -                   │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFESubquery       │ subQuery=subQuery1 │ -                   │ 0        │ 0         │ 0.00  │ 84.00     ║
╟────┼────────┼────────┼───────────────────┼────────────────────┼─────────────────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ -      │ -      │ TermResolution    │ vars=[?p]          │ id2value_opencypher │ 0        │ 0         │ 0.00  │ 0         ║
╚════╧════════╧════════╧═══════════════════╧════════════════════╧═════════════════════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery1
╔════╤════════╤════════╤═══════════════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                  │ Arguments                                                                                                    │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════════╪══════════════════════════════════════════════════════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFEPipelineScan       │ pattern=Node(?a) with property 'code' as ?a_code7 and label 'ALL'                                            │ -    │ 0        │ 1         │ 0.00  │ 0.68      ║
║    │        │        │                       │ inlineFilters=[(?a_code7 IN ["YPO"^^xsd:string])]                                                            │      │          │           │       │           ║
║    │        │        │                       │ patternEstimate=1                                                                                            │      │          │           │       │           ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#cc05129f-d07e-4622-bbe3-9e99558eca46/graph_1 │ -    │ 1        │ 1         │ 1.00  │ 0.03      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 3      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#cc05129f-d07e-4622-bbe3-9e99558eca46/graph_2 │ -    │ 1        │ 1         │ 1.00  │ 0.02      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 4      │ -      │ DFEChunkLocalSubQuery │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#cc05129f-d07e-4622-bbe3-9e99558eca46/graph_3 │ -    │ 1        │ 0         │ 0.00  │ 0.04      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ 5      │ -      │ DFEBindRelation       │ inputVars=[?__gen_path6, ?anon_rel26, ?b_code8, ?b, ?a_code7, ?a, ?__gen_path6]                              │ -    │ 0        │ 0         │ 0.00  │ 0.10      ║
║    │        │        │                       │ outputVars=[?__gen_path6, ?anon_rel26, ?b_code8, ?b, ?a_code7, ?a, ?p]                                       │      │          │           │       │           ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 5  │ 6      │ -      │ DFEProject            │ columns=[?p]                                                                                                 │ -    │ 0        │ 0         │ 0.00  │ 0.05      ║
╟────┼────────┼────────┼───────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 6  │ -      │ -      │ DFEDrain              │ -                                                                                                            │ -    │ 0        │ 0         │ 0.00  │ 0.02      ║
╚════╧════════╧════════╧═══════════════════════╧══════════════════════════════════════════════════════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#cc05129f-d07e-4622-bbe3-9e99558eca46/graph_1
╔════╤════════╤════════╤══════════════════════╤════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                 │ Arguments                                                  │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪══════════════════════╪════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFESolutionInjection │ outSchema=[?a, ?a_code7]                                   │ -    │ 0        │ 1         │ 0.00  │ 0.01      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ 3      │ DFETee               │ -                                                          │ -    │ 1        │ 2         │ 2.00  │ 0.01      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 4      │ -      │ DFEDistinctColumn    │ column=?a                                                  │ -    │ 1        │ 1         │ 1.00  │ 0.25      ║
║    │        │        │                      │ ordered=false                                              │      │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 5      │ -      │ DFEHashIndexBuild    │ vars=[?a]                                                  │ -    │ 1        │ 1         │ 1.00  │ 0.05      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ 5      │ -      │ DFEPipelineJoin      │ pattern=Node(?a) with property 'ALL' and label '?a_label1' │ -    │ 1        │ 1         │ 1.00  │ 0.47      ║
║    │        │        │                      │ patternEstimate=3506                                       │      │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 5  │ 6      │ 7      │ DFESync              │ -                                                          │ -    │ 2        │ 2         │ 1.00  │ 0.04      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 6  │ 8      │ -      │ DFEForwardValue      │ -                                                          │ -    │ 1        │ 1         │ 1.00  │ 0.01      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 7  │ 8      │ -      │ DFEForwardValue      │ -                                                          │ -    │ 1        │ 1         │ 1.00  │ 0.01      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 8  │ 9      │ -      │ DFEHashIndexJoin     │ -                                                          │ -    │ 2        │ 1         │ 0.50  │ 0.26      ║
╟────┼────────┼────────┼──────────────────────┼────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 9  │ -      │ -      │ DFEDrain             │ -                                                          │ -    │ 1        │ 0         │ 0.00  │ 0.02      ║
╚════╧════════╧════════╧══════════════════════╧════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#cc05129f-d07e-4622-bbe3-9e99558eca46/graph_2
╔════╤════════╤════════╤══════════════════════╤═══════════════════════════════════════════════════════════════════╤══════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                 │ Arguments                                                         │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪══════════════════════╪═══════════════════════════════════════════════════════════════════╪══════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ DFEPipelineScan      │ pattern=Node(?b) with property 'code' as ?b_code8 and label 'ALL' │ -    │ 0        │ 1         │ 0.00  │ 0.38      ║
║    │        │        │                      │ inlineFilters=[(?b_code8 IN ["LAX"^^xsd:string])]                 │      │          │           │       │           ║
║    │        │        │                      │ patternEstimate=1                                                 │      │          │           │       │           ║
╟────┼────────┼────────┼──────────────────────┼───────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ DFEMergeChunks       │ -                                                                 │ -    │ 1        │ 1         │ 1.00  │ 0.02      ║
╟────┼────────┼────────┼──────────────────────┼───────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 4      │ -      │ DFERelationalJoin    │ joinVars=[]                                                       │ -    │ 2        │ 1         │ 0.50  │ 0.19      ║
╟────┼────────┼────────┼──────────────────────┼───────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 2      │ -      │ DFESolutionInjection │ outSchema=[?a, ?a_code7]                                          │ -    │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼──────────────────────┼───────────────────────────────────────────────────────────────────┼──────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ -      │ -      │ DFEDrain             │ -                                                                 │ -    │ 1        │ 0         │ 0.00  │ 0.01      ║
╚════╧════════╧════════╧══════════════════════╧═══════════════════════════════════════════════════════════════════╧══════╧══════════╧═══════════╧═══════╧═══════════╝


subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#cc05129f-d07e-4622-bbe3-9e99558eca46/graph_3
╔════╤════════╤════════╤═══════════════════════╤══════════════════════════════════════════════════════════════════════════════════════════════════════════════╤══════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name                  │ Arguments                                                                                                    │ Mode     │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════════╪══════════════════════════════════════════════════════════════════════════════════════════════════════════════╪══════════╪══════════╪═══════════╪═══════╪═══════════╣
...
║ 17 │ 18     │ -      │ DFELoopSubQuery       │ subQuery=http://aws.amazon.com/neptune/vocab/v01/dfe/past/graph#cc05129f-d07e-4622-bbe3-9e99558eca46/graph_5 │ -        │ 1        │ 2         │ 2.00  │ 0.31      ║
...
```

# Transacciones en openCypher de Neptune
<a name="access-graph-opencypher-transactions"></a>

La implementación de openCypher en Amazon Neptune utiliza la [semántica de transacciones definida por Neptune](transactions-neptune.md). Sin embargo, los niveles de aislamiento proporcionados por el controlador Bolt tienen algunas implicaciones específicas para la semántica de las transacciones de Bolt, tal y como se describe en las secciones siguientes.

## Consultas de transacciones de Bolt de solo lectura
<a name="access-graph-opencypher-transactions-ro"></a>

Hay varias formas de procesar las consultas de solo lectura, con diferentes modelos de transacciones y niveles de aislamiento, como se indica a continuación:

### Consultas de transacciones implícitas de solo lectura
<a name="access-graph-opencypher-transactions-ro-implicit"></a>

A continuación se ofrece un ejemplo de una transacción implícita de solo lectura:

```
public void executeReadImplicitTransaction()
{
  // end point
  final String END_POINT = "(End Point URL)";

  // read query
  final String READ_QUERY = "MATCH (n) RETURN n limit 10";

  // create the driver
  final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(),
          Config.builder().withEncryption()
                          .withTrustStrategy(TrustStrategy.trustSystemCertificates())
                          .build());

  // create the session config
  SessionConfig sessionConfig = SessionConfig.builder()
                                             .withFetchSize(1000)
                                             .withDefaultAccessMode(AccessMode.READ)
                                             .build();

  // run the query as access mode read
  driver.session(sessionConfig).readTransaction(new TransactionWork<String>()
    {
      final StringBuilder resultCollector = new StringBuilder();

      @Override
      public String execute(final Transaction tx)
      {
        // execute the query
        Result queryResult = tx.run(READ_QUERY);

        // Read the result
        for (Record record : queryResult.list())
        {
          for (String key : record.keys())
          {
            resultCollector.append(key)
                           .append(":")
                           .append(record.get(key).asNode().toString());
          }
        }
        return resultCollector.toString();
      }

    }
  );

  // close the driver.
  driver.close();
}
```

Dado que las réplicas de lectura solo aceptan consultas de solo lectura, todas las consultas realizadas con respecto a réplicas de lectura se ejecutan como transacciones de lectura implícita, independientemente del modo de acceso establecido en la configuración de la sesión. Neptune evalúa las transacciones implícitas de lectura como [consultas de solo lectura](transactions-neptune.md#transactions-neptune-read-only) según la semántica de aislamiento de `SNAPSHOT`.

En caso de error, las transacciones de lectura implícita se vuelven a intentar de forma predeterminada.

### Consultas de transacciones de solo lectura con confirmación automática
<a name="access-graph-opencypher-transactions-ro-autocommit"></a>

A continuación se ofrece un ejemplo de una transacción con confirmación automática de solo lectura:

```
public void executeAutoCommitTransaction()
{
  // end point
  final String END_POINT = "(End Point URL)";

  // read query
  final String READ_QUERY = "MATCH (n) RETURN n limit 10";

  // Create the session config.
  final SessionConfig sessionConfig = SessionConfig
    .builder()
    .withFetchSize(1000)
    .withDefaultAccessMode(AccessMode.READ)
    .build();

  // create the driver
  final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(),
    Config.builder()
          .withEncryption()
          .withTrustStrategy(TrustStrategy.trustSystemCertificates())
          .build());

  // result collector
  final StringBuilder resultCollector = new StringBuilder();

  // create a session
  final Session session = driver.session(sessionConfig);

  // run the query
  final Result queryResult = session.run(READ_QUERY);
  for (final Record record : queryResult.list())
  {
    for (String key : record.keys())
    {
      resultCollector.append(key)
                     .append(":")
                     .append(record.get(key).asNode().toString());
    }
  }

  // close the session
  session.close();

  // close the driver
  driver.close();
}
```

Si el modo de acceso está establecido en `READ` en la configuración de la sesión, Neptune evalúa las consultas de transacciones de confirmación automática como [consultas de solo lectura](transactions-neptune.md#transactions-neptune-read-only) en la semántica de aislamiento `SNAPSHOT`. Tenga en cuenta que las réplicas de lectura solo aceptan consultas de solo lectura.

Si no se pasa una configuración de sesión, las consultas con confirmación automática se procesan de forma predeterminada con el aislamiento de las consultas por mutación, por lo que es importante incluir una configuración de sesión que establezca explícitamente el modo de acceso en `READ`.

En caso de error, las consultas con confirmación automática de solo lectura no se vuelven a intentar.

### Consultas de transacciones explícitas de solo lectura
<a name="access-graph-opencypher-transactions-ro-explicit"></a>

A continuación, se muestra un ejemplo de una transacción de solo lectura explícita:

```
public void executeReadExplicitTransaction()
{
  // end point
  final String END_POINT = "(End Point URL)";

  // read query
  final String READ_QUERY = "MATCH (n) RETURN n limit 10";

  // Create the session config.
  final SessionConfig sessionConfig = SessionConfig
    .builder()
    .withFetchSize(1000)
    .withDefaultAccessMode(AccessMode.READ)
    .build();

  // create the driver
  final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(),
    Config.builder()
          .withEncryption()
          .withTrustStrategy(TrustStrategy.trustSystemCertificates())
          .build());

  // result collector
  final StringBuilder resultCollector = new StringBuilder();

  // create a session
  final Session session = driver.session(sessionConfig);

  // begin transaction
  final Transaction tx = session.beginTransaction();

  // run the query on transaction
  final List<Record> list = tx.run(READ_QUERY).list();

  // read the result
  for (final Record record : list)
  {
    for (String key : record.keys())
    {
      resultCollector
        .append(key)
        .append(":")
        .append(record.get(key).asNode().toString());
    }
  }

  // commit the transaction and for rollback we can use beginTransaction.rollback();
  tx.commit();

  // close the driver
  driver.close();
}
```

Si el modo de acceso está establecido en `READ` en la configuración de la sesión, Neptune evalúa las transacciones de solo lectura explícitas como [consultas de solo lectura](transactions-neptune.md#transactions-neptune-read-only) en la semántica de aislamiento `SNAPSHOT`. Tenga en cuenta que las réplicas de lectura solo aceptan consultas de solo lectura.

Si no se pasa una configuración de sesión, las transacciones de solo lectura explícitas se procesan de forma predeterminada con el aislamiento de las consultas por mutación, por lo que es importante incluir una configuración de sesión que establezca explícitamente el modo de acceso en `READ`.

En caso de error, las consultas explícitas de solo lectura se vuelven a intentar de forma predeterminada.

## Consultas de transacciones de Bolt de mutación
<a name="access-graph-opencypher-transactions-wr"></a>

Al igual que con las consultas de solo lectura, hay varias formas de procesar las consultas de mutación, con diferentes modelos de transacciones y niveles de aislamiento, como se indica a continuación:

### Consultas de transacciones de mutación implícita
<a name="access-graph-opencypher-transactions-wr-implicit"></a>

A continuación, se muestra un ejemplo de una transacción de mutación implícita:

```
public void executeWriteImplicitTransaction()
{
  // end point
  final String END_POINT = "(End Point URL)";

  // create node with label as label and properties.
  final String WRITE_QUERY = "CREATE (n:label {name : 'foo'})";

  // Read the vertex created with label as label.
  final String READ_QUERY = "MATCH (n:label) RETURN n";

  // create the driver
  final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(),
    Config.builder()
          .withEncryption()
          .withTrustStrategy(TrustStrategy.trustSystemCertificates())
          .build());

  // create the session config
  SessionConfig sessionConfig = SessionConfig
    .builder()
    .withFetchSize(1000)
    .withDefaultAccessMode(AccessMode.WRITE)
    .build();

  final StringBuilder resultCollector = new StringBuilder();

  // run the query as access mode write
  driver.session(sessionConfig).writeTransaction(new TransactionWork<String>()
  {
    @Override
    public String execute(final Transaction tx)
    {
      // execute the write query and consume the result.
      tx.run(WRITE_QUERY).consume();

      // read the vertex written in the same transaction
      final List<Record> list = tx.run(READ_QUERY).list();

      // read the result
      for (final Record record : list)
      {
        for (String key : record.keys())
        {
          resultCollector
            .append(key)
            .append(":")
            .append(record.get(key).asNode().toString());
        }
      }
      return resultCollector.toString();
    }
  }); // at the end, the transaction is automatically committed.

  // close the driver.
  driver.close();
}
```

Las lecturas realizadas como parte de las consultas de mutación se ejecutan en el aislamiento `READ COMMITTED` con las garantías habituales para las [transacciones de mutación de Neptune](transactions-neptune.md#transactions-neptune-mutation).

Independientemente de si se pasa específicamente una configuración de sesión o no, la transacción siempre se trata como una transacción de escritura.

Para conocer los conflictos, consulte [Resolución de conflictos mediante tiempos de espera de bloqueo](transactions-neptune.md#transactions-neptune-conflicts).

### Consultas de transacciones de mutación con confirmación automática
<a name="access-graph-opencypher-transactions-wr-autocommit"></a>

Las consultas con confirmación automática de mutación heredan el mismo comportamiento que las transacciones implícitas de mutación.

Si no se pasa una configuración de sesión, la transacción se trata de forma predeterminada como una transacción de escritura.

En caso de error, las consultas con confirmación automática de mutación no se vuelven a intentar automáticamente.

### Consultas de transacciones de mutación explícita
<a name="access-graph-opencypher-transactions-wr-explicit"></a>

A continuación, se muestra un ejemplo de una transacción de mutación explícita:

```
public void executeWriteExplicitTransaction()
{
  // end point
  final String END_POINT = "(End Point URL)";

  // create node with label as label and properties.
  final String WRITE_QUERY = "CREATE (n:label {name : 'foo'})";

  // Read the vertex created with label as label.
  final String READ_QUERY = "MATCH (n:label) RETURN n";

  // create the driver
  final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(),
    Config.builder()
          .withEncryption()
          .withTrustStrategy(TrustStrategy.trustSystemCertificates())
          .build());

  // create the session config
  SessionConfig sessionConfig = SessionConfig
    .builder()
    .withFetchSize(1000)
    .withDefaultAccessMode(AccessMode.WRITE)
    .build();

  final StringBuilder resultCollector = new StringBuilder();

  final Session session = driver.session(sessionConfig);

  // run the query as access mode write
  final Transaction tx = driver.session(sessionConfig).beginTransaction();

  // execute the write query and consume the result.
  tx.run(WRITE_QUERY).consume();

  // read the result from the previous write query in a same transaction.
  final List<Record> list = tx.run(READ_QUERY).list();

  // read the result
  for (final Record record : list)
  {
    for (String key : record.keys())
    {
      resultCollector
        .append(key)
        .append(":")
        .append(record.get(key).asNode().toString());
    }
  }

  // commit the transaction and for rollback we can use tx.rollback();
  tx.commit();

  // close the session
  session.close();

  // close the driver.
  driver.close();
}
```

Las consultas de mutación explícita heredan el mismo comportamiento que las transacciones de mutación implícita.

Si no se pasa una configuración de sesión, la transacción se trata de forma predeterminada como una transacción de escritura.

Para conocer los conflictos, consulte [Resolución de conflictos mediante tiempos de espera de bloqueo](transactions-neptune.md#transactions-neptune-conflicts).

# Sugerencias de consulta de openCypher
<a name="opencypher-query-hints"></a>

**importante**  
 La sugerencia de consulta de openCypher solo está disponible a partir de la versión [1.3.2.0](https://docs.aws.amazon.com//neptune/latest/userguide/engine-releases-1.3.2.0.html) del motor. 

 En Amazon Neptune, puede usar la cláusula `USING` para especificar sugerencias de consulta en las consultas de openCypher. Estas sugerencias le permiten controlar las estrategias de optimización y evaluación. 

 La sintaxis de las sugerencias de consulta es: 

```
USING {scope}:{hint} {value}
```

1.  `{scope}` define el ámbito en el que se aplica la sugerencia: `Query` o`Clause`. 

    Un valor de ámbito `Query` significa que la sugerencia de consulta se aplica a toda la consulta (nivel de consulta). 

    Un valor de ámbito `Clause` significa que la sugerencia de consulta se aplica a la cláusula que precede a la sugerencia (nivel de cláusula). 

1.  `{hint}` es el nombre de la sugerencia de consulta que se aplica. 

1.  `{value}` es el argumento para la `{hint}`. 

 Los valores pueden no distinguir entre mayúsculas y minúsculas. 

 Por ejemplo, para habilitar la caché del plan de consultas para una consulta: 

```
Using QUERY:PLANCACHE "enabled" 
MATCH (a:Person {firstName: "Erin", lastName: $lastName})
 RETURN a
```

**nota**  
 Actualmente, las sugerencias de **consulta** del ámbito de la consulta son compatibles con **PLANCACHE**, **TIMEOUTMILISEGUNDOS** y **assumeConsistentDataTypes**. A continuación aparecen las sugerencias de consulta compatibles. 

**Topics**
+ [Sugerencia de caché del plan de consultas de openCypher](opencypher-query-hints-qpc-hint.md)
+ [AssumeConsistentDataTypes sugerencia](opencypher-query-hints-AssumeConsistentDataTypes.md)
+ [Sugerencia de tiempo de espera de consulta de OpenCypher](opencypher-query-hints-timeout-hint.md)

# Sugerencia de caché del plan de consultas de openCypher
<a name="opencypher-query-hints-qpc-hint"></a>

 Se puede anular el comportamiento de la caché del plan de consultas en cada consulta (parametrizada o no) mediante la sugerencia de consulta en el ámbito de la consulta `QUERY:PLANCACHE`. Debe usarse con la cláusula `USING`. La sugerencia de consulta acepta como valor `enabled` o `disabled`. Para obtener más información sobre la caché del plan de consultas, consulte [Memoria caché de planes de consultas en Amazon Neptune](access-graph-qpc.md). 

```
# Forcing plan to be cached or reused
% curl -k https://<endpoint>:<port>/opencypher \
  -d "query=Using QUERY:PLANCACHE \"enabled\" MATCH(n) RETURN n LIMIT 1"
  
% curl -k https://<endpoint>:<port>/opencypher \
  -d "query=Using QUERY:PLANCACHE \"enabled\" RETURN \$arg" \
  -d "parameters={\"arg\": 123}"
  
# Forcing plan to be neither cached nor reused
% curl -k https://<endpoint>:<port>/opencypher \
  -d "query=Using QUERY:PLANCACHE \"disabled\" MATCH(n) RETURN n LIMIT 1"
```

# AssumeConsistentDataTypes sugerencia
<a name="opencypher-query-hints-AssumeConsistentDataTypes"></a>

 openCypher sigue un paradigma en el que las coincidencias de tipos de datos numéricos (por ejemplo, int, byte, short, long, etc.) se llevan a cabo bajo la semántica de promoción de tipos. Por ejemplo, al buscar todas las propiedades con un valor de entrada 10 con tipo short, bajo la semántica de promoción de tipos, también coincidiría con las propiedades que tienen 10 como valor long. En algunos casos, la conversión de tipos puede generar una sobrecarga y dar lugar a planes de consulta menos eficientes de lo que podrían ser si no se hubiera realizado la conversión de tipos. En particular, en los casos en que los tipos de datos se utilizan de forma coherente en los datos (por ejemplo, si todas las edades de las personas se almacenan como valores long), la promoción de tipos genera una sobrecarga sin afectar al resultado de la consulta. 

 Para permitir la optimización en los casos en los que se sabe que los valores de los datos de propiedades numéricas almacenados en la base de datos son de tipo coherente, se puede utilizar una sugerencia de consulta denominada `assumeConsistentDataTypes` (con valor `true/false`, siendo `false` el valor predeterminado). Cuando esta sugerencia de consulta se proporciona con un valor `true`, el motor asume que los únicos valores de las propiedades son siempre long o double y omite la semántica de promoción de tipos. Los valores numéricos especificados en la consulta se consideran valores long (para valores que no son de coma flotante) o double (para valores de coma flotante). 

 Si los datos utilizan de forma coherente un único tipo de datos (por ejemplo, todas las edades se almacenan como `long`), el uso de la sugerencia `assumeConsistentDataTypes` puede optimizar la consulta al omitir comprobaciones de igualdad innecesarias para diferentes tipos numéricos. Sin embargo, si los datos tienen tipos de datos incoherentes para la misma propiedad, el uso de la sugerencia puede provocar que se pierdan algunos resultados, ya que la consulta solo coincidirá con el único tipo de datos que asume la sugerencia. 

```
# Database loaded with following openCypher CSV's

# File 1
:ID,age:Int
n1,20
n2,25

# File 2
:ID,age:Long
n3,25


# Example (no hint)
MATCH (n:Person) 
WHERE n.age >= 25
RETURN n

# Result
n2
n3

Returns all person whose age is >= 25 and the values >= 25 can be with any of these datatypes
i.e. byte, short, int, long, double or float

-----------------------------------------------------------------------------------

# Example (with hint present)
USING QUERY:assumeConsistentDataTypes "true"
MATCH (n:Person)
WHERE n.age >= 25
RETURN n

# Result
n3

Returns only "n3" and not "n2". The reason is that even though the numerical value
matches (25), the datatype is "int" and is considered a non-match.
```

 La diferencia también se puede validar mediante la explicación. 

 Sin la explicación: 

```
# Query
MATCH (n)
WHERE n.age = 20
RETURN n

# Explain Snippet
╔═════╤══════════╤══════════╤══════════════════════════════╤═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╤════════╤════════════╤══════════════╤═════════╤══════════════╗
║ ID │ Out #1 │ Out #2 │ Name                   │ Arguments                                                                                                                            │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠═════╪══════════╪══════════╪══════════════════════════════╪═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╪════════╪════════════╪══════════════╪═════════╪══════════════╣
║ 0  │ 1      │ -      │ DFEPipelineScan (DFX)  │ pattern=Node(?n) with property 'age' as ?n_age2 and label 'ALL'                                                                      │ -    │ 0        │ 1         │ 0.00  │ 0.10      ║
║    │        │        │                        │ inlineFilters=[(?n_age2 IN ["20"^^xsd:byte, "20"^^xsd:int, "20"^^xsd:long, "20"^^xsd:short, "20.0"^^xsd:double, "20.0"^^xsd:float])] │      │          │           │       │           ║
║    │        │        │                        │ patternEstimate=1                                                                                                                    │      │          │           │       │           ║
╟─────┼──────────┼──────────┼──────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────┼────────────┼──────────────┼─────────┼──────────────╢

# The inFilters field contains all numeric types
```

 Con la sugerencia: 

```
# Query
MATCH (n)
WHERE n.age = 20
RETURN n

# Explain Snippet
╔═════╤══════════╤══════════╤══════════════════════════════╤═════════════════════════════════════════════════════════════════════════════════╤════════╤════════════╤══════════════╤═════════╤══════════════╗
║ ID │ Out #1 │ Out #2 │ Name                   │ Arguments                                                       │ Mode │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠═════╪══════════╪══════════╪══════════════════════════════╪═════════════════════════════════════════════════════════════════════════════════╪════════╪════════════╪══════════════╪═════════╪══════════════╣
║ 0  │ 1      │ -      │ DFEPipelineScan (DFX)  │ pattern=Node(?n) with property 'age' as ?n_age2 and label 'ALL' │ -    │ 0        │ 1         │ 0.00  │ 0.07      ║
║    │        │        │                        │ inlineFilters=[(?n_age2 IN ["20"^^xsd:long])]                   │      │          │           │       │           ║
║    │        │        │                        │ patternEstimate=1                                               │      │          │           │       │           ║
╟─────┼──────────┼──────────┼──────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────┼────────┼────────────┼──────────────┼─────────┼──────────────╢

# The inFilters field only contains long datatype
```

# Sugerencia de tiempo de espera de consulta de OpenCypher
<a name="opencypher-query-hints-timeout-hint"></a>

 El comportamiento del tiempo de espera de las consultas se puede configurar para cada consulta mediante una sugerencia de consulta a nivel de consulta. `QUERY:TIMEOUTMILLISECONDS` Debe usarse con la cláusula. `USING` La sugerencia de consulta acepta como valor una longitud no negativa. 

```
# Using query-level timeout hint 

% curl https://<endpoint>:<port>/opencypher \
  -d "query=USING QUERY:TIMEOUTMILLISECONDS 100 MATCH(n) RETURN n LIMIT 1"
```

 El comportamiento del tiempo de espera de la consulta tendrá en cuenta el tiempo de espera mínimo a nivel de clúster y el tiempo de espera a nivel de consulta. Consulte los ejemplos siguientes para comprender el comportamiento del tiempo de espera de las consultas. [Para obtener más información sobre el tiempo de espera de las consultas a nivel de clúster, consulta neptune\$1query\$1timeout.](https://docs.aws.amazon.com/neptune/latest/userguide/parameters.html#parameters-db-cluster-parameters-neptune_query_timeout) 

```
# Suppose `neptune_query_timeout` is 10000 ms and query-level timeout is set to 100 ms
# It will consider 100 ms as the final timeout 

% curl https://<endpoint>:<port>/opencypher \
  -d "query=USING QUERY:TIMEOUTMILLISECONDS 100 MATCH(n) RETURN n LIMIT 1"

# Suppose `neptune_query_timeout` is 100 ms and query-level timeout is set to 10000 ms
# It will still consider 100 ms as the final timeout 

% curl https://<endpoint>:<port>/opencypher \
  -d "query=USING QUERY:TIMEOUTMILLISECONDS 10000 MATCH(n) RETURN n LIMIT 1"
```

# Restricciones de openCypher de Neptune
<a name="access-graph-opencypher-limitations"></a>

La versión de openCypher para Amazon Neptune aún no admite todo lo que se especifica en la [referencia del lenguaje de consulta de Cypher, versión 9](https://s3.amazonaws.com/artifacts.opencypher.org/openCypher9.pdf), como se detalla en [Conformidad con la especificación de openCypher](feature-opencypher-compliance.md). Se espera que en las versiones futuras aborden muchas de esas limitaciones.

# Excepciones de openCypher de Neptune
<a name="access-graph-opencypher-exceptions"></a>

Al trabajar con openCypher en Amazon Neptune, pueden producirse diversas excepciones. A continuación, se detallan las excepciones más comunes que puede recibir, ya sea del punto de conexión de HTTPS o del controlador de Bolt (todas las excepciones del controlador de Bolt se registran como excepciones del estado del servidor):


| Código de HTTP | Mensaje de error | ¿Se puede volver a intentar? | Solución | 
| --- | --- | --- | --- | 
| 400 | *(error de sintaxis, propagado directamente desde el analizador de openCypher)* | No | Corrija la sintaxis de la consulta y vuelva a intentarlo. | 
| 500 | `Operation terminated (out of memory)` | Sí | Vuelva a crear la consulta para añadir criterios de filtrado adicionales con el fin de reducir la memoria requerida | 
| 500 | La operación ha finalizado (se ha superado el plazo) | Sí | Aumente el tiempo de espera de la consulta en el grupo de parámetros del clúster de base de datos o [vuelva a intentar la solicitud](https://docs.aws.amazon.com/general/latest/gr/api-retries.html). | 
| 500 | La operación ha finalizado (el usuario la ha cancelado) | Sí | Intente realizar de nuevo la solicitud . | 
| 500 | El restablecimiento de la base de datos está en curso. Vuelva a intentar la consulta cuando el clúster esté disponible. | Sí | Vuelva a intentarlo cuando se haya completado el restablecimiento. | 
| 500 | La operación ha fallado debido a operaciones simultáneas conflictivas (vuelva a intentarlo). En estos momentos, las transacciones se están revirtiendo. | Sí | Vuelva a intentarlo con una [estrategia de retroceso exponencial y reintento](best-practices-opencypher-retry-logic.md). | 
| 400 | *(operation name)* operation/feature excepción no admitida | No | La operación especificada no es compatible. | 
| 400 | Se intentó una actualización de openCypher en una réplica de solo lectura | No | Cambie el punto final de destino por el punto final del escritor. | 
| 400 | MalformedQueryException (Neptune no muestra el estado interno del analizador) | No | Corrija la sintaxis de la consulta y vuelva a intentarlo. | 
| 400 | No se puede eliminar el nodo porque todavía tiene relaciones. Para eliminar este nodo, primero debe eliminar sus relaciones. | No | En lugar de usar, `MATCH (n) DELETE n` utilice `MATCH(n) DETACH DELETE(n)` | 
| 400 | Operación no válida: intento de eliminar la última etiqueta de un nodo. Un nodo debe tener al menos una etiqueta. | No | Neptune requiere que todos los nodos tengan al menos una etiqueta y, si los nodos se crean sin una etiqueta explícita, se asigna una etiqueta predeterminada `vertex`. Cambie la lógica de and/or la aplicación de consultas para no eliminar la última etiqueta. La etiqueta singleton de un nodo se puede actualizar configurando una nueva etiqueta y, a continuación, quitando la antigua. | 
| 500 | Se ha superado el número máximo de solicitudes, ConfiguredQueueCapacity = \$1\$1 para ConnID = \$1\$1 | Sí | Actualmente, solo se pueden procesar 8192 solicitudes simultáneas, independientemente de la pila y el protocolo. | 
| 500 | Se ha superado el límite máximo de conexiones. | Sí | Solo se permiten 1000 conexiones de Bolt simultáneas por instancia (para HTTP no hay límite). | 
| 400 | Se esperaba [uno de los siguientes: nodo, relación o ruta] y se obtuvo un literal | No | Compruebe que está pasando los argumentos correctos y que la sintaxis de consulta es correcta, y vuelva a intentarlo. | 
| 400 | El valor de la propiedad debe ser un literal simple. O bien: se esperaba un mapa para las propiedades del conjunto, pero no se encontró ninguno. | No | Una cláusula SET solo acepta literales simples, no tipos compuestos. | 
| 400 | se encuentra la entidad pasada para su eliminación | No | Compruebe que la entidad que está intentando eliminar existe en la base de datos.  | 
| 400 | El usuario no tiene acceso a la base de datos. | No | Compruebe la política en el rol de IAM que se está utilizando. | 
| 400 | No se ha pasado ningún token como parte de la solicitud | No | Se debe pasar un token debidamente firmado como parte de la solicitud de consulta en un clúster habilitado para IAM. | 
| 400 | El mensaje de error se propaga. | No | Póngase en contacto con AWS Support con el identificador de la solicitud. | 
| 500 | La operación ha finalizado (error interno) | Sí | Póngase en contacto con AWS Support con el identificador de la solicitud. | 

# Extensiones de openCypher en Amazon Neptune
<a name="access-graph-opencypher-extensions"></a>

 Amazon Neptune es compatible con la versión 9 de referencia de la especificación de openCypher. Para obtener más información, consulte [Conformidad con la especificación de openCypher en Amazon Neptune](feature-opencypher-compliance.md) en Amazon Neptune. Además, Amazon Neptune es compatible con las características que se indican aquí. A menos que se mencionen versiones específicas, las características están disponibles en Neptune Database y Neptune Analytics. 

## Acceso a datos de S3 en tiempo de consulta
<a name="opencypher-compliance-neptune-read"></a>

Disponible en Neptune Database 1.4.7.0 y versiones posteriores.

Neptune admite la `neptune.read()` función de leer datos CSV o Parquet de Amazon S3 directamente en las consultas de OpenCypher. A diferencia del cargador masivo, que importa los datos antes de realizar las consultas, `neptune.read()` accede a los datos de Amazon S3 en el momento de la ejecución de la consulta.

Para obtener la documentación completa, consulte. [neptune.read ()](access-graph-opencypher-21-extensions-s3-read.md)

## La función `join()` específica de Neptune
<a name="opencypher-compliance-join-function"></a>

Disponible en Neptune Database y Neptune Analytics.

Neptune implementa una función `join()` que no está presente en la especificación de openCypher. Crea un literal de cadena a partir de una lista de literales de cadena y un delimitador de cadenas. Adopta dos argumentos:
+ El primer argumento es una lista de literales de cadena.
+ El segundo argumento es la cadena delimitadora, que puede tener cero, uno o más caracteres.

Ejemplo:

```
join(["abc", "def", "ghi"], ", ")    // Returns "abc, def, ghi"
```

## La función `removeKeyFromMap()` específica de Neptune
<a name="opencypher-compliance-removeKeyFromMap-function"></a>

Disponible en Neptune Database y Neptune Analytics.

Neptune implementa una función `removeKeyFromMap()` que no está presente en la especificación de openCypher. Elimina una clave específica de un mapa y devuelve el nuevo mapa resultante.

La función toma dos argumentos:
+ El primer argumento es el mapa del que se debe eliminar la clave.
+ El segundo argumento es la clave que hay que eliminar del mapa.

La función `removeKeyFromMap()` resulta especialmente útil en situaciones en las que se desean establecer valores para un nodo o una relación presentando una lista de mapas. Por ejemplo:

```
UNWIND [{`~id`: 'id1', name: 'john'}, {`~id`: 'id2', name: 'jim'}] as val
CREATE (n {`~id`: val.`~id`})
SET n = removeKeyFromMap(val, '~id')
```

## Valores de identificadores personalizados para las propiedades de los nodos y las relaciones
<a name="opencypher-compliance-custom-ids"></a>

Disponible en Neptune Database 1.2.0.2 y posteriores y en Neptune Analytics.

A partir de la [versión 1.2.0.2 del motor](engine-releases-1.2.0.2.md), Neptune ha ampliado la especificación de openCypher para que pueda especificar los valores `id` para los nodos y las relaciones en las cláusulas `CREATE`, `MERGE` y `MATCH`. Esto le permite asignar cadenas fáciles de usar en lugar de generadas por el sistema UUIDs para identificar nodos y relaciones.

En Neptune Analytics, los valores de ID personalizados no están disponibles para los bordes.

**aviso**  
Esta extensión de la especificación de openCypher no es compatible con versiones anteriores, ya que ahora `~id` se considera un nombre de propiedad reservada. Si ya utiliza `~id` como propiedad en sus datos y consultas, tendrá que migrar la propiedad existente a una nueva clave de propiedad y eliminar la antigua. Consulte [Qué hacer si actualmente utiliza `~id` como propiedad](#opencypher-compliance-custom-ids-migrating).

A continuación, tenemos un ejemplo que muestra cómo crear nodos y relaciones con identificadores personalizados:

```
CREATE (n {`~id`: 'fromNode', name: 'john'})
  -[:knows {`~id`: 'john-knows->jim', since: 2020}]
  ->(m {`~id`: 'toNode', name: 'jim'})
```

Si intenta crear un identificador personalizado que ya esté en uso, Neptune mostrará un error `DuplicateDataException`.

A continuación, se muestra un ejemplo del uso de un identificador personalizado en una cláusula `MATCH`:

```
MATCH (n {`~id`: 'id1'})
RETURN n
```

A continuación, se muestra un ejemplo del uso de la personalización IDs en una `MERGE` cláusula:

```
MATCH (n {name: 'john'}), (m {name: 'jim'})
MERGE (n)-[r {`~id`: 'john->jim'}]->(m)
RETURN r
```

### Qué hacer si actualmente utiliza `~id` como propiedad
<a name="opencypher-compliance-custom-ids-migrating"></a>

Con la [versión 1.2.0.2 del motor](engine-releases-1.2.0.2.md), la clave `~id` de las cláusulas de openCypher ahora se trata como `id` y no como una propiedad. Esto significa que si tiene una propiedad denominada `~id`, es imposible acceder a ella.

Si utiliza una propiedad `~id`, lo que debe hacer antes de actualizarla a la versión del motor `1.2.0.2` o posterior es migrar primero la propiedad `~id` existente a una nueva clave de propiedad y, a continuación, eliminar la propiedad `~id`. Por ejemplo, la siguiente consulta:
+ Crea una nueva propiedad denominada 'newID' para todos los nodos,
+ copia el valor de la propiedad '\$1id' en la propiedad 'newId'
+ y elimina la propiedad '\$1id' de los datos.

```
MATCH (n)
WHERE exists(n.`~id`)
SET n.newId = n.`~id`
REMOVE n.`~id`
```

Se debe hacer lo mismo con cualquier relación en los datos que tenga una propiedad `~id`.

También tendrá que cambiar las consultas que esté utilizando que hagan referencia a una propiedad `~id`. Por ejemplo, esta consulta:

```
MATCH (n)
WHERE n.`~id` = 'some-value'
RETURN n
```

...se convertiría en esto:

```
MATCH (n)
WHERE n.newId = 'some-value'
RETURN n
```

## Soporte de subconsultas CALL en Neptune
<a name="call-subquery-support"></a>

 Disponible en Neptune Database 1.4.1.0 y posteriores y en Neptune Analytics. 

 Amazon Neptune admite subconsultas `CALL`. Una subconsulta `CALL` es una parte de la consulta principal que se ejecuta en un ámbito aislado para cada entrada de la subconsulta `CALL`. 

 Por ejemplo, supongamos que un gráfico contiene datos sobre personas, sus amigos y las ciudades en las que vivían. Podemos recuperar las dos ciudades más grandes en las que vivían los amigos de una persona mediante una subconsulta `CALL`: 

```
MATCH (person:Person)-[:knows]->(friend) 
CALL { 
  WITH friend 
  MATCH (friend)-[:lived_in]->(city) 
  RETURN city 
  ORDER BY city.population DESC
  LIMIT 2 
} 
RETURN person, friend, city
```

 En este ejemplo, la parte de la consulta dentro de `CALL { ... }` se ejecuta para cada `friend` que coincide con la cláusula MATCH anterior. Cuando se ejecuta la consulta interna, las cláusulas `LIMIT` y `ORDER` corresponden a las ciudades en las que vivía un amigo específico, por lo que obtenemos (como máximo) dos ciudades por amigo. 

 Todas las cláusulas de consulta están disponibles dentro de las subconsultas `CALL`. Esto también incluye las subconsultas `CALL` anidadas. Existen algunas restricciones para la primera cláusula `WITH` y las variables emitidas, que se explican a continuación. 

### Ámbito de las variables dentro de la subconsulta CALL
<a name="variable-scope-inside-call-subquery"></a>

 La cláusula `WITH` inicial debe importar las variables de las cláusulas anteriores a la subconsulta `CALL` que se utilizan dentro de ella. A diferencia de las cláusulas `WITH` normales, solo puede contener una lista de variables, pero no permite la creación de alias y no se puede usar junto con `DISTINCT`, `ORDER BY`, `WHERE`, `SKIP` o `LIMIT`. 

### Variables devueltas desde la subconsulta CALL
<a name="variables-returned-call-subquery"></a>

 Las variables que se emiten desde la subconsulta `CALL` se especifican con la cláusula `RETURN` final. Tenga en cuenta que las variables emitidas no pueden superponerse con las variables anteriores a la subconsulta `CALL`. 

### Limitaciones
<a name="call-subquery-limitations"></a>

 Por el momento, no se admiten actualizaciones dentro de una subconsulta `CALL`. 

## Funciones de openCypher en Neptune
<a name="opencypher-compliance-new-functions"></a>

 Disponible en Neptune Database 1.4.1.0 y posteriores y en Neptune Analytics. 

**textIndexOf**

 `textIndexOf(text :: STRING, lookup :: STRING, from = 0 :: INTEGER?, to = -1 :: INTEGER?) :: (INTEGER?)` 

 Devuelve el índice de la primera aparición de `lookup` en el rango de `text` empezando por el desfase `from` (incluido) hasta el desfase `to` (excluido). Si `to` es -1, el rango continúa hasta el final de `text`. La indexación se basa en cero y se expresa en valores escalares Unicode (puntos de código no sustitutos). 

```
RETURN textIndexOf('Amazon Neptune', 'e')
{
  "results": [{
      "textIndexOf('Amazon Neptune', 'e')": 8
    }]
}
```

**collToSet**

 `collToSet(values :: LIST OF ANY?) :: (LIST? OF ANY?)` 

 Devuelve una nueva lista que contiene solo los elementos únicos de la lista original. Se **mantiene** el orden de la lista original (por ejemplo, `[1, 6, 5, 1, 5]` devuelve`[1, 6, 5]`). 

```
RETURN collToSet([1, 6, 5, 1, 1, 5])
{
  "results": [{
      "collToSet([1, 6, 5, 1, 1, 5])": [1, 6, 5]
    }]
}
```

**collSubtract**

 `collSubtract(first :: LIST OF ANY?, second :: LIST OF ANY?) :: (LIST? OF ANY?)` 

 Devuelve una nueva lista que contiene todos los elementos únicos de `first`, sin incluir los elementos de `second`. 

```
RETURN collSubtract([2, 5, 1, 0], [1, 5])
{
  "results": [{
      "collSubtract([2, 5, 1, 0], [1, 5])": [0, 2]
    }]
}
```

**collIntersection**

 `collIntersection(first :: LIST? OF ANY?, second :: LIST? OF ANY?) :: (LIST? OF ANY?)` 

 Devuelve una nueva lista que contiene todos los elementos únicos de la intersección de `first` y `second`. 

```
RETURN collIntersection([2, 5, 1, 0], [1, 5])
{
  "results": [{
      "collIntersection([2, 5, 1, 0], [1, 5])": [1, 5]
    }]
}
```

## Funciones de ordenación
<a name="sorting-functions"></a>

 Las siguientes secciones definen funciones para ordenar recopilaciones. Estas funciones utilizan argumentos de `config` mapa (en algunos casos opcionales) o una lista de varios mapas de este tipo, que definen la clave de clasificación y and/or la dirección de clasificación: 

```
{ key: STRING, order: STRING }
```

 En este caso, `key` es un mapa o una propiedad de nodo cuyo valor se utilizará para la ordenación. `order` es “`asc`” o “`desc`” (sin distinción entre mayúsculas y minúsculas) para especificar una ordenación ascendente o descendente, respectivamente. De forma predeterminada, la ordenación se realizará en orden ascendente. 

**collSort**

 `collSort(coll :: LIST OF ANY, config :: MAP?) :: (LIST? OF ANY?)` 

 Devuelve una nueva lista ordenada que contiene los elementos de la lista de entrada `coll`. 

```
RETURN collSort([5, 3, 1], {order: 'asc'})
{
  "results": [{
      "collSort([5, 3, 1])": [1, 3, 5]
    }]
}
```

**collSortMaps**

 `collSortMaps(coll :: LIST OF MAP, config :: MAP) :: (LIST? OF ANY?)` 

 Devuelve una lista de mapas ordenados por el valor de la propiedad `key` especificada. 

```
RETURN collSortMaps([{name: 'Alice', age: 25}, {name: 'Bob', age: 35}, {name: 'Charlie', age: 18}], {key: 'age', order: 'desc'})
{
  "results": [{
      "x": [{
          "age": 35,
          "name": "Bob"
        }, {
          "age": 25,
          "name": "Alice"
        }, {
          "age": 18,
          "name": "Charlie"
        }]
    }]
}
```

**collSortMulti**

```
collSortMulti(coll :: LIST OF MAP?, 
configs = [] :: LIST OF MAP, 
limit = -1 :: INTEGER?, 
skip = 0 :: INTEGER?) :: (LIST? OF ANY?)
```

 Devuelve una lista de mapas ordenados por el valor de las propiedades `key` especificadas y, opcionalmente, aplica límites y omisiones. 

```
RETURN collSortMulti([{name: 'Alice', age: 25}, {name: 'Bob', age: 35}, {name: 'Charlie', age: 18}], [{key: 'age', order: 'desc'}, {key:'name'}]) as x
{
  "results": [{
      "x": [{
          "age": 35,
          "name": "Bob"
        }, {
          "age": 25,
          "name": "Alice"
        }, {
          "age": 18,
          "name": "Charlie"
        }]
    }]
}
```

**collSortNodes**

 `collSortNodes(coll :: LIST OF NODE, config :: MAP) :: (LIST? OF NODE?)` 

 Devuelve una versión ordenada de la lista de entrada `coll`, en la que los elementos del nodo se ordenan según los valores de sus respectivas propiedades `key`. 

```
create (n:person {name: 'Alice', age: 23}), (m:person {name: 'Eve', age: 21}), (o:person {name:'Bob', age:25})
{"results":[]}

match (n:person) with collect(n) as people return collSortNodes(people, {key: 'name', order: 'desc'})
{
  "results": [{
      "collSortNodes(people, 'name')": [{
          "~id": "e599240a-8c23-4337-8aa8-f603c8fb5488",
          "~entityType": "node",
          "~labels": ["person"],
          "~properties": {
            "age": 21,
            "name": "Eve"
          }
        }, {
          "~id": "8a6ef785-59e3-4a0b-a0ff-389655a9c4e6",
          "~entityType": "node",
          "~labels": ["person"],
          "~properties": {
            "age": 25,
            "name": "Bob"
          }
        }, {
          "~id": "466bc826-f47f-452c-8a27-6b7bdf7ae9b4",
          "~entityType": "node",
          "~labels": ["person"],
          "~properties": {
            "age": 23,
            "name": "Alice"
          }
        }]
    }]
}

match (n:person) with collect(n) as people return collSortNodes(people, {key: 'age'})
{
  "results": [{
      "collSortNodes(people, '^age')": [{
          "~id": "e599240a-8c23-4337-8aa8-f603c8fb5488",
          "~entityType": "node",
          "~labels": ["person"],
          "~properties": {
            "age": 21,
            "name": "Eve"
          }
        }, {
          "~id": "466bc826-f47f-452c-8a27-6b7bdf7ae9b4",
          "~entityType": "node",
          "~labels": ["person"],
          "~properties": {
            "age": 23,
            "name": "Alice"
          }
        }, {
          "~id": "8a6ef785-59e3-4a0b-a0ff-389655a9c4e6",
          "~entityType": "node",
          "~labels": ["person"],
          "~properties": {
            "age": 25,
            "name": "Bob"
          }
        }]
    }]
}
```

## Funciones temporales
<a name="temporal-functions"></a>

 Las funciones temporales están disponibles a partir de la versión [1.4.5.0](https://docs.aws.amazon.com/releases/release-1.4.5.0.xml) de Neptune. 

### day
<a name="temporal-functions-day"></a>

 `day(temporal :: (datetime | date)) :: (LONG)` 

 Devuelve el `day` del mes a partir de un valor de `datetime` o `date`. Para `datetime`: los valores se normalizan a UTC en función de la entrada antes de extraer el día. Para `date`: el día se extrae en función de la zona horaria. 

 La entrada `datetime` está disponible en Neptune Database y en Neptune Analytics: 

```
RETURN day(datetime('2021-06-03T01:48:14Z'))
{
  "results": [{
      "day(datetime('2021-06-03T01:48:14Z'))": 3
    }]
}
```

 En este caso, `datetime` se normaliza a UTC, por lo que, \$108:00 se desplaza hacia atrás al 2 de junio. 

```
RETURN day(datetime('2021-06-03T00:00:00+08:00'))
{
  "results": [{
      "day(datetime('2021-06-03T00:00:00+08:00'))": 2
    }]
}
```

 La entrada `date` solo está disponible en Neptune Analytics: 

```
RETURN day(date('2021-06-03Z'))
{
  "results": [{
      "day(date('2021-06-03Z'))": 3
    }]
}
```

 `date` conserva la zona horaria y se mantiene el 3 de junio. 

```
RETURN day(date('2021-06-03+08:00'))
{
  "results": [{
      "day(date('2021-06-03+08:00'))": 3
    }]
}
```

### month
<a name="temporal-functions-month"></a>

 `month(temporal :: (datetime | date)) :: (LONG)` 

 Devuelve el mes a partir de un valor de `datetime` o `date` (1-12). Para `datetime`: los valores se normalizan a UTC en función de la entrada antes de extraer el mes. Para `date`: el mes se extrae en función de la zona horaria. 

 La entrada `datetime` está disponible en Neptune Database y en Neptune Analytics: 

```
RETURN month(datetime('2021-06-03T01:48:14Z'))
{
  "results": [{
      "month(datetime('2021-06-03T01:48:14Z'))": 6
    }]
}
```

 En este caso, `datetime` se normaliza a UTC, por lo que, \$108:00 se desplaza hacia atrás al 31 de mayo. 

```
RETURN month(datetime('2021-06-01T00:00:00+08:00'))
{
  "results": [{
      "month(datetime('2021-06-01T00:00:00+08:00'))": 5
    }]
}
```

 La entrada `date` solo está disponible en Neptune Analytics: 

```
RETURN month(date('2021-06-03Z'))
{
  "results": [{
      "month(date('2021-06-03Z'))": 6
    }]
}
```

 `date` conserva la zona horaria y se mantiene el 1 de junio. 

```
RETURN month(date('2021-06-01+08:00'))
{
  "results": [{
      "month(date('2021-06-01+08:00'))": 6
    }]
}
```

### year
<a name="temporal-functions-year"></a>

 `year(temporal :: (datetime | date)) :: (LONG)` 

 Devuelve el año a partir de un valor de `datetime` o `date`. Para `datetime`: los valores se normalizan a UTC en función de la entrada antes de extraer el año. Para `date`: el año se extrae en función de la zona horaria. 

 La entrada `datetime` está disponible en Neptune Database y en Neptune Analytics: 

```
RETURN year(datetime('2021-06-03T01:48:14Z'))
{
  "results": [{
      "year(datetime('2021-06-03T01:48:14Z'))": 2021
    }]
}
```

 En este caso, `datetime` se normaliza a UTC, por lo que, \$108:00 se desplaza hacia atrás al 31 de diciembre de 2020. 

```
RETURN year(datetime('2021-01-01T00:00:00+08:00'))
{
  "results": [{
      "year(datetime('2021-01-01T00:00:00+08:00'))": 2020
    }]
}
```

 La entrada `date` solo está disponible en Neptune Analytics: 

```
RETURN year(date('2021-06-03Z'))
{
  "results": [{
      "year(date('2021-06-03Z'))": 2021
    }]
}
```

 `date` conserva la zona horaria y se mantiene en junio de 2021. 

```
RETURN year(date('2021-01-01+08:00'))
{
  "results": [{
      "year(date('2021-01-01+08:00'))": 2021
    }]
}
```

### Funciones de openCypher en Neptune
<a name="openCypher-functions"></a>

 Disponible en Neptune Database 1.4.6.0 y posteriores y en Neptune Analytics. 

#### reduce()
<a name="openCypher-functions-reduce"></a>

 Reduce procesa secuencialmente cada elemento de la lista combinándolo con un total acumulado o “acumulador”. Empezando por un valor inicial, actualiza el acumulador después de cada operación y utiliza ese valor actualizado en la siguiente iteración. 

 `for i in (0, ..., n) acc = acc X list[I], where X denotes any binary operator` 

 Una vez procesados todos los elementos, devuelve el resultado final acumulado. 

 Una estructura típica de reduce() sería: `reduce(accumulator = initial , variable IN list | expression)` 

**Especificaciones de tipo:**  
 `- initial: starting value for the accumulator :: (Long | FLOAT | STRING | LIST? OF (STRING, LONG, FLOAT)) - list: the input list :: LIST OF T where T matches initial type - variable :: represents each element in the input list - expression :: Only supports '+' and '*' operator - return :: Same type as initial ` 

**Restricciones:**  
 Actualmente, la expresión `reduce()` solo admite: 
+  Multiplicación numérica 
+  Suma numérica 
+  Concatenación de cadenas 
+  Concatenación de listas 

 Se representan mediante el operador `*` o `+`. La expresión debe ser binaria, tal y como se especifica a continuación: `expression pattern: accumulator + any variable or accumulator * any variable` 

**Gestión del desbordamiento:**  
 Neptune detecta los desbordamientos numéricos durante la evaluación de `reduce()` y responde de forma diferente según el tipo de datos: 

```
LONG (signed 64‑bit)
--------------------
• Valid range: –9 223 372 036 854 775 808 … 9 223 372 036 854 775 807  
• If any intermediate or final value falls outside this range,
  Neptune aborts the query with long overflow error message.
  
FLOAT (IEEE‑754 double)
-----------------------
• Largest finite value ≈ 1.79 × 10^308  
• Larger results overflow to INF
  Once `INF` is produced, it propagates through the remainder
  of the reduction.
```

**Ejemplos:**  
Consulte los siguientes ejemplos de la función reduce().

```
1. Long Addition:
RETURN reduce(sum = 0, n IN [1, 2, 3] | sum + n)
{
  "results": [{
      "reduce(sum = 0, n IN [1, 2, 3] | sum + n)": 6
    }]
}

2. String Concatenation:
RETURN reduce(str = "", x IN ["A", "B", "C"] | str + x) 
{
  "results": [{
      "reduce(str = "", x IN ["A", "B", "C"] | str + x)": "ABC"
    }]
}

3. List Combination:
RETURN reduce(lst = [], x IN [1, 2, 3] | lst + x)
{
  "results": [{
      "reduce(lst = [], x IN [1, 2, 3] | lst + x)": [1, 2, 3]
    }]
}

4. Float Addition:
RETURN reduce(total = 0.0, x IN [1.5, 2.5, 3.5] | total + x) 
{
  "results": [{
      "reduce(total = 0.0, x IN [1.5, 2.5, 3.5] | total + x)": 7.5
    }]
}

5. Long Multiplication:
RETURN reduce(product = 1, n IN [1, 2, 3] | product * n)
{
  "results": [{
      "reduce(product = 0, n IN [1, 2, 3] | product * n)": 6
    }]
}

6. Float Multiplication:
RETURN reduce(product = 1.0, n IN [1.5, 2.5, 3.5] | product * n)
{
  "results": [{
      "reduce(product = 1.0, n IN [1.5, 2.5, 3.5] | product * n)": 13.125
    }]
}

7. Long Overflow (Exception):
RETURN reduce(s = 9223372036854775807, x IN [2, 3] | s * x) AS result
{
"results": [{
    "reduce(s = 9223372036854775807, x IN [2, 3] | s * x) AS result": long overflow
    }]
}

8. Float Overflow:
RETURN reduce(s = 9.0e307, x IN [8.0e307, 1.0e307] | s + x) AS result
{
"results": [{
    "reduce(s = 9.0e307, x IN [8.0e307, 1.0e307] | s + x) AS result": INF
    }]
}
```

# neptune.read ()
<a name="access-graph-opencypher-21-extensions-s3-read"></a>

 Neptune admite un `CALL` procedimiento `neptune.read` para leer datos de Amazon S3 y, a continuación, ejecutar una consulta de OpenCypher (lectura, inserción, actualización) con los datos. El procedimiento convierte cada fila del archivo en una fila de variables de resultado declaradas. Utiliza las credenciales de IAM de la persona que llama para acceder a los datos de Amazon S3. Consulte [Administrar los permisos de neptune.read ()](access-graph-opencypher-21-extensions-s3-read-permissions.md) para configurar los permisos. La AWS región del bucket de Amazon S3 debe estar en la misma región en la que se encuentra la instancia. Actualmente, no se admiten las lecturas entre regiones. 

 **Sintaxis** 

```
CALL neptune.read(
  {
    source: "string",
    format: "parquet/csv",
    concurrency: 10
  }
)
YIELD row
...
```

**Entradas**
+  **source** (obligatorio): URI de Amazon S3 para un **solo** objeto. No se admite el prefijo Amazon S3 para varios objetos. 
+  **formato** (obligatorio): `parquet` y `csv` son compatibles. 
  +  Encontrará más información sobre el formato Parquet compatible en[Tipos de columnas de parquet compatibles](access-graph-opencypher-21-extensions-s3-read-parquet.md#access-graph-opencypher-21-extensions-s3-read-parquet-column-types). 
  +  Para obtener más información sobre el formato csv compatible, consulte[Formato de carga para los datos de openCypher](bulk-load-tutorial-format-opencypher.md). 
+  **simultaneidad** (opcional): tipo: número entero 0 o superior. Valor predeterminado: 0. Especifica el número de subprocesos que se utilizarán para leer el archivo. Si el valor es 0, se utilizará el número máximo de subprocesos permitido por el recurso. En el caso de Parquet, se recomienda configurarlo en varios grupos de filas. 

**Outputs**

 El archivo neptune.read devuelve: 
+  **fila: tipo: mapa** 
  +  Cada fila del archivo, donde las claves son las columnas y los valores son los datos que se encuentran en cada columna. 
  +  Puede acceder a los datos de cada columna como si fuera una propiedad access (`row.col`). 

## Mejores prácticas para neptune.read ()
<a name="access-graph-opencypher-21-extensions-s3-read-best-practices"></a>

Las operaciones de lectura de Neptune S3 pueden consumir mucha memoria. Utilice tipos de instancias adecuados para las cargas de trabajo de producción, tal y como se describe en [Elegir tipos de instancias para Amazon Neptune](instance-types.md).

El uso de la memoria y el rendimiento de `neptune.read()` las solicitudes se ven afectados por diversos factores, como el tamaño del archivo, el número de columnas, el número de filas y el formato del archivo. Según la estructura, los archivos pequeños (por ejemplo, los archivos CSV de 100 MB o menos y los archivos Parquet de 20 MB o menos) pueden funcionar de forma fiable en la mayoría de los tipos de instancias adecuados para la producción, mientras que los archivos más grandes pueden requerir una cantidad considerable de memoria que los tipos de instancias más pequeños no pueden proporcionar.

Al probar esta función, se recomienda empezar con archivos pequeños y ampliarlos gradualmente para garantizar que la carga de trabajo de lectura se adapte al tamaño de la instancia. Si observas que `neptune.read()` las solicitudes provocan out-of-memory excepciones o se reinician las instancias, considera la posibilidad de dividir los archivos en fragmentos más pequeños, reducir la complejidad de los archivos o actualizarlos a tipos de instancias más grandes.

# Consulta ejemplos con parquet
<a name="access-graph-opencypher-21-extensions-s3-read-parquet"></a>

El siguiente ejemplo de consulta devuelve el número de filas de un archivo Parquet determinado:

```
CALL neptune.read(
  {
    source: "<s3 path>",
    format: "parquet"
  }
)
YIELD row
RETURN count(row)
```

Puede ejecutar el ejemplo de consulta mediante la `execute-open-cypher-query` operación de AWS CLI ejecutando el siguiente código:

```
aws neptunedata execute-open-cypher-query \
--open-cypher-query "CALL neptune.read({source: '<s3 path>', format: 'parquet'}) YIELD row RETURN count(row)" \
--endpoint-url https://my-cluster-name.cluster-abcdefgh1234.us-east-1.neptune.amazonaws.com:8182
```

Una consulta puede ser flexible en cuanto a lo que hace con las filas leídas de un archivo Parquet. Por ejemplo, la siguiente consulta crea un nodo con un campo que se establece con los datos que se encuentran en el archivo Parquet:

```
CALL neptune.read(
  {
    source: "<s3 path>",
    format: "parquet"
  }
)
YIELD row
CREATE (n {someField: row.someCol}) 
RETURN n
```

**aviso**  
No se considera una buena práctica utilizar una cláusula extensa que produzca resultados como `MATCH(n)` antes de una `CALL` cláusula. Esto llevaría a una consulta de larga duración, debido a la combinación de productos entre las soluciones entrantes de cláusulas anteriores y las filas leídas por neptune.read. Se recomienda iniciar la consulta con neptune.read. `CALL`

## Tipos de columnas de parquet compatibles
<a name="access-graph-opencypher-21-extensions-s3-read-parquet-column-types"></a>

**Tipos de datos de parquet:**
+ NULL
+ BOOLEANO
+ FLOAT
+ DOUBLE
+ STRING
+ ENTERO CON SIGNO: UINT8, UINT16, UINT32, UINT64
+ MAPA: solo admite un nivel. No es compatible con anidado.
+ LISTA: solo admite un nivel. No es compatible con anidados.

**Tipos de datos específicos de Neptuno:**

A diferencia de los encabezados de las columnas de propiedades del formato CSV, los encabezados de las columnas de propiedades del formato Parquet solo necesitan tener los nombres de las propiedades, por lo que no es necesario tener los nombres de los tipos ni la cardinalidad.

Sin embargo, hay algunos tipos de columnas especiales en el formato Parquet que requieren anotaciones en los metadatos, como el tipo Any, el tipo Date, el tipo DateTime y el tipo Geometry. El siguiente objeto es un ejemplo de la anotación de metadatos necesaria para los archivos que contienen columnas de estos tipos especiales:

```
"metadata": {
    "anyTypeColumns": ["UserCol1"],
    "dateTypeColumns": ["UserCol2"],
    "dateTimeTypeColumns": ["UserCol3"],
    "geometryTypeColumns": ["UserCol4"]
}
```

A continuación, se muestran detalles sobre la carga útil esperada asociada a estos tipos:
+ En las columnas de usuario se admite el tipo de columna Any. Un tipo Any es un tipo de «azúcar sintáctico» para todos los demás tipos que admitimos. Es extremadamente útil si una columna de usuario tiene varios tipos. La carga útil de un valor de cualquier tipo es una lista de cadenas json de la siguiente manera:`{"value": "10", "type": "Int"};{"value": "1.0", "type": "Float"}`, que tiene un campo de valor y un campo de tipo en cada cadena json individual. El valor de cardinalidad de una columna Any está establecido, lo que significa que la columna puede aceptar varios valores. 
  + Neptune admite los siguientes tipos en cualquier tipo: Bool (o booleano), Byte, Short, Int, Long,,,, Float, UnsignedByte Double UnsignedShort, Date UnsignedInt UnsignedLong, DateTime, String y Geometry.
  + El tipo vectorial no es compatible con Any Type.
  + No se admite ningún tipo anidado. Por ejemplo, `{"value": {"value": "10", "type": "Int"}, "type": "Any"}`.
+ Las columnas de usuario admiten columnas de tipo Fecha y Fecha y hora. La carga útil de estas columnas debe proporcionarse como cadenas que sigan el formato XSD o uno de los siguientes formatos: 
  + aaaa-MM-dd
  + YYYY-MM-DDTHH:mm
  + YYYY-MM-DDTHH: MM:SS
  + YYYY-MM-DDTHH: MM:SSZ
  + YYYY-MM-DDTHH: MM:SS.SSSZ
  + YYYY-MM-DDTHH:mm:ss [\$1\$1-] hhmm
  + aaaa-mm-ddthh:mm:ss.sss [\$1\$1-] hhmm
+ En las columnas de usuario se admite un tipo de columna geométrica. La carga útil de estas columnas solo debe contener primitivas geométricas de tipo Point, proporcionadas como cadenas en formato de texto conocido (WKT). Por ejemplo, POINT (30 10) sería un valor geométrico válido.

## Ejemplo de salida de parquet
<a name="sample-parquet-output"></a>

Dado un archivo de parquet como este:

```
<s3 path>

Parquet Type:
    int8     int16       int32             int64              float      double    string
+--------+---------+-------------+----------------------+------------+------------+----------+
|   Byte |   Short |       Int   |                Long  |     Float  |    Double  | String   |
|--------+---------+-------------+----------------------+------------+------------+----------|
|   -128 |  -32768 | -2147483648 | -9223372036854775808 |    1.23456 |    1.23457 | first    |
|    127 |   32767 |  2147483647 |  9223372036854775807 |  nan       |  nan       | second   |
|      0 |       0 |           0 |                    0 | -inf       | -inf       | third    |
|      0 |       0 |           0 |                    0 |  inf       |  inf       | fourth   |
+--------+---------+-------------+----------------------+------------+------------+----------+
```

Este es un ejemplo del resultado devuelto por neptune.read mediante la siguiente consulta:

```
aws neptunedata execute-open-cypher-query \
--open-cypher-query "CALL neptune.read({source: '<s3 path>', format: 'parquet'}) YIELD row RETURN row" \
--endpoint-url https://my-cluster-name.cluster-abcdefgh1234.us-east-1.neptune.amazonaws.com:8182
```

```
{
 "results": [{
 "row": {
 "Float": 1.23456,
 "Byte": -128,
 "Int": -2147483648,
 "Long": -9223372036854775808,
 "String": "first",
 "Short": -32768,
 "Double": 1.2345678899999999
 }
 }, {
 "row": {
 "Float": "NaN",
 "Byte": 127,
 "Int": 2147483647,
 "Long": 9223372036854775807,
 "String": "second",
 "Short": 32767,
 "Double": "NaN"
 }
 }, {
 "row": {
 "Float": "-INF",
 "Byte": 0,
 "Int": 0,
 "Long": 0,
 "String": "third",
 "Short": 0,
 "Double": "-INF"
 }
 }, {
 "row": {
 "Float": "INF",
 "Byte": 0,
 "Int": 0,
 "Long": 0,
 "String": "fourth",
 "Short": 0,
 "Double": "INF"
 }
 }]
}
```

Actualmente, no hay forma de establecer una etiqueta de nodo o borde en un campo de datos procedente de un archivo Parquet. Se recomienda dividir las consultas en varias consultas, una para cada etiqueta/tipo.

```
CALL neptune.read({source: '<s3 path>', format: 'parquet'})
 YIELD row 
WHERE row.`~label` = 'airport'
CREATE (n:airport)

CALL neptune.read({source: '<s3 path>', format: 'parquet'})
YIELD row 
WHERE row.`~label` = 'country'
CREATE (n:country)
```

# Ejemplos de consultas mediante CSV
<a name="access-graph-opencypher-21-extensions-s3-read-csv"></a>

En este ejemplo, la consulta devuelve el número de filas de un archivo CSV determinado:

```
CALL neptune.read(
  {
    source: "<s3 path>",
    format: "csv"
  }
)
YIELD row
RETURN count(row)
```

Puede ejecutar el ejemplo de consulta mediante la execute-open-cypher-query operación de AWS CLI ejecutando el siguiente código:

```
aws neptunedata execute-open-cypher-query \
--open-cypher-query "CALL neptune.read({source: '<s3 path>', format: 'csv'}) YIELD row RETURN count(row)" \
--endpoint-url https://my-cluster-name.cluster-abcdefgh1234.us-east-1.neptune.amazonaws.com:8182
```

Una consulta puede ser flexible en cuanto a lo que hace con las filas leídas de un archivo CSV. Por ejemplo, la siguiente consulta crea un nodo con un campo configurado con datos de un archivo CSV:

```
CALL neptune.read(
  {
    source: "<s3 path>",
    format: "csv"
  }
)
YIELD row
CREATE (n {someField: row.someCol}) 
RETURN n
```

**aviso**  
No se considera una buena práctica utilizar una cláusula extensa que produzca resultados, como MATCH (n), antes de una cláusula CALL. Esto llevaría a una consulta de larga duración debido a la combinación de productos entre las soluciones entrantes de cláusulas anteriores y las filas leídas por neptune.read. Se recomienda iniciar la consulta con CALL neptune.read.

## Encabezados de columnas de propiedades
<a name="property-column-headers"></a>

Para especificar una columna (`:`) para una propiedad, use la sintaxis siguiente. Los nombres de tipos no distinguen entre mayúsculas y minúsculas. Si aparecen dos puntos dentro del nombre de una propiedad, deben estar separados por una barra invertida:. `\:`

```
propertyname:type
```

**nota**  
No se permiten espacios, comas, retornos de carro ni caracteres de nueva línea en los encabezados de las columnas, por lo que los nombres de las propiedades no pueden incluir estos caracteres.
Para especificar una columna para un tipo de matriz, añada `[]` al tipo:  

  ```
                          propertyname:type[]
  ```
Las propiedades de borde solo pueden tener un único valor y provocarán un error si se especifica un tipo de matriz o si se especifica un segundo valor. El siguiente ejemplo muestra el encabezado de columna de una propiedad denominada age de tipo Int:  

  ```
  age:Int
  ```

Cada fila del archivo debe tener un entero en esa posición o bien dejarse en blanco. Se permiten matrices de cadenas, pero las cadenas de una matriz no pueden incluir el carácter de punto y coma (`;`) a menos que se escape mediante una barra invertida (). `\;`

## Tipos de columnas CSV compatibles
<a name="supported-csv-column-types"></a>
+ **BOOL (o BOOLEAN)**: valores permitidos: verdadero, falso. Indica un campo booleano. Cualquier valor que no sea verdadero se considerará falso.
+ **FLOAT**: rango: punto flotante IEEE 754 de 32 bits que incluye Infinity, INF, -Infinity, -INF y NaN (). not-a-number
+ **DOBLE**: rango: punto flotante IEEE 754 de 64 bits que incluye Infinity, INF, -Infinity, -INF y NaN ()not-a-number.
+ **CADENA -** 
  + El uso de las comillas es opcional. Las comas, los caracteres de línea nueva y los caracteres de retorno se escapan automáticamente si se incluyen en una cadena entre comillas dobles («). Ejemplo: «Hola, mundo».
  + Para incluir comillas en una cadena entre comillas, puede evitar las comillas utilizando dos en fila: por ejemplo: «Hola «" Mundo "».
  + Se permiten matrices de cadenas, pero las cadenas de una matriz no pueden incluir el carácter de punto y coma (;) a menos que se escape mediante una barra invertida (\$1;).
  + Si desea rodear las cadenas de una matriz con comillas, debe rodear toda la matriz con un conjunto de comillas. Ejemplo: «Cadena uno; Cadena 2; Cadena 3".
+ **FECHA, FECHA Y HORA**: los valores de fecha y hora se pueden proporcionar en formato XSD o en uno de los siguientes formatos: 
  + aaaa-MM-dd
  + aaaa-mm-ddthh:mm
  + YYYY-MM-DDTHH: MM:SS
  + YYYY-MM-DDTHH: MM:SSZ
  + YYYY-MM-DDTHH: MM:SS.SSSZ
  + YYYY-MM-DDTHH:mm:ss [\$1\$1-] hhmm
  + aaaa-mm-ddthh:mm:ss.sss [\$1\$1-] hhmm
+ **ENTERO CON SIGNO -** 
  + Byte: de -128 a 127
  + Corto: -32768 a 32767
  + Int: -2^31 a 2^31-1
  + Largo: de -2^63 a 2^63-1

**Tipos de columnas específicos de Neptuno:**
+ Las columnas de usuario admiten un tipo de columna Any. Un tipo Any es un tipo de «azúcar sintáctico» para todos los demás tipos que admitimos. Es extremadamente útil si una columna de usuario tiene varios tipos. La carga útil de un valor de cualquier tipo es una lista de cadenas json de la siguiente manera:`{"value": "10", "type": "Int"};{"value": "1.0", "type": "Float"}`, que tiene un campo de valor y un campo de tipo en cada cadena json individual. El encabezado de columna de un tipo Any es propertyName:Any. El valor de cardinalidad de una columna Any está establecido, lo que significa que la columna puede aceptar varios valores. 
  + Neptune admite los siguientes tipos en cualquier tipo: Bool (o booleano), Byte, Short, Int, Long,,,, Float, UnsignedByte Double UnsignedShort, Date UnsignedInt UnsignedLong, DateTime, String y Geometry.
  + El tipo vectorial no es compatible con Any Type.
  + No se admite ningún tipo anidado. Por ejemplo, `{"value": {"value": "10", "type": "Int"}, "type": "Any"}`.
+ En las columnas de usuario se admite un tipo de columna geométrica. La carga útil de estas columnas solo debe contener primitivas geométricas de tipo Point, proporcionadas como cadenas en formato de texto conocido (WKT). Por ejemplo, POINT (30 10) sería un valor geométrico válido.

## Ejemplo de salida CSV
<a name="sample-csv-output"></a>

Dado el siguiente archivo CSV:

```
<s3 path>
colA:byte,colB:short,colC:int,colD:long,colE:float,colF:double,colG:string
-128,-32768,-2147483648,-9223372036854775808,1.23456,1.23457,first
127,32767,2147483647,9223372036854775807,nan,nan,second
0,0,0,0,-inf,-inf,third
0,0,0,0,inf,inf,fourth
```

En este ejemplo, se muestra el resultado devuelto por neptune.read mediante la siguiente consulta:

```
aws neptunedata execute-open-cypher-query \
--open-cypher-query "CALL neptune.read({source: '<s3 path>', format: 'csv'}) YIELD row RETURN row" \
--endpoint-url https://my-cluster-name.cluster-abcdefgh1234.us-east-1.neptune.amazonaws.com:8182
```

```
{
  "results": [{
      "row": {
        "colD": -9223372036854775808,
        "colC": -2147483648,
        "colE": 1.23456,
        "colB": -32768,
        "colF": 1.2345699999999999,
        "colG": "first",
        "colA": -128
      }
    }, {
      "row": {
        "colD": 9223372036854775807,
        "colC": 2147483647,
        "colE": "NaN",
        "colB": 32767,
        "colF": "NaN",
        "colG": "second",
        "colA": 127
      }
    }, {
      "row": {
        "colD": 0,
        "colC": 0,
        "colE": "-INF",
        "colB": 0,
        "colF": "-INF",
        "colG": "third",
        "colA": 0
      }
    }, {
      "row": {
        "colD": 0,
        "colC": 0,
        "colE": "INF",
        "colB": 0,
        "colF": "INF",
        "colG": "fourth",
        "colA": 0
      }
    }]
}
```

Actualmente, no hay forma de establecer una etiqueta de nodo o borde en un campo de datos procedente de un archivo CSV. Se recomienda dividir las consultas en varias consultas, una para cada etiqueta o tipo.

```
CALL neptune.read({source: '<s3 path>', format: 'csv'})
 YIELD row 
WHERE row.`~label` = 'airport'
CREATE (n:airport)

CALL neptune.read({source: '<s3 path>', format: 'csv'})
YIELD row 
WHERE row.`~label` = 'country'
CREATE (n:country)
```

# Administrar los permisos de neptune.read ()
<a name="access-graph-opencypher-21-extensions-s3-read-permissions"></a>

## Políticas de IAM obligatorias
<a name="access-graph-opencypher-21-extensions-s3-read-permissions-iam"></a>

Para ejecutar las consultas de OpenCypher que utilice`neptune.read()`, debe tener los permisos adecuados para acceder a los datos de la base de datos de Neptune. Las consultas de solo lectura requieren esta acción. `ReadDataViaQuery` Las consultas que modifican datos requieren `WriteDataViaQuery` inserciones o `DeleteDataViaQuery` eliminaciones. El siguiente ejemplo permite realizar las tres acciones en el clúster especificado.

Además, necesita permisos para acceder al depósito de S3 que contiene sus archivos de datos. La declaración de política de Neptunes3access otorga los permisos de S3 necesarios:
+ **`s3:ListBucket`**: necesario para verificar la existencia del bucket y enumerar su contenido.
+ **`s3:GetObject`**: Necesario para acceder al objeto especificado para poder leer su contenido e integrarlo en las consultas de OpenCypher.

Si su bucket de S3 utiliza el cifrado del lado del servidor AWS KMS, también debe conceder permisos de KMS. La declaración de KMSAccess política de Neptunes3 permite a Neptune descifrar datos y generar claves de datos al acceder a objetos S3 cifrados. La condición restringe las operaciones de KMS a las solicitudes que se originen en los servicios de S3 y RDS de su región.
+ **`kms:Decrypt`**: Necesario para descifrar el objeto cifrado para que Neptune pueda leer sus datos.
+ **`kms:GenerateDataKey`**: También lo requiere la API de S3 que se utiliza para recuperar los objetos que se van a leer.

```
{
  "Sid": "NeptuneQueryAccess",
  "Effect": "Allow",
  "Action": [
      "neptune-db:ReadDataViaQuery",
      "neptune-db:WriteDataViaQuery",
      "neptune-db:DeleteDataViaQuery"
  ],
  "Resource": "arn:aws:neptune-db:<REGION>:<AWS_ACCOUNT_ID>:<CLUSTER_RESOURCE_ID>/*"
},
{
  "Sid": "NeptuneS3Access",
  "Effect": "Allow",
  "Action": [
      "s3:ListBucket",
      "s3:GetObject"
  ],
  "Resource": [
      "arn:aws:s3:::neptune-read-bucket",
      "arn:aws:s3:::neptune-read-bucket/*"
  ]
},
{
  "Sid": "NeptuneS3KMSAccess",
  "Effect": "Allow",
  "Action": [
      "kms:Decrypt",
      "kms:GenerateDataKey"
  ],
  "Resource": "arn:aws:kms:<REGION>:<AWS_ACCOUNT_ID>:key/<KEY_ID>",
  "Condition": {
      "StringEquals": {
        "kms:ViaService": [
            "s3.<REGION>.amazonaws.com",
            "rds.<REGION>.amazonaws.com"
        ]
      }
  }
}
```

## Requisitos previos importantes
<a name="access-graph-opencypher-21-extensions-s3-read-permissions-prerequisites"></a>

Estos permisos y requisitos previos garantizan una integración segura y confiable de los datos de S3 en las consultas de OpenCypher, al tiempo que mantienen los controles de acceso y las medidas de protección de datos adecuados.
+ **Autenticación de IAM**: esta función solo es compatible con los clústeres de Neptune con la autenticación de IAM habilitada. Consulte [Proteger la base de datos de Amazon Neptune](security.md) para obtener instrucciones detalladas sobre cómo crear clústeres habilitados para la autenticación de IAM y conectarse a ellos.
+ **Punto de conexión de VPC:**
  + Para que Neptune pueda comunicarse con Amazon S3e, se requiere un punto de conexión de VPC de tipo puerta de enlace para Amazon S3.
  + Para usar el AWS KMS cifrado personalizado en la consulta, se requiere un punto de enlace de VPC de tipo interfaz AWS KMS para permitir que Neptune se comunique con él. AWS KMS
  + Para obtener instrucciones detalladas sobre cómo configurar este punto de enlace, consulte [Creación del punto de enlace de VPC de Amazon S3](bulk-load-tutorial-IAM.md).

# Datos espaciales
<a name="access-graph-opencypher-22-spatial-data"></a>

Amazon Neptune ahora admite consultas espaciales, lo que le permite almacenar y analizar datos geométricos en su gráfica. Si bien se utilizan habitualmente para ubicaciones geográficas (como las coordenadas de un mapa), las características espaciales funcionan con cualquier dato bidimensional en el que la posición y la proximidad sean importantes. Usa esta función para responder a preguntas como «¿Qué tiendas están a menos de 8 km de este cliente?» , «Busque todas las rutas de entrega que se crucen con esta área de servicio» o «¿Qué componentes de este plano de planta se superponen con la zona de HVAC?» Neptune implementa el soporte espacial mediante funciones de tipos espaciales estándares del sector que funcionan con puntos, polígonos y otras formas geométricas. Puede almacenar datos espaciales como propiedades en nodos y bordes y, a continuación, utilizar funciones espaciales para calcular distancias, comprobar si los puntos se encuentran dentro de los límites o buscar regiones superpuestas, todo ello dentro de sus consultas de OpenCypher.

Casos de **uso comunes**:
+ **Aplicaciones geográficas**: recomendaciones basadas en la ubicación, geocercado, planificación de rutas y análisis del territorio
+ **Administración de instalaciones y espacios**: distribución de los planos de planta, ubicación de los equipos y cobertura de zonas
+ **Topología de la red**: mapeo de la infraestructura física, áreas de cobertura y límites de servicio
+ **Diseño y CAD**: posicionamiento de componentes, detección de colisiones y relaciones espaciales en diseños 2D
+ **Desarrollo del juego**: posicionamiento de personajes, detección de colisiones y area-of-effect cálculos

La implementación de tipos espaciales en Amazon Neptune sigue las directivas ISO/IEC 13249-3:2016, al igual que otras bases de datos. [Funciones espaciales](access-graph-opencypher-22-spatial-functions.md)Están disponibles en el lenguaje de consulta OpenCypher.

## Sistema de coordenadas
<a name="access-graph-opencypher-22-spatial-data-coordinate-system"></a>

Neptune tiene un identificador de referencia espacial (SRID) para toda la base de datos. La homogeneidad del sistema de coordenadas reduce los errores de los usuarios al realizar consultas y mejora el rendimiento de la base de datos. La primera versión (1.4.7.0) admite el sistema de coordenadas cartesianas, también denominado SRID 0.

La implementación del SRID 0 en Neptune es compatible con los valores de longitud y latitud. Se utiliza `ST_DistanceSpheroid` para calcular las distancias en función del WGS84 /SRID 4326.

La implementación actual admite el almacenamiento de coordenadas tridimensionales. Actualmente, las funciones espaciales solo admiten el uso de las coordenadas de los ejes x e y (bidimensionales). Actualmente, las funciones espaciales disponibles no admiten las coordenadas del eje z.

## Almacenamiento de datos de ubicación
<a name="storing-spatial-data"></a>

Almacene los datos de ubicación en los nodos y las aristas mediante el tipo de propiedad Geometría. Cree valores geométricos a partir del formato de texto conocido (WKT), una forma estándar de representar formas geográficas como texto. Por ejemplo, para almacenar la ubicación de un punto:

```
CREATE (n:airport {code: 'ATL', location: ST_GeomFromText('POINT (-84.4281 33.6367)')})
```

Al trabajar con coordenadas geográficas, el primer argumento (x) representa la longitud y el segundo argumento (y) representa la latitud. Esto sigue el orden de coordenadas estándar utilizado en las bases de datos espaciales y la norma ISO 19125.

**nota**  
 Neptune ahora admite un nuevo tipo de datos llamado «Geometría». La propiedad geométrica de un nodo o una arista se puede crear a partir de una cadena WKT mediante la `ST_GeomFromText` función.  
Neptune almacenará automáticamente los datos de puntos en un índice espacial especializado para mejorar el rendimiento de las funciones de tipos espaciales. Por ejemplo, el índice espacial especializado acelera el proceso que se `ST_Contains` utiliza para encontrar los puntos dentro de un polígono.  
[Página de Wikipedia sobre una conocida representación textual de la geometría](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry)

## Carga de datos espaciales de forma masiva
<a name="loading-spatial-data-bulk"></a>

Al cargar datos de forma masiva, especifique el tipo de geometría en el encabezado de su CSV. Neptune analizará las cadenas WKT y creará las propiedades geométricas adecuadas:

```
:ID,:LABEL,code:String,city:String,location:Geometry
21,airport,ATL,Atlanta,POINT (-84.42810059 33.63669968)
32,airport,ANC,Anchorage,POINT (-149.9960022 61.17440033)
43,airport,AUS,Austin,POINT (-97.66989899 30.19449997)
```

Para obtener información completa sobre el formato CSV, consulte el formato de carga masiva de [OpenCypher](bulk-load-tutorial-format-opencypher.md).

## Consulta de datos espaciales
<a name="querying-spatial-data"></a>

Los siguientes ejemplos de consultas utilizan el [conjunto de datos de rutas aéreas](https://github.com/krlawrence/graph/tree/main/sample-data) para demostrar cómo utilizar las funciones espaciales en Neptune.

Si los datos tienen propiedades de latitud y longitud independientes en lugar de una propiedad geométrica, puede convertirlos en puntos en el momento de la consulta. Busque los 10 aeropuertos más cercanos a una ubicación determinada:

```
MATCH (a:airport)
WITH a, ST_GeomFromText('POINT (' + a.lon + ' ' + a.lat + ')') AS airportLocation
WITH a, airportLocation, ST_Distance(ST_GeomFromText('POINT (-84.4281 33.6367)'), airportLocation) AS distance
WHERE distance IS NOT NULL
RETURN a.code, a.city, distance
ORDER BY distance ASC
LIMIT 10
```

Si ya tienes ubicaciones almacenadas`ST_Point`, puedes usar esos valores de ubicación directamente:

1. Establecimiento de la propiedad de 

   ```
   MATCH (a:airport)
   SET a.location = ST_GeomFromText('POINT (' + a.lon + ' ' + a.lat + ')')
   ```

1. Consulta mediante ST\$1Distance:

   ```
   MATCH (a:airport)
   WHERE a.location IS NOT NULL
   WITH a, ST_Distance(ST_GeomFromText('POINT (-84.4281 33.6367)'), a.location) AS distance
   RETURN a.code, a.city, distance
   ORDER BY distance ASC
   LIMIT 10
   ```

### Uso del controlador Bolt
<a name="querying-spatial-data-bolt"></a>

La mayoría de los métodos de consulta devuelven los valores geométricos como cadenas WKT, que son legibles por humanos. Si utiliza el controlador Bolt, los valores geométricos se devuelven en formato WKB (conocido binario) para mejorar la eficiencia. Convierta WKB en un objeto geométrico en su aplicación:

```
try (Session session = driver.session()) {
    Result result = session.run("MATCH (n:airport {code: 'ATL'}) RETURN n.location as geom");
    
    Record record = result.single();
    byte[] wkbBytes = record.get("geom").asByteArray();
    
    // Convert WKB to Geometry object using JTS library
    WKBReader wkbReader = new WKBReader();
    Geometry geom = wkbReader.read(wkbBytes);
}
```

# Funciones espaciales
<a name="access-graph-opencypher-22-spatial-functions"></a>

Las siguientes funciones espaciales están disponibles en Neptune OpenCypher para trabajar con tipos de datos geométricos:
+ [ST\$1Point](access-graph-opencypher-22-spatial-functions-st-point.md)
+ [ST\$1 GeomFromText](access-graph-opencypher-22-spatial-functions-st-geomfromtext.md)
+ [ST\$1 AsText](access-graph-opencypher-22-spatial-functions-st-astext.md)
+ [ST\$1 GeometryType](access-graph-opencypher-22-spatial-functions-st-geometrytype.md)
+ [ST\$1Equals](access-graph-opencypher-22-spatial-functions-st-equals.md)
+ [ST\$1Contains](access-graph-opencypher-22-spatial-functions-st-contains.md)
+ [ST\$1Intersects](access-graph-opencypher-22-spatial-functions-st-intersect.md)
+ [ST\$1Distance](access-graph-opencypher-22-spatial-functions-st-distance.md)
+ [ST\$1 DistanceSpheroid](access-graph-opencypher-22-spatial-functions-st-distancespheroid.md)
+ [ST\$1Envelope](access-graph-opencypher-22-spatial-functions-st-envelope.md)
+ [ST\$1Buffer](access-graph-opencypher-22-spatial-functions-st-buffer.md)

# ST\$1Point
<a name="access-graph-opencypher-22-spatial-functions-st-point"></a>

ST\$1Point devuelve un punto a partir de los valores de las coordenadas de entrada.

**Sintaxis**

```
ST_Point(x, y, z)
```

**Argumentos**
+ `x`- Un valor del tipo de dato DOUBLE PRECISION que representa una primera coordenada.
+ `y`- Un valor del tipo de dato DOUBLE PRECISION que representa una segunda coordenada.
+ `z`- (opcional)

**Orden de coordenadas**

Cuando se trabaja con coordenadas geográficas, el primer argumento (`x`) representa la **longitud** y el segundo argumento (`y`) representa la **latitud**. Esto sigue el orden de coordenadas estándar utilizado en las bases de datos espaciales y la norma ISO 19125.

```
// Correct: longitude first, latitude second
ST_Point(-84.4281, 33.6367)  // Atlanta airport

// Incorrect: latitude first, longitude second
ST_Point(33.6367, -84.4281)  // This will return NaN in distance calculations
```

**Intervalos de coordenadas válidos**

En el caso de los datos geográficos, asegúrese de que las coordenadas estén dentro de rangos válidos:
+ Longitud (`x`): de -180 a 180
+ Latitud (`y`): de -90 a 90

Las coordenadas fuera de estos rangos se devolverán `NaN` (no un número) cuando se usen con funciones de cálculo de distancia como`ST_DistanceSpheroid`.

**Tipo de devolución**

GEOMETRÍA del subtipo PUNTO

Si x o y son null, entonces se devuelve el valor null.

**Ejemplos**

A continuación, se construye una geometría de puntos a partir de las coordenadas de entrada.

```
RETURN ST_Point(5.0, 7.0); 
POINT(5 7)
```

# ST\$1 GeomFromText
<a name="access-graph-opencypher-22-spatial-functions-st-geomfromtext"></a>

ST\$1 GeomFromText construye un objeto geométrico a partir de una representación textual (WKT) conocida de una geometría de entrada.

**Sintaxis**

```
ST_GeomFromText(wkt_string)
```

**Argumentos**
+ `wkt_string`- Un valor del tipo de dato STRING que es una representación en WKT de una geometría.

**Tipo de devolución**

GEOMETRY

Si wkt\$1string es nulo, se devuelve el valor nulo.

Si wkt\$1string no es válido, se devuelve a. BadRequestException 

**Ejemplos**

```
RETURN ST_GeomFromText('POLYGON((0 0,0 1,1 1,1 0,0 0))')             
POLYGON((0 0,0 1,1 1,1 0,0 0))
```

# ST\$1 AsText
<a name="access-graph-opencypher-22-spatial-functions-st-astext"></a>

ST\$1 AsText devuelve la conocida representación textual (WKT) de una geometría de entrada.

**Sintaxis**

```
ST_AsText(geo)
```

**Argumentos**
+ `geo`- Un valor del tipo de dato GEOMETRY o una expresión que da como resultado una GEOMETRÍA.

**Tipo de devolución**

STRING

Si geom es nulo, entonces se devuelve el valor nulo.

Si el parámetro de entrada no es una geometría, BadRequestException se devuelve a.

Si el resultado es mayor que una CADENA de 64 KB, se devuelve un error.

**Ejemplos**

```
RETURN ST_AsText(ST_GeomFromText('POLYGON((0 0,0 1,1 1,1 0,0 0))'))             
POLYGON((0 0,0 1,1 1,1 0,0 0))
```

# ST\$1 GeometryType
<a name="access-graph-opencypher-22-spatial-functions-st-geometrytype"></a>

ST\$1 GeometryType devuelve el tipo de geometría en forma de cadena.

**Sintaxis**

```
ST_GeometryType(geom)
```

**Argumentos**
+ `geom`- Un valor del tipo de dato GEOMETRY o una expresión que da como resultado un tipo de GEOMETRÍA.

**Tipo de devolución**

STRING

Si geom es nulo, entonces se devuelve el valor nulo.

Si el parámetro de entrada no es una geometría, BadRequestException se devuelve a.

**Ejemplos**

```
RETURN ST_GeometryType(ST_GeomFromText('LINESTRING(77.29 29.07,77.42 29.26,77.27 29.31,77.29 29.07)'));
ST_LineString
```

# ST\$1Equals
<a name="access-graph-opencypher-22-spatial-functions-st-equals"></a>

ST\$1Equals devuelve el valor true si las proyecciones 2D de las geometrías de entrada son topológicamente iguales. Las geometrías se consideran topológicamente iguales si tienen conjuntos de puntos iguales. En geometrías topológicamente iguales, el orden de los vértices puede diferir y, al mismo tiempo, mantener esta igualdad.

**Sintaxis**

```
ST_Equals(geom1, geom2)
```

**Argumentos**
+ `geom1`- Un valor del tipo de datos GEOMETRY o una expresión que da como resultado un tipo de GEOMETRÍA.
+ `geom2`- Un valor del tipo de dato GEOMETRY o una expresión que da como resultado un tipo de GEOMETRÍA. Este valor se compara con geom1 para determinar si es igual a geom1.

**Tipo de devolución**

BOOLEANO

Si geom1 o geom2 son null, entonces se devuelve el valor null.

Si geom1 o geom2 no son geometrías, se devuelve a. BadRequestException 

**Ejemplos**

```
RETURN ST_Equals(
    ST_GeomFromText('POLYGON ((0 2,1 1,0 -1,0 2))'), 
    ST_GeomFromText('POLYGON((-1 3,2 1,0 -3,-1 3))'));
false
```

Lo siguiente comprueba si las dos cadenas de líneas son geométricamente iguales.

```
RETURN ST_Equals(
    ST_GeomFromText('LINESTRING (1 0, 10 0)'), 
    ST_GeomFromText('LINESTRING(1 0,5 0,10 0)'));
true
```

# ST\$1Contains
<a name="access-graph-opencypher-22-spatial-functions-st-contains"></a>

ST\$1Contains devuelve true si la proyección 2D de la primera geometría de entrada contiene la proyección 2D de la segunda geometría de entrada. La geometría A contiene la geometría B si todos los puntos de B son puntos de A y sus interiores no tienen una intersección vacía. ST\$1Contains (A, B) es equivalente a ST\$1Within (B, A).

**Sintaxis**

```
ST_Contains(geom1, geom2)
```

**Argumentos**
+ `geom1`- Un valor de tipo GEOMETRY o una expresión que da como resultado un tipo de GEOMETRÍA.
+ `geom2`- Un valor de tipo GEOMETRÍA o una expresión que da como resultado un tipo de GEOMETRÍA. Este valor se compara con geom1 para determinar si está contenido dentro de geom1.

**Tipo de devolución**

BOOLEANO

Si geom1 o geom2 son null, entonces se devuelve el valor null.

Si el parámetro de entrada no es una geometría, BadRequestException se devuelve a.

**Ejemplos**

```
RETURN ST_Contains(
    ST_GeomFromText('POLYGON((0 2,1 1,0 -1,0 2))'), 
    ST_GeomFromText('POLYGON((-1 3,2 1,0 -3,-1 3))'));
false
```

# ST\$1Intersects
<a name="access-graph-opencypher-22-spatial-functions-st-intersect"></a>

ST\$1Intersects devuelve true si las proyecciones 2D de las dos geometrías de entrada tienen como mínimo un punto en común.

**Sintaxis**

```
ST_Intersects(geom1, geom2)
```

**Argumentos**
+ `geom1`- Un valor del tipo de dato GEOMETRY o una expresión que da como resultado un tipo de GEOMETRÍA.
+ `geom2`- Un valor del tipo de dato GEOMETRY o una expresión que da como resultado un tipo de GEOMETRÍA.

**Tipo de devolución**

BOOLEANO

Si geom1 o geom2 son null, entonces se devuelve el valor null.

Si el parámetro de entrada no es una geometría, BadRequestException se devuelve a.

**Ejemplos**

```
RETURN ST_Intersects(
    ST_GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0),(2 2,2 5,5 5,5 2,2 2))'), 
    ST_GeomFromText('MULTIPOINT((4 4),(6 6))'));
true
```

# ST\$1Distance
<a name="access-graph-opencypher-22-spatial-functions-st-distance"></a>

En el caso de las geometrías de entrada, ST\$1Distance devuelve la distancia euclidiana mínima entre las proyecciones 2D de los dos valores de geometría de entrada.

**Sintaxis**

```
ST_Distance(geo1, geo2)
```

**Argumentos**
+ `geo1`- Un valor del tipo de dato GEOMETRY o una expresión que dé como resultado un tipo de GEOMETRÍA.
+ `geo2`- Un valor del tipo de dato GEOMETRY o una expresión que dé como resultado una GEOMETRÍA.

**Tipo de devolución**

DOBLE PRECISIÓN en las mismas unidades que las geometrías de entrada.

Si geo1 o geo2 son nulos, se devuelve un valor nulo.

Si el parámetro de entrada no es una geometría, se devuelve a BadRequestException .

**Ejemplos**

```
RETURN ST_Distance(
    ST_GeomFromText('POLYGON((0 2,1 1,0 -1,0 2))'), 
    ST_GeomFromText('POLYGON((-1 -3,-2 -1,0 -3,-1 -3))'));
1.4142135623731
```

# ST\$1 DistanceSpheroid
<a name="access-graph-opencypher-22-spatial-functions-st-distancespheroid"></a>

Devuelve la distancia mínima en metros entre dos lon/lat geometrías. El esferoide es /SRID 4326. WGS84

**Sintaxis**

```
ST_DistanceSpheroid(geom1, geom2);
```

**Argumentos**
+ `geom1`- Un valor del tipo de dato GEOMETRY o una expresión que da como resultado un tipo de GEOMETRÍA.
+ `geom2`- Un valor del tipo de dato GEOMETRY o una expresión que da como resultado un tipo de GEOMETRÍA.

**Tipo de devolución**

FLOAT

Si geom es nulo, entonces se devuelve el valor nulo.

**Ejemplos**

```
RETURN ST_DistanceSpheroid(
    ST_GeomFromText('POINT(-110 42)'),
    ST_GeomFromText('POINT(-118 38)'))
814278.77
```

# ST\$1Envelope
<a name="access-graph-opencypher-22-spatial-functions-st-envelope"></a>

ST\$1Envelope devuelve el cuadro delimitador mínimo de la geometría de entrada, de la siguiente manera:
+ Si la geometría de entrada está vacía, la geometría devuelta será POINT EMPTY.
+ Si el cuadro delimitador mínimo de la geometría de entrada degenera en un punto, la geometría devuelta es un punto.
+ Si nada de lo anterior es verdadero, la función devuelve un counter-clockwise-oriented polígono cuyos vértices son las esquinas del cuadro delimitador mínimo.

Para todas las entradas no vacías, la función opera en la proyección 2D de la geometría de entrada.

**Sintaxis**

```
ST_Envelope(geom)
```

**Argumentos**
+ `geom`- Un valor del tipo de dato GEOMETRÍA o una expresión que da como resultado un tipo de GEOMETRÍA.

**Tipo de devolución**

GEOMETRY

Si geom es nulo, entonces se devuelve el valor nulo.

**Ejemplos**

```
RETURN ST_Envelope(ST_GeomFromText("POLYGON ((2 1, 4 3, 6 1, 5 5, 3 4, 2 1))"))
POLYGON ((2 1, 6 1, 6 5, 2 5, 2 1))
```

# ST\$1Buffer
<a name="access-graph-opencypher-22-spatial-functions-st-buffer"></a>

ST\$1Buffer devuelve una geometría 2D que representa todos los puntos cuya distancia desde la geometría de entrada proyectada en el plano cartesiano XY es menor o igual a la distancia de entrada.

**Sintaxis**

```
ST_Buffer(geom, distance, number_of_segments_per_quarter_circle)
```

**Argumentos**
+ `geom`- Un valor del tipo de dato GEOMETRY o una expresión que da como resultado un tipo de GEOMETRÍA.
+ `distance`- Un valor del tipo de dato DOUBLE PRECISION que representa la distancia (o el radio) del búfer.
+ `number_of_segments_per_quarter_circle`- Un valor del tipo de dato INTEGER (debe ser mayor o igual a 0). Este valor determina el número de puntos que se aproximan a un cuarto de círculo en torno a cada vértice de la geometría de entrada. Los valores negativos son cero de forma predeterminada. El valor predeterminado es 8.

**Tipo de devolución**

GEOMETRY

La función ST\$1Buffer devuelve geometría bidimensional (2D) en el plano cartesiano XY.

**Ejemplos**

```
RETURN ST_Buffer(ST_GeomFromText('LINESTRING (1 2,5 2,5 8)'), 2, 4);
POLYGON ((3 4, 3 8, 3.1522409349774265 8.76536686473018,
         3.585786437626905 9.414213562373096, 4.234633135269821 9.847759065022574,
         5 10, 5.765366864730179 9.847759065022574,
         6.414213562373095 9.414213562373096, 6.847759065022574 8.76536686473018,
         7 8, 7 2, 6.847759065022574 1.2346331352698203,
         6.414213562373095 0.5857864376269051, 5.765366864730179 0.1522409349774265,
         5 0, 1 0, 0.2346331352698193 0.152240934977427,
         -0.4142135623730954 0.5857864376269051,
         -0.8477590650225737 1.2346331352698208, -1 2.0000000000000004,
         -0.8477590650225735 2.7653668647301797,
         -0.4142135623730949 3.414213562373095,
         0.2346331352698206 3.8477590650225735, 1 4, 3 4))
```

A continuación, se devuelve el búfer de la geometría del punto de entrada que se aproxima a un círculo. Dado que el comando especifica 3 como el número de segmentos por cuarto de círculo, la función utiliza tres segmentos para aproximarse al cuarto de círculo.

```
RETURN ST_Buffer(ST_GeomFromText('POINT (1 1)'), 1.0, 8));
POLYGON ((2 1, 1.9807852804032304 0.8049096779838718,
     1.9238795325112867 0.6173165676349102, 1.8314696123025453 0.4444297669803978,
     1.7071067811865475 0.2928932188134525, 1.5555702330196022 0.1685303876974548,
     1.3826834323650898 0.0761204674887133, 1.1950903220161284 0.0192147195967696,
     1 0, 0.8049096779838718 0.0192147195967696, 0.6173165676349103 0.0761204674887133,
    0.444429766980398 0.1685303876974545, 0.2928932188134525 0.2928932188134524,
     0.1685303876974546 0.4444297669803978, 0.0761204674887133 0.6173165676349102,
     0.0192147195967696 0.8049096779838714, 0 0.9999999999999999,
     0.0192147195967696 1.1950903220161284, 0.0761204674887132 1.3826834323650896,
     0.1685303876974545 1.555570233019602, 0.2928932188134523 1.7071067811865475,
     0.4444297669803978 1.8314696123025453, 0.6173165676349097 1.9238795325112865,
     0.8049096779838714 1.9807852804032304, 0.9999999999999998 2,
     1.1950903220161284 1.9807852804032304, 1.38268343236509 1.9238795325112865,
     1.5555702330196017 1.8314696123025453, 1.7071067811865475 1.7071067811865477,
     1.8314696123025453 1.5555702330196022, 1.9238795325112865 1.3826834323650905,
     1.9807852804032304 1.1950903220161286, 2 1))
```

# Acceso al gráfico de Neptune con SPARQL
<a name="access-graph-sparql"></a>

SPARQL es un lenguaje de consulta para el marco de descripción de recursos (RDF), que es un formato de datos de gráficos diseñado para la web. Amazon Neptune es compatible con SPARQL 1.1. Esto significa que puede conectarse a una instancia de base de datos de Neptune y consultar el gráfico utilizando el lenguaje de consulta descrito en la especificación de [SPARQL 1.1 Query Language](https://www.w3.org/TR/sparql11-query/).

 Una consulta en SPARQL se compone de una cláusula `SELECT` para especificar las variables que se devolverán y una cláusula `WHERE` para especificar los datos del gráfico que deben corresponderse. Si no está familiarizado con las consultas SPARQL, consulte la sección sobre [escritura de consultas sencillas](https://www.w3.org/TR/sparql11-query/#WritingSimpleQueries) en la documentación del [lenguaje de consulta SPARQL 1.1](https://www.w3.org/TR/sparql11-query/).

**importante**  
Para cargar datos, `SPARQL UPDATE INSERT` puede funcionar bien para un conjunto de datos pequeño, pero si necesita cargar una cantidad considerable de datos de un archivo, consulte [Uso del programa de carga masiva de Amazon Neptune para adquirir datos](bulk-load.md).

Para obtener más información sobre la información específica de la implementación de SPARQL de Neptune, consulte [Conformidad con los estándares de SPARQL](feature-sparql-compliance.md).

Antes de comenzar, debe disponer de lo siguiente:
+ Una instancia de base de datos de Neptune. Para obtener información acerca de la creación de una instancia de base de datos de Neptune, consulte [Creación de un clúster de Amazon Neptune](get-started-create-cluster.md).
+ Una instancia de Amazon EC2 en la misma nube privada virtual (VPC) que su instancia de base de datos de Neptune.

**Topics**
+ [Uso de la consola RDF4 J para conectarse a una instancia de base de datos de Neptune](access-graph-sparql-rdf4j-console.md)
+ [Uso de RDF4 J Workbench para conectarse a una instancia de base de datos de Neptune](access-graph-sparql-rdf4j-workbench.md)
+ [Uso de Java para conectarse a una instancia de base de datos de Neptune](access-graph-sparql-java.md)
+ [API HTTP de SPARQL](sparql-api-reference.md)
+ [Sugerencias de consulta SPARQL](sparql-query-hints.md)
+ [Comportamiento de DESCRIBE de SPARQL con respecto al gráfico predeterminado](sparql-default-describe.md)
+ [API de estado de la consulta SPARQL](sparql-api-status.md)
+ [Cancelación de consultas SPARQL](sparql-api-status-cancel.md)
+ [Uso del protocolo HTTP de almacén de gráficos (GSP) de SPARQL 1.1 en Amazon Neptune](sparql-graph-store-protocol.md)
+ [Análisis de la ejecución de las consultas de Neptune con `explain` de SPARQL](sparql-explain.md)
+ [Consultas federadas de SPARQL en Neptune mediante la extensión `SERVICE`](sparql-service.md)

# Uso de la consola RDF4 J para conectarse a una instancia de base de datos de Neptune
<a name="access-graph-sparql-rdf4j-console"></a>



La consola RDF4 J le permite experimentar con gráficos y consultas del Resource Description Framework (RDF) en un entorno REPL (read-eval-print bucle). 

Puede añadir una base de datos de gráficos remota como repositorio y consultarla desde la consola RDF4 J. En esta sección se explica la configuración de la consola RDF4 J para conectarse de forma remota a una instancia de base de datos de Neptune.

**Para conectarse a Neptune mediante la consola J RDF4**

1. Descargue el RDF4 J SDK desde la [página de descargas](http://rdf4j.org/download/) del sitio web de RDF4 J.

1. Descomprima el archivo zip del RDF4 J SDK.

1. En una terminal, navegue hasta el directorio RDF4 J SDK y, a continuación, introduzca el siguiente comando para ejecutar la consola RDF4 J:

   ```
   bin/console.sh
   ```

   Debería ver una salida similar a esta:

   ```
   14:11:51.126 [main] DEBUG o.e.r.c.platform.PlatformFactory - os.name = linux
   14:11:51.130 [main] DEBUG o.e.r.c.platform.PlatformFactory - Detected Posix platform
   Connected to default data directory
   RDF4J Console 3.6.1
   
   3.6.1
   Type 'help' for help.
   >
   ```

   Ahora se encuentra en `>`. Esta es la línea de comandos general de la consola RDF4 J. que se utiliza para configurar los repositorios y otras operaciones. Un repositorio tiene su propio símbolo para ejecutar consultas.

1. En el símbolo del sistema de `>`, escriba lo siguiente si quiere crear un repositorio de SPARQL para la instancia de base de datos de Neptune:

    

   ```
   create sparql
   ```

1. La consola RDF4 J le solicita los valores de las variables necesarias para conectarse al punto final de SPARQL.

   ```
   Please specify values for the following variables:
   ```

   Especifique los siguientes valores:    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/neptune/latest/userguide/access-graph-sparql-rdf4j-console.html)

   Para obtener información acerca de cómo encontrar la dirección de la instancia de base de datos de Neptune, consulte la sección [Conexión a los puntos de conexión de Amazon Neptune](feature-overview-endpoints.md).

   Si la operación se realiza correctamente, aparecerá el mensaje siguiente:

    

   ```
   Repository created
   ```

1. En el símbolo del sistema de `>`, escriba lo siguiente para conectarse a la instancia de base de datos de Neptune:

   ```
   open neptune
   ```

   Si la operación se realiza correctamente, aparecerá el mensaje siguiente:

    

   ```
   Opened repository 'neptune'
   ```

   Ahora se encuentra en `neptune>`. Aquí puede ejecutar consultas en el gráfico de Neptune.

    
**nota**  
Una vez que ha añadido el repositorio, la próxima vez que ejecute `bin/console.sh`, podrá ejecutar inmediatamente el comando `open neptune` para conectarse a la instancia de base de datos de Neptune.

1. Cuando se `neptune>` le solicite, introduzca lo siguiente para ejecutar una consulta de SPARQL que devuelva hasta 10 de los triples (subject-predicate-object) del gráfico utilizando la `?s ?p ?o` consulta con un límite de 10. Para otras consultas, sustituya el texto después del comando `sparql` por otra consulta SPARQL.

   ```
   sparql select ?s ?p ?o where {?s ?p ?o} limit 10
   ```

# Uso de RDF4 J Workbench para conectarse a una instancia de base de datos de Neptune
<a name="access-graph-sparql-rdf4j-workbench"></a>

En esta sección, se explica cómo conectarse a una instancia de base de datos de Amazon Neptune mediante RDF4 J Workbench y RDF4 J Server. RDF4Se requiere el servidor J porque actúa como proxy entre el punto final REST HTTP SPARQL de Neptune y RDF4 J Workbench. 

RDF4J Workbench proporciona una interfaz sencilla para experimentar con un gráfico, incluida la carga de archivos locales. Para obtener más información, consulte la [sección Añadir](https://rdf4j.org/documentation/tools/server-workbench/#add) de la documentación de RDF4 J.

**Requisitos previos**  
Antes de comenzar, haga lo siguiente:
+ Instale Java 1.8 o versiones posteriores.
+ Instale RDF4 J Server y RDF4 J Workbench. Para obtener información, consulte [Instalación de RDF4 J Server y RDF4 J Workbench](https://rdf4j.org/documentation/tools/server-workbench/#installing-rdf4j-server-and-rdf4j-workbench).

**Para usar RDF4 J Workbench para conectarse a Neptune**

1. En un navegador web, navegue hasta la URL en la que está implementada la aplicación web RDF4 J Workbench. Por ejemplo, si utiliza Apache Tomcat, la URL es: [https: //:8080/rdf4j-workbench/](http://localhost:8080/rdf4j-workbench/). *ec2\$1hostname*

1. Si se le pide que se **conecte a RDF4 J Server**, compruebe que **RDF4J Server** esté instalado, en ejecución y que la URL del servidor sea correcta. Después, continúe con el siguiente paso.

1. En el panel izquierdo, elija **New repository (Nuevo repositorio)**.

   En **New repository (Nuevo repositorio)**:
   + En la lista desplegable **Type (Tipo)**, elija **SPARQL endpoint proxy (Proxy de punto de enlace de SPARQL)**.
   + En **ID**, escriba **neptune**.
   + En **Título**, escriba **Instancia de base de datos de Neptune**.

   Elija **Siguiente**.

1. En **New repository (Nuevo repositorio)**:
   + En **SPARQL query endpoint URL (URL de punto de enlace de consulta de SPARQL)**, escriba `https://your-neptune-endpoint:port/sparql`.
   + En **SPARQL update endpoint URL (URL de punto de enlace de actualización de SPARQL)**, escriba `https://your-neptune-endpoint:port/sparql`.

   Para obtener información acerca de cómo encontrar la dirección de la instancia de base de datos de Neptune, consulte la sección [Conexión a los puntos de conexión de Amazon Neptune](feature-overview-endpoints.md). 

   Seleccione **Crear**.

1. El repositorio de **neptune** aparece ahora en la lista de repositorios. Puede que tenga que esperar unos minutos antes de poder usar el nuevo repositorio.

1. En la columna **Id** de la tabla, elija el enlace **neptune**.

1. En el panel izquierdo, elija **Query (Consulta)**. 

    
**nota**  
Si los elementos del menú de **Explore** están deshabilitados, es posible que deba volver a conectarse al servidor RDF4 J y volver a elegir el repositorio **Neptune**.  
Para ello, puede usar los enlaces **[change] ([cambiar])** en la esquina superior derecha.

1. En el campo de consulta, escriba la siguiente consulta SPARQL y, a continuación, elija **Execute (Ejecutar)**.

    

   ```
   select ?s ?p ?o where {?s ?p ?o} limit 10
   ```

    

El ejemplo anterior devuelve hasta 10 de los triples (subject-predicate-object) del gráfico mediante la `?s ?p ?o` consulta con un límite de 10. 

# Uso de Java para conectarse a una instancia de base de datos de Neptune
<a name="access-graph-sparql-java"></a>

En esta sección, se indica cómo ejecutar un ejemplo de Java completo que se conecta a una instancia de base de datos de Amazon Neptune y realiza una consulta SPARQL.

Siga estas instrucciones desde una instancia de Amazon EC2 que esté en la misma nube privada virtual (VPC) que su instancia de base de datos de Neptune.

**Para conectarse a Neptune mediante Java**

1. Instale Apache Maven en la instancia EC2. Si utiliza Amazon Linux 2023 (preferido), utilice:

   ```
   sudo dnf update -y
   sudo dnf install maven -y
   ```

   Si utiliza Amazon Linux 2, descargue el archivo binario más reciente desde [https://maven.apache.org/download.cgi:](https://maven.apache.org/download.cgi:)

   ```
   sudo yum remove maven -y
   wget https://dlcdn.apache.org/maven/maven-3/ <version>/binaries/apache-maven-<version>-bin.tar.gz
   sudo tar -xzf apache-maven-<version>-bin.tar.gz -C /opt/
   sudo ln -sf /opt/apache-maven-<version> /opt/maven
   echo 'export MAVEN_HOME=/opt/maven' >> ~/.bashrc
   echo 'export PATH=$MAVEN_HOME/bin:$PATH' >> ~/.bashrc
   source ~/.bashrc
   ```

1. Este ejemplo solo se ha probado con Java 8. Escriba lo siguiente para instalar Java 8 en la instancia EC2:

   ```
   sudo yum install java-1.8.0-devel
   ```

1. Escriba lo siguiente para establecer Java 8 como tiempo de ejecución predeterminado en la instancia EC2:

   ```
   sudo /usr/sbin/alternatives --config java
   ```

   Cuando se le solicite, escriba el número para Java 8.

1. Escriba lo siguiente para establecer Java 8 como compilador predeterminado en la instancia EC2: 

   ```
   sudo /usr/sbin/alternatives --config javac
   ```

   Cuando se le solicite, escriba el número para Java 8.

1. En un directorio nuevo, cree un archivo `pom.xml` y, a continuación, ábralo en un editor de texto.

1. Copie lo siguiente en el archivo `pom.xml` y guárdelo (normalmente puede ajustar los números de versión a la última versión estable):

   ```
   <project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.amazonaws</groupId>
     <artifactId>RDFExample</artifactId>
     <packaging>jar</packaging>
     <version>1.0-SNAPSHOT</version>
     <name>RDFExample</name>
     <url>https://maven.apache.org</url>
     <dependencies>
       <dependency>
         <groupId>org.eclipse.rdf4j</groupId>
         <artifactId>rdf4j-runtime</artifactId>
         <version>3.6</version>
       </dependency>
     </dependencies>
     <build>
       <plugins>
         <plugin>
             <groupId>org.codehaus.mojo</groupId>
             <artifactId>exec-maven-plugin</artifactId>
             <version>1.2.1</version>
             <configuration>
               <mainClass>com.amazonaws.App</mainClass>
             </configuration>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <configuration>
             <source>1.8</source>
             <target>1.8</target>
           </configuration>
         </plugin>
       </plugins>
     </build>
   </project>
   ```
**nota**  
Si está modificando un proyecto de Maven ya existente, la dependencia requerida aparece resaltada en el código anterior.

1. Escriba lo siguiente en la línea de comandos a fin de crear subdirectorios para el código fuente de ejemplo (`src/main/java/com/amazonaws/`):

   ```
   mkdir -p src/main/java/com/amazonaws/
   ```

1. En el directorio `src/main/java/com/amazonaws/`, cree un archivo llamado `App.java` y, a continuación, ábralo en un editor de texto.

1. Copie lo siguiente en el archivo `App.java`. *your-neptune-endpoint*Sustitúyala por la dirección de la instancia de base de datos de Neptune.
**nota**  
Para obtener información acerca de cómo encontrar el nombre de host de la instancia de base de datos de Neptune, consulte la sección [Conexión a los puntos de conexión de Amazon Neptune](feature-overview-endpoints.md). 

   ```
   package com.amazonaws;
   
   import org.eclipse.rdf4j.repository.Repository;
   import org.eclipse.rdf4j.repository.http.HTTPRepository;
   import org.eclipse.rdf4j.repository.sparql.SPARQLRepository;
   
   import java.util.List;
   import org.eclipse.rdf4j.RDF4JException;
   import org.eclipse.rdf4j.repository.RepositoryConnection;
   import org.eclipse.rdf4j.query.TupleQuery;
   import org.eclipse.rdf4j.query.TupleQueryResult;
   import org.eclipse.rdf4j.query.BindingSet;
   import org.eclipse.rdf4j.query.QueryLanguage;
   import org.eclipse.rdf4j.model.Value;
   
   public class App
   {
       public static void main( String[] args )
       {
           String sparqlEndpoint = "https://your-neptune-endpoint:port/sparql";
           Repository repo = new SPARQLRepository(sparqlEndpoint);
           repo.initialize();
   
           try (RepositoryConnection conn = repo.getConnection()) {
              String queryString = "SELECT ?s ?p ?o WHERE { ?s ?p ?o } limit 10";
   
              TupleQuery tupleQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, queryString);
   
              try (TupleQueryResult result = tupleQuery.evaluate()) {
                 while (result.hasNext()) {  // iterate over the result
                      BindingSet bindingSet = result.next();
   
                      Value s = bindingSet.getValue("s");
                      Value p = bindingSet.getValue("p");
                      Value o = bindingSet.getValue("o");
   
                      System.out.print(s);
                      System.out.print("\t");
                      System.out.print(p);
                      System.out.print("\t");
                      System.out.println(o);
                 }
              }
           }
       }
   }
   ```

1. Use el siguiente comando Maven para compilar y ejecutar el ejemplo:

   ```
   mvn compile exec:java
   ```

El ejemplo anterior devuelve hasta 10 de los triples (subject-predicate-object) del gráfico utilizando la `?s ?p ?o` consulta con un límite de 10. Para otras consultas, sustitúyala por otra consulta SPARQL.

La iteración de los resultados en el ejemplo imprime el valor de cada variable devuelta. El objeto `Value` se convierte en `String` y después se imprime. Si cambia la parte `SELECT` de la consulta, debe modificar el código.

# API HTTP de SPARQL
<a name="sparql-api-reference"></a>

Las solicitudes HTTP de SPARQL se aceptan en el siguiente punto de enlace: `https://your-neptune-endpoint:port/sparql`

Para obtener más información acerca de la conexión a Amazon Neptune con SPARQL, consulte [Acceso al gráfico de Neptune con SPARQL](access-graph-sparql.md).

Para obtener más información acerca del lenguaje de consulta y el protocolo SPARQL, consulte la especificación sobre el [protocolo SPARQL 1.1](https://www.w3.org/TR/sparql11-protocol/#protocol) y el [lenguaje de consulta SPARQL 1.1](https://www.w3.org/TR/sparql11-query/).

En los siguientes temas, se proporciona información acerca de los formatos de serialización RDF y SPARQL y cómo utilizar la API HTTP de SPARQL con Neptune.

**Contents**
+ [Uso del punto de conexión HTTP REST para conectarse a una instancia de base de datos de Neptune](access-graph-sparql-http-rest.md)
+ [Encabezados finales HTTP opcionales para las respuestas de SPARQL de varias partes](access-graph-sparql-http-trailing-headers.md)
+ [Tipos de medios de RDF utilizados por SPARQL en Neptune](sparql-media-type-support.md)
  + [Formatos de serialización de RDF utilizados por SPARQL para Neptune](sparql-media-type-support.md#sparql-serialization-formats)
  + [Formatos de serialización de resultados de SPARQL utilizados en Neptune SPARQL](sparql-media-type-support.md#sparql-serialization-formats-neptune-output)
  + [Tipos de medios que Neptune puede utilizar para importar datos de RDF](sparql-media-type-support.md#sparql-serialization-formats-input)
  + [Tipos de medios que Neptune puede utilizar para exportar los resultados de las consultas](sparql-media-type-support.md#sparql-serialization-formats-output)
+ [Uso de SPARQL UPDATE LOAD para importar datos a Neptune](sparql-api-reference-update-load.md)
+ [Uso de SPARQL UPDATE UNLOAD para eliminar datos de Neptune](sparql-api-reference-unload.md)

# Uso del punto de conexión HTTP REST para conectarse a una instancia de base de datos de Neptune
<a name="access-graph-sparql-http-rest"></a>

**nota**  
Actualmente, Neptune no admite HTTP/2 para las solicitudes de la API REST. Los clientes deben usar HTTP/1.1 al conectarse a los puntos finales.

Las siguientes instrucciones le ayudarán a conectar con el punto de enlace de SPARQL mediante el comando **curl** a través de HTTPS y mediante la sintaxis HTTP. Siga estas instrucciones desde una instancia de Amazon EC2 que esté en la misma nube privada virtual (VPC) que su instancia de base de datos de Neptune.

El punto de conexión HTTP para las consultas de SPARQL a una instancia de base de datos de Neptune es: `https://your-neptune-endpoint:port/sparql`.

**nota**  
Para obtener información acerca de cómo encontrar el nombre de host de la instancia de base de datos de Neptune, consulte la sección [Conexión a los puntos de conexión de Amazon Neptune](feature-overview-endpoints.md).

Amazon Neptune proporciona un punto de conexión HTTP para las consultas SPARQL. La interfaz de REST es compatible con SPARQL versión 1.1.

**QUERY con HTTP POST**  
En el siguiente ejemplo se utiliza **curl** para enviar una **`QUERY`** de SPARQL a través de HTTP **POST**.

```
curl -X POST --data-binary 'query=select ?s ?p ?o where {?s ?p ?o} limit 10' https://your-neptune-endpoint:port/sparql
```

El ejemplo anterior devuelve hasta 10 de los triples (subject-predicate-object) del gráfico mediante la `?s ?p ?o` consulta con un límite de 10. Para otras consultas, sustitúyalo por otra consulta SPARQL.

**nota**  
El tipo de medio MIME predeterminado de una respuesta es `application/sparql-results+json` para las consultas `SELECT` y `ASK`.  
El tipo MIME predeterminado de una respuesta es `application/n-quads` para las consultas `CONSTRUCT` y `DESCRIBE`.  
Para obtener una lista de los tipos de medios que utiliza Neptune para la serialización, consulte [Formatos de serialización de RDF utilizados por SPARQL para Neptune](sparql-media-type-support.md#sparql-serialization-formats).

**UPDATE con HTTP POST**  
En el siguiente ejemplo se utiliza **curl** para enviar una **`UPDATE`** de SPARQL a través de HTTP **POST**.

```
curl -X POST --data-binary 'update=INSERT DATA { <https://test.com/s> <https://test.com/p> <https://test.com/o> . }' https://your-neptune-endpoint:port/sparql
```

El ejemplo anterior inserta el siguiente triple en el gráfico predeterminado de SPARQL: `<https://test.com/s> <https://test.com/p> <https://test.com/o>`

# Encabezados finales HTTP opcionales para las respuestas de SPARQL de varias partes
<a name="access-graph-sparql-http-trailing-headers"></a>

La respuesta HTTP a las consultas y actualizaciones de SPARQL suele devolverse en más de una parte o en fragmentos. Puede resultar difícil diagnosticar un error que se produce después de que una consulta o actualización comience a enviar estos fragmentos, especialmente si se tiene en cuenta que el primero llega con un código de estado HTTP de `200`.

A menos que solicite explícitamente los encabezados finales, Neptune solo informa de este error añadiendo un mensaje de error al cuerpo del mensaje, que suele estar dañado.

Para facilitar la detección y el diagnóstico de este tipo de problemas, puede incluir un encabezado final de codificación por transferencia (TE) (`te: trailers`) en su solicitud (consulte, por ejemplo, la [página de MDN sobre los encabezados de solicitudes de TE](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/TE)). Si lo hace, Neptune incluirá dos nuevos campos de encabezados dentro de los encabezados finales de los fragmentos de respuesta:
+ `X-Neptune-Status`: contiene el código de respuesta seguido de un nombre abreviado. Por ejemplo, en caso de que se realizara correctamente, el encabezado final sería: `X-Neptune-Status: 200 OK`. En caso de fallo, el código de respuesta sería uno de los [códigos de error del motor de Neptune](errors-engine-codes.md), como `X-Neptune-Status: 500 TimeLimitExceededException`.
+ `X-Neptune-Detail`: está vacío si las solicitudes se han realizado correctamente. En caso de errores, contiene el mensaje de error JSON. Como solo se permiten caracteres ASCII en los valores de los encabezados HTTP, la cadena JSON está codificada en URL. El mensaje de error también se sigue adjuntando al cuerpo del mensaje de respuesta.

# Tipos de medios de RDF utilizados por SPARQL en Neptune
<a name="sparql-media-type-support"></a>

Los datos del marco de descripción de recursos (RDF) se pueden serializar de varias maneras diferentes, pudiendo SPARQL producir o consumir la mayoría:

## Formatos de serialización de RDF utilizados por SPARQL para Neptune
<a name="sparql-serialization-formats"></a>
+ **RDF/XML**: serialización de XML de RDF, definida en [RDF 1.1 XML Syntax](https://www.w3.org/TR/rdf-syntax-grammar/). Tipo de medios:`application/rdf+xml`. Extensión de archivo típica: `.rdf`.
+ **N-Triples**: un formato basado en líneas no cifrado para codificar un gráfico de RDF, definido en [RDF 1.1 N-Triples](https://www.w3.org/TR/n-triples/). Tipo de medios: `application/n-triples`, `text/turtle` o `text/plain`. Extensión de archivo típica: `.nt`.
+ **N-Quads**: un formato basado en líneas no cifrado para codificar un gráfico de RDF, definido en [RDF 1.1 N-Quads](https://www.w3.org/TR/n-quads/). Es una extensión de N-Triples. Tipo de medios: `application/n-quads` o `text/x-nquads` cuando se codifique con US-ASCII de 7 bits. Extensión de archivo típica: `.nq`.
+ **Turtle**: una sintaxis textual para RDF definida en [RDF 1.1 Turtle](https://www.w3.org/TR/turtle/) que permite que un gráfico RDF se escriba por completo en un formato de texto compacto y natural, con abreviaturas para los patrones de uso y tipos de datos comunes. Turtle proporciona niveles de compatibilidad con el formato N-Triples además de con la sintaxis del patrón triple de SPARQL. Tipo de medios: `text/turtle` Extensión de archivo típica: `.ttl`.
+ **TriG**: una sintaxis textual para RDF definida en [RDF 1.1 TriG](https://www.w3.org/TR/trig/) que permite que un gráfico RDF se escriba por completo en un formato de texto compacto y natural, con abreviaturas para los patrones de uso y tipos de datos comunes. TriG es una extensión del formato Turtle. Tipo de medios:`application/trig`. Extensión de archivo típica: `.trig`.
+ **N3 (Notation3**: un lenguaje de lógica y de aserción definido en [Notation3 (N3): A readable RDF syntax](https://www.w3.org/TeamSubmission/n3/). N3 amplía el modelo de datos de RDF al añadir fórmulas (literales que son gráficos en sí mismos), variables, implicación lógica, predicados funcionales y ofrece una sintaxis textual alternativa a RDF/XML. Tipo de medios:`text/n3`. Extensión de archivo típica: `.n3`.
+ **JSON-LD**: una serialización de datos y un formato de mensajería definido en [JSON-LD 1.0](https://www.w3.org/TR/json-ld/). Tipo de medios: `application/ld+json`. Extensión de archivo típica: `.jsonld`.
+ **TriX**: una serialización de RDF en XML, definida en [TriX: RDF Triples in XML](https://www.hpl.hp.com/techreports/2004/HPL-2004-56.html). Tipo de medios:`application/trix`. Extensión de archivo típica: `.trix`.
+ **Resultados de JSON para SPARQL**: una serialización de RDF utilizando [SPARQL 1.1 Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json). Tipo de medios:`application/sparql-results+json`. Extensión de archivo típica: `.srj`.
+ **RDF4Formato binario J: formato** binario para codificar datos RDF, documentado en formato [RDF binario RDF4 J.](https://rdf4j.org/documentation/reference/rdf4j-binary) Tipo de medios:`application/x-binary-rdf`.

## Formatos de serialización de resultados de SPARQL utilizados en Neptune SPARQL
<a name="sparql-serialization-formats-neptune-output"></a>
+ **Resultados de XML para SPARQL**: un formato XML para los formatos de resultados booleanos y vinculantes variables ofrecidos por el lenguaje de consulta SPARQL, definido en [SPARQL Query Results XML Format (Second Edition)](https://www.w3.org/TR/rdf-sparql-XMLres/). Tipo de medios:`application/sparql-results+xml`. Extensión de archivo típica: `.srx`.
+ **Resultados de CSV y TSV para SPARQL**: el uso de los valores separados por comas y los valores separados por tabulaciones para expresar los resultados de la consulta de SPARQL de las consultas de `SELECT`, definidas en [SPARQL 1.1 Query Results CSV and TSV Formats](https://www.w3.org/TR/sparql11-results-csv-tsv/). Tipo de medios: `text/csv` para valores separados por comas y `text/tab-separated-values` para valores separados por tabulaciones. Extensiones de archivo típicas: `.csv` para los valores separados por comas y `.tsv` para los valores separados por tabulaciones.
+ **Tabla de resultados binarios**: un formato binario para codificar la salida de las consultas SPARQL. Tipo de medios:`application/x-binary-rdf-results-table`.
+ **Resultados de JSON para SPARQL**: una serialización de RDF utilizando [SPARQL 1.1 Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json/). Tipo de medios:`application/sparql-results+json`.

## Tipos de medios que Neptune puede utilizar para importar datos de RDF
<a name="sparql-serialization-formats-input"></a>

**Tipos de medios que admite el [programa de carga masivo de Neptune](bulk-load.md)**
+ [N-Triples](https://www.w3.org/TR/n-triples/)
+ [N-Quads](https://www.w3.org/TR/n-quads/)
+ [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/)
+ [Turtle](https://www.w3.org/TR/turtle/)

**Tipos de medios que puede importar SPARQL UPDATE LOAD**
+ [N-Triples](https://www.w3.org/TR/n-triples/)
+ [N-Quads](https://www.w3.org/TR/n-quads/)
+ [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/)
+ [Turtle](https://www.w3.org/TR/turtle/)
+ [TriG](https://www.w3.org/TR/trig/)
+ [N3](https://www.w3.org/TeamSubmission/n3/)
+ [JSON-LD](https://www.w3.org/TR/json-ld/)

## Tipos de medios que Neptune puede utilizar para exportar los resultados de las consultas
<a name="sparql-serialization-formats-output"></a>

Para especificar el formato de salida para la respuesta de una consulta SPARQL, envíe un encabezado`"Accept: media-type"` con la respuesta de la consulta. Por ejemplo:

```
curl -H "Accept: application/nquads" ...
```

**Tipos de medios de RDF que SPARQL SELECT puede producir desde Neptune**
+ [Resultados de JSON para SPARQL](https://www.w3.org/TR/sparql11-results-json) (Esta es la opción predeterminada)
+ [Resultados de XML para SPARQL](https://www.w3.org/TR/rdf-sparql-XMLres/)
+ **Tabla de resultados binarios** (tipo de medios: `application/x-binary-rdf-results-table`)
+ [Valores separados por comas (CSV)](https://www.w3.org/TR/sparql11-results-csv-tsv/)
+ [TSV (valores separados por tabulaciones)](https://www.w3.org/TR/sparql11-results-csv-tsv/)

**Tipos de medios de RDF que SPARQL ASK puede producir desde Neptune**
+ [Resultados de JSON para SPARQL](https://www.w3.org/TR/sparql11-results-json) (Esta es la opción predeterminada)
+ [Resultados de XML para SPARQL](https://www.w3.org/TR/rdf-sparql-XMLres/)
+ **Booleano** (tipo de medios: `text/boolean`, significa “verdadero” o “falso”)

**Tipos de medios de RDF que SPARQL CONSTRUCT puede producir desde Neptune**
+ [N-Quads](https://www.w3.org/TR/n-quads/) (Esta es la opción predeterminada)
+ [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/)
+ [JSON-LD](https://www.w3.org/TR/json-ld/)
+ [N-Triples](https://www.w3.org/TR/n-triples/)
+ [Turtle](https://www.w3.org/TR/turtle/)
+ [N3](https://www.w3.org/TeamSubmission/n3/)
+ [TriX](https://www.hpl.hp.com/techreports/2004/HPL-2004-56.html)
+ [TriG](https://www.w3.org/TR/trig/)
+ [Resultados de JSON para SPARQL](https://www.w3.org/TR/sparql11-results-json)
+ [RDF4Formato RDF binario J](https://rdf4j.org/documentation/reference/rdf4j-binary)

**Tipos de medios de RDF que SPARQL DESCRIBE puede producir desde Neptune**
+ [N-Quads](https://www.w3.org/TR/n-quads/) (Esta es la opción predeterminada)
+ [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/)
+ [JSON-LD](https://www.w3.org/TR/json-ld/)
+ [N-Triples](https://www.w3.org/TR/n-triples/)
+ [Turtle](https://www.w3.org/TR/turtle/)
+ [N3](https://www.w3.org/TeamSubmission/n3/)
+ [TriX](https://www.hpl.hp.com/techreports/2004/HPL-2004-56.html)
+ [TriG](https://www.w3.org/TR/trig/)
+ [Resultados de JSON para SPARQL](https://www.w3.org/TR/sparql11-results-json)
+ [RDF4Formato RDF binario J](https://rdf4j.org/documentation/reference/rdf4j-binary)

# Uso de SPARQL UPDATE LOAD para importar datos a Neptune
<a name="sparql-api-reference-update-load"></a>

La sintaxis del comando UPDATE LOAD de SPARQL se especifica en la [recomendación de actualización de SPARQL 1.1](https://www.w3.org/TR/sparql11-update/#load):

```
LOAD SILENT (URL of data to be loaded) INTO GRAPH (named graph into which to load the data)
```
+ **`SILENT`**: (*opcional*) hace que la operación se realice correctamente aunque se haya producido un error durante el procesamiento.

  Puede resultar útil cuando una sola transacción contiene varias instrucciones, como `"LOAD ...; LOAD ...; UNLOAD ...; LOAD ...;"`, y si desea que la transacción se complete aunque algunos de los datos remotos no se hayan podido procesar.
+ *URL of data to be loaded*— (*Obligatorio*) Especifica un archivo de datos remoto que contiene los datos que se van a cargar en un gráfico.

  El archivo remoto debe tener una de las siguientes extensiones:
  + `.nt`para NTriples.
  + `.nq`para NQuads.
  + `.trig` para Trig.
  + `.rdf` para RDF/XML.
  + `.ttl` para Turtle.
  + `.n3` para N3.
  + `.jsonld` para JSON-LD.
+ **`INTO GRAPH`***(named graph into which to load the data)*— (*Opcional*) Especifica el gráfico en el que se deben cargar los datos.

  Neptune asocia cada triple con un gráfico con nombre. Puede especificar el gráfico con nombre predeterminado utilizando el URI de gráfico con nombre de reserva, `http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph`, de la siguiente manera:

  ```
  INTO GRAPH <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph>
  ```

**nota**  
Cuando necesite cargar muchos datos, le recomendamos que utilice el programa de carga masiva de Neptune en lugar de UPDATE LOAD. Para obtener más información acerca del programa de carga masiva, consulte [Uso del programa de carga masiva de Amazon Neptune para adquirir datos](bulk-load.md).

Puede utilizar `SPARQL UPDATE LOAD` para cargar datos directamente desde Amazon S3 o desde archivos obtenidos de un servidor web con alojamiento propio. Los recursos que se cargarán deben residir en la misma región que el servidor de Neptune y el punto de conexión de los recursos debe estar permitido en la VPC. Para obtener información sobre cómo crear un punto de conexión de Amazon S3, consulte [Creación de un punto de conexión de VPC de Amazon S3](bulk-load-data.md#bulk-load-prereqs-s3).

Todo `SPARQL UPDATE LOAD` URIs debe empezar con`https://`. Esto incluye Amazon S3 URLs.

En contraste con el programa de carga masiva de Neptune, una llamada a `SPARQL UPDATE LOAD` es completamente transaccional.

**Carga de archivos directamente desde Amazon S3 en Neptune con SPARQL UPDATE LOAD**

Dado que Neptune no le permite pasar un rol de IAM a Amazon S3 al usar SPARQL UPDATE LOAD, el bucket de Amazon S3 en cuestión debe ser público o debe usar una [URL prefirmada de Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html) en la consulta LOAD.

Para generar una URL prefirmada para un archivo de Amazon S3, puede utilizar un AWS CLI comando como este:

```
aws s3 presign --expires-in (number of seconds) s3://(bucket name)/(path to file of data to load)
```

A continuación, puede utilizar la URL prefirmada resultante en su comando de la `LOAD`:

```
curl https://(a Neptune endpoint URL):8182/sparql \
  --data-urlencode 'update=load (pre-signed URL of the remote Amazon S3 file of data to be loaded) \
                           into graph (named graph)'
```

Para obtener más información, consulte la sección sobre [autenticación de solicitudes: uso de parámetros de consulta](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html). La [documentación de Boto3](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-presigned-urls.html) muestra cómo usar un script de Python para generar una URL prefirmada.

además, el tipo de contenido de los archivos se cargarán se debe configurar correctamente.

1. Establezca el tipo de contenido de los archivos cuando los cargue en Amazon S3 con el parámetro `-metadata` de esta manera:

   ```
   aws s3 cp test.nt s3://bucket-name/my-plain-text-input/test.nt --metadata Content-Type=text/plain
   aws s3 cp test.rdf s3://bucket-name/my-rdf-input/test.rdf --metadata Content-Type=application/rdf+xml
   ```

1. Confirme que la información del tipo de medios está presente. Ejecuta:

   ```
   curl -v bucket-name/folder-name
   ```

   El resultado de este comando debería mostrar la información del tipo de medios que configura cuando carga los archivos.

1. A continuación puede utilizar el comando de la `SPARQL UPDATE LOAD` para importar estos archivos a Neptune:

   ```
   curl https://your-neptune-endpoint:port/sparql \
     -d "update=LOAD <https://s3.amazonaws.com/bucket-name/my-rdf-input/test.rdf>"
   ```

Los pasos anteriores solo funcionan para un bucket de Amazon S3 público o para un bucket al que se accede mediante una [URL de Amazon S3 prefirmada](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html) en la consulta LOAD.

 También puede configurar un servidor proxy web para cargarlo desde un bucket de Amazon S3 privado, como se muestra a continuación:

**Uso de un servidor web para cargar archivos en Neptune con SPARQL UPDATE LOAD**

1. Instale un servidor web en una maquina que se ejecute en la VPC que hospeda Neptune y los archivos que se cargarán. Por ejemplo, si utiliza Amazon Linux, puede instalar Apache de la siguiente manera:

   ```
   sudo yum install httpd mod_ssl
   sudo /usr/sbin/apachectl start
   ```

1. Defina los tipos MIME del contenido del archivo de RDF que va a cargar. SPARQL utiliza el encabezado `Content-type` que envía el servidor web para determinar el formato de entrada del contenido, por lo tanto debe definir los tipos MIME relevantes para el servidor web.

   Por ejemplo, suponga que utiliza las siguientes extensiones de archivo para identificar formatos de archivo:
   + `.nt`para. NTriples
   + `.nq`para NQuads.
   + `.trig` para Trig.
   + `.rdf` para RDF/XML.
   + `.ttl` para Turtle.
   + `.n3` para N3.
   + `.jsonld` para JSON-LD.

   Si utiliza Apache 2 como servidor web, editaría el archivo `/etc/mime.types` y agregaría los siguientes tipos:

   ```
    text/plain nt
    application/n-quads nq
    application/trig trig
    application/rdf+xml rdf
    application/x-turtle ttl
    text/rdf+n3 n3
    application/ld+json jsonld
   ```

1. Confirme que la asignación del tipo MIME funciona. Una vez que tenga su servidor web en funcionamiento y hospede archivos RDF con el formato de su elección, puede probar la configuración enviando una solicitud al servidor web desde su host local.

   Por ejemplo, podría enviar una solicitud como esta:

   ```
   curl -v http://localhost:80/test.rdf
   ```

   Después, en la salida detallada de `curl`, debería ver una línea como:

   ```
   Content-Type: application/rdf+xml
   ```

   Esto muestra que la asignación del tipo de contenido se definió con éxito.

1. Ahora está listo para cargar datos utilizando el comando SPARQL UDPATE:

   ```
   curl https://your-neptune-endpoint:port/sparql \
       -d "update=LOAD <http://web_server_private_ip:80/test.rdf>"
   ```

**nota**  
El uso de `SPARQL UPDATE LOAD` puede desencadenar un tiempo de espera en el servidor web cuando el archivo de origen que se está cargando es grande. Neptune procesa los datos del archivo a medida que se transmiten y para un archivo grande puede tardar un tiempo superior al tiempo de espera configurado en el servidor. Esto, a su vez, puede hacer que el servidor cierre la conexión, lo que puede dar lugar al siguiente mensaje de error cuando Neptune encuentra un EOF inesperado en la secuencia:  

```
{
  "detailedMessage":"Invalid syntax in the specified file",
  "code":"InvalidParameterException"
}
```
Si recibe este mensaje y no cree que el archivo de origen contenga sintaxis no válida, pruebe a aumentar la configuración de tiempo de espera en el servidor web. También puede diagnosticar el problema habilitando los registros de depuración en el servidor y buscando los tiempos de espera.

# Uso de SPARQL UPDATE UNLOAD para eliminar datos de Neptune
<a name="sparql-api-reference-unload"></a>

Neptune también proporciona una operación SPARQL personalizada, `UNLOAD`, para eliminar los datos que se especifican en un origen remoto. `UNLOAD` puede considerarse como una contrapartida de la operación `LOAD`. Su sintaxis es la siguiente:

```
UNLOAD SILENT (URL of the remote data to be unloaded) FROM GRAPH (named graph from which to remove the data)
```
+ **`SILENT`**: (*opcional*) hace que la operación se realice correctamente aunque se haya producido un error al procesar los datos.

  Puede resultar útil cuando una sola transacción contiene varias instrucciones, como `"LOAD ...; LOAD ...; UNLOAD ...; LOAD ...;"`, y si desea que la transacción se complete aunque algunos de los datos remotos no se hayan podido procesar.
+ *URL of the remote data to be unloaded*— (*Obligatorio*) Especifica un archivo de datos remoto que contiene datos que se van a descargar de un gráfico.

  El archivo remoto debe tener una de las siguientes extensiones (son los mismos formatos que admite UPDATE-LOAD):
  + `.nt`para. NTriples
  + `.nq`para NQuads.
  + `.trig` para Trig.
  + `.rdf` para RDF/XML.
  + `.ttl` para Turtle.
  + `.n3` para N3.
  + `.jsonld` para JSON-LD.

  La operación `UNLOAD` eliminará todos los datos que contiene este archivo del clúster de base de datos.

  Cualquier autenticación de Amazon S3 debe incluirse en la URL para que se descarguen los datos. Puede prefirmar un archivo de Amazon S3 y, a continuación, utilizar la URL resultante para acceder a él de forma segura. Por ejemplo:

  ```
  aws s3 presign --expires-in (number of seconds) s3://(bucket name)/(path to file of data to unload)
  ```

  Después:

  ```
  curl https://(a Neptune endpoint URL):8182/sparql \
    --data-urlencode 'update=unload (pre-signed URL of the remote Amazon S3 data to be unloaded) \
                             from graph (named graph)'
  ```

  Para obtener más información, consulte la sección sobre [autenticación de solicitudes: uso de parámetros de consulta](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html).
+ **`FROM GRAPH `***(named graph from which to remove the data)*— (*Opcional*) Especifica el gráfico con nombre del que se deben descargar los datos remotos.

  Neptune asocia cada triple con un gráfico con nombre. Puede especificar el gráfico con nombre predeterminado utilizando el URI de gráfico con nombre de reserva, `http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph`, de la siguiente manera:

  ```
  FROM GRAPH <http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph>
  ```

De la misma manera que `LOAD` se corresponde con `INSERT DATA { (inline data) }`, `UNLOAD` se corresponde con `DELETE DATA { (inline data) }`. Al igual que `DELETE DATA`, `UNLOAD` no funciona con datos que contienen nodos en blanco.

Por ejemplo, si un servidor web local publica un archivo denominado `data.nt` que contiene los dos triples siguientes:

```
<http://example.org/resource#a> <http://example.org/resource#p> <http://example.org/resource#b> .
<http://example.org/resource#a> <http://example.org/resource#p> <http://example.org/resource#c> .
```

El siguiente comando `UNLOAD` eliminaría esos dos triples del gráfico con nombre, `<http://example.org/graph1>`.

```
UNLOAD <http://localhost:80/data.nt> FROM GRAPH <http://example.org/graph1>
```

Esto tendría el mismo efecto que usar el siguiente comando `DELETE DATA`:

```
DELETE DATA {
  GRAPH <http://example.org/graph1> {
    <http://example.org/resource#a> <http://example.org/resource#p> <http://example.org/resource#b> .
    <http://example.org/resource#a> <http://example.org/resource#p> <http://example.org/resource#c> .
  }
}
```

**Excepciones generadas por el comando `UNLOAD`**
+ **`InvalidParameterException`**: había nodos en blanco en los datos. *Estado HTTP*: solicitud incorrecta 400.

  *Mensaje*: ` Blank nodes are not allowed for UNLOAD`

   
+ **`InvalidParameterException`**: había una sintaxis interrumpida en los datos. *Estado HTTP*: solicitud incorrecta 400.

  *Mensaje*: `Invalid syntax in the specified file.`

   
+ **`UnloadUrlAccessDeniedException `**: el acceso se ha denegado. *Estado HTTP*: solicitud incorrecta 400.

  *Mensaje*: `Update failure: Endpoint (Neptune endpoint) reported access denied error. Please verify access.`

   
+ **`BadRequestException `**: no se pueden recuperar los datos remotos. *Estado HTTP*: solicitud incorrecta 400.

  *Mensaje*: *(depende de la respuesta HTTP).*

# Sugerencias de consulta SPARQL
<a name="sparql-query-hints"></a>

Puede utilizar sugerencias de consulta para especificar estrategias de optimización y evaluación para una consulta SPARQL concreta en Amazon Neptune. 

Las sugerencias de consulta se expresan utilizando patrones triples adicionales que están insertados en la consulta SPARQL con las siguientes partes:

```
scope hint value
```
+ *scope*: determina la parte de la consulta a la que se aplica la sugerencia de consulta, como un grupo determinado de la consulta o la consulta completa.
+ *hint*: identifica el tipo de sugerencia que se va a aplicar.
+ *value*: determina el comportamiento del aspecto del sistema considerado.

Las sugerencias y los ámbitos de consulta se exponen como términos predefinidos en el espacio de nombres `http://aws.amazon.com/neptune/vocab/v01/QueryHints#` de Amazon Neptune. Los ejemplos de esta sección incluyen el espacio de nombres como un prefijo `hint` que se define y se incluye en la consulta:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
```

Por ejemplo, a continuación se muestra cómo incluir una sugerencia `joinOrder` en una consulta `SELECT`:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT ... {
 hint:Query hint:joinOrder "Ordered" .
 ...
}
```

La consulta anterior indica al motor de Neptune que evalúe las uniones en la consulta en el orden *indicado* y deshabilita cualquier reordenación automática.

Tenga en cuenta lo siguiente al usar sugerencias de consulta:
+ Puede combinar diferentes sugerencias de consulta en una sola consulta. Por ejemplo, puede utilizar la sugerencia de consulta `bottomUp` para anotar una subconsulta para la evaluación ascendente y una sugerencia de consulta `joinOrder` para corregir el orden de unión dentro de la subconsulta.
+ Puede utilizar la misma sugerencia de consulta varias veces, en diferentes ámbitos no solapados.
+ Las sugerencias de consulta son sugerencias. Aunque el motor de consulta generalmente tiene como objetivo tener en cuenta determinadas sugerencias de consulta, también puede ignorarlas.
+ Las sugerencias de consulta mantienen la semántica. La adición de una sugerencia de consulta no modifica la salida de la consulta (excepto el orden potencial de los resultados cuando no se dan garantías de ordenación, es decir, cuando el orden de los resultados no se aplica explícitamente mediante el uso de ORDER BY). 

En las siguientes secciones se proporciona más información sobre las sugerencias de consulta disponibles y su uso en Neptune.

**Topics**
+ [Ámbito de las sugerencias de consulta SPARQL en Neptune](#sparql-query-hints-scope)
+ [La sugerencia de consulta SPARQL `joinOrder`](sparql-query-hints-joinOrder.md)
+ [La sugerencia de consulta `evaluationStrategy` de SPARQL](sparql-query-hints-evaluationStrategy.md)
+ [La sugerencia de consulta `queryTimeout` de SPARQL](sparql-query-hints-queryTimeout.md)
+ [La sugerencia de consulta `rangeSafe` de SPARQL](sparql-query-hints-rangeSafe.md)
+ [La sugerencia de consulta SPARQL `queryId`](sparql-query-hints-queryId.md)
+ [La sugerencia de consulta `useDFE` de SPARQL](sparql-query-hints-useDFE.md)
+ [Sugerencias de consulta de SPARQL utilizadas con DESCRIBE](sparql-query-hints-for-describe.md)

## Ámbito de las sugerencias de consulta SPARQL en Neptune
<a name="sparql-query-hints-scope"></a>

En la siguiente tabla, se muestran los ámbitos disponibles, las sugerencias asociadas y las descripciones para las sugerencias de consulta SPARQL en Amazon Neptune. El prefijo `hint` en estas entradas representa el espacio de nombres de Neptune para las sugerencias:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
```


| Alcance | Sugerencia admitida | Description (Descripción) | 
| --- | --- | --- | 
| hint:Query | [joinOrder](sparql-query-hints-joinOrder.md) | La sugerencia de consulta se aplica a toda la consulta. | 
| hint:Query | [queryTimeout](sparql-query-hints-queryTimeout.md) | El valor de expiración se aplica a toda la consulta | 
| hint:Query | [rangeSafe](sparql-query-hints-rangeSafe.md) | La promoción de tipos está deshabilitada para toda la consulta. | 
| hint:Query | [queryId](sparql-query-hints-queryId.md) | El valor del identificador de consulta se aplica a toda la consulta | 
| hint:Query | [useDFE](sparql-query-hints-useDFE.md) | El uso del DFE está habilitado (o deshabilitado) para toda la consulta. | 
| hint:Group | [joinOrder](sparql-query-hints-joinOrder.md) | La sugerencia de consulta se aplica a los elementos de nivel superior del grupo especificado, pero no a los elementos anidados (como las subconsultas) o a los elementos principales. | 
| hint:SubQuery | [evaluationStrategy](sparql-query-hints-evaluationStrategy.md) | La sugerencia se especifica y se aplica a una subconsulta SELECT anidada. La subconsulta se evalúa de forma independiente, sin tener en cuenta las soluciones calculadas antes de la subconsulta. | 

# La sugerencia de consulta SPARQL `joinOrder`
<a name="sparql-query-hints-joinOrder"></a>

Cuando envía una consulta SPARQL, el motor de consultas de Amazon Neptune investiga la estructura de la consulta. Reordena partes de la consulta y trata de minimizar la cantidad de trabajo necesario para la evaluación y el tiempo de respuesta de la consulta.

Por ejemplo, una secuencia de patrones triples conectados normalmente no se evalúa en el orden dado. Se reordena mediante heurística y estadísticas como la selectividad de los patrones individuales y cómo están conectados a través de variables compartidas. Además, si la consulta contiene patrones más complejos, como subconsultas o bloques OPTIONAL o MINUS complejos FILTERs, el motor de consultas de Neptune los reordena siempre que sea posible, con el objetivo de lograr un orden de evaluación eficiente.

Para consultas más complejas, el orden en el que Neptune decide evaluar la consulta puede no ser siempre el óptimo. Por ejemplo, Neptune podría perder las características específicas de datos de instancias (como el alcance de nodos Power en el gráfico) que surgen durante la evaluación de la consulta.

Si conoce las características exactas de los datos y desea dictar manualmente el orden de ejecución de la consulta, utilice la sugerencia de consulta `joinOrder` de Neptune para especificar que la consulta se evalúe en el orden indicado.

## Sintaxis de sugerencias SPARQL de `joinOrder`
<a name="sparql-query-hints-joinOrder-syntax"></a>

La sugerencia de consulta `joinOrder` se especifica como un patrón triple incluido en una consulta SPARQL.

Para una mayor claridad, los siguientes usos de sintaxis utilizan un prefijo `hint` que se define e incluye en la consulta para especificar el espacio de nombres de la sugerencia de consulta de Neptune:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
scope hint:joinOrder "Ordered" .
```

**Ámbitos disponibles**
+ `hint:Query`
+ `hint:Group`

Para obtener más información acerca de los ámbitos de sugerencia de consulta, vea [Ámbito de las sugerencias de consulta SPARQL en Neptune](sparql-query-hints.md#sparql-query-hints-scope).

## Ejemplo de sugerencia SPARQL `joinOrder`
<a name="sparql-query-hints-joinOrder-example"></a>

En esta sección se muestra una consulta escrita con la sugerencia de consulta `joinOrder` y sin, así como las optimizaciones relacionadas.

En este ejemplo, suponga que el conjunto de datos contiene lo siguiente:
+ Una sola persona llamada `John` que indica que le gustan (`:likes`) 1000 personas, incluyendo a `Jane`.
+ Una sola persona llamada `Jane` que indica que le gustan (`:likes`) 10 personas, incluyendo a `John`.

**Sin sugerencias de consulta**  
La siguiente consulta SPARQL extrae todos los pares de personas que se llaman `John` y `Jane`, y que se gustan entre sí de un conjunto de datos de redes sociales:

```
PREFIX : <https://example.com/>
SELECT ?john ?jane {
  ?person1 :name "Jane" .
  ?person1 :likes ?person2 .
  ?person2 :name "John" .
  ?person2 :likes ?person1 .
}
```

El motor de consultas de Neptune puede evaluar las instrucciones en un orden diferente al escrito. Por ejemplo, puede elegir evaluar en el siguiente orden:

1. Buscar todas las personas llamadas `John`.

1. Buscar todas las personas conectadas a `John` por un borde `:likes`.

1. Filtrar este conjunto por personas llamadas `Jane`.

1. Filtrar este conjunto por las conectadas a `John` por un borde `:likes`.

Según el conjunto de datos, la evaluación en este orden da como resultado la extracción de 1000 entidades en el segundo paso. El tercer paso lo limita hasta el nodo individual, `Jane`. El paso final determina que a `Jane` también le gusta (`:likes`) el nodo `John`.

**Sugerencia de consulta**  
Sería favorable comenzar con el nodo `Jane` porque solo tiene 10 bordes `:likes` salientes. De este modo se reduce la cantidad de trabajo durante la evaluación de la consulta al evitar la extracción de las 1000 entidades durante el segundo paso.

En el ejemplo siguiente, se utiliza la sugerencia de consulta **joinOrder** para asegurar que el nodo `Jane` y sus bordes de salida se procesen primero deshabilitando de todo reordenamiento automático de uniones para la consulta:

```
PREFIX : <https://example.com/>
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT ?john ?jane {
  hint:Query hint:joinOrder "Ordered" .
  ?person1 :name "Jane" .
  ?person1 :likes ?person2 .
  ?person2 :name "John" .
  ?person2 :likes ?person1 .
}
```

Un escenario aplicable real podría ser una aplicación de red social en la que las personas de dicha red se clasifican como personas con influencia con muchas conexiones o como usuarios normales con pocas conexiones. En tal escenario, podría asegurarse de que el usuario normal (`Jane`) se procese antes que la persona influyente (`John`) en una consulta como la del ejemplo anterior.

**Sugerencia de consulta y reordenación**  
Puede llevar este ejemplo un paso más allá. Si sabe que el atributo `:name` es único para un solo nodo, podría acelerar la consulta mediante la reordenación y el uso de la sugerencia de consulta `joinOrder`. Este paso garantiza que los nodos únicos se extraigan primero.

```
PREFIX : <https://example.com/>
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT ?john ?jane {
  hint:Query hint:joinOrder "Ordered" .
  ?person1 :name "Jane" .
  ?person2 :name "John" .
  ?person1 :likes ?person2 .
  ?person2 :likes ?person1 .
}
```

En este caso, puede reducir la consulta a las siguientes acciones individuales en cada paso:

1. Buscar la persona con `:name` `Jane`.

1. Buscar la persona con `:name` `John`.

1. Comprobar que el primer nodo está conectado al segundo con un borde `:likes`.

1. Comprobar que el segundo nodo está conectado al primero con un borde `:likes`.



**importante**  
Si elige un orden incorrecto, la sugerencia de consulta `joinOrder` puede provocar un descenso considerable del rendimiento. Por ejemplo, el ejemplo anterior no sería eficiente si los atributos `:name` no fueran únicos. Si los 100 nodos se llamaran `Jane` y los 1000 nodos se llamaran `John`, la consulta terminaría verificando 1000 \$1 100 (100.000) pares para los bordes `:likes`.

# La sugerencia de consulta `evaluationStrategy` de SPARQL
<a name="sparql-query-hints-evaluationStrategy"></a>

La sugerencia de consulta `evaluationStrategy` indica al motor de consultas de Amazon Neptune que el fragmento de la consulta anotada debe evaluarse de abajo arriba como una unidad independiente. Esto significa que no se utiliza ninguna solución de los pasos de evaluación anteriores para calcular el fragmento de consulta. El fragmento de consulta se evalúa como una unidad independiente y las soluciones producidas se unen con el resto de la consulta una vez calculada.

El uso de la sugerencia de consulta `evaluationStrategy` implica un plan de consulta de bloqueo (sin canalización), lo que significa que las soluciones del fragmento anotado con la sugerencia de consulta se materializan y se almacenan en búfer en la memoria principal. El uso de esta sugerencia de consulta puede aumentar considerablemente la cantidad de memoria principal necesaria para evaluar la consulta, especialmente si el fragmento de consulta anotado calcula un gran número de resultados.

## Sintaxis de sugerencias SPARQL de `evaluationStrategy`
<a name="sparql-query-hints-evaluationStrategy-syntax"></a>

La sugerencia de consulta `evaluationStrategy` se especifica como un patrón triple incluido en una consulta SPARQL.

Para una mayor claridad, los siguientes usos de sintaxis utilizan un prefijo `hint` que se define e incluye en la consulta para especificar el espacio de nombres de la sugerencia de consulta de Neptune:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
hint:SubQuery hint:evaluationStrategy "BottomUp" .
```

**Ámbitos disponibles**
+ `hint:SubQuery`

**nota**  
Esta sugerencia de consulta solo es compatible con las subconsultas anidadas.

Para obtener más información acerca de los ámbitos de sugerencia de consulta, vea [Ámbito de las sugerencias de consulta SPARQL en Neptune](sparql-query-hints.md#sparql-query-hints-scope).

## Ejemplo de sugerencia SPARQL `evaluationStrategy`
<a name="sparql-query-hints-evaluationStrategy-example"></a>



En esta sección se muestra una consulta escrita con la sugerencia de consulta `evaluationStrategy` y sin, así como las optimizaciones relacionadas.

En este ejemplo, suponga que el conjunto de datos tiene las siguientes características:
+ Contiene 1000 bordes etiquetados `:connectedTo`.
+ Cada nodo `component` está conectado a una media de otros 100 nodos `component`.
+ El número típico de conexiones cíclicas de cuatro saltos entre nodos es de unos 100.

Como ejemplo típico, la sugerencia `evaluationStrategy` puede ser útil para optimizar los patrones de consulta que contienen ciclos.

**Sin sugerencias de consulta**  
La siguiente consulta SPARQL extrae todos los nodos `component` que están cíclicamente conectados entre sí a través de cuatro saltos:

```
PREFIX : <https://example.com/>
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT * {
  ?component1 :connectedTo ?component2 .
  ?component2 :connectedTo ?component3 .
  ?component3 :connectedTo ?component4 .
  ?component4 :connectedTo ?component1 .
}
```

El enfoque del motor de consultas de Neptune es evaluar esta consulta utilizando los siguientes pasos:
+ Extraer los 1000 bordes `connectedTo` del gráfico.
+ Expandir 100 veces (el número de bordes `connectedTo` de salida desde component2).

  Resultados intermedios: 100.000 nodos.
+ Expandir 100 veces (el número de bordes `connectedTo` de salida desde component3).

  Resultados intermedios: 10.000.000 nodos.
+ Escanear los 10.000.000 de nodos para el cierre del ciclo.

Esto da como resultado un plan de consulta de streaming, que tiene una cantidad constante de memoria principal.

**Sugerencia de consulta y subconsultas**  
Es posible que desee intercambiar el espacio de memoria principal para acelerar el cálculo. Al reescribir la consulta mediante una sugerencia de consulta `evaluationStrategy`, puede forzar al motor a calcular una unión entre dos subconjuntos más pequeños y materializados.

```
PREFIX : <https://example.com/>
          PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT * {
  {
    SELECT * WHERE {
      hint:SubQuery hint:evaluationStrategy "BottomUp" .
      ?component1 :connectedTo ?component2 .
      ?component2 :connectedTo ?component3 .
    }
  }
  {
    SELECT * WHERE {
      hint:SubQuery hint:evaluationStrategy "BottomUp" .
      ?component3 :connectedTo ?component4 .
      ?component4 :connectedTo ?component1 .
    }
  }
}
```

En lugar de evaluar los patrones triples en secuencia mientras se usan iterativamente los resultados del patrón triple anterior como entrada para los patrones siguientes, la sugerencia `evaluationStrategy` provoca que las dos subconsultas se evalúen de forma independiente. Ambas subconsultas producen 100.000 nodos para los resultados intermedios, que después se unen para formar la salida final. 

En concreto, cuando ejecuta Neptune en los tipos de instancia más grandes, el almacenamiento temporal de estos dos subconjuntos de 100 000 en la memoria principal aumenta el uso de memoria a cambio de acelerar considerablemente la evaluación.

# La sugerencia de consulta `queryTimeout` de SPARQL
<a name="sparql-query-hints-queryTimeout"></a>

La sugerencia de consulta `queryTimeout` especifica un tiempo de espera que es menor que el conjunto de valores de`neptune_query_timeout` en el grupo de parámetros de base de datos.

Si la consulta termina como resultado de esta sugerencia, se genera una `TimeLimitExceededException`, con un mensaje `Operation terminated (deadline exceeded)`.

## Sintaxis de sugerencias SPARQL de `queryTimeout`
<a name="sparql-query-hints-queryTimeout-syntax"></a>

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT ... WHERE {
    hint:Query hint:queryTimeout 10 .
    # OR
    hint:Query hint:queryTimeout "10" .
    # OR
    hint:Query hint:queryTimeout "10"^^xsd:integer .
 ...
}
```

El valor del tiempo de espera se expresa en milisegundos.

El valor del tiempo de espera debe ser menor que el valor de `neptune_query_timeout` establecido en el grupo de parámetros de la base de datos. De lo contrario, se generará una excepción `MalformedQueryException` con un mensaje `Malformed query: Query hint 'queryTimeout' must be less than neptune_query_timeout DB Parameter Group`.

Se debe especificar la sugerencia de consulta `queryTimeout` en la cláusula `WHERE` de la consulta principal o en la cláusula `WHERE` de uno de las subconsultas tal como se en el ejemplo a continuación:

Debe configurarse una sola vez en todas las secciones de actualizaciones queries/subqueries y en las de SPARQL (como INSERT y DELETE). De lo contrario, se generará una excepción `MalformedQueryException` con un mensaje `Malformed query: Query hint 'queryTimeout' must be set only once`.

**Ámbitos disponibles**

La sugerencia `queryTimeout` se puede aplicar tanto a las consultas SPARQL como a las actualizaciones.
+ En una consulta SPARQL, puede aparecer en la cláusula WHERE de la consulta principal o en una subconsulta.
+ En una actualización de SPARQL, se puede establecer en la cláusula INSERT, DELETE o WHERE. Si hay varias cláusulas de actualización, solo se puede establecer en una de ellas.

Para obtener más información acerca de los ámbitos de sugerencia de consulta, vea [Ámbito de las sugerencias de consulta SPARQL en Neptune](sparql-query-hints.md#sparql-query-hints-scope).

## Ejemplo de sugerencia SPARQL `queryTimeout`
<a name="sparql-query-hints-queryTimeout-example"></a>

A continuación se muestra un ejemplo de uso de `hint:queryTimeout` en la cláusula principal `WHERE` de una consulta `UPDATE`:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
INSERT {
    ?s ?p ?o
} WHERE {
    hint:Query hint:queryTimeout 100 .
    ?s ?p ?o .
}
```

Aquí, la `hint:queryTimeout` se encuentra en la cláusula `WHERE` de una subconsulta:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT * {
   ?s ?p ?o .
   {
      SELECT ?s WHERE {
         hint:Query hint:queryTimeout 100 .
         ?s ?p1 ?o1 .
      }
   }
}
```

# La sugerencia de consulta `rangeSafe` de SPARQL
<a name="sparql-query-hints-rangeSafe"></a>

Utilice esta sugerencia de consulta para desactivar la promoción de tipos en una consulta de SPARQL.

Cuando envía una consulta SPARQL que incluye un `FILTER` en un valor o rango numérico, normalmente el motor de consultas de Neptune debe usar la promoción de tipos cuando ejecuta la consulta. Esto significa que tiene que examinar los valores de todos los tipos que puedan contener el valor por el que se está filtrando.

Por ejemplo, si está filtrando valores iguales a 55, el motor debe buscar números enteros iguales a 55, enteros largos iguales a 55L, flotantes iguales a 55,0, etc. Cada promoción de tipo requiere una búsqueda adicional en el almacenamiento, lo que puede provocar que una consulta aparentemente simple tarde un tiempo inesperadamente largo en completarse.

A menudo, la promoción de tipos no es necesaria porque ya sabe de antemano que solo necesita encontrar valores de un tipo específico. Cuando sea así, puede acelerar considerablemente sus consultas utilizando la sugerencia de consulta `rangeSafe` para desactivar la promoción de tipos.

## Sintaxis de sugerencias SPARQL de `rangeSafe`
<a name="sparql-query-hints-rangeSafe-syntax"></a>

La sugerencia de consulta `rangeSafe` toma un valor de `true` para desactivar la promoción de tipos. También acepta un valor de `false` (el valor predeterminado).

**Ejemplo.** El siguiente ejemplo muestra cómo desactivar la promoción de tipos al filtrar por un valor entero de `o` superior a 1:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT * {
   ?s ?p ?o .
   hint:Prior hint:rangeSafe 'true' .
   FILTER (?o > '1'^^<http://www.w3.org/2001/XMLSchema#int>)
```

# La sugerencia de consulta SPARQL `queryId`
<a name="sparql-query-hints-queryId"></a>

Utilice esta sugerencia de consulta para asignar su propio valor de queryId a una consulta SPARQL.

Ejemplo:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
SELECT * WHERE {
  hint:Query hint:queryId "4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47"
  {?s ?p ?o}}
```

El valor que asigne debe ser único en todas las consultas de la base de datos de Neptune.

# La sugerencia de consulta `useDFE` de SPARQL
<a name="sparql-query-hints-useDFE"></a>

Utilice esta sugerencia de consulta para permitir el uso del DFE para ejecutar la consulta. De forma predeterminada, Neptune no usa el DFE sin que esta sugerencia de consulta esté establecida en `true`, ya que el parámetro de instancia [neptune\$1dfe\$1query\$1engine](parameters.md#parameters-instance-parameters-neptune_dfe_query_engine) tiene el valor predeterminado `viaQueryHint`. Si establece ese parámetro de instancia en `enabled`, el motor DFE se utiliza para todas las consultas excepto para las que la sugerencia de consulta `useDFE` está establecida en `false`.

Ejemplo de cómo habilitar el uso del DFE para una consulta:

```
PREFIX : <https://example.com/>
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>

SELECT ?john ?jane
{
  hint:Query hint:useDFE true .
  ?person1 :name "Jane" .
  ?person1 :likes ?person2 .
  ?person2 :name "John" .
  ?person2 :likes ?person1 .
}
```

# Sugerencias de consulta de SPARQL utilizadas con DESCRIBE
<a name="sparql-query-hints-for-describe"></a>

Una consulta `DESCRIBE` de SPARQL proporciona un mecanismo flexible para solicitar descripciones de recursos. Sin embargo, las especificaciones de SPARQL no definen la semántica precisa de `DESCRIBE`.

A partir de la [versión 1.2.0.2 del motor](engine-releases-1.2.0.2.md), Neptune admite varios modos y algoritmos `DESCRIBE`diferentes que se adaptan a diferentes situaciones.

Este conjunto de datos de muestra puede ayudar a ilustrar los diferentes modos:

```
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix : <https://example.com/> .

:JaneDoe :firstName "Jane" .
:JaneDoe :knows :JohnDoe .
:JohnDoe :firstName "John" .
:JaneDoe :knows _:b1 .
_:b1 :knows :RichardRoe .

:RichardRoe :knows :JaneDoe .
:RichardRoe :firstName "Richard" .

_:s1 rdf:type rdf:Statement .
_:s1 rdf:subject :JaneDoe .
_:s1 rdf:predicate :knows .
_:s1 rdf:object :JohnDoe .
_:s1 :knowsFrom "Berlin" .

:ref_s2 rdf:type rdf:Statement .
:ref_s2 rdf:subject :JaneDoe .
:ref_s2 rdf:predicate :knows .
:ref_s2 rdf:object :JohnDoe .
:ref_s2 :knowsSince 1988 .
```

En los ejemplos siguientes, se supone que se solicita una descripción del recurso `:JaneDoe` mediante una consulta SPARQL como esta:

```
DESCRIBE <https://example.com/JaneDoe>
```

## La sugerencia de consulta `describeMode` de SPARQL
<a name="sparql-query-hints-describeMode"></a>

La sugerencia de consulta `hint:describeMode` de SPARQL se utiliza para seleccionar uno de los siguientes modos `DESCRIBE` de SPARQL compatibles con Neptune:

### El modo `ForwardOneStep` de DESCRIBE
<a name="sparql-query-hints-describeMode-ForwardOneStep"></a>

El modo `ForwardOneStep` se invoca con la sugerencia de consulta `describeMode` de la siguiente manera:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
DESCRIBE <https://example.com/JaneDoe>
{
  hint:Query hint:describeMode "ForwardOneStep"
}
```

El modo `ForwardOneStep` solo devuelve los atributos y los enlaces de reenvío del recurso que se va a describir. En el caso de ejemplo, esto significa que devuelve los triples que tienen `:JaneDoe`, que es el recurso que se va a describir, como asunto:

```
:JaneDoe :firstName "Jane" .
:JaneDoe :knows :JohnDoe .
:JaneDoe :knows _:b301990159 .
```

Tenga en cuenta que la consulta DESCRIBE puede devolver triples con nodos en blanco, por ejemplo`_:b301990159`, que son diferentes IDs cada vez en comparación con el conjunto de datos de entrada.

### El modo `SymmetricOneStep` de DESCRIBE
<a name="sparql-query-hints-describeMode-SymmetricOneStep"></a>

`SymmetricOneStep` es el modo de DESCRIBE predeterminado si no proporciona una sugerencia de consulta. También puede invocarlo de forma explícita con la sugerencia de consulta `describeMode` de la siguiente manera:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
DESCRIBE <https://example.com/JaneDoe>
{
  hint:Query hint:describeMode "SymmetricOneStep"
}
```

En la semántica `SymmetricOneStep`, `DESCRIBE` devuelve los atributos, los enlaces directos y los enlaces inversos del recurso que se va a describir:

```
:JaneDoe :firstName "Jane" .
:JaneDoe :knows :JohnDoe .
:JaneDoe :knows _:b318767375 .

_:b318767631 rdf:subject :JaneDoe .

:RichardRoe :knows :JaneDoe .

:ref_s2 rdf:subject :JaneDoe .
```

### El modo de DESCRIBE de descripción limitada concisa (`CBD`)
<a name="sparql-query-hints-describeMode-CBD"></a>

El modo de descripción limitada concisa (`CBD`) se invoca con la sugerencia de consulta `describeMode` de la siguiente manera:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
DESCRIBE <https://example.com/JaneDoe>
{
  hint:Query hint:describeMode "CBD"
}
```

En la semántica `CBD`, `DESCRIBE` devuelve la descripción limitada concisa ([tal como la define el W3C](http://www.w3.org/Submission/CBD)) del recurso que se va a describir:

```
:JaneDoe :firstName "Jane" .
:JaneDoe :knows :JohnDoe .
:JaneDoe :knows _:b285212943 .
_:b285212943 :knows :RichardRoe .

_:b285213199 rdf:subject :JaneDoe .
_:b285213199 rdf:type rdf:Statement .
_:b285213199 rdf:predicate :knows .
_:b285213199 rdf:object :JohnDoe .
_:b285213199 :knowsFrom "Berlin" .

:ref_s2 rdf:subject :JaneDoe .
```

La descripción limitada concisa de un recurso de RDF (es decir, un nodo de un gráfico RDF) es el subgráfico más pequeño centrado alrededor de ese nodo que puede ser independiente. En la práctica, esto significa que si piensa en este gráfico como un árbol, con el nodo designado como raíz, no hay nodos en blanco (nodos b) como hojas de ese árbol. Como los nodos b no se pueden direccionar externamente ni se pueden usar en consultas posteriores, no basta con navegar por el gráfico para encontrar los siguientes saltos individuales desde el nodo actual. También hay que ir lo suficientemente lejos como para encontrar algo que pueda usarse en consultas posteriores (es decir, algo que no sea un nodo b).

#### Cálculo del CBD
<a name="sparql-query-hints-describeMode-CBD-computing"></a>

Dado un nodo en particular (el nodo de inicio o raíz) en el gráfico RDF de origen, el CBD de ese nodo se calcula de la siguiente manera:

1. Incluya en el subgráfico todas las instrucciones del gráfico de origen en las que el *sujeto* de la instrucción sea el nodo inicial.

1. De forma recursiva, para todas las instrucciones del subgráfico que hasta ahora tengan un *objeto* de nodo en blanco, incluya en el subgráfico todas las instrucciones del gráfico de origen cuyo *sujeto* sea ese nodo en blanco y que aún no estén incluidas en el subgráfico.

1. De forma recursiva, para todas las instrucciones incluidas en el subgráfico hasta ahora, para todas las reificaciones de estas instrucciones en el gráfico de origen, incluya el CBD comenzando por el nodo `rdf:Statement` de cada reificación.

Esto da como resultado un subgráfico en el que los nodos *objeto* son referencias o literales del IRI, o nodos en blanco que no sirven de *sujeto* a ninguna instrucción del gráfico. Tenga en cuenta que el CBD no se puede calcular con una sola consulta SELECT o CONSTRUCT de SPARQL.

### El modo DESCRIBE de descripción limitada concisa simétrica (`SCBD`)
<a name="sparql-query-hints-describeMode-SCBD"></a>

El modo de descripción limitada concisa simétrica (`SCBD`) se invoca con la sugerencia de consulta `describeMode` de la siguiente manera:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
DESCRIBE <https://example.com/JaneDoe>
{
  hint:Query hint:describeMode "SCBD"
}
```

En la semántica de `SCBD`, `DESCRIBE` devuelve la descripción limitada concisa simétrica del recurso (tal como la define el W3C en [Describing Linked Datasets with the VoID Vocabulary](http://www.w3.org/TR/void/):

```
:JaneDoe :firstName "Jane" .
:JaneDoe :knows :JohnDoe .
:JaneDoe :knows _:b335544591 .
_:b335544591 :knows :RichardRoe .

:RichardRoe :knows :JaneDoe .

_:b335544847 rdf:subject :JaneDoe .
_:b335544847 rdf:type rdf:Statement .
_:b335544847 rdf:predicate :knows .
_:b335544847 rdf:object :JohnDoe .
_:b335544847 :knowsFrom "Berlin" .

:ref_s2 rdf:subject :JaneDoe .
```

La ventaja de CBD y SCBD con respecto a los modos `ForwardOneStep` y `SymmetricOneStep` es que los nodos vacíos siempre se expanden para incluir su representación. Esto puede ser una ventaja importante, ya que no se puede consultar un nodo en blanco con SPARQL. Además, los modos CBD y SCBD también consideran las reificaciones.

Tenga en cuenta que la sugerencia de consulta `describeMode` también puede formar parte de una cláusula `WHERE`:

```
PREFIX hint: <http://aws.amazon.com/neptune/vocab/v01/QueryHints#>
DESCRIBE ?s
WHERE {
  hint:Query hint:describeMode "CBD" .
  ?s rdf:type <https://example.com/Person>
}
```

## La sugerencia de consulta `describeIterationLimit` de SPARQL
<a name="sparql-query-hints-describeIterationLimit"></a>

La sugerencia de consulta de SPARQL `hint:describeIterationLimit` proporciona una restricción **opcional** con respecto al número máximo de expansiones iterativas que se deben realizar para los algoritmos DESCRIBE iterativos, como CBD y SCBD.

Los límites de DESCRIBE están ANDed juntos. Por lo tanto, si se especifican tanto el límite de iteración como el límite de las instrucciones, ambos límites deben cumplirse antes de que se interrumpa la consulta DESCRIBE.

El valor predeterminado de este valor es 5. Puede establecerlo en CERO (0) para no especificar ningún límite en el número de expansiones iterativas.

## La sugerencia de consulta `describeStatementLimit` de SPARQL
<a name="sparql-query-hints-describeStatementLimit"></a>

La sugerencia de consulta `hint:describeStatementLimit` de SPARQL proporciona una restricción **opcional** con respecto al número máximo de instrucciones que pueden estar presentes en una respuesta a una consulta DESCRIBE. Solo se aplica a los algoritmos DESCRIBE iterativos, como CBD y SCBD.

Los límites de DESCRIBE están ANDed juntos. Por lo tanto, si se especifican tanto el límite de iteración como el límite de las instrucciones, ambos límites deben cumplirse antes de que se interrumpa la consulta DESCRIBE.

El valor predeterminado de este valor es 5000. Puede establecerlo en CERO (0) para no especificar ningún límite en el número de instrucciones devueltas.

# Comportamiento de DESCRIBE de SPARQL con respecto al gráfico predeterminado
<a name="sparql-default-describe"></a>

El formulario de consulta [https://www.w3.org/TR/sparql11-query/#describe](https://www.w3.org/TR/sparql11-query/#describe) de SPARQL le permite recuperar información sobre los recursos sin conocer la estructura de los datos y sin tener que redactar una consulta. La forma en que se recopila esta información depende de la implementación de SPARQL. Neptune proporciona [varias sugerencias de consulta](sparql-query-hints-for-describe.md) que invocan diferentes modos y algoritmos para que los use `DESCRIBE`.

En la implementación de Neptune, independientemente del modo, `DESCRIBE` solo usa los datos presentes en el [gráfico predeterminado de SPARQL](feature-sparql-compliance.md#sparql-default-graph). Esto es coherente con la forma en que SPARQL trata los conjuntos de datos (consulte [Specifying RDF Datasets](https://www.w3.org/TR/sparql11-query/#specifyingDataset) en la especificación de SPARQL).

En Neptune, el gráfico predeterminado contiene todos los triples únicos en la unión de todos los gráficos con nombre de la base de datos, a menos que los gráficos con nombre determinados se especifiquen mediante cláusulas. `FROM` and/or `FROM NAMED` Todos los datos RDF de Neptune se almacenan en un gráfico con nombre. Si se inserta un triple sin un contexto de gráfico con nombre, Neptune lo almacena en un gráfico con nombre designado `http://aws.amazon.com/neptune/vocab/v01/DefaultNamedGraph`.

Cuando se especifican uno o más gráficos con nombre mediante la cláusula `FROM`, el gráfico predeterminado es la unión de todos los triples únicos de esos gráficos con nombre. Si no hay ninguna cláusula `FROM` y hay una o más cláusulas `FROM NAMED`, el gráfico predeterminado está vacío.

## Ejemplos de `DESCRIBE` de SPARQL
<a name="sparql-default-describe-examples"></a>

Analice los siguientes datos:

```
PREFIX ex: <https://example.com/>

GRAPH ex:g1 {
    ex:s ex:p1 "a" .
    ex:s ex:p2 "c" .
}

GRAPH ex:g2 {
    ex:s ex:p3 "b" .
    ex:s ex:p2 "c" .
}

ex:s ex:p3 "d" .
```

Para esta consulta:

```
PREFIX ex: <https://example.com/>
DESCRIBE ?s
FROM ex:g1
FROM NAMED ex:g2
WHERE {
  GRAPH ex:g2 { ?s ?p "b" . }
}
```

Neptune devolvería:

```
ex:s ex:p1 "a" .
ex:s ex:p2 "c" .
```

Aquí, el patrón del gráfico `GRAPH ex:g2 { ?s ?p "b" }` se evalúa primero, lo que da como resultado enlaces para `?s`, y luego la parte `DESCRIBE` se evalúa con respecto al gráfico predeterminado, que ahora es solo `ex:g1`.

Sin embargo, para esta consulta:

```
PREFIX ex: <https://example.com/>
DESCRIBE ?s 
FROM NAMED ex:g1 
WHERE { 
  GRAPH ex:g1 { ?s ?p "a" . } 
}
```

Neptune no devolvería nada, porque cuando una cláusula `FROM NAMED` está presente sin ninguna cláusula `FROM`, el gráfico predeterminado está vacío.

En la siguiente consulta, `DESCRIBE` se usa sin presencia de ninguna cláusula `FROM` o `FROM NAMED`:

```
PREFIX ex: <https://example.com/>
DESCRIBE ?s 
WHERE { 
  GRAPH ex:g1 { ?s ?p "a" . } 
}
```

En esta situación, el gráfico predeterminado se compone de todos los triples únicos en la unión de todos los gráficos con nombre de la base de datos (formalmente, la combinación RDF), por lo que Neptune devolvería:

```
ex:s ex:p1 "a" . 
ex:s ex:p2 "c" . 
ex:s ex:p3 "b" .
ex:s ex:p3 "d" .
```

# API de estado de la consulta SPARQL
<a name="sparql-api-status"></a>

Para obtener el estado de las consultas SPARQL, utilice HTTP `GET` o `POST` para realizar una solicitud al punto de enlace `https://your-neptune-endpoint:port/sparql/status`. 

## Parámetros de solicitud de estado de consultas SPARQL
<a name="sparql-api-status-get-request"></a>

**queryId (opcional)**  
El ID de una consulta SPARQL en ejecución. Solo muestra el estado de la consulta especificada.

## Sintaxis de respuesta de estado de consultas SPARQL
<a name="sparql-api-status-get-response-syntax"></a>

```
{
    "acceptedQueryCount": integer,
    "runningQueryCount": integer,
    "queries": [
      {
        "queryId":"guid",
        "queryEvalStats":
          {
            "subqueries": integer,
            "elapsed": integer,
            "cancelled": boolean
          },
        "queryString": "string"
      }
    ]
}
```

## Valores de respuesta de estado de consultas SPARQL
<a name="sparql-api-status-get-response-values"></a>

**acceptedQueryCount**  
El número de consultas aceptadas desde el último reinicio del motor de Neptune.

**runningQueryCount**  
El número de consultas SPARQL que se están ejecutando actualmente.

**consultas**  
Una lista de las consultas SPARQL actuales.

**queryId**  
Un ID de GUID para la consulta. Neptune asigna automáticamente este valor de identificador a cada consulta o también puede asignar su propio identificador (consulte [Inserte un identificador personalizado en una consulta de Neptune Gremlin o SPARQL](features-query-id.md)). 

**queryEvalStats**  
Estadísticas de esta consulta.

**subqueries**  
El número de subconsultas de esta consulta.

**elapsed**  
El número de milisegundos que la consulta lleva en ejecución.

**cancelled**  
True indica que se canceló la consulta.

**queryString**  
La consulta enviada.

## Ejemplo de estado de consultas SPARQL
<a name="sparql-api-status-get-example"></a>

A continuación se muestra un ejemplo de comando de estado que utiliza `curl` y HTTP `GET`.

```
curl https://your-neptune-endpoint:port/sparql/status
```

Esta salida muestra una única consulta en ejecución.

```
{
    "acceptedQueryCount":9,
    "runningQueryCount":1,
    "queries": [
        {
            "queryId":"fb34cd3e-f37c-4d12-9cf2-03bb741bf54f",
            "queryEvalStats":
                {
                    "subqueries": 0,
                    "elapsed": 29256,
                    "cancelled": false
                },
            "queryString": "SELECT ?s ?p ?o WHERE {?s ?p ?o}"
        }
    ]
}
```

# Cancelación de consultas SPARQL
<a name="sparql-api-status-cancel"></a>

Para obtener el estado de las consultas SPARQL, utilice HTTP `GET` o `POST` para realizar una solicitud al punto de enlace `https://your-neptune-endpoint:port/sparql/status`.

## Parámetros de solicitud de cancelación de consultas SPARQL
<a name="sparql-api-status-cancel-request"></a>

**cancelQuery**  
Indica al comando de estado que cancele una consulta (obligatorio). Este parámetro no selecciona un valor.

**queryId**  
El ID de la consulta SPARQL en ejecución que se va a cancelar (obligatorio).

**silent**  
Si es `silent=true`, se cancela la consulta en ejecución y el código de respuesta HTTP es 200 (opcional). Si `silent` no está presente o `silent=false`, la consulta se cancela con un código de estado HTTP 500.

## Ejemplos de cancelación de consultas SPARQL
<a name="sparql-api-status-cancel-example"></a>

**Ejemplo 1: Cancelación con `silent=false`**  
A continuación se muestra un ejemplo de comando de estado que utiliza `curl` para cancelar una consulta con el parámetro `silent` establecido en `false`:

```
curl https://your-neptune-endpoint:port/sparql/status \
  -d "cancelQuery" \
  -d "queryId=4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47" \
  -d "silent=false"
```

A menos que la consulta ya haya comenzado a transmitir resultados, la consulta cancelada devolvería un código HTTP 500 con una respuesta como esta:

```
{
  "code": "CancelledByUserException",
  "requestId": "4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47",
  "detailedMessage": "Operation terminated (cancelled by user)"
}
```

Si la consulta ya ha devuelto un código HTTP 200 (OK) y ha iniciado la transmisión de resultados antes de su cancelación, la información de excepción de tiempo de espera se enviará a la secuencia de salida normal.

**Ejemplo 2: Cancelación con `silent=true`**  
A continuación se muestra un ejemplo del mismo comando de estado que el anterior, excepto con el parámetro `silent` ahora establecido en `true`:

```
curl https://your-neptune-endpoint:port/sparql/status \
  -d "cancelQuery" \
  -d "queryId=4d5c4fae-aa30-41cf-9e1f-91e6b7dd6f47" \
  -d "silent=true"
```

Este comando devolvería la misma respuesta que con `silent=false`, pero la consulta cancelada devolvería ahora un código HTTP 200 con una respuesta similar a la siguiente:

```
{
  "head" : {
    "vars" : [ "s", "p", "o" ]
  },
  "results" : {
    "bindings" : [ ]
  }
}
```

# Uso del protocolo HTTP de almacén de gráficos (GSP) de SPARQL 1.1 en Amazon Neptune
<a name="sparql-graph-store-protocol"></a>

En la recomendación del [protocolo HTTP de almacén de gráficos de SPARQL 1.1](https://www.w3.org/TR/sparql11-http-rdf-update/), el W3C definió un protocolo HTTP para administrar los gráficos RDF. Define las operaciones para eliminar, crear y reemplazar el contenido de los gráficos RDF, así como para añadir instrucciones RDF al contenido existente.

El protocolo de almacén de gráficos (GSP) proporciona una forma cómoda de manipular todo el gráfico sin tener que escribir consultas SPARQL complejas.

Neptune es totalmente compatible con este protocolo.

El punto de conexión del protocolo de almacén de gráficos (GSP) es:

```
https://your-neptune-cluster:port/sparql/gsp/
```

Para acceder al gráfico predeterminado con GSP, utilice:

```
https://your-neptune-cluster:port/sparql/gsp/?default
```

Para acceder a un gráfico con nombre con GSP, utilice:

```
https://your-neptune-cluster:port/sparql/gsp/?graph=named-graph-URI
```

## Detalles especiales de la implementación de GSP en Neptune
<a name="sparql-graph-store-protocol-special"></a>

Neptune implementa plenamente la [recomendación del W3C](https://www.w3.org/TR/sparql11-http-rdf-update/) que define GSP. Sin embargo, hay algunas situaciones que la especificación no cubre.

Una de ellas es cuando una solicitud `PUT` o `POST` especifica uno o más gráficos con nombre en el cuerpo de la solicitud que difieren del gráfico especificado en la URL de la solicitud. Esto solo puede ocurrir cuando el formato RDF del cuerpo de la solicitud admite gráficos con nombre, como, por ejemplo, si se utiliza `Content-Type: application/n-quads` o `Content-Type: application/trig`.

En esta situación, Neptune añade o actualiza todos los gráficos con nombre presentes en el cuerpo, así como el gráfico con nombre especificado en la URL.

Por ejemplo, supongamos que, partiendo de una base de datos vacía, se envía una solicitud `PUT` para realizar actualizaciones o inserciones en los votos en tres gráficos. Uno, denominado `urn:votes`, contiene todos los votos de todos los años electorales. Otros dos, denominados `urn:votes:2005` y `urn:votes:2019`, contienen votos de años electorales específicos. La solicitud y su carga tienen el siguiente aspecto:

```
PUT "http://your-Neptune-cluster:port/sparql/gsp/?graph=urn:votes"
  Host: example.com
  Content-Type: application/n-quads

  PAYLOAD:

  <urn:JohnDoe> <urn:votedFor> <urn:Labour> <urn:votes:2005>
  <urn:JohnDoe> <urn:votedFor> <urn:Conservative> <urn:votes:2019>
  <urn:JaneSmith> <urn:votedFor> <urn:LiberalDemocrats> <urn:votes:2005>
  <urn:JaneSmith> <urn:votedFor> <urn:Conservative> <urn:votes:2019>
```

Una vez ejecutada la solicitud, los datos de la base de datos tienen el siguiente aspecto:

```
<urn:JohnDoe>   <urn:votedFor> <urn:Labour>           <urn:votes:2005>
<urn:JohnDoe>   <urn:votedFor> <urn:Conservative>     <urn:votes:2019>
<urn:JaneSmith> <urn:votedFor> <urn:LiberalDemocrats> <urn:votes:2005>
<urn:JaneSmith> <urn:votedFor> <urn:Conservative>     <urn:votes:2019>
<urn:JohnDoe>   <urn:votedFor> <urn:Labour>           <urn:votes>
<urn:JohnDoe>   <urn:votedFor> <urn:Conservative>     <urn:votes>
<urn:JaneSmith> <urn:votedFor> <urn:LiberalDemocrats> <urn:votes>
<urn:JaneSmith> <urn:votedFor> <urn:Conservative>     <urn:votes>
```

Otra situación ambigua es cuando se especifica más de un gráfico en la propia URL de la solicitud, utilizando `PUT`, `POST`, `GET` o `DELETE`. Por ejemplo:

```
POST "http://your-Neptune-cluster:port/sparql/gsp/?graph=urn:votes:2005&graph=urn:votes:2019"
```

O bien:

```
GET "http://your-Neptune-cluster:port/sparql/gsp/?default&graph=urn:votes:2019"
```

En esta situación, Neptune devuelve un HTTP 400 con un mensaje que indica que solo se puede especificar un gráfico en la URL de la solicitud.

# Análisis de la ejecución de las consultas de Neptune con `explain` de SPARQL
<a name="sparql-explain"></a>

Amazon Neptune ha añadido una característica de SPARQL denominada *explain*. Esta característica es una herramienta de autoservicio para comprender el enfoque de ejecución adoptado por el motor de Neptune. Puede invocarla añadiendo un parámetro `explain` a una llamada HTTP que envíe una consulta SPARQL.

La característica `explain` proporciona información sobre la estructura lógica de los planes de ejecución de consultas. Puede utilizar esta información para identificar posibles cuellos de botella en la evaluación y la ejecución. A continuación, puede usar [sugerencias de consulta](sparql-query-hints.md) para mejorar los planes de ejecución de consultas.

**Topics**
+ [Funcionamiento del motor de consultas de SPARQL en Neptune](sparql-explain-engine.md)
+ [Cómo utilizar `explain` de SPARQL para analizar la ejecución de consultas de Neptune](sparql-explain-using.md)
+ [Ejemplos de invocación de `explain` de SPARQL en Neptune](sparql-explain-examples.md)
+ [Operadores `explain` de SPARQL en Neptune](sparql-explain-operators.md)
+ [Limitaciones de `explain` de SPARQL en Neptune](sparql-explain-limitations.md)

# Funcionamiento del motor de consultas de SPARQL en Neptune
<a name="sparql-explain-engine"></a>

Para utilizar la información que proporciona la característica `explain` de SPARQL, debe comprender algunos detalles sobre el funcionamiento del motor de consultas de SPARQL de Amazon Neptune.

El motor traduce cada consulta SPARQL en una canalización de operadores. Empezando en el primer operador, las soluciones intermedias denominadas *listas de enlaces* fluyen a través de esta canalización de operadores. Una lista de enlaces se puede considerar una tabla en la que los encabezados son un subconjunto de las variables utilizadas en la consulta. Cada fila de la tabla representa un resultado, hasta el punto de evaluación.

Supongamos que se han definido dos prefijos de espacio de nombres para los datos:

```
  @prefix ex:   <http://example.com> .
  @prefix foaf: <http://xmlns.com/foaf/0.1/> .
```

El siguiente sería un ejemplo de una lista de enlaces sencilla en este contexto:

```
  ?person       | ?firstName
  ------------------------------------------------------
  ex:JaneDoe    | "Jane"
  ex:JohnDoe    | "John"
  ex:RichardRoe | "Richard"
```

Para cada una de las tres personas, la lista vincula la variable `?person` a un identificador de la persona y la variable `?firstName` al nombre de la persona.

En el caso general, las variables pueden permanecer sin vincular si, por ejemplo, hay una selección `OPTIONAL` de una variable en una consulta para la que no hay ningún valor presente en los datos.

El operador `PipelineJoin` es un ejemplo de un operador de motor de consultas de Neptune presente en la salida de `explain`. Toma como entrada un conjunto vinculante entrante del operador anterior y lo une a un patrón triple, por ejemplo `(?person, foaf:lastName, ?lastName)`. Esta operación usa los enlaces para la variable `?person` en su secuencia de entrada, los sustituye en el patrón triple y busca los triples en la base de datos.

Cuando se ejecuta en el contexto de los enlaces entrantes de la tabla anterior, `PipelineJoin` evaluará tres búsquedas, en concreto las siguientes:

```
  (ex:JaneDoe,    foaf:lastName, ?lastName)
  (ex:JohnDoe,    foaf:lastName, ?lastName)
  (ex:RichardRoe, foaf:lastName, ?lastName)
```

Este enfoque se denomina evaluación *según vinculación*. Las soluciones de este proceso de evaluación se unen a las soluciones entrantes, rellenando el `?lastName` detectado en estas. Suponiendo que encuentra un apellido para las tres personas, el operador produciría una lista de enlaces saliente que tendría un aspecto similar al siguiente:

```
  ?person       | ?firstName | ?lastName
  ---------------------------------------
  ex:JaneDoe    | "Jane"     | "Doe"
  ex:JohnDoe    | "John"     | "Doe"
  ex:RichardRoe | "Richard"  | "Roe"
```

A continuación, esta lista de enlaces saliente sirve como entrada para el siguiente operador de la canalización. Al final, la salida del último operador de la canalización define el resultado de la consulta.

Las canalizaciones de operadores suelen ser lineales, en el sentido de que cada operador emite soluciones para un único operador conectado. Sin embargo, en algunos casos, pueden tener estructuras más complejas. Por ejemplo, un operador `UNION` de una consulta SPARQL se mapea a una operación `Copy`. Esta operación duplica los enlaces y reenvía las copias en dos subplanes, uno para el lado izquierdo y el otro para el lado derecho de la `UNION`.

Para obtener más información sobre los operadores, consulte [Operadores `explain` de SPARQL en Neptune](sparql-explain-operators.md).

# Cómo utilizar `explain` de SPARQL para analizar la ejecución de consultas de Neptune
<a name="sparql-explain-using"></a>

La característica `explain` de SPARQL es una herramienta de autoservicio de Amazon Neptune que le ayuda a entender el enfoque de ejecución adoptado por el motor de Neptune. Para invocar `explain`, debe pasar un parámetro a una solicitud HTTP o HTTPS con el formato `explain=mode`.

El valor del modo puede ser: `static`, `dynamic` o `details`.
+ En el modo *estático*, `explain` solo imprime la estructura estática del plan de consulta.
+ En el modo *dinámico*, `explain` también incluye aspectos dinámicos del plan de consulta. Estos aspectos pueden incluir el número de enlaces intermedios que fluyen a través de los operadores, la proporción entre los enlaces entrantes y los enlaces salientes y el tiempo total que necesitan los operadores.
+ En el modo de *detalles*, `explain` imprime la información mostrada en el modo `dynamic` más detalles adicionales como la cadena de consulta SPARQL real y el recuento de intervalo estimado para el patrón subyacente de un operador de unión.

Neptune admite el uso de `explain` con los tres protocolos de acceso a consultas de SPARQL que figuran en la especificación del [protocolo W3C SPARQL 1.1](https://www.w3.org/TR/sparql11-protocol/#query-operation), en concreto:

1. HTTP GET

1. HTTP POST con parámetros codificados en URL

1. HTTP POST con parámetros de texto

Para obtener información sobre el motor de consultas de SPARQL, consulte [Funcionamiento del motor de consultas de SPARQL en Neptune](sparql-explain-engine.md).

Para obtener información sobre el tipo de salida producida al invocar a SPARQL `explain`, consulte [Ejemplos de invocación de `explain` de SPARQL en Neptune](sparql-explain-examples.md).

# Ejemplos de invocación de `explain` de SPARQL en Neptune
<a name="sparql-explain-examples"></a>

Los ejemplos de esta sección muestran los diferentes tipos de salida que puede producir al invocar la característica `explain` de SPARQL para analizar la ejecución de consultas en Amazon Neptune.

**Topics**
+ [Descripción de la salida de explain](#sparql-explain-example-output)
+ [Ejemplo de salida del modo de detalles](#sparql-explain-example-details)
+ [Ejemplo de salida del modo estático](#sparql-explain-example-static)
+ [Distintas formas de codificar los parámetros](#sparql-explain-example-parameters)
+ [Otros tipos de salida distintos de text/plain](#sparql-explain-output-options)
+ [Ejemplo de salida `explain` de SPARQL cuando el DFE está habilitado](#sparql-explain-output-dfe)

## Descripción de la salida de explain
<a name="sparql-explain-example-output"></a>

En este ejemplo, Jane Doe conoce a dos personas, John Doe y Richard Roe:

```
@prefix ex: <http://example.com> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .

ex:JaneDoe foaf:knows ex:JohnDoe .
ex:JohnDoe foaf:firstName "John" .
ex:JohnDoe foaf:lastName "Doe" .
ex:JaneDoe foaf:knows ex:RichardRoe .
ex:RichardRoe foaf:firstName "Richard" .
ex:RichardRoe foaf:lastName "Roe" .
.
```

Para determinar los nombres de todas las personas a las que conoce Jane Doe, puede escribir la siguiente consulta:

```
 curl http(s)://your_server:your_port/sparql \
   -d "query=PREFIX foaf: <https://xmlns.com/foaf/0.1/> PREFIX ex: <https://www.example.com/> \
       SELECT ?firstName WHERE { ex:JaneDoe foaf:knows ?person . ?person foaf:firstName ?firstName }" \
   -H "Accept: text/csv"
```

Esta consulta sencilla devuelve lo siguiente:

```
firstName
John
Richard
```

A continuación, cambie el comando `curl` para invocar a `explain` añadiendo `-d "explain=dynamic"` y utilizando el tipo de salida predeterminado en lugar de `text/csv`:

```
 curl http(s)://your_server:your_port/sparql \
   -d "query=PREFIX foaf: <https://xmlns.com/foaf/0.1/> PREFIX ex: <https://www.example.com/> \
       SELECT ?firstName WHERE { ex:JaneDoe foaf:knows ?person . ?person foaf:firstName ?firstName }" \
   -d "explain=dynamic"
```

La consulta ahora devuelve la salida en formato ASCII bien escrito (tipo de contenido HTTP `text/plain`), que es el tipo de salida predeterminado:

```
╔════╤════════╤════════╤═══════════════════╤═══════════════════════════════════════════════════════╤══════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments                                             │ Mode     │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════╪═══════════════════════════════════════════════════════╪══════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]                                        │ -        │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ PipelineJoin      │ pattern=distinct(ex:JaneDoe, foaf:knows, ?person)     │ -        │ 1        │ 2         │ 2.00  │ 1         ║
║    │        │        │                   │ joinType=join                                         │          │          │           │       │           ║
║    │        │        │                   │ joinProjectionVars=[?person]                          │          │          │           │       │           ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 3      │ -      │ PipelineJoin      │ pattern=distinct(?person, foaf:firstName, ?firstName) │ -        │ 2        │ 2         │ 1.00  │ 1         ║
║    │        │        │                   │ joinType=join                                         │          │          │           │       │           ║
║    │        │        │                   │ joinProjectionVars=[?person, ?firstName]              │          │          │           │       │           ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 4      │ -      │ Projection        │ vars=[?firstName]                                     │ retain   │ 2        │ 2         │ 1.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ -      │ -      │ TermResolution    │ vars=[?firstName]                                     │ id2value │ 2        │ 2         │ 1.00  │ 1         ║
╚════╧════════╧════════╧═══════════════════╧═══════════════════════════════════════════════════════╧══════════╧══════════╧═══════════╧═══════╧═══════════╝
```

Para obtener más información sobre las operaciones en la columna `Name` y sus argumentos, consulte [Operadores explain](sparql-explain-operators.md).

A continuación, se describe la salida fila por fila:

1. El primer paso de la consulta principal siempre utiliza el operador `SolutionInjection` para inyectar una solución. A continuación, la solución se expande al resultado final a través del proceso de evaluación.

   En este caso, se inyecta lo que se denomina la solución universal `{ }`. En la presencia de cláusulas `VALUES` o un valor `BIND`, este paso también podría inyectar enlaces de variables más complejos para comenzar.

   La columna `Units Out` indica que esta solución única fluye del operador. La columna `Out #1` especifica el operador en el que este operador inyecta el resultado. En este ejemplo, todos los operadores están conectados al operador que se indica a continuación en la tabla.

1. El segundo paso es un `PipelineJoin`. Recibe como entrada la solución universal (sin restricciones) única producida por el operador anterior (`Units In := 1`). Lo une al patrón de tuplas definido por su argumento `pattern`. Esto corresponde a una búsqueda sencilla del patrón. En este caso, el patrón triple se define del modo siguiente:

   ```
   distinct( ex:JaneDoe, foaf:knows, ?person )
   ```

   El argumento `joinType := join` indica que esta es una unión normal (otros tipos incluyen uniones `optional`, `existence check`, etc.).

   El argumento `distinct := true` dice que solo se extraen coincidencias distintas de la base de datos (sin duplicados) y que dichas coincidencias se enlazan a la variable `joinProjectionVars := ?person`, desduplicadas.

   El hecho de que el valor de la columna `Units Out` sea 2 indica que hay dos soluciones fluyendo. En concreto, se trata de los enlaces para la variable `?person`, que reflejan las dos personas que los datos muestran que Jane Doe conoce:

   ```
    ?person
    -------------
    ex:JohnDoe
    ex:RichardRoe
   ```

1. Las dos soluciones de la etapa 2 fluyen como entrada (`Units In := 2`) en la segunda `PipelineJoin`. Este operador une las dos soluciones anteriores con el siguiente patrón triple:

   ```
   distinct(?person, foaf:firstName, ?firstName)
   ```

   Se sabe que la variable `?person` está enlazada a `ex:JohnDoe` o a `ex:RichardRoe` por la solución entrante del operador. Teniendo en cuenta esto, `PipelineJoin` extrae los nombres, John y Richard. Las dos soluciones salientes (Units Out := 2) son las siguientes:

   ```
    ?person       | ?firstName
    ---------------------------
    ex:JohnDoe    | John
    ex:RichardRoe | Richard
   ```

1. El siguiente operador de proyección toma como entrada la dos soluciones de la etapa 3 (`Units In := 2`) y las proyecta en la variable `?firstName`. Esto elimina el resto de enlaces de variables en los mapeos y pasa los dos enlaces (`Units Out := 2`):

   ```
    ?firstName
    ----------
    John
    Richard
   ```

1. Para mejorar el rendimiento, Neptune funciona siempre que es posible con identificadores internos que asigna a términos como URIs y literales de cadena, en lugar de hacerlo en las propias cuerdas. El operador final, `TermResolution`, vuelve a mapear estos identificadores internos a las cadenas de términos correspondientes.

   En una evaluación de consulta normal (que no es de explain), el resultado calculado por el último operador se serializa en el formato de serialización solicitado y se transmite en streaming al cliente.

## Ejemplo de salida del modo de detalles
<a name="sparql-explain-example-details"></a>

Supongamos que ejecuta la misma consulta que la anterior en modo de *detalles* en lugar de hacerlo en modo *dinámico*:

```
 curl http(s)://your_server:your_port/sparql \
   -d "query=PREFIX foaf: <https://xmlns.com/foaf/0.1/> PREFIX ex: <https://www.example.com/> \
       SELECT ?firstName WHERE { ex:JaneDoe foaf:knows ?person . ?person foaf:firstName ?firstName }" \
   -d "explain=details"
```

Como muestra este ejemplo, la salida es la misma con algunos detalles adicionales como la cadena de consulta en la parte superior de la salida y el recuento de `patternEstimate` para el operador `PipelineJoin`:

```
Query:
PREFIX foaf: <https://xmlns.com/foaf/0.1/> PREFIX ex: <https://www.example.com/>
SELECT ?firstName WHERE { ex:JaneDoe foaf:knows ?person . ?person foaf:firstName ?firstName }

╔════╤════════╤════════╤═══════════════════╤═══════════════════════════════════════════════════════╤══════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments                                             │ Mode     │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════╪═══════════════════════════════════════════════════════╪══════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]                                        │ -        │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ PipelineJoin      │ pattern=distinct(ex:JaneDoe, foaf:knows, ?person)     │ -        │ 1        │ 2         │ 2.00  │ 13        ║
║    │        │        │                   │ joinType=join                                         │          │          │           │       │           ║
║    │        │        │                   │ joinProjectionVars=[?person]                          │          │          │           │       │           ║
║    │        │        │                   │ patternEstimate=2                                     │          │          │           │       │           ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 3      │ -      │ PipelineJoin      │ pattern=distinct(?person, foaf:firstName, ?firstName) │ -        │ 2        │ 2         │ 1.00  │ 3         ║
║    │        │        │                   │ joinType=join                                         │          │          │           │       │           ║
║    │        │        │                   │ joinProjectionVars=[?person, ?firstName]              │          │          │           │       │           ║
║    │        │        │                   │ patternEstimate=2                                     │          │          │           │       │           ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 4      │ -      │ Projection        │ vars=[?firstName]                                     │ retain   │ 2        │ 2         │ 1.00  │ 1         ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ -      │ -      │ TermResolution    │ vars=[?firstName]                                     │ id2value │ 2        │ 2         │ 1.00  │ 7         ║
╚════╧════════╧════════╧═══════════════════╧═══════════════════════════════════════════════════════╧══════════╧══════════╧═══════════╧═══════╧═══════════╝
```

## Ejemplo de salida del modo estático
<a name="sparql-explain-example-static"></a>

Supongamos que ejecuta la consulta anterior en modo *estático* (el valor predeterminado) en lugar de en modo de *detalles*:

```
 curl http(s)://your_server:your_port/sparql \
   -d "query=PREFIX foaf: <https://xmlns.com/foaf/0.1/> PREFIX ex: <https://www.example.com/> \
       SELECT ?firstName WHERE { ex:JaneDoe foaf:knows ?person . ?person foaf:firstName ?firstName }" \
   -d "explain=static"
```

Tal como muestra este ejemplo, el resultado es el mismo, salvo que omite las tres últimas columnas:

```
╔════╤════════╤════════╤═══════════════════╤═══════════════════════════════════════════════════════╤══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments                                             │ Mode     ║
╠════╪════════╪════════╪═══════════════════╪═══════════════════════════════════════════════════════╪══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]                                        │ -        ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────╢
║ 1  │ 2      │ -      │ PipelineJoin      │ pattern=distinct(ex:JaneDoe, foaf:knows, ?person)     │ -        ║
║    │        │        │                   │ joinType=join                                         │          ║
║    │        │        │                   │ joinProjectionVars=[?person]                          │          ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────╢
║ 2  │ 3      │ -      │ PipelineJoin      │ pattern=distinct(?person, foaf:firstName, ?firstName) │ -        ║
║    │        │        │                   │ joinType=join                                         │          ║
║    │        │        │                   │ joinProjectionVars=[?person, ?firstName]              │          ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────╢
║ 3  │ 4      │ -      │ Projection        │ vars=[?firstName]                                     │ retain   ║
╟────┼────────┼────────┼───────────────────┼───────────────────────────────────────────────────────┼──────────╢
║ 4  │ -      │ -      │ TermResolution    │ vars=[?firstName]                                     │ id2value ║
╚════╧════════╧════════╧═══════════════════╧═══════════════════════════════════════════════════════╧══════════╝
```

## Distintas formas de codificar los parámetros
<a name="sparql-explain-example-parameters"></a>

Las siguientes consultas de ejemplo ilustran dos formas distintas de codificar parámetros al invocar la característica `explain` de SPARQL.

**Uso de la codificación URL***: en este ejemplo, se utiliza la codificación URL de parámetros y se especifica la salida dinámica*:

```
curl -XGET "http(s)://your_server:your_port/sparql?query=SELECT%20*%20WHERE%20%7B%20%3Fs%20%3Fp%20%3Fo%20%7D%20LIMIT%20%31&explain=dynamic"
```

**Especificación de los parámetros directamente**: es igual que en la consulta anterior, excepto que pasa los parámetros directamente a través de POST:

```
 curl http(s)://your_server:your_port/sparql \
   -d "query=SELECT * WHERE { ?s ?p ?o } LIMIT 1" \
   -d "explain=dynamic"
```

## Otros tipos de salida distintos de text/plain
<a name="sparql-explain-output-options"></a>

Los ejemplos anteriores utilizan el tipo de salida `text/plain` predeterminado. Neptune también puede formatear la salida `explain` de SPARQL en otros dos formatos de tipo MIME, es decir, `text/csv` y `text/html`. Para invocarlos, establezca el encabezado HTTP `Accept` mediante la marca `-H` en `curl`, tal y como se indica a continuación:

```
  -H "Accept: output type"
```

Estos son algunos ejemplos:

**Salida `text/csv`**  
Esta consulta solicita un salida CSV de tipo MIME especificando `-H "Accept: text/csv"`:

```
 curl http(s)://your_server:your_port/sparql \
   -d "query=SELECT * WHERE { ?s ?p ?o } LIMIT 1" \
   -d "explain=dynamic" \
   -H "Accept: text/csv"
```

El formato CSV, que resulta útil para la importación en una hoja de cálculo o una base de datos, separa los campos de cada fila de `explain` mediante punto y coma (`;`), de la siguiente manera:

```
ID;Out #1;Out #2;Name;Arguments;Mode;Units In;Units Out;Ratio;Time (ms)
0;1;-;SolutionInjection;solutions=[{}];-;0;1;0.00;0
1;2;-;PipelineJoin;pattern=distinct(?s, ?p, ?o),joinType=join,joinProjectionVars=[?s, ?p, ?o];-;1;6;6.00;1
2;3;-;Projection;vars=[?s, ?p, ?o];retain;6;6;1.00;2
3;-;-;Slice;limit=1;-;1;1;1.00;1
```

 

**Salida `text/html`**  
Si se especifica `-H "Accept: text/html"`, `explain` genera una tabla HTML:

```
<!DOCTYPE html>
<html>
  <body>
    <table border="1px">
      <thead>
        <tr>
          <th>ID</th>
          <th>Out #1</th>
          <th>Out #2</th>
          <th>Name</th>
          <th>Arguments</th>
          <th>Mode</th>
          <th>Units In</th>
          <th>Units Out</th>
          <th>Ratio</th>
          <th>Time (ms)</th>
        </tr>
      </thead>

      <tbody>
        <tr>
          <td>0</td>
          <td>1</td>
          <td>-</td>
          <td>SolutionInjection</td>
          <td>solutions=[{}]</td>
          <td>-</td>
          <td>0</td>
          <td>1</td>
          <td>0.00</td>
          <td>0</td>
        </tr>

        <tr>
          <td>1</td>
          <td>2</td>
          <td>-</td>
          <td>PipelineJoin</td>
          <td>pattern=distinct(?s, ?p, ?o)<br>
              joinType=join<br>
              joinProjectionVars=[?s, ?p, ?o]</td>
          <td>-</td>
          <td>1</td>
          <td>6</td>
          <td>6.00</td>
          <td>1</td>
        </tr>

        <tr>
          <td>2</td>
          <td>3</td>
          <td>-</td>
          <td>Projection</td>
          <td>vars=[?s, ?p, ?o]</td>
          <td>retain</td>
          <td>6</td>
          <td>6</td>
          <td>1.00</td>
          <td>2</td>
        </tr>

        <tr>
          <td>3</td>
          <td>-</td>
          <td>-</td>
          <td>Slice</td>
          <td>limit=1</td>
          <td>-</td>
          <td>1</td>
          <td>1</td>
          <td>1.00</td>
          <td>1</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>
```

El HTML representa en un navegador algo parecido a lo siguiente:

![\[Ejemplo de salida HTML de explain de SPARQL.\]](http://docs.aws.amazon.com/es_es/neptune/latest/userguide/images/sparql-explain-dynamic-html-output.png)


## Ejemplo de salida `explain` de SPARQL cuando el DFE está habilitado
<a name="sparql-explain-output-dfe"></a>

El siguiente es un ejemplo de una salida `explain` de SPARQ cuando el motor de consultas alternativo DFE de Neptune está habilitado:

```
╔════╤════════╤════════╤═══════════════════╤═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╤══════════╤══════════╤═══════════╤═══════╤═══════════╗
║ ID │ Out #1 │ Out #2 │ Name              │ Arguments                                                                                                                                                                                                               │ Mode     │ Units In │ Units Out │ Ratio │ Time (ms) ║
╠════╪════════╪════════╪═══════════════════╪═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╪══════════╪══════════╪═══════════╪═══════╪═══════════╣
║ 0  │ 1      │ -      │ SolutionInjection │ solutions=[{}]                                                                                                                                                                                                          │ -        │ 0        │ 1         │ 0.00  │ 0         ║
╟────┼────────┼────────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 1  │ 2      │ -      │ HashIndexBuild    │ solutionSet=solutionSet1                                                                                                                                                                                                │ -        │ 1        │ 1         │ 1.00  │ 22        ║
║    │        │        │                   │ joinVars=[]                                                                                                                                                                                                             │          │          │           │       │           ║
║    │        │        │                   │ sourceType=pipeline                                                                                                                                                                                                     │          │          │           │       │           ║
╟────┼────────┼────────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 2  │ 3      │ -      │ DFENode           │ DFE Stats=                                                                                                                                                                                                                    │ -        │ 101      │ 100       │ 0.99  │ 32        ║
║    │        │        │                   │ ====> DFE execution time (measured by DFEQueryEngine)                                                                                                                                                                   │          │          │           │       │           ║
║    │        │        │                   │ accepted [micros]=127                                                                                                                                                                                                   │          │          │           │       │           ║
║    │        │        │                   │ ready [micros]=2                                                                                                                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │ running [micros]=5627                                                                                                                                                                                                   │          │          │           │       │           ║
║    │        │        │                   │ finished [micros]=0                                                                                                                                                                                                     │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ ===> DFE execution time (measured in DFENode)                                                                                                                                                                           │          │          │           │       │           ║
║    │        │        │                   │ -> setupTime [ms]=1                                                                                                                                                                                                     │          │          │           │       │           ║
║    │        │        │                   │ -> executionTime [ms]=14                                                                                                                                                                                                │          │          │           │       │           ║
║    │        │        │                   │ -> resultReadTime [ms]=0                                                                                                                                                                                                │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ ===> Static analysis statistics                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ --> 35907 micros spent in parser.                                                                                                                                                                                       │          │          │           │       │           ║
║    │        │        │                   │ --> 7643 micros spent in range count estimation                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ --> 2895 micros spent in value resolution                                                                                                                                                                               │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ --> 39974925 micros spent in optimizer loop                                                                                                                                                                             │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ DFEJoinGroupNode[ children={                                                                                                                                                                                            │          │          │           │       │           ║
║    │        │        │                   │   DFEPatternNode[(?1, TERM[117442062], ?2, ?3) . project DISTINCT[?1, ?2] {rangeCountEstimate=100},                                                                                                                     │          │          │           │       │           ║
║    │        │        │                   │     OperatorInfoWithAlternative[                                                                                                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │       rec=OperatorInfo[                                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │         type=INCREMENTAL_PIPELINE_JOIN,                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │         costEstimates=OperatorCostEstimates[                                                                                                                                                                            │          │          │           │       │           ║
║    │        │        │                   │           costEstimate=OperatorCostEstimate[in=1.0000,out=100.0000,io=0.0002,comp=0.0000,mem=0],                                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │           worstCaseCostEstimate=OperatorCostEstimate[in=1.0000,out=100.0000,io=0.0002,comp=0.0000,mem=0]]],                                                                                                             │          │          │           │       │           ║
║    │        │        │                   │       alt=OperatorInfo[                                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │         type=INCREMENTAL_HASH_JOIN,                                                                                                                                                                                     │          │          │           │       │           ║
║    │        │        │                   │         costEstimates=OperatorCostEstimates[                                                                                                                                                                            │          │          │           │       │           ║
║    │        │        │                   │           costEstimate=OperatorCostEstimate[in=1.0000,out=100.0000,io=0.0003,comp=0.0000,mem=3212],                                                                                                                     │          │          │           │       │           ║
║    │        │        │                   │           worstCaseCostEstimate=OperatorCostEstimate[in=1.0000,out=100.0000,io=0.0003,comp=0.0000,mem=3212]]]]],                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │   DFEPatternNode[(?1, TERM[150997262], ?4, ?5) . project DISTINCT[?1, ?4] {rangeCountEstimate=100},                                                                                                                     │          │          │           │       │           ║
║    │        │        │                   │     OperatorInfoWithAlternative[                                                                                                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │       rec=OperatorInfo[                                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │         type=INCREMENTAL_HASH_JOIN,                                                                                                                                                                                     │          │          │           │       │           ║
║    │        │        │                   │         costEstimates=OperatorCostEstimates[                                                                                                                                                                            │          │          │           │       │           ║
║    │        │        │                   │           costEstimate=OperatorCostEstimate[in=100.0000,out=100.0000,io=0.0003,comp=0.0000,mem=6400],                                                                                                                   │          │          │           │       │           ║
║    │        │        │                   │           worstCaseCostEstimate=OperatorCostEstimate[in=100.0000,out=100.0000,io=0.0003,comp=0.0000,mem=6400]]],                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │       alt=OperatorInfo[                                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │         type=INCREMENTAL_PIPELINE_JOIN,                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │         costEstimates=OperatorCostEstimates[                                                                                                                                                                            │          │          │           │       │           ║
║    │        │        │                   │           costEstimate=OperatorCostEstimate[in=100.0000,out=100.0000,io=0.0010,comp=0.0000,mem=0],                                                                                                                      │          │          │           │       │           ║
║    │        │        │                   │           worstCaseCostEstimate=OperatorCostEstimate[in=100.0000,out=100.0000,io=0.0010,comp=0.0000,mem=0]]]]]                                                                                                          │          │          │           │       │           ║
║    │        │        │                   │ },                                                                                                                                                                                                                      │          │          │           │       │           ║
║    │        │        │                   │ ]                                                                                                                                                                                                                       │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ ===> DFE configuration:                                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │ solutionChunkSize=5000                                                                                                                                                                                                  │          │          │           │       │           ║
║    │        │        │                   │ ouputQueueSize=20                                                                                                                                                                                                       │          │          │           │       │           ║
║    │        │        │                   │ numComputeCores=3                                                                                                                                                                                                       │          │          │           │       │           ║
║    │        │        │                   │ maxParallelIO=10                                                                                                                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │ numInitialPermits=12                                                                                                                                                                                                    │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ ====> DFE configuration (reported back)                                                                                                                                                                                 │          │          │           │       │           ║
║    │        │        │                   │ numComputeCores=3                                                                                                                                                                                                       │          │          │           │       │           ║
║    │        │        │                   │ maxParallelIO=2                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ numInitialPermits=12                                                                                                                                                                                                    │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ ===> Statistics & operator histogram                                                                                                                                                                                    │          │          │           │       │           ║
║    │        │        │                   │ ==> Statistics                                                                                                                                                                                                          │          │          │           │       │           ║
║    │        │        │                   │ -> 3741 / 3668 micros total elapsed (incl. wait / excl. wait)                                                                                                                                                           │          │          │           │       │           ║
║    │        │        │                   │ -> 3741 / 3 millis total elapse (incl. wait / excl. wait)                                                                                                                                                               │          │          │           │       │           ║
║    │        │        │                   │ -> 3741 / 0 secs total elapsed (incl. wait / excl. wait)                                                                                                                                                                │          │          │           │       │           ║
║    │        │        │                   │ ==> Operator histogram                                                                                                                                                                                                  │          │          │           │       │           ║
║    │        │        │                   │ -> 47.66% of total time (excl. wait): pipelineScan (2 instances)                                                                                                                                                        │          │          │           │       │           ║
║    │        │        │                   │ -> 10.99% of total time (excl. wait): merge (1 instances)                                                                                                                                                               │          │          │           │       │           ║
║    │        │        │                   │ -> 41.17% of total time (excl. wait): symmetricHashJoin (1 instances)                                                                                                                                                   │          │          │           │       │           ║
║    │        │        │                   │ -> 0.19% of total time (excl. wait): drain (1 instances)                                                                                                                                                                │          │          │           │       │           ║
║    │        │        │                   │                                                                                                                                                                                                                         │          │          │           │       │           ║
║    │        │        │                   │ nodeId | out0   | out1 | opName            | args                                             | rowsIn | rowsOut | chunksIn | chunksOut | elapsed* | outWait | outBlocked | ratio    | rate* [M/s] | rate [M/s] | %     │          │          │           │       │           ║
║    │        │        │                   │ ------ | ------ | ---- | ----------------- | ------------------------------------------------ | ------ | ------- | -------- | --------- | -------- | ------- | ---------- | -------- | ----------- | ---------- | ----- │          │          │           │       │           ║
║    │        │        │                   │ node_0 | node_2 | -    | pipelineScan      | (?1, TERM[117442062], ?2, ?3) DISTINCT [?1, ?2]  | 0      | 100     | 0        | 1         | 874      | 0       | 0          | Infinity | 0.1144      | 0.1144     | 23.83 │          │          │           │       │           ║
║    │        │        │                   │ node_1 | node_2 | -    | pipelineScan      | (?1, TERM[150997262], ?4, ?5) DISTINCT [?1, ?4]  | 0      | 100     | 0        | 1         | 874      | 0       | 0          | Infinity | 0.1144      | 0.1144     | 23.83 │          │          │           │       │           ║
║    │        │        │                   │ node_2 | node_4 | -    | symmetricHashJoin |                                                  | 200    | 100     | 2        | 2         | 1510     | 73      | 0          | 0.50     | 0.0662      | 0.0632     | 41.17 │          │          │           │       │           ║
║    │        │        │                   │ node_3 | -      | -    | drain             |                                                  | 100    | 0       | 1        | 0         | 7        | 0       | 0          | 0.00     | 0.0000      | 0.0000     | 0.19  │          │          │           │       │           ║
║    │        │        │                   │ node_4 | node_3 | -    | merge             |                                                  | 100    | 100     | 2        | 1         | 403      | 0       | 0          | 1.00     | 0.2481      | 0.2481     | 10.99 │          │          │           │       │           ║
╟────┼────────┼────────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 3  │ 4      │ -      │ HashIndexJoin     │ solutionSet=solutionSet1                                                                                                                                                                                                │ -        │ 100      │ 100       │ 1.00  │ 4         ║
║    │        │        │                   │ joinType=join                                                                                                                                                                                                           │          │          │           │       │           ║
╟────┼────────┼────────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 4  │ 5      │ -      │ Distinct          │ vars=[?s, ?o, ?o1]                                                                                                                                                                                                      │ -        │ 100      │ 100       │ 1.00  │ 9         ║
╟────┼────────┼────────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 5  │ 6      │ -      │ Projection        │ vars=[?s, ?o, ?o1]                                                                                                                                                                                                      │ retain   │ 100      │ 100       │ 1.00  │ 2         ║
╟────┼────────┼────────┼───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────┼──────────┼───────────┼───────┼───────────╢
║ 6  │ -      │ -      │ TermResolution    │ vars=[?s, ?o, ?o1]                                                                                                                                                                                                      │ id2value │ 100      │ 100       │ 1.00  │ 11        ║
╚════╧════════╧════════╧═══════════════════╧═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╧══════════╧══════════╧═══════════╧═══════╧═══════════╝
```

# Operadores `explain` de SPARQL en Neptune
<a name="sparql-explain-operators"></a>

En las siguientes secciones, se describen los operadores y los parámetros de la característica `explain` de SPARQL disponible actualmente en Amazon Neptune.

**importante**  
La característica `explain` de SPARQL está en proceso de mejora. Los operadores y los parámetros que se documentan aquí pueden cambiar en versiones futuras.

**Topics**
+ [operador `Aggregation`](#sparql-explain-operator-aggregation)
+ [operador `ConditionalRouting`](#sparql-explain-operator-conditional-routing)
+ [operador `Copy`](#sparql-explain-operator-copy)
+ [operador `DFENode`](#sparql-explain-operator-dfenode)
+ [operador `Distinct`](#sparql-explain-operator-distinct)
+ [operador `Federation`](#sparql-explain-operator-federation)
+ [operador `Filter`](#sparql-explain-operator-filter)
+ [operador `HashIndexBuild`](#sparql-explain-operator-hash-index-build)
+ [operador `HashIndexJoin`](#sparql-explain-operator-hash-index-join)
+ [operador `MergeJoin`](#sparql-explain-operator-merge-join)
+ [operador `NamedSubquery`](#sparql-explain-operator-named-subquery)
+ [operador `PipelineJoin`](#sparql-explain-operator-pipeline-join)
+ [operador `PipelineCountJoin`](#sparql-explain-operator-pipeline-count-join)
+ [operador `PipelinedHashIndexJoin`](#sparql-explain-operator-pipeline-hash-index-join)
+ [operador `Projection`](#sparql-explain-operator-projection)
+ [operador `PropertyPath`](#sparql-explain-operator-property-path)
+ [operador `TermResolution`](#sparql-explain-operator-term-resolution)
+ [operador `Slice`](#sparql-explain-operator-slice)
+ [operador `SolutionInjection`](#sparql-explain-operator-solution-injection)
+ [operador `Sort`](#sparql-explain-operator-sort)
+ [operador `VariableAlignment`](#sparql-explain-operator-variable-alignment)

## operador `Aggregation`
<a name="sparql-explain-operator-aggregation"></a>

Realiza una o varias agregaciones, implementando la semántica de los operadores de agregación de SPARQL como `count`, `max`, `min`, `sum`, etc.

`Aggregation` incluye una agrupación opcional que utiliza cláusulas `groupBy` y restricciones `having` opcionales.

**Argumentos**
+ `groupBy`: (*opcional*) proporciona una cláusula `groupBy` que especifica la secuencia de expresiones según la cual se agrupan las soluciones entrantes.
+ `aggregates`: (*obligatorio*) especifica una lista ordenada de expresiones de agregación.
+ `having`: (*opcional*) añade restricciones para filtrar los grupos, tal y como se implica en la cláusula `having` de la consulta de SPARQL.

## operador `ConditionalRouting`
<a name="sparql-explain-operator-conditional-routing"></a>

Direcciona las soluciones entrantes en función de una condición determinada. Las soluciones que cumplen la condición se direccionan al ID de operador al que hace referencia `Out #1`, mientras que las soluciones que no la cumplen se direccionan al operador al que hace referencia `Out #2`.

**Argumentos**
+ `condition`: (*obligatorio*) la condición de enrutamiento.

## operador `Copy`
<a name="sparql-explain-operator-copy"></a>

Delega el flujo de la solución tal y como indica el modo especificado.

**Modos**
+ `forward`: reenvía las soluciones al operador posterior identificado por `Out #1`. 
+ `duplicate`: duplica las soluciones y las reenvía a cada uno de los dos operadores identificados mediante `Out #1` y `Out #2`.

`Copy` no tiene argumentos.

## operador `DFENode`
<a name="sparql-explain-operator-dfenode"></a>

Este operador es una abstracción del plan que ejecuta el motor de consultas alternativo del DFE. El plan detallado del DFE se describe en los argumentos de este operador. Actualmente, el argumento está sobrecargado para contener las estadísticas de tiempo de ejecución detalladas del plan del DFE. Contiene el tiempo empleado en los distintos pasos de la ejecución de la consulta por parte del DFE.

El árbol de sintaxis abstracta (AST) lógico optimizado para el plan de consultas del DFE se imprime con información sobre los tipos de operadores que se tuvieron en cuenta en la planificación y los costos de funcionamiento de los operadores, en el mejor y peor de los casos. Por el momento, el AST consta de los siguientes tipos de nodos:
+ `DFEJoinGroupNode`: representa una unión de uno o más `DFEPatternNodes`.
+ `DFEPatternNode`: encapsula un patrón subyacente mediante el cual las tuplas coincidentes se proyectan fuera de la base de datos subyacente.

La subsección, `Statistics & Operator histogram`, contiene detalles sobre el tiempo de ejecución del plan `DataflowOp` y el desglose del tiempo de CPU utilizado por cada operador. Debajo hay una tabla que muestra estadísticas detalladas de tiempo de ejecución del plan ejecutado por el DFE.

**nota**  
Dado que el DFE es una característica experimental publicada en el modo de laboratorio, el formato exacto de la salida de `explain` está sujeto a cambios.

## operador `Distinct`
<a name="sparql-explain-operator-distinct"></a>

Calcula la proyección distintiva en un subconjunto de las variables, eliminando los duplicados. Como resultado, el número de soluciones entrantes es mayor o igual que el número de soluciones salientes.

**Argumentos**
+ `vars`: (*obligatorio*) las variables a las que se va a aplicar la proyección `Distinct`.

## operador `Federation`
<a name="sparql-explain-operator-federation"></a>

Pasa una consulta especificada a un punto de enlace SPARQL remoto especificado.

**Argumentos**
+ `endpoint`: (*obligatorio*) la URL del punto de conexión de la instrucción `SERVICE` de SPARQL. Puede ser una cadena constante o si el punto de enlace de consulta se determina en función de una variable dentro de la misma consulta, puede ser el nombre de la variable.
+ `query`: (*obligatorio*) la cadena de consulta reconstruida que se enviará al punto de conexión remoto. El motor añade prefijos predeterminados a esta consulta incluso cuando el cliente no especifica ninguno.
+ `silent`: (*obligatorio*) un booleano que indica si la palabra clave `SILENT` ha aparecido después de la palabra clave. `SILENT` le dice al motor que no falle en toda la consulta, incluso si la parte `SERVICE` remota falla.

## operador `Filter`
<a name="sparql-explain-operator-filter"></a>

Filtra las soluciones entrantes. Solo las soluciones que satisfacen la condición de filtro se reenvían al operador anterior; todas las demás se descartan.

**Argumentos**
+ `condition`: (*obligatorio*) el estado del filtro.

## operador `HashIndexBuild`
<a name="sparql-explain-operator-hash-index-build"></a>

Toma una lista de enlaces y los incorpora a un índice hash cuyo nombre viene definido por el argumento `solutionSet`. Normalmente, los operadores posteriores realizan uniones en este conjunto de soluciones, haciendo referencia a él por ese nombre.

**Argumentos**
+ `solutionSet`: (*obligatorio*) el nombre del conjunto de soluciones de índice hash.
+ `sourceType`: (*obligatorio*) el tipo de origen del que se obtienen los enlaces que se van a almacenar en el índice hash:
  + `pipeline`: reúne las soluciones entrantes desde el operador que hay más abajo en la canalización del operador en el índice hash.
  + `binding set`: coloca el conjunto de enlaces fijos especificado por el argumento `sourceBindingSet` en el índice hash.
+ `sourceBindingSet`: (*opcional*) si el valor del argumento `sourceType` es `binding set`, este argumento especifica el conjunto de enlaces estáticos que se incluirá en el índice hash.

## operador `HashIndexJoin`
<a name="sparql-explain-operator-hash-index-join"></a>

Une las soluciones entrantes con el conjunto de soluciones del índice hash identificado por el argumento `solutionSet`.

**Argumentos**
+ `solutionSet`: (*obligatorio*) nombre del conjunto de soluciones en el que se va a unir. Debe ser un índice hash que se haya construido en un paso anterior con el operador `HashIndexBuild`.
+ `joinType`: (*obligatorio*) tipo de unión que se va a realizar:
  + `join`: unión normal, que requiere una coincidencia exacta de todas las variables compartidas.
  + `optional`: unión `optional` que utiliza la semántica del operador `OPTIONAL` de SPARQL.
  + `minus`: una operación `minus` conserva un mapeo para el que no existe ningún socio de unión, utilizando la semántica del operador `MINUS` de SPARQL.
  + `existence check`: comprueba si hay un socio de unión o no y vincula la variable `existenceCheckResultVar` al resultado de esta comprobación.
+ `constraints`: (*opcional*) restricciones de unión adicionales que se tienen en cuenta durante la unión. Las uniones que no satisfacen estas restricciones se descartan.
+ `existenceCheckResultVar`: (*opcional*) solo se usa para uniones donde `joinType` es igual a `existence check` (consulte el argumento `joinType` anterior).

## operador `MergeJoin`
<a name="sparql-explain-operator-merge-join"></a>

Una unión de fusión entre varios conjuntos de soluciones, que se indican en el argumento `solutionSets`.

**Argumentos**
+ `solutionSets`: (*obligatorio*) conjuntos de la solución que van a unirse.

## operador `NamedSubquery`
<a name="sparql-explain-operator-named-subquery"></a>

Activa la evaluación de la subconsulta indicada por el argumento `subQuery` e incorpora el resultado al conjunto de soluciones especificado por el argumento `solutionSet`. Las soluciones entrantes para el operador se reenvían a la subconsulta y, a continuación, al siguiente operador.

**Argumentos**
+ `subQuery`: (*obligatorio*) nombre de la subconsulta que se va a evaluar. La subconsulta se representa explícitamente en la salida.
+ `solutionSet`: (*obligatorio*) nombre del conjunto de soluciones en el que se almacenará el resultado de la subconsulta.

## operador `PipelineJoin`
<a name="sparql-explain-operator-pipeline-join"></a>

Recibe como entrada la salida del operador anterior y la une con el patrón de tuplas definido por el argumento `pattern`.

**Argumentos**
+ `pattern`— (*Obligatorio*) El patrón, que adopta la forma de una tupla gráfica y subject-predicate-object, opcionalmente, una tupla gráfica que subyace a la unión. Si se especifica `distinct` para el patrón, la unión únicamente extrae soluciones distintas de las variables de proyección especificadas por el argumento `projectionVars`, en lugar de extraer todas las soluciones coincidentes.
+ `inlineFilters`: (*opcional*) conjunto de filtros que se aplicarán a las variables del patrón. El patrón se evalúa junto con estos filtros.
+ `joinType`: (*obligatorio*) tipo de unión que se va a realizar:
  + `join`: unión normal, que requiere una coincidencia exacta de todas las variables compartidas.
  + `optional`: unión `optional` que utiliza la semántica del operador `OPTIONAL` de SPARQL.
  + `minus`: una operación `minus` conserva un mapeo para el que no existe ningún socio de unión, utilizando la semántica del operador `MINUS` de SPARQL.
  + `existence check`: comprueba si hay un socio de unión o no y vincula la variable `existenceCheckResultVar` al resultado de esta comprobación.
+ `constraints`: (*opcional*) restricciones de unión adicionales que se tienen en cuenta durante la unión. Las uniones que no satisfacen estas restricciones se descartan.
+ `projectionVars`: (*opcional*) variables de proyección. Se utiliza junto con `distinct := true` para aplicar la extracción de proyecciones distintas en un conjunto de variables especificado.
+ `cutoffLimit`: (*opcional*) límite del número de socios de unión extraídos. Aunque no existe ningún límite de forma predeterminada, puede establecer este en 1 al realizar uniones para implementar cláusulas `FILTER (NOT) EXISTS`, donde es suficiente con demostrar o refutar que hay un socio de unión.

## operador `PipelineCountJoin`
<a name="sparql-explain-operator-pipeline-count-join"></a>

Variante del operador `PipelineJoin`. En lugar de realizar la unión, solo cuenta los socios de unión coincidentes y vincula el recuento a la variable especificada por el argumento `countVar`.

**Argumentos**
+ `countVar`: (*obligatorio*) variable a la que se debe vincular el resultado del recuento, es decir, el número de socios que se unen.
+ `pattern`— (*Obligatorio*) El patrón, que adopta la forma de una tupla gráfica y subject-predicate-object, opcionalmente, una tupla gráfica que subyace a la unión. Si se especifica `distinct` para el patrón, la unión únicamente extrae soluciones distintas de las variables de proyección especificadas por el argumento `projectionVars`, en lugar de extraer todas las soluciones coincidentes.
+ `inlineFilters`: (*opcional*) conjunto de filtros que se aplicarán a las variables del patrón. El patrón se evalúa junto con estos filtros.
+ `joinType`: (*obligatorio*) tipo de unión que se va a realizar:
  + `join`: unión normal, que requiere una coincidencia exacta de todas las variables compartidas.
  + `optional`: unión `optional` que utiliza la semántica del operador `OPTIONAL` de SPARQL.
  + `minus`: una operación `minus` conserva un mapeo para el que no existe ningún socio de unión, utilizando la semántica del operador `MINUS` de SPARQL.
  + `existence check`: comprueba si hay un socio de unión o no y vincula la variable `existenceCheckResultVar` al resultado de esta comprobación.
+ `constraints`: (*opcional*) restricciones de unión adicionales que se tienen en cuenta durante la unión. Las uniones que no satisfacen estas restricciones se descartan.
+ `projectionVars`: (*opcional*) variables de proyección. Se utiliza junto con `distinct := true` para aplicar la extracción de proyecciones distintas en un conjunto de variables especificado.
+ `cutoffLimit`: (*opcional*) límite del número de socios de unión extraídos. Aunque no existe ningún límite de forma predeterminada, puede establecer este en 1 al realizar uniones para implementar cláusulas `FILTER (NOT) EXISTS`, donde es suficiente con demostrar o refutar que hay un socio de unión.

## operador `PipelinedHashIndexJoin`
<a name="sparql-explain-operator-pipeline-hash-index-join"></a>

Se trata de un índice hash de all-in-one creación y un operador de unión. Toma una lista de enlaces, los agrupa en un índice hash y, a continuación, une las soluciones entrantes con el índice hash.

**Argumentos**
+ `sourceType`: (*obligatorio*) el tipo de origen del que se obtienen los enlaces que se van a almacenar en el índice hash. Es uno de los siguientes:
  + `pipeline`: hace que `PipelinedHashIndexJoin` agrupe las soluciones entrantes desde el operador que está más abajo en la canalización del operador en el índice hash.
  + `binding set`: hace que `PipelinedHashIndexJoin` agrupe el conjunto de enlaces fijos especificado por el argumento `sourceBindingSet` en el índice hash.
+ `sourceSubQuery `: (*opcional*) si el valor del argumento `sourceType` es `pipeline`, este argumento especifica la subconsulta que se evalúa y se incluye en el índice hash.
+ `sourceBindingSet `: (*opcional*) si el valor del argumento `sourceType` es `binding set`, este argumento especifica el conjunto de enlaces estáticos que se incluirá en el índice hash.
+ `joinType`: (*obligatorio*) tipo de unión que se va a realizar:
  + `join`: unión normal, que requiere una coincidencia exacta de todas las variables compartidas.
  + `optional`: unión `optional` que utiliza la semántica del operador `OPTIONAL` de SPARQL.
  + `minus`: una operación `minus` conserva un mapeo para el que no existe ningún socio de unión, utilizando la semántica del operador `MINUS` de SPARQL.
  + `existence check`: comprueba si hay un socio de unión o no y vincula la variable `existenceCheckResultVar` al resultado de esta comprobación.
+ `existenceCheckResultVar`: (*opcional*) solo se usa para uniones donde `joinType` es igual a `existence check` (consulte el argumento joinType anterior).

## operador `Projection`
<a name="sparql-explain-operator-projection"></a>

Realiza una proyección en un subconjunto de las variables. El número de soluciones entrantes es igual al número de soluciones salientes, pero la forma de la solución es diferente, en función de la configuración de modo.

**Modos**
+ `retain`: en las soluciones se conservan únicamente las variables especificadas por el argumento `vars`.
+ `drop`: se eliminan todas las variables especificadas por el argumento `vars`.

**Argumentos**
+ `vars`: (*obligatorio*) las variables que se deben conservar o eliminar, según la configuración del modo.

## operador `PropertyPath`
<a name="sparql-explain-operator-property-path"></a>

Habilita las rutas de propiedades recursivas como `+` o `*`. Neptune implementa un enfoque de iteración de punto fijo basado en una plantilla especificada por el argumento `iterationTemplate`. Las variables del lado izquierdo o del lado derecho conocidas se incluyen en la plantilla para cada iteración de punto fijo hasta que no se encuentren más soluciones nuevas.

**Argumentos**
+ `iterationTemplate`: (*obligatorio*) nombre de la plantilla de subconsulta utilizada para implementar la iteración de punto fijo.
+ `leftTerm`: (*obligatorio*) el término (variable o constante) que se encuentra en el lado izquierdo de la ruta de la propiedad.
+ `rightTerm`: (*obligatorio*) el término (variable o constante) que se encuentra en el lado derecho de la ruta de la propiedad.
+ `lowerBound`: (*obligatorio*) el límite inferior de la iteración de punto fijo (ya sea `0` para consultas `*` o `1` para consultas `+`).

## operador `TermResolution`
<a name="sparql-explain-operator-term-resolution"></a>

Traduce de nuevo los valores de identificador de cadena internos a sus cadenas externas correspondientes o traduce las cadenas externas a valores de identificador de cadena internos, en función del modo.

**Modos**
+ `value2id`— Asigna términos como literales y URIs a los valores de ID internos correspondientes (codificando según los valores internos).
+ `id2value`— Asigna los valores de ID internos a los términos correspondientes, como los literales y URIs (decodificación de los valores internos).

**Argumentos**
+ `vars`— (*Obligatorio*) Especifica las variables cuyas cadenas o cadenas internas IDs deben mapearse.

## operador `Slice`
<a name="sparql-explain-operator-slice"></a>

Implementa un sector en el flujo de soluciones entrantes, utilizando la semántica de las cláusulas `LIMIT` y `OFFSET` de SPARQL.

**Argumentos**
+ `limit`: (*opcional*) límite en las soluciones que se van a reenviar.
+ `offset`: (*opcional*) desfase con el que se evalúan las soluciones para su reenvío.

## operador `SolutionInjection`
<a name="sparql-explain-operator-solution-injection"></a>

No recibe ninguna entrada. Inyecta soluciones de forma estática en el plan de consulta y las registra en el argumento `solutions`.

Los planes de consulta siempre comienzan con esta inyección estática. Si las soluciones estáticas que se van a inyectar se pueden obtener de la propia consulta uniendo distintas fuentes de enlaces estáticos (por ejemplo, de las cláusulas `VALUES` o `BIND`), el operador `SolutionInjection` inyecta estas soluciones estáticas derivadas. En el caso más sencillo, estas soluciones reflejan enlaces que se encuentran implícitos en una cláusula `VALUES` exterior.

Si no se puede obtener ninguna solución estática de la consulta, `SolutionInjection` inyecta lo que se denominada la solución universal vacía, que se expande y multiplica en todo el proceso de evaluación de consultas.

**Argumentos**
+ `solutions`: (*obligatorio*) secuencia de soluciones que inyecta el operador.

## operador `Sort`
<a name="sparql-explain-operator-sort"></a>

Ordena el conjunto de soluciones utilizando las condiciones de ordenación especificadas.

**Argumentos**
+ `sortOrder`: (*obligatorio*) lista ordenada de variables, cada una de las cuales contiene un identificador `ASC` (ascendente) o `DESC` (descendente), que se utiliza secuencialmente para ordenar el conjunto de soluciones.

## operador `VariableAlignment`
<a name="sparql-explain-operator-variable-alignment"></a>

Inspecciona las soluciones de una en una, realizando la alineación en cada una de ellas con respecto a dos variables especificadas: `sourceVar` y `targetVar`.

Si `sourceVar` y `targetVar` tienen el mismo valor en una solución, las variables se consideran alineadas y la solución se reenvía con la variable `sourceVar` redundante proyectada.

Si las variables se vinculan a valores diferentes, la solución se excluye por completo.

**Argumentos**
+ `sourceVar`: (*obligatorio*) variable de origen, que se va a comparar con la variable de destino. Si la alineación se realiza correctamente en una solución, lo que significa que las dos variables tienen el mismo valor, la variable de origen se proyecta.
+ `targetVar`: (*obligatorio*) variable de destino con la que se compara la variable de origen. Se conserva incluso si la alineación se realiza correctamente.

# Limitaciones de `explain` de SPARQL en Neptune
<a name="sparql-explain-limitations"></a>

La versión de la característica `explain` de SPARQL en Neptune tiene las siguientes limitaciones.

**Actualmente, Neptune solo admite explain en las consultas SELECT de SPARQL**  
Para obtener información sobre el proceso de evaluación para otros formatos de consulta, como por ejemplo las consultas `ASK`, `CONSTRUCT`, `DESCRIBE` y `SPARQL UPDATE`, puede transformar estas consultas en una consulta SELECT. A continuación, utilice `explain` para inspeccionar la consulta SELECT correspondiente.

Por ejemplo, para obtener información de `explain` sobre una consulta `ASK WHERE {...}`, ejecute la consulta `SELECT WHERE {...} LIMIT 1` correspondiente con `explain`.

Del mismo modo, para una consulta `CONSTRUCT {...} WHERE {...}`, elimine la parte `CONSTRUCT {...}` y ejecute una consulta `SELECT` con `explain` en la segunda cláusula `WHERE {...}`. Normalmente, la evaluación de la segunda cláusula `WHERE` revela los principales desafíos del procesamiento de la consulta `CONSTRUCT`, ya que las soluciones que fluyen de la segunda cláusula `WHERE` a la plantilla `CONSTRUCT` solo suelen requerir una sustitución sencilla.

**Los operadores de explain pueden cambiar en versiones futuras**  
Los operadores de `explain` de SPARQL y sus parámetros pueden cambiar en versiones futuras.

**La salida de explain puede cambiar en versiones futuras**  
Por ejemplo, los encabezados de columna pueden cambiar y es posible que se añadan más columnas a las tablas.

# Consultas federadas de SPARQL en Neptune mediante la extensión `SERVICE`
<a name="sparql-service"></a>

Amazon Neptune admite totalmente la extensión de consulta federada de SPARQL que utiliza la palabra clave `SERVICE`. Para obtener más información, consulte [Consulta federada de SPARQL 1.1](https://www.w3.org/TR/sparql11-federated-query/).

La palabra clave `SERVICE` indica al motor de consultas SPARQL que ejecute una parte de la consulta en un punto de enlace SPARQL remoto y que componga el resultado final de la consulta. Solo son posibles las operaciones `READ`. Las operaciones `WRITE` y `DELETE` no se admiten. Neptune solo puede ejecutar consultas federadas con respecto a puntos de conexión de SPARQL a los que se pueda acceder desde su nube privada virtual (VPC). Sin embargo, también puede utilizar un proxy inverso en la VPC para hacer accesible un origen de datos externo dentro de la VPC.

**nota**  
Cuando se utiliza `SERVICE` de SPARQ para federar una consulta en dos o más clústeres de Neptune situados en la misma VPC, los grupos de seguridad deben estar configurados de modo que permitan que todos esos clústeres de Neptune se comuniquen entre sí.

**importante**  
La federación SPARQL 1.1 realiza solicitudes de servicio en su nombre al pasar consultas y parámetros a puntos de enlace SPARQL externos. Es su responsabilidad verificar que los puntos de enlace SPARQL externos satisfagan los requisitos de seguridad y gestión de datos de su aplicación.

## Ejemplo de una consulta federada de Neptune
<a name="sparql-service-example-1"></a>

El siguiente ejemplo sencillo muestra cómo funcionan las consultas federadas SPARQL.

Supongamos que un cliente envía la siguiente consulta a *Neptune-1* en `http://neptune-1:8182/sparql`.

```
SELECT * WHERE {
   ?person rdf:type foaf:Person .
   SERVICE <http://neptune-2:8182/sparql> {
       ?person foaf:knows ?friend .
    }
}
```

1. *Neptune-1* evalúa el primer patrón de consulta (*Q-1*), que es `?person rdf:type foaf:Person`, utiliza los resultados para resolver `?person` en *Q-2* (`?person foaf:knows ?friend`) y reenvía el patrón a *Neptune-2* en `http://neptune-2:8182/sparql`.

1. *Neptune-2* evalúa *Q-2* y vuelve a enviar los resultados a *Neptune-1*.

1. *Neptune-1* combina las soluciones para ambos patrones y vuelve a enviar los resultados al cliente.

Este flujo se muestra en el siguiente diagrama.

![\[Diagrama de flujo que muestra los patrones de consultas federadas SPARQL que se evalúan y las respuestas que se vuelven a enviar al cliente.\]](http://docs.aws.amazon.com/es_es/neptune/latest/userguide/images/federated.png)


**nota**  
De forma predeterminada, el optimizador determina en qué punto de la ejecución de la consulta se ejecuta la instrucción `SERVICE`. Puede anular esta ubicación utilizando la sugerencia de consulta [joinOrder](sparql-query-hints-joinOrder.md).

## Control de acceso para consultas federadas en Neptune
<a name="sparql-service-auth"></a>

Neptune usa AWS Identity and Access Management (IAM) para la autenticación y la autorización. El control de acceso de una consulta federada puede implicar más de una instancia de base de datos de Neptune. Estas instancias pueden tener diferentes requisitos de control de acceso. En determinadas circunstancias, esto puede limitar su capacidad para realizar una consulta federada.

Analice el sencillo ejemplo que se presenta en la sección anterior. *Neptune-1* llama a *Neptune-2* con las mismas credenciales con las que se ha llamado.
+ Si *Neptune-1* requiere autenticación y autorización de IAM, pero *Neptune-2* no, lo único que necesita son los permisos de IAM adecuados para que *Neptune-1* realice la consulta federada.
+ Si *Neptune-1* y *Neptune-2* requieren autenticación y autorización de IAM, debe asociar permisos de IAM para ambas bases de datos para realizar la consulta federada. Ambos clústeres también deben estar en la misma AWS cuenta y en la misma región. Actualmente, no se and/or admiten las arquitecturas de consultas federadas entre cuentas y regiones.
+ Sin embargo, en el caso en el que *Neptune-1* no disponga de IAM, pero *Neptune-2* sí, no podrá realizar una consulta federada. El motivo es que *Neptune-1* no puede recuperar sus credenciales de IAM y pasarlas a *Neptune-2* para autorizar la segunda parte de la consulta.