

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.

# Programación con Amazon DocumentDB
<a name="program-docdb"></a>

El servicio admite la validación de esquemas JSON.

**Topics**
+ [Guía de programación de DocumentDB en Java](docdb-java-pg.md)
+ [Cómo utilizar la validación de esquemas JSON](json-schema-validation.md)

# Guía de programación de Amazon DocumentDB en Java
<a name="docdb-java-pg"></a>

Esta guía completa proporciona una guía detallada para trabajar con Amazon DocumentDB mediante los controladores Java de MongoDB y abarca aspectos esenciales de las operaciones y la administración de bases de datos.

**Topics**
+ [Introducción](#java-pg-intro)
+ [Requisitos previos](#java-pg-prereqs)
+ [Modelo de datos](#java-pg-data-models)
+ [Conexión con un controlador Java](java-pg-connect-mongo-driver.md)
+ [Operaciones CRUD con Java](java-crud-operations.md)
+ [Administración de índices con Java](index-management-java.md)
+ [Programación basada en eventos](event-driven-programming.md)

## Introducción
<a name="java-pg-intro"></a>

La guía comienza con la conectividad y explica cómo establecer conexiones seguras a los clústeres de DocumentDB mediante el controlador Java de MongoDB. Detalla los componentes de la cadena de conexión, SSL/TLS la implementación y las diversas opciones de conexión, incluida la autenticación de IAM y la agrupación de conexiones, junto con estrategias sólidas de gestión de errores.

En la sección de operaciones CRUD (crear, leer, actualizar, eliminar) de la guía, se trata exhaustivamente la manipulación de documentos y muestra cómo crear, leer, actualizar y eliminar documentos mediante operaciones únicas o masivas. Se explica el uso de filtros, consultas y diversas opciones de operación, al tiempo que se hace hincapié en las mejores prácticas para la gestión de errores y la implementación de la lógica de reintento para mejorar la fiabilidad. La guía también cubre ampliamente la gestión de índices y detalla la creación y el mantenimiento de diferentes tipos de índices, incluidos los índices de un solo campo, compuestos, dispersos y de texto. Se explica cómo optimizar el rendimiento de las consultas mediante una selección adecuada de los índices y el uso de la función `explain()` para analizar los planes de ejecución de consultas.

La última sección se centra en la programación basada en eventos mediante los flujos de cambios de Amazon DocumentDB y demuestra cómo implementar la supervisión de cambios de datos en tiempo real en aplicaciones Java. Abarca la implementación de cursores de flujo de cambios, la gestión de los tokens de reanudación para un funcionamiento continuo y las operaciones basadas en el tiempo para el procesamiento de datos históricos. A lo largo de la guía, se proporcionan ejemplos de código prácticos y prácticas recomendadas, lo que la convierte en un recurso inestimable para crear aplicaciones sólidas en Java con Amazon DocumentDB.

## Requisitos previos
<a name="java-pg-prereqs"></a>

Antes de empezar, asegúrese de que tiene lo siguiente:
+ Una AWS cuenta con un clúster de DocumentDB configurado. Consulte esta [entrada del blog de introducción](https://aws.amazon.com/blogs/database/part-1-getting-started-with-amazon-documentdb-using-amazon-ec2/) para obtener información sobre la configuración del clúster de DocumentDB.
+ El kit de desarrollo de Java (JDK) está instalado (utilizaremos [Amazon Corretto 21](https://docs.aws.amazon.com/corretto/latest/corretto-21-ug/downloads-list.html) para esta guía).
+ Maven para la administración de dependencias.

## Modelos de datos para esta guía
<a name="java-pg-data-models"></a>

Todo el código de ejemplo de esta guía presupone una conexión a una base de datos de prueba «ProgGuideData» que tiene la colección «Restaurantes». Todos los códigos de muestra de esta guía funcionan en un sistema de listas de restaurantes y, a continuación, se muestra un ejemplo del aspecto de un documento de este sistema:

```
{
    "_id": "ab6ad8f119b5bca3efa2c7ae",
    "restaurantId": "REST-CRT9BL",
    "name": "Thai Curry Palace",
    "description": "Amazing Restaurant, must visit",
    "cuisine": "Thai",
    "address": {
        "street": "914 Park Street",
        "city": "Bryan",
        "state": "AL",
        "zipCode": "96865",
        "location": {
            "type": "Point",
            "coordinates": [-25.4619, 8.389]
        }
    },
    "contact": {
        "phone": "(669) 915-9056 x6657"
    },
    "rating": {
        "average": 3.4,
        "totalReviews": 275
    },
    "priceRange": "$",
    "menu": [{
        "category": "Appetizers",
        "items": [{
            "name": "Buffalo Chicken Wings",
            "price": 13.42
        }]
    }],
    "features": [
        "Private Dining"
    ],
    "isActive": false“ michelin”: {“
        star”: 3,
        “ranking_years”: 4
    }
}
```

Todos los ejemplos de código que muestran las operaciones CRUD, la gestión de índices y la programación basada en eventos suponen que tiene un `dbClient` objeto de [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html), una `connectionDB` objeto de [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoDatabase.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoDatabase.html) y una `collection` objeto de [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#find()](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#find()).

**nota**  
Todos los ejemplos de código de esta guía se han probado con el controlador Java de MongoDB, versión 5.3.0.

# Conexión a Amazon DocumentDB con un controlador Java de MongoDB
<a name="java-pg-connect-mongo-driver"></a>

En esta sección se proporciona una step-by-step guía para conectarse a Amazon DocumentDB mediante controladores Java. Esto le permitirá empezar a integrar DocumentDB en sus aplicaciones Java.

**Topics**
+ [Paso 1: Configuración del proyecto](#step1-set-up)
+ [Paso 2: crear una cadena de conexión](#step2-create-connection-string)
+ [Paso 3: escribir el código de conexión](#step3-write-connect-code)
+ [Paso 4: gestionar excepciones de conexión](#step4-handle-connect-exceptions)
+ [Paso 5: ejecutar el código](#step5-running-code)
+ [Prácticas recomendadas de conexión](#java-connect-best-practices)

## Paso 1: Configuración del proyecto
<a name="step1-set-up"></a>

1. Cree un proyecto java con Maven:

   ```
   mvn archetype:generate -DgroupId=com.docdb.guide -DartifactId=my-docdb-project -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
   ```

1. Añada el controlador Java de MongoDB como una dependencia para el proyecto en su archivo «pom.xml»:

   ```
   <dependency>
       <groupId>org.mongodb</groupId>
       <artifactId>mongodb-driver-sync</artifactId> 
       <version>5.3.0</version> 
   </dependency>
   ```

## Paso 2: crear una cadena de conexión
<a name="step2-create-connection-string"></a>

La cadena de conexión de Amazon DocumentDB es esencial para establecer una conexión entre la aplicación y el clúster de DocumentDB. Esta cadena encapsula información crucial, como el punto de conexión del clúster, el puerto, los detalles de autenticación y varias opciones de conexión. Para crear una cadena de conexión de DocumentDB, normalmente se empieza con el formato básico:

```
"mongodb://username:password@cluster-endpoint:port/?[connection options]"
```

Deberá sustituir los términos «nombre de usuario» y «contraseña» por sus credenciales reales. Puede encontrar el punto final y el número de puerto de su clúster Consola de administración de AWS tanto en el AWS CLI. Consulte [Búsqueda de puntos de conexión de un clúster](db-cluster-endpoints-find.md) para encontrar el punto de conexión del clúster. El puerto 27017 es el puerto predeterminado de DocumentDB.

**Ejemplos de cadenas de conexión**
+ Para establecer una conexión a DocumentDB mediante cifrado en tránsito y asegurarse de que las solicitudes de lectura vayan a las réplicas de lectura y escriban a la principal:

  ```
  "mongodb://username:password@cluster-endpoint:27017/?tls=true& 
     tlsCAFile=global-bundle.pem& 
     readPreference=secondaryPreferred&
     retryWrites=false"
  ```
+ Para establecer una conexión a DocumentDB mediante la autenticación de IAM:

  ```
  "mongodb://cluster-endpoint:27017/?tls=true& 
     tlsCAFile=global-bundle.pem& 
     readPreference=secondaryPreferred&
     retryWrites=false&
     authSource=%24external&
     authMechanism=MONGODB-AWS"
  ```

Las diferentes opciones disponibles para la cadena de conexión son las siguientes:
+ [Certificado TLS](#connection-string-tls)
+ [Lectura a partir de réplicas de lectura](#connection-string-read-rep)
+ [Write concern y registro en diario](#connection-string-write-journal)
+ [RetryWrites](#connection-string-retry-writes)
+ [Autenticación de IAM](#connection-string-iam-auth)
+ [Grupo de conexiones](#connection-string-pool)
+ [Parámetros de tiempo de espera de conexión](#connection-string-timeout)

### Certificado TLS
<a name="connection-string-tls"></a>

**`tls=true|false`**: esta opción habilita o deshabilita la seguridad de la capa de transporte (TLS). De forma predeterminada, el cifrado en tránsito está habilitado en el clúster de Amazon DocumentDB y, por lo tanto, a menos que TLS esté deshabilitada en el nivel del clúster, el valor de esta opción debería ser `true`.

Cuando se utiliza TLS, el código debe proporcionar un certificado SSL cuando crea la conexión a un clúster de DocumentDB. Descargue el certificado necesario para realizar la conexión segura al clúster: [https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem](https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem). Hay dos formas de usar el archivo `global-bundle.pem`.
+ **Opción 1**: extraer todos los certificados del archivo `global-bundle.pem` y usar la herramienta clave de Java para almacenarlos en un archivo `.jks` que luego se pueda usar en el código. Consulte la pestaña Java en [Conexión con TLS habilitado](connect_programmatically.md#connect_programmatically-tls_enabled) para ver el script que muestra cómo hacerlo.
+ **Opción 2**: añadir dinámicamente el archivo `global-bundle.pem` al código, crear un almacén de claves en memoria y usar `SSLContext` para proporcionar el certificado como parte de la conexión.

### Lectura a partir de réplicas de lectura
<a name="connection-string-read-rep"></a>

**`replicaSet=rs0&readPreference=secondaryPreferred`**: si se especifican estas dos opciones, se redirigen todas las solicitudes de lectura a las réplicas de lectura y las solicitudes de escritura a la instancia principal. El uso de `replicaSet=rs0` en la cadena de conexión permite que el controlador MongoDB mantenga una vista actualizada automáticamente de la topología del clúster, lo que permite que las aplicaciones mantengan la visibilidad de las configuraciones de nodos actuales a medida que se añaden o eliminan instancias. Si no se proporcionan estas opciones ni se especifica `readPreference=primary`, se envían todas las lecturas y escrituras a la instancia principal. Para obtener más opciones de `readPreference`, consulte [Opciones de preferencia de lectura](how-it-works.md#durability-consistency-isolation).

### Write concern y registro en diario
<a name="connection-string-write-journal"></a>

La opción Write concern determina el nivel de reconocimiento solicitado a la base de datos para las operaciones de escritura. Los controladores MongoDB ofrecen una opción para ajustar los archivos de Write concern y de diario. Amazon DocumentDB no espera que establezca la opción de Write concern y diario, e ignora los valores enviados para `w` y `j` (`writeConcern` y `journal`). DocumentDB siempre escribe datos con `writeConcern`: `majority` y`journal`: `true` de modo que las escrituras se registren de forma duradera en la gran mayoría de los nodos antes de enviar una confirmación al cliente.

### RetryWrites
<a name="connection-string-retry-writes"></a>

**`retryWrites=false`**: DocumentDB no admite reintentos de escritura y, por lo tanto, este atributo siempre debe estar establecido en `false`.

### Autenticación de IAM
<a name="connection-string-iam-auth"></a>

**`authSource=%24external`y `authMechanism=MONGODB-AWS`**: estos dos parámetros se utilizan para autenticarse mediante AWS Identity and Access Management. En la actualidad, la autenticación de IAM está disponible solamente en la versión 5.0 del clúster basado en instancias. Para obtener más información, consulte [Autenticación mediante la identidad de IAM](iam-identity-auth.md).

### Grupo de conexiones
<a name="connection-string-pool"></a>

Estas opciones están disponibles para la agrupación de conexiones:
+ **`maxPoolSize`**: establece el número máximo de conexiones que se pueden crear en el grupo. Cuando todas las conexiones están en uso y llega una nueva solicitud, espera a que haya una conexión disponible. El valor predeterminado para los controladores Java de MongoDB es 100.
+ **`minPoolSize`**: indica el número mínimo de conexiones que deben mantenerse en el grupo en todo momento. El valor predeterminado para los controladores Java de MongoDB es 0.
+ **`maxIdleTimeMS`**: determina cuánto tiempo puede permanecer inactiva una conexión en el grupo antes de cerrarla y eliminarla. El valor predeterminado para los controladores Java de MongoDB es 100 milisegundos.
+ **`waitQueueTimeoutMS`**: configura cuánto tiempo debe esperar un subproceso para que una conexión esté disponible cuando el grupo esté en su tamaño máximo. Si una conexión no está disponible en este tiempo, se produce una excepción. El valor predeterminado para los controladores Java de MongoDB es 120 000 milisegundos (2 minutos).

### Parámetros de tiempo de espera de conexión
<a name="connection-string-timeout"></a>

El tiempo de espera es un mecanismo para limitar el tiempo que puede tardar una operación o un intento de conexión antes de que se considere fallido. Los siguientes parámetros de tiempo de espera están disponibles para evitar esperas indefinidas y administrar la asignación de recursos:
+ **`connectTimeoutMS`**: configura cuánto tiempo esperará el controlador para establecer una conexión con el clúster. El valor predeterminado es de 10 000 milisegundos (10 segundos).
+ **`socketTimeoutMS`**: especifica cuánto tiempo esperará el controlador para recibir una respuesta del servidor para una operación que no sea de escritura. El valor predeterminado es 0 (sin tiempo de espera o es infinito).
+ **`serverSelectionTimeoutMS`**: especifica cuánto tiempo esperará el controlador para encontrar un servidor disponible en el clúster. El valor predeterminado de esta configuración es de 30 segundos y es suficiente para que se elija una nueva instancia principal durante la conmutación por error.

## Paso 3: escribir el código de conexión
<a name="step3-write-connect-code"></a>

En el siguiente ejemplo de código se muestra cómo establecer una conexión de TLS a Amazon DocumentDB:
+ Crea los objetos [https://docs.oracle.com/javase/8/docs/api/java/security/KeyStore.html](https://docs.oracle.com/javase/8/docs/api/java/security/KeyStore.html) y [`SSLContext`>](https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/SSLContext.html) de Java.
+ También crea el objeto [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoClientSettings.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoClientSettings.html) pasándolo al objeto [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/ConnectionString.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/ConnectionString.html). Para establecer una conexión TLS, debe usar el objeto `MongoClientSettings` para vincular la `connectionstring` y el `sslcontext`.
+ El uso de [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClients.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClients.html) obtiene un objeto [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html).

```
public static MongoClient makeDbConnection(String dbName, String DbUserName, String DbPassword,
    String DbClusterEndPoint, String keyStorePass) throws Exception {
    MongoClient connectedClient;
    String connectionOptions = "?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false";
    String connectionUrl = "mongodb://" + DbUserName + ":" + DbPassword + "@" + DbClusterEndPoint + ":27017/" +
        dbName + connectionOptions;

    try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        try (FileInputStream fis = new FileInputStream("src/main/resources/certs/truststore.jks")) {
            trustStore.load(fis, keyStorePass.toCharArray());
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(trustStore);

            SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
            sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
            ConnectionString connectionString = new ConnectionString(connectionUrl);
            MongoClientSettings settings = MongoClientSettings.builder()
                .applyConnectionString(connectionString)
                .applyToSslSettings(builder - > {
                    builder.enabled(true);
                    builder.context(sslContext);
                })
                .build();
            connectedClient = MongoClients.create(settings);
        }
        return connectedClient;
    } catch (MongoException e5) {
        throw new RuntimeException(e5);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
```

## Paso 4: gestionar excepciones de conexión
<a name="step4-handle-connect-exceptions"></a>

Cuando se trabaja con DocumentDB en aplicaciones Java, el manejo de las excepciones de conexión es crucial para mantener operaciones de base de datos sólidas y fiables. Si se gestionan adecuadamente, estas excepciones no solo ayudan a diagnosticar los problemas rápidamente, sino que también garantizan que la aplicación pueda gestionar sin problemas las interrupciones temporales de la red o la falta de disponibilidad del servidor, lo que mejora la estabilidad y la experiencia del usuario. Algunas de las excepciones más importantes relacionadas con el establecimiento de la conexión son las siguientes:
+ **`MongoException`**: es una excepción general y podría emitirse en varias situaciones no cubiertas por excepciones más específicas. Asegúrese de que esta excepción se gestione después de todas las demás excepciones específicas, ya que se trata de una excepción general de MongoDB.
+ **`MongoTimeoutException`**: se emite cuando se agota el tiempo de espera de una operación. Por ejemplo, cuando consulta un punto de conexión de un clúster que no existe.
+ **`MongoSocketException`**: se emite por problemas relacionados con la red. Por ejemplo, una desconexión repentina de la red durante una operación. 
+ **`MongoSecurityException`**: se emite cuando se produce un error en la autenticación. Por ejemplo, cuando se intenta conectar con credenciales incorrectas. 
+ **`MongoConfigurationException`**: se emite cuando hay un error en la configuración del cliente. Por ejemplo, si se utiliza una cadena de conexión no válida.

## Paso 5: ejecutar el código
<a name="step5-running-code"></a>

El siguiente ejemplo de código crea una conexión a Amazon DocumentDB e imprime todas las bases de datos:

```
public static void TestConnection() {
    try (MongoClient mongoClient = makeDbConnection(DATABASE_NAME, DB_USER_NAME, DB_PASSWORD, DB_CLUSTER_ENDPOINT, KEYSTORE_PASSWORD)) {
        List < String > databases = mongoClient.listDatabaseNames().into(new ArrayList < > ());
        System.out.println("Databases: " + databases);
    } catch (MongoException e) {
        System.err.println("MongoDB error: " + e.getMessage());
        throw new RuntimeException(e);
    }
}
```

## Prácticas recomendadas de conexión
<a name="java-connect-best-practices"></a>

Las siguientes son prácticas recomendadas que se deben tener en cuenta cuando se conecta a Amazon DocumentDB con un controlador Java de MongoDB:
+ Cierre siempre el [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html) cuando ya no necesite que el cliente libere recursos.
+ Gestione las excepciones de forma adecuada e implemente un registro de errores adecuado.
+ Utilice variables de entorno o AWS Secrets Manager almacene información confidencial, como nombres de usuario y contraseñas.

# Realización de operaciones CRUD en Amazon DocumentDB con Java
<a name="java-crud-operations"></a>

En esta sección se describe cómo realizar las operaciones CRUD (crear, leer, actualizar, eliminar) en Amazon DocumentDB con los controladores Java de MongoDB.

**Topics**
+ [Creación e inserción de documentos en una colección de DocumentDB](#creating-inserting)
+ [Lectura y recuperación de datos de una colección de DocumentDB](#reading-retrieving)
+ [Actualización de documentos existentes en una colección de DocumentDB](#updating-documents)
+ [Eliminación de documentos de una colección de DocumentDB](#deleting-documents)
+ [Gestión de errores con lógica de reintento](#error-handling)

## Creación e inserción de documentos en una colección de DocumentDB
<a name="creating-inserting"></a>

La inserción de documentos en Amazon DocumentDB le permite añadir nuevos datos a sus colecciones. Existen varias formas de realizar las inserciones, en función de sus necesidades y del volumen de datos con el que esté trabajando. El método más básico para insertar un documento individual en la colección es `insertOne()`. Para insertar varios documentos a la vez, puede utilizar el método de `insertMany()`, que le permite añadir una serie de documentos en una sola operación. Otro método para insertar muchos documentos en una colección de DocumentDB es `bulkWrite()`. En esta guía, analizamos todos estos métodos para crear documentos en una colección de DocumentDB.

**`insertOne()`**

Empecemos por examinar cómo insertar un documento individual en una colección de Amazon DocumentDB. La inserción de un único documento se realiza mediante el método `insertOne()`. Este método toma un objeto [BsonDocument](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/bson/org/bson/BsonDocument.html)para insertarlo y devuelve un [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/InsertOneResult.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/InsertOneResult.html)objeto que se puede utilizar para obtener el identificador de objeto del nuevo documento insertado. El siguiente código de ejemplo muestra la inserción de un documento de restaurante en la colección:

```
Document article = new Document()
    .append("restaurantId", "REST-21G145")
    .append("name", "Future-proofed Intelligent Bronze Hat")
    .append("cuisine", "International")
    .append("rating", new Document()
        .append("average", 1.8)
        .append("totalReviews", 267))
    .append("features", Arrays.asList("Outdoor Seating", "Live Music"));

try {
    InsertOneResult result = collection.insertOne(article);
    System.out.println("Inserted document with the following id: " + result.getInsertedId());
} catch (MongoWriteException e) {
    // Handle duplicate key or other write errors
    System.err.println("Failed to insert document: " + e.getMessage());
    throw e;
} catch (MongoException e) {
    // Handle other MongoDB errors
    System.err.println("MongoDB error: " + e.getMessage());
    throw e;
}
```

Cuando use `insertOne()`, asegúrese de incluir una gestión de errores adecuada. Por ejemplo, en el código anterior, «`restaurantId`» tiene un índice único y, por lo tanto, cuando vuelva a ejecutar este código, se generará la siguiente `MongoWriteException`:

```
Failed to insert document: Write operation error on server docdbCluster.docdb.amazonaws.com:27017. 
Write error: WriteError{code=11000, message='E11000 duplicate key error collection: Restaurants index: restaurantId_1', details={}}.
```

**InsertMany()**

Los métodos principales utilizados para insertar muchos documentos en una colección son insertMany() y `bulkWrite()`. 

El método `insertMany()` es la forma más sencilla de insertar varios documentos en una sola operación. Acepta una lista de documentos y los inserta en la colección. Este método es ideal cuando se inserta un lote de documentos nuevos que son independientes entre sí y no requieren ningún procesamiento especial ni operaciones mixtas. El siguiente código muestra la lectura de documentos JSON de un archivo y su inserción en la colección. La `insertMany()` función devuelve un [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/InsertManyResult.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/InsertManyResult.html)`InsertManyResult`objeto que se puede utilizar para obtener todos IDs los documentos insertados.

```
// Read JSON file content
String content = new String(Files.readAllBytes(Paths.get(jsonFileName)));
JSONArray jsonArray = new JSONArray(content);

// Convert JSON articles to Documents
List < Document > restaurants = new ArrayList < > ();
for (int i = 0; i < jsonArray.length(); i++) {
    JSONObject jsonObject = jsonArray.getJSONObject(i);
    Document doc = Document.parse(jsonObject.toString());
    restaurants.add(doc);
}
//insert documents in collection
InsertManyResult result = collection.insertMany(restaurants);

System.out.println("Count of inserted documents: " + result.getInsertedIds().size());
```

**[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/package-summary.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/package-summary.html)**

El método `bulkWrite()` permite realizar múltiples operaciones de escritura (insertar, actualizar, eliminar) en un solo lote. Puede usar `bulkWrite()` cuando necesite realizar diferentes tipos de operaciones en un solo lote, como insertar algunos documentos y actualizar otros. `bulkWrite()` admite dos tipos de escritura por lotes, ordenada y desordenada:
+ *Operaciones ordenadas* (predeterminado): Amazon DocumentDB procesa las operaciones de escritura de forma secuencial y se detiene ante el primer error que detecta. Esto resulta útil cuando el orden de las operaciones es importante, por ejemplo, cuando las operaciones posteriores dependen de las anteriores. Sin embargo, las operaciones ordenadas suelen ser más lentas que las desordenadas. En el caso de las operaciones ordenadas, debe abordar el caso de que el lote se detenga ante el primer error, lo que podría dejar algunas operaciones sin procesar.
+ *Operaciones desordenadas*: permite a Amazon DocumentDB procesar las inserciones como una sola ejecución en la base de datos. Si se produce un error en un documento, la operación continúa con el resto de los documentos. Esto resulta especialmente útil cuando se insertan grandes cantidades de datos y se pueden tolerar algunos errores, como durante la migración de datos o las importaciones masivas, donde algunos documentos pueden fallar debido a la duplicación de claves. En el caso de operaciones desordenadas, debe abordar las situaciones de éxito parcial, en los que algunas operaciones se realizan correctamente y otras no.

Al trabajar con el método `bulkWrite()`, se requieren algunas clases esenciales. En primer lugar, la clase [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/WriteModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/WriteModel.html) sirve como clase base para todas las operaciones de escritura y tiene implementaciones específicas como [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/InsertOneModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/InsertOneModel.html), [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateOneModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateOneModel.html), [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateManyModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateManyModel.html), [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/DeleteOneModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/DeleteOneModel.html) y [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/DeleteManyModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/DeleteManyModel.html), que manejan diferentes tipos de operaciones.

La [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/BulkWriteOptions.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/BulkWriteOptions.html)clase es necesaria para configurar el comportamiento de las operaciones masivas, como configurar la ordered/unordered ejecución o omitir la validación de los documentos. La clase [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/BulkWriteResult.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/BulkWriteResult.html) proporciona información detallada sobre los resultados de la ejecución, incluidos los recuentos de documentos insertados, actualizados y eliminados.

Para la gestión de errores, la clase [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoBulkWriteException.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoBulkWriteException.html) es fundamental, ya que contiene información sobre los posibles errores que se produzcan durante la operación masiva, mientras que la clase [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/BulkWriteError.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/BulkWriteError.html) proporciona detalles específicos sobre los errores de las operaciones individuales. El siguiente código muestra un ejemplo de inserción de una lista de documentos, así como de actualización y eliminación de un solo documento, todo ello mediante la ejecución de una llamada a un único método `bulkWrite()`. El código también muestra cómo trabajar con [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/BulkWriteOptions.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/BulkWriteOptions.html) y [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/BulkWriteResult.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/BulkWriteResult.html), así como la correcta gestión de errores de la operación de `bulkWrite()`. 

```
List < WriteModel < Document >> bulkOperations = new ArrayList < > ();
// get list of 10 documents representing 10 restaurants
List < Document > restaurantsToInsert = getSampleData();

for (Document doc: restaurantsToInsert) {
    bulkOperations.add(new InsertOneModel < > (doc));
}
// Update operation
bulkOperations.add(new UpdateOneModel < > (
    new Document("restaurantId", "REST-Y2E9H5"),
    new Document("", new Document("stats.likes", 20))
    .append("", new Document("rating.average", 4.5))));
// Delete operation
bulkOperations.add(new DeleteOneModel < > (new Document("restaurantId", "REST-D2L431")));

// Perform bulkWrite operation
try {
    BulkWriteOptions options = new BulkWriteOptions()
        .ordered(false); // Allow unordered inserts

    BulkWriteResult result = collection.bulkWrite(bulkOperations, options);

    System.out.println("Inserted: " + result.getInsertedCount());
    System.out.println("Updated: " + result.getModifiedCount());
    System.out.println("Deleted: " + result.getDeletedCount());
} catch (MongoBulkWriteException e) {
    System.err.println("Bulk write error occurred: " + e.getMessage());
    // Log individual write errors
    for (BulkWriteError error: e.getWriteErrors()) {
        System.err.printf("Error at index %d: %s (Code: %d)%n", error.getIndex(), error.getMessage(),
            error.getCode());

        // Log the problematic document
        Document errorDoc = new Document(error.getDetails());
        if (errorDoc != null) {
            System.err.println("Problematic document: " + errorDoc);
        }
    }
} catch (Exception e) {
    System.err.println("Error during bulkWrite: " + e.getMessage());
}
```

**Escrituras reintentables**

A diferencia de MongoDB, Amazon DocumentDB no admite el reintento de las escrituras. En consecuencia, debe implementar una lógica de reintento personalizada en sus aplicaciones, especialmente para solucionar problemas de red o la falta temporal de disponibilidad del servicio. Por lo general, una estrategia de reintentos bien implementada implica aumentar la demora entre los reintentos y limitar el número total de reintentos. Consulte [Gestión de errores con lógica de reintento](#error-handling) a continuación para conocer un ejemplo de código sobre cómo crear una lógica de reintentos con gestión de errores.

## Lectura y recuperación de datos de una colección de DocumentDB
<a name="reading-retrieving"></a>

La consulta de documentos en Amazon DocumentDB gira en torno a varios componentes clave que permiten recuperar y manipular datos con precisión. El [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#find()](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#find())método es la consulta fundamental APIs en los controladores Java de MongoDB. Permite la recuperación de datos complejos con numerosas opciones para filtrar, ordenar y proyectar resultados. Además del método `find()`, [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Filters.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Filters.html) y [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/FindIterable.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/FindIterable.html) son otros dos componentes fundamentales que proporcionan los componentes básicos para las operaciones de consulta en los controladores Java de MongoDB.

La clase `Filters` es una clase de utilidad del controlador Java de MongoDB que proporciona una API fluida para generar un constructo de filtros de consultas. Esta clase ofrece métodos de fábrica estáticos que crean instancias de objetos `Bson` que representan diversas condiciones de consulta. Los métodos más comúnmente utilizados son `eq()` para las comparaciones de igualdad, `gt()`, `lt()`, `gte()` y `lte()` para las comparaciones numéricas, `and()` y `or()` para las combinaciones de condiciones múltiples, `in()` y `nin()` para las pruebas de suscripción a matrices y `regex()` para la coincidencia de patrones. La clase está diseñada para ser segura en relación con los tipos y proporciona una mejor comprobación en tiempo de compilación en comparación con las consultas basadas en documentos sin procesar, lo que la convierte en el enfoque preferido para crear consultas de DocumentDB en aplicaciones Java. La gestión de errores es sólida, con claras excepciones en el caso de constructos de filtros no válidas.

`FindIterable` es una interfaz especializada diseñada para gestionar el resultado del método `find()`. Proporciona un amplio conjunto de métodos para refinar y controlar la ejecución de consultas, y ofrece una API fluida para el encadenamiento de métodos. La interfaz incluye métodos esenciales de modificación de consultas, como `limit()` para restringir el número de documentos devueltos, `skip()` para paginar, `sort()` para ordenar los resultados, `projection()` para seleccionar campos específicos y `hint()` para seleccionar índices. Las operaciones de agrupar en lotes, omitir y limitar en `FindIterable` son herramientas esenciales de paginación y administración de datos que ayudan a controlar la forma en que se recuperan y procesan los documentos de la base de datos.

Agrupar en lotes (`batchSize`) controla el número de documentos que DocumentDB devuelve al cliente en una única red de ida y vuelta. Cuando establece un tamaño de lote, DocumentDB no devuelve todos los documentos coincidentes a la vez, sino que los devuelve en grupos del tamaño de lote especificado. 

Omitir le permite desplazar el punto de partida de los resultados y, básicamente, le indica a DocumentDB que omita un número específico de documentos antes de empezar a devolver las coincidencias. Por ejemplo, `skip(20)` omitirá los primeros 20 documentos coincidentes. Esto se suele utilizar en situaciones de paginación en los que se desean recuperar páginas de resultados posteriores. 

Limitar restringe el número total de documentos que se pueden devolver a partir de una consulta. Si especifica `limit(n)`, DocumentDB dejará de devolver documentos después de haber devuelto «n» documentos, incluso si hay más coincidencias en la base de datos. 

`FindIterable` admite patrones de iterador y cursor cuando recupera documentos de Amazon DocumentDB. La ventaja de usar `FindIterable` como iterador es que permite la carga diferida de los documentos y solo los recupera cuando la aplicación los solicita. Otra ventaja de usar el iterador es que no es responsable de mantener la conexión con el clúster y, por lo tanto, no es necesario cerrar la conexión de forma explícita. 

`FindIterable` también proporciona soporte para [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCursor.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCursor.html), que permite utilizar patrones de cursor cuando trabaja con consultas de Amazon DocumentDB. El `MongoCursor` es una implementación específica del controlador Java de MongoDB que proporciona control sobre las operaciones de la base de datos y la administración de recursos. Implementa la `AutoCloseable` interfaz, lo que permite una gestión explícita de los recursos mediante try-with-resources bloques, lo cual es crucial para cerrar correctamente las conexiones de las bases de datos y liberar los recursos del servidor. De forma predeterminada, el tiempo de espera del cursor es de 10 minutos y DocumentDB no le da la opción de cambiar este comportamiento de tiempo de espera. Cuando trabaje con datos agrupados, asegúrese de recuperar el siguiente lote de datos antes de que se agote el tiempo de espera del cursor. Una consideración clave a la hora de usar el `MongoCursor` es que requiere un cierre explícito para evitar la pérdida de recursos.

En esta sección, se presentan varios ejemplos de `find()`, `Filters` y `FindIterable`.

En el siguiente ejemplo de código se muestra cómo usar `find()` para recuperar un solo documento con su campo «restaurantId»:

```
Document filter = new Document("restaurantId", "REST-21G145");
Document result = collection.find(filter).first();
```

Si bien el uso de `Filters` permite comprobar mejor los errores en el momento de la compilación, el controlador java también permite especificar un filtro de `Bson` directamente en el método `find()`. El siguiente código de ejemplo pasa el documento `Bson` a `find()`:

```
result = collection.find(new Document("$and", Arrays.asList(
    new Document("rating.totalReviews", new Document("$gt", 1000)),
    new Document("priceRange", "$$"))))
```

El siguiente código de ejemplo muestra varios ejemplos del uso de la clase `Filters` con `find()`:

```
FindIterable < Document > results;

// Exact match
results = collection.find(Filters.eq("name", "Thai Curry Palace"));

// Not equal
results = collection.find(Filters.ne("cuisine", "Thai"));

// find an element in an array
results = collection.find(Filters.in("features", Arrays.asList("Private Dining")));

// Greater than
results = collection.find(Filters.gt("rating.average", 3.5));

// Between (inclusive)
results = collection.find(Filters.and(
    Filters.gte("rating.totalReviews", 100),
    Filters.lte("rating.totalReviews", 200)));
// AND
results = collection.find(Filters.and(
    Filters.eq("cuisine", "Thai"),
    Filters.gt("rating.average", 4.5)));

// OR
results = collection.find(Filters.or(
    Filters.eq("cuisine", "Thai"),
    Filters.eq("cuisine", "American")));


// All document where the Field exists
results = collection.find(Filters.exists("michelin"));

// Regex
results = collection.find(Filters.regex("name", Pattern.compile("Curry", Pattern.CASE_INSENSITIVE)));

// Find all document where the array contain the list of value regardless of its order
results = collection.find(Filters.all("features", Arrays.asList("Private Dining", "Parking")));

// Array size
results = collection.find(Filters.size("features", 4));
```

En el siguiente ejemplo se muestra cómo encadenar las operaciones de `sort()`, `skip()`, `limit()` y `batchSize()` en un objeto de `FindIterable`. El orden en que se proporcionen estas operaciones influirá en el rendimiento de la consulta. Como práctica recomendada, el orden de estas operaciones debe ser `sort()`, `projection()`, `skip()`, `limit()` y `batchSize()`.

```
FindIterable < Document > results = collection.find(Filters.gt("rating.totalReviews", 1000))
    // Sorting
    .sort(Sorts.orderBy(
        Sorts.descending("address.city"),
        Sorts.ascending("cuisine")))
    // Field selection
    .projection(Projections.fields(
        Projections.include("name", "cuisine", "priceRange"),
        Projections.excludeId()))

    // Pagination
    .skip(20)
    .limit(10)
    .batchSize(2);
```

El siguiente ejemplo de código muestra cómo crear un iterador en `FindIterable`. Usa el constructo `forEach` de Java para recorrer el conjunto de resultados.

```
collection.find(Filters.eq("cuisine", "American")).forEach(doc -> System.out.println(doc.toJson()));
```

En el último ejemplo de código de `find()`, se muestra cómo usar el `cursor()` para la recuperación de documentos. Crea el cursor en el bloque de prueba, lo que garantiza que el cursor se cierre cuando el código salga del bloque de prueba.

```
try (MongoCursor < Document > cursor = collection.find(Filters.eq("cuisine", "American"))
    .batchSize(25)
    .cursor()) {
    while (cursor.hasNext()) {
        Document doc = cursor.next();
        System.out.println(doc.toJson());
    }
} // Cursor automatically closed
```

## Actualización de documentos existentes en una colección de DocumentDB
<a name="updating-documents"></a>

Amazon DocumentDB proporciona mecanismos flexibles y potentes para modificar los documentos existentes e insertar otros nuevos cuando no existen. El controlador Java de MongoDB ofrece varios métodos de actualización: `updateOne()` para actualizaciones de un solo documento, `updateMany()` para actualizaciones de varios documentos y `replaceOne()` para la sustitución completa de documentos. Además de estos tres métodos, [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Updates.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Updates.html), [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateOptions.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateOptions.html) y [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/UpdateResult.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/UpdateResult.html) son otros componentes fundamentales que proporcionan los componentes básicos para las operaciones de actualización en los controladores Java de MongoDB. 

La clase `Updates` del controlador Java de MongoDB es una clase de utilidad que proporciona métodos de fábrica estáticos para crear operadores de actualización. Sirve como el compilador principal para crear operaciones de actualización de forma legible y segura. Métodos básicos como `set()`, `unset()` y `inc()` permiten la modificación directa de los documentos. El poder de esta clase se hace evidente cuando se combinan varias operaciones mediante el método `Updates.combine()`, que permite ejecutar varias operaciones de actualización de forma atómica, lo que garantiza la coherencia de datos.

`UpdateOptions` es una potente clase de configuración del controlador Java de MongoDB que proporciona capacidades de personalización esenciales para las operaciones de actualización de documentos. Dos aspectos importantes de esta clase son la compatibilidad con actualizaciones y filtros de matriz para las operaciones de actualización. La característica de actualización, habilitada mediante `upsert(true)`, permite crear nuevos documentos cuando no se encuentra ningún documento coincidente durante una operación de actualización. Mediante `arrayFilters()`, la operación de actualización puede actualizar con precisión los elementos de la matriz que cumplan criterios específicos.

`UpdateResult` en el controlador Java de MongoDB proporciona el mecanismo de retroalimentación que detalla el resultado de una operación de actualización. Esta clase agrupa tres métricas clave: el número de documentos que cumplen los criterios de actualización (`matchedCount`), el número de documentos realmente modificados (`modifiedCount`) y la información sobre cualquier documento alterado (`upsertedId`). Comprender estas métricas es esencial para gestionar correctamente los errores, verificar las operaciones de actualización y mantener la coherencia de datos en las aplicaciones.

### Actualización y sustitución de un solo documento
<a name="update-single-doc"></a>

En DocumentDB, la actualización de un solo documento se puede realizar mediante el método updateOne(). Este método utiliza un parámetro de filtro, normalmente proporcionado por la clase `Filters`, para identificar el documento que se va a actualizar, un parámetro de `Updat` que determina qué campos se van a actualizar y un parámetro de `UpdateOptions` opcional para establecer diferentes opciones de actualización. El uso del método `updateOne()` solo actualizará el primer documento que coincida con los criterios de selección. El siguiente código de ejemplo actualiza un solo campo de un documento:

```
collection.updateOne(Filters.eq("restaurantId", "REST-Y2E9H5"),
    Updates.set("name", "Amazing Japanese sushi"));
```

Para actualizar varios campos de un documento, use `updateOne()` con `Update.combine()` como se muestra en el siguiente ejemplo. En este ejemplo también se muestra cómo agregar un elemento a una matriz del documento.

```
List<Bson> updates = new ArrayList<>();
// Basic field updates
updates.add(Updates.set("name", "Shanghai Best"));
// Array operations
updates.add(Updates.addEachToSet("features", Arrays.asList("Live Music")));
// Counter updates
updates.add(Updates.inc("rating.totalReviews", 10));
// Combine all updates
Bson combinedUpdates = Updates.combine(updates);
// Execute automic update with one call
collection.updateOne(Filters.eq("restaurantId","REST-1J83NH"), combinedUpdates);
```

En el siguiente ejemplo de código se muestra cómo actualizar un documento de la base de datos. Si el documento especificado no existe, la operación lo insertará automáticamente como documento nuevo. Este código también muestra cómo utilizar las métricas disponibles a través del objeto de `UpdateResult`.

```
Bson filter = Filters.eq("restaurantId", "REST-0Y9GL0");
Bson update = Updates.set("cuisine", "Indian");
// Upsert operation
UpdateOptions options = new UpdateOptions().upsert(true);
UpdateResult result = collection.updateOne(filter, update, options);

if (result.getUpsertedId() != null) {
   	System.out.println("Inserted document with _id: " + result.getUpsertedId());
} else {
    	System.out.println("Updated " + result.getModifiedCount() + " document(s)");
}
```

El siguiente ejemplo de código muestra cómo reemplazar completamente un documento existente por uno nuevo mediante el método `replaceOne()`, en lugar de actualizar campos individuales. El método `replaceOne()` sobrescribe todo el documento y conserva únicamente el campo `_id` del original. Si varios documentos coinciden con los criterios del filtro, solo se reemplaza el primer documento encontrado.

```
Document newDocument = new Document()
                .append("restaurantId", "REST-0Y9GL0")
                .append("name", "Bhiryani Adda")
                .append("cuisine", "Indian")
                .append("rating", new Document()
                        .append("average", 4.8)
                        .append("totalReviews", 267))
                .append("features", Arrays.asList("Outdoor Seating", "Live Music"));

UpdateResult result = collection.replaceOne(
                    Filters.eq("restaurantId", "REST-0Y9GL0"),
                    newDocument);
System.out.printf("Modified %d document%n", result.getModifiedCount());
```

### Actualización de documentos múltiples
<a name="update-multiple-docs"></a>

Hay dos formas de actualizar varios documentos de una colección de forma simultánea. Puede utilizar el método de `updateMany()` o usar el [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateManyModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateManyModel.html) con el método `bulkWrite()`. El método `updateMany()` utiliza un parámetro de filtro para seleccionar los documentos que se van a actualizar, el parámetro `Update` para identificar los campos que se van a actualizar y un parámetro `UpdateOptions` opcional para especificar las opciones de actualización.

El siguiente ejemplo de código demuestra el uso del método `updateMany()`:

```
Bson filter = Filters.and(
    Filters.in("features", Arrays.asList("Private Dining")),
    Filters.eq("cuisine", "Thai"));
UpdateResult result1 = collection.updateMany(filter, Updates.set("priceRange", "$$$"));
```

El siguiente ejemplo de código demuestra el método de `bulkWrite()` con la misma actualización:

```
BulkWriteOptions options = new BulkWriteOptions().ordered(false);
List < WriteModel < Document >> updates = new ArrayList < > ();
Bson filter = Filters.and(
    Filters.in("features", Arrays.asList("Private Dining")),
    Filters.eq("cuisine", "Indian"));
Bson updateField = Updates.set("priceRange", "$$$");
updates.add(new UpdateManyModel < > (filter, updateField));
BulkWriteResult result = collection.bulkWrite(updates, options);
System.out.printf("Modified %d document%n", result.getModifiedCount());
```

## Eliminación de documentos de una colección de DocumentDB
<a name="deleting-documents"></a>

El controlador Java de MongoDB ofrece `deleteOne()` para eliminar un solo documento y `deleteMany()` para eliminar varios documentos que coincidan con criterios específicos. Al igual que la actualización, la operación de eliminación también se puede utilizar con el método `bulkWrite()`. Ambos `deleteOne()` y `deleteMany()` devuelven un objeto [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/DeleteResult.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/DeleteResult.html) que proporciona información sobre el resultado de la operación, incluido el recuento de documentos eliminados. A continuación, se muestra un ejemplo de cómo usar `deleteMany()` para eliminar varios documentos:

```
Bson filter = Filters.and(
    Filters.eq("cuisine", "Thai"),
    Filters.lt("rating.totalReviews", 50));
DeleteResult result = collection.deleteMany(filter);
System.out.printf("Deleted %d document%n", result.getDeletedCount());
```

## Gestión de errores con lógica de reintento
<a name="error-handling"></a>

Una estrategia sólida de gestión de errores con Amazon DocumentDB debería incluir la categorización de los errores en reintentables (como tiempos de espera de la red o problemas de conexión) y no reintentables (como errores de autenticación o consultas no válidas). En el caso de los errores de operación debidos a errores que deben reintentarse, se debe implementar un intervalo de tiempo entre cada reintento, así como un máximo de reintentos. Las operaciones CRUD deben realizarse en un bloque try-catch que capture [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoException.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoException.html) y sus subclases. Además, debe incluir la supervisión y el registro de los errores para garantizar la visibilidad operativa. El siguiente es un ejemplo de código que muestra cómo implementar la gestión de errores de reintentos:

```
int MAX_RETRIES = 3;
int INITIAL_DELAY_MS = 1000;
int retryCount = 0;

while (true) {
    try {
        crud_operation(); //perform crud that will throw MongoException or one of its subclass
        break;
    } catch (MongoException e) {
        if (retryCount < MAX_RETRIES) {
            retryCount++;
            long delayMs = INITIAL_DELAY_MS * (long) Math.pow(2, retryCount - 1);
            try {
                TimeUnit.MILLISECONDS.sleep(delayMs);
            } catch (InterruptedException t) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Retry interrupted", t);
            }
            continue;
        } else
            throw new RuntimeException("Crud operation failed", e);
    }
}
```

# Administración de índices en Amazon DocumentDB con Java
<a name="index-management-java"></a>

Los índices permiten una recuperación eficaz de los datos de una colección de Amazon DocumentDB. Sin índices, DocumentDB debe digitalizar todos los documentos de la colección para obtener resultados que satisfagan una consulta determinada. En este tema se proporciona información sobre cómo crear, eliminar y enumerar índices mediante los controladores Java de MongoDB. También se explica cómo determinar si se está utilizando un índice específico en la consulta y cómo hacer sugerencias a Amazon DocumentDB para que utilice un índice específico.

**Topics**
+ [Creación de índices](#creating-indexes)
+ [Eliminar índices](#dropping-indes)
+ [Determinación de la selección del índice y proporción de una sugerencia sobre el índice](#w2aac43b9b7c17c13)

Amazon DocumentDB admite muchos tipos de índices. Para obtener una descripción general integral de todos los índices compatibles, consulte esta [entrada del blog](https://aws.amazon.com/blogs/database/how-to-index-on-amazon-documentdb-with-mongodb-compatibility/). 

## Creación de índices con Java
<a name="creating-indexes"></a>

Existen dos mecanismos para crear índices en Amazon DocumentDB con controladores Java de MongoDB: mediante `runCommand()`, y mediante el método `createIndex()` para un índice único o el método `createIndexes()` para varios índices. Uno de los motivos para utilizar los métodos `createIndex()` y `createIndexes()` es que permiten gestionar mejor los errores, ya que detecta errores específicos relacionados con la creación de índices. Otra razón para usar estos métodos en lugar de `runCommand()` es que el controlador Java de MongDB proporciona un amplio conjunto de clases admitidas para la creación y manipulación de índices. Tenga en cuenta que estas clases admitidas pueden utilizarse solamente cuando use los métodos `createIndex()` o `createIndexes()`. Hay tres clases admitidas:
+ **[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Indexes.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Indexes.html)**: esta clase sirve como clase de utilidad y ofrece métodos de fábrica estáticos para crear varios tipos de índices. Simplifica el proceso de creación de definiciones de índices complejas y se suele usar junto con otras clases relacionadas con los índices.
+ **[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html)**: se trata de una clase fundamental que condensa tanto la definición de las claves de índice como sus opciones. Representa una especificación de índice completa, que combina qué indexar (las claves) con cómo indexar (las opciones). Esta clase es particularmente útil cuando se crean varios índices simultáneamente, ya que permite definir un conjunto de especificaciones de índices que se pueden pasar al método `createIndexes()`.
+ **[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexOptions.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexOptions.html)**: se trata de una clase de configuración completa que proporciona un amplio conjunto de métodos para personalizar el comportamiento de los índices. Incluye ajustes para índices únicos, índices dispersos, tiempo de caducidad (TTL) y expresiones de filtrado parcial. Mediante el encadenamiento de métodos, puede configurar varias opciones, como la creación de índices en segundo plano y las restricciones únicas.

**Creación de un índice único**

En este ejemplo se muestra cómo crear un índice único con el método `createIndex(`) en segundo plano. Para entender la creación de índices en segundo plano y en primer plano, consulte [Tipos de creación de índices](managing-indexes.md#index-build-types). El siguiente ejemplo de código usa [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexOptions.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexOptions.html) para crear un índice único con el nombre «unique\$1restaurantId\$1idx» en segundo plano. A continuación, este objeto `IndexOptions` se pasa al método `createIndex()`.

```
collection.createIndex(
    Indexes.ascending("restaurantId"),
    new IndexOptions()
        .unique(true)
        .name("unique_restaurantId_idx")
        .background(true));
```

**Creación de índices múltiples**

En este ejemplo, se crean índices múltiples mediante el método `createIndexes()`. Primero crea la opción para cada índice mediante el objeto de [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html) y, a continuación, pasa una lista de objetos de `IndexModel` al método `createIndexes()`. En el siguiente ejemplo de código se muestra cómo crear un índice compuesto mediante la clase de utilidad [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Indexes.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Indexes.html). Esta clase también se usa para especificar si desea crear un índice con un orden de clasificación ascendente o descendente. Tras crear índices múltiples, llama al método `listIndexes()` para verificar la creación del índice.

```
// Single Field Index on cuisine
IndexModel singleIndex = new IndexModel(
    Indexes.ascending("cuisine"),
    new IndexOptions().name("cuisine_idx"));

// Compound Index
IndexModel compoundIndex = new IndexModel(
    Indexes.compoundIndex(
        Indexes.ascending("address.state"),
        Indexes.ascending("priceRange")),
    new IndexOptions().name("location_price_idx"));

// Build a list of IndexModel for the indexes
List < IndexModel > indexes = Arrays.asList(
    singleIndex,
    compoundIndex
);

collection.createIndexes(indexes);

// Verify created indexes
collection.listIndexes().forEach(index - > System.out.println("Created index: " + index.toJson()));
```

**Creación de índices dispersos y parciales**

En este ejemplo se muestra la creación de un índice disperso y uno parcial mediante la creación de un [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html) para cada tipo de índice.

```
// Sparse Index Model, this will identify only those documents that have a
// michelin star rating
IndexModel sparseIndex = new IndexModel(
    Indexes.ascending("michelin.star"),
    new IndexOptions()
    .name("michelin_sparse_idx")
    .sparse(true));

// Partial Index Model where the restaurant is active and has a rating of 4 and above
IndexModel partialIndex = new IndexModel(
    Indexes.ascending("rating.average"),
    new IndexOptions()
    .name("high_rated_active_idx")
    .partialFilterExpression(
        Filters.and(
            Filters.eq("isActive", true),
            Filters.gte("rating.average", 4.0))));
```

**Creación de un índice de texto**

En este ejemplo se muestra cómo crear un índice de texto. Solo se permite un índice de texto en una colección, pero ese índice de texto puede ser un índice compuesto que abarque campos múltiples. Cuando usa varios campos en el índice de texto, también puede asignar pesos a cada uno de los campos del índice. Amazon DocumentDB no admite los índices de texto en campos de matriz y, aunque puede utilizar hasta 30 campos en el índice de texto compuesto, solo se puede asignar un peso a tres campos.

```
IndexModel textIndex = new IndexModel(
    new Document()
    .append("name", "text")
    .append("description", "text")
    .append("cuisine", "text"),
    new IndexOptions()
    .name("restaurant_text_idx")
    .weights(new Document()
        .append("name", 10) // Restaurant name gets highest weight
        .append("description", 5) // Description get medium weight
        .append("cuisine", 2) // Cuisine type gets low weight
    ));

collection.createIndex(textIndex.getKeys(), textIndex.getOptions());
```

**Creación de un índice mediante el `runCommand()`**

Amazon DocumentDB admite la creación de índices en paralelo para reducir el tiempo que se tarda en crear índices. La indexación paralela utiliza varios procesos de trabajo simultáneos. La cantidad predeterminada de procesos de trabajo que se utilizan para la creación de índices son dos. Esta [entrada del blog](https://aws.amazon.com/blogs/database/unlock-the-power-of-parallel-indexing-in-amazon-documentdb/) ofrece una discusión en profundidad sobre la indexación en paralelo. Actualmente, los controladores Java de MongDB no permiten especificar la opción de proceso de trabajo cuando se utiliza `createIndex()` o `createIndexes()` y, por lo tanto, la única forma de especificar los procesos de trabajo es mediante el `runCommand`. El siguiente ejemplo de código muestra cómo usar `runCommand` para crear un índice que aumente el número de procesos de trabajo a cuatro:

```
Document command = new Document("createIndexes", "Restaurants")
    .append("indexes", Arrays.asList(
        new Document("key", new Document("name", 1))
        .append("name", "restaurant_name_idx")
        .append("workers", 4) // Specify number of workers
    ));

Document commendResult = connectedDB.runCommand(command);
```

## Eliminar índices
<a name="dropping-indes"></a>

El controlador Java de MongoDB proporciona varios métodos para eliminar índices, ya que se adapta a diferentes situaciones y a sus preferencias. Puede eliminar los índices por nombre, por especificación de clave o eliminar todos los índices a la vez. Los métodos `dropIndex()` y `dropIndexes()` pueden invocarse en un objeto de colección para eliminar un índice. Cuando elimina un índice por nombre, debe asegurarse de que se use el nombre de índice correcto, lo que puede no ser siempre intuitivo, especialmente para los índices compuestos o generados de forma automática. Si intenta eliminar un índice inexistente, se obtendrá una [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoCommandException.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoCommandException.html). El índice `default _id` no se puede eliminar, ya que garantiza la exclusividad del documento dentro de la colección.

En el siguiente ejemplo de código se muestra cómo eliminar un índice con el nombre del campo donde se creó el índice o mediante la eliminación de todos los índices: 

```
String indexName = "unique_restaurantId_idx";
Document keys = new Document("cuisine", 1);
// Drop index by name
collection.dropIndex(indexName);
            
// Drop index by keys
collection.dropIndex(keys);
            
// Drop all indexes
collection.dropIndexes();
```

Cuando borra índices con claves múltiples, asegúrese de que haya un índice compuesto que contenga todas las claves especificadas y de que el orden de las claves sea correcto. El código de ejemplo de creación del índice anterior muestra una clave compuesta sobre «cocina» y características. Si intenta eliminar esa clave compuesta pero el orden no es el que se utilizó para la creación, se produce MongoCommnadException el siguiente error:

```
Document keys = new Document("features", 1)
    .append("cuisine", 1);
try {
    // Drop index by keys
    collection.dropIndex(keys);
    System.out.println("Successfully dropped index with keys: " + keys.toJson());

} catch (MongoCommandException commErr) {
    System.out.println("Error dropping index: " + commErr.getErrorMessage());
    throw new RuntimeException("MongoCommandException was thrown while dropping index", commErr);
}
```

Se muestra el siguiente error:

```
Error dropping index: Cannot drop index: index not found.
Tests run: 3, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.819 sec <<< FAILURE!
com.amazon.docdb.guide.DocDBGuideTest.testindexGuide()  Time elapsed: 0.817 sec  <<< FAILURE!
org.opentest4j.AssertionFailedError: Unexpected exception thrown: java.lang.RuntimeException: MongoCommandException was thrown while dropping index
```

## Determinación de la selección del índice y proporción de una sugerencia sobre el índice
<a name="w2aac43b9b7c17c13"></a>

Trabajar con la funcionalidad de explicación de Amazon DocumentDB es fundamental para comprender el rendimiento de las consultas y el uso de los índices. Cuando ejecuta una consulta, puede añadir el método `explain()` para obtener información detallada sobre el plan de consulta, incluidos los índices, si los hubiera, que se están utilizando. El resultado `explain()` proporciona información sobre las etapas de ejecución de la consulta, el número de documentos examinados y el tiempo necesario para cada etapa. Esta información es inestimable para identificar si un índice en particular se está utilizando de manera eficaz o si la consulta podría beneficiarse de una estructura de índice diferente.

El método `explain()` se puede encadenar con el método `find()`. El método `explain()` puede utilizar un enumerador opcional [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/ExplainVerbosity.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/ExplainVerbosity.html) que determine el nivel de verbosidad devuelto por `explain()`. En este momento, DocumentDB admite solamente los enumeradores `EXECUTION_STATS` y `QUERY_PLANNER`. En el siguiente ejemplo de código se muestra cómo obtener el planificador de consultas para una consulta específica:

```
// Query we want to analyze
Document query = new Document()
    .append("cuisine", "Thai")
    .append("rating.average", new Document("$gte", 4.0));


Document allPlansExplain = collection.find(query).explain(ExplainVerbosity.QUERY_PLANNER);
System.out.println("All Plans Explain:\n" + allPlansExplain.toJson());
```

Se devuelve el siguiente documento JSON para el nivel de verbosidad del planificador de consultas:

```
{
  "queryPlanner": {
    "plannerVersion": 1,
    "namespace": "ProgGuideData.Restaurants",
    "winningPlan": {
      "stage": "IXSCAN",
      "indexName": "cuisine_idx",
      "direction": "forward"
    }
  },
  "serverInfo": {
    "host": "guidecluster3",
    "port": 27017,
    "version": "5.0.0"
  },
  "ok": 1,
  "operationTime": {
    "$timestamp": {
      "t": 1739221668,
      "i": 1
    }
  }
}
```

Dispone de varias opciones para incitar o forzar a Amazon DocumentDB a utilizar un índice específico. Los métodos `hint()` y `hintString()` le permiten anular el comportamiento de selección de índices predeterminado del optimizador de consultas y especificar explícitamente qué índice debe usarse para una consulta. Si bien el optimizador de consultas de DocumentDB suele ser una buena opción para la selección de índices, hay situaciones en las que forzar la aprobación de un índice específico mediante `hint()` o `hintString()` puede resultar beneficioso, como cuando se trata de datos sesgados o se comprueba el rendimiento de un índice.

El siguiente ejemplo de código fuerza el uso de un índice compuesto «cuisine\$1features\$1idx» para la misma consulta que se ejecutó en el código anterior:

```
// Query we want to analyze
Document query = new Document()
    .append("cuisine", "Thai")
    .append("rating.average", new Document("$gte", 4.0));

List < Document > queryDocs = new ArrayList < > ();
collection.find(query).hintString("cuisine_features_idx").forEach(doc - > queryDocs.add(doc));
```

# Programación basada en eventos con Amazon DocumentDB y Java
<a name="event-driven-programming"></a>

La programación basada en eventos en el contexto de Amazon DocumentDB representa un patrón arquitectónico potente en el que los cambios en la base de datos actúan como los principales generadores de eventos que desencadenan la lógica y los procesos empresariales posteriores. Cuando se insertan, actualizan o eliminan registros en una colección de DocumentDB, estos cambios actúan como eventos que inician automáticamente varios procesos posteriores, notificaciones o tareas de sincronización de datos. Este patrón es particularmente valioso en los sistemas distribuidos modernos, donde aplicaciones o servicios múltiples deben reaccionar a los cambios en los datos en tiempo real. El mecanismo principal de implementación de la programación basada en eventos en DocumentDB es mediante flujos de cambios.

**nota**  
En esta guía se asume que habilitó los flujos de cambios en una colección con la que está trabajando. Consulte [Uso de flujos de cambios con Amazon DocumentDB](change_streams.md) para obtener información sobre cómo habilitar los flujos de cambios en la colección. 

**Trabajo con flujos de cambios desde la aplicación Java**

El método `watch()` del controlador Java de MongoDB es el mecanismo principal para supervisar los cambios de datos en tiempo real en Amazon DocumentDB. Los objetos [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html), [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoDatabase.html#watch(com.mongodb.client.ClientSession,java.lang.Class)](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoDatabase.html#watch(com.mongodb.client.ClientSession,java.lang.Class)) y [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#watch()](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#watch()) pueden llamar al método `watch()`.

El método `watch()` devuelve una instancia de [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)) que admite varias opciones de configuración, entre las que se incluyen la búsqueda completa de actualizaciones en los documentos, el suministro de tokens de reanudación y una marca de tiempo para garantizar la fiabilidad, y las etapas de agregación de canalizaciones para filtrar los cambios. 

[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)) implementa la interfaz básica de Java `Iterable` y puede usarse con `forEach()`. Para capturar eventos mediante `forEach()`, transfiera una función de devolución de llamada a `forEach()` que procese el evento cambiado. En el siguiente fragmento de código se muestra cómo abrir un flujo de cambios en una colección para iniciar la supervisión de eventos de cambio:

```
ChangeStreamIterable < Document > iterator = collection.watch();
iterator.forEach(event - > {
    System.out.println("Received a change: " + event);
});
```

Otra forma de recorrer todos los eventos de cambio consiste en abrir un cursor que mantenga una conexión con el clúster y reciba continuamente nuevos eventos de cambio a medida que se producen. Para obtener un cursor de flujo de cambios, utilice el método `cursor()` del objeto [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)). En el siguiente ejemplo de código se muestra cómo supervisar eventos de cambio con el cursor:

```
try (MongoChangeStreamCursor < ChangeStreamDocument < Document >> cursor = collection.watch().cursor()) {
    System.out.println(cursor.tryNext());
}
```

Como práctica recomendada, puede crearla [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoChangeStreamCursor.html#getResumeToken()](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoChangeStreamCursor.html#getResumeToken())en una try-with-resource sentencia o cerrar el cursor manualmente. Si se llama al método `cursor()` en [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)), se devuelve un `MongoChangeStreamCursor` creado sobre un objeto de [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/ChangeStreamDocument.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/ChangeStreamDocument.html). 

La clase [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/ChangeStreamDocument.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/ChangeStreamDocument.html) es un componente crucial que representa los eventos de cambio individuales en el flujo. Contiene información detallada sobre cada modificación, incluido el tipo de operación (insertar, actualizar, eliminar, reemplazar), la clave del documento, la información del espacio de nombres y el contenido completo del documento, cuando esté disponible. La clase proporciona métodos para acceder a varios aspectos del evento de cambio, como `getOperationType()` para determinar el tipo de cambio, `getFullDocument()` para acceder al estado completo del documento y `getDocumentKey()` para identificar el documento modificado.

El objeto [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/ChangeStreamDocument.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/ChangeStreamDocument.html) proporciona dos datos importantes: un token de reanudación y la hora del evento de cambio.

Los tokens de reanudación y las operaciones basadas en el tiempo en los flujos de cambios de DocumentDB proporcionan mecanismos cruciales para mantener la continuidad y administrar el acceso a los datos históricos. Un token de reanudación es un identificador único que se genera para cada evento de cambio y sirve como marcador que permite que las aplicaciones reinicien el procesamiento del flujo de cambios desde un punto específico tras una desconexión o un fallo. Cuando se crea un cursor de flujo de cambios, se puede usar un token de reanudación previamente almacenado mediante la opción `resumeAfter()`, lo que permite que el flujo continúe desde donde dejó en lugar de empezar desde el principio o perder los eventos.

Las operaciones basadas en el tiempo en los flujos de cambios ofrecen diferentes enfoques para gestionar el punto de partida de la supervisión de los eventos de cambio. La opción `startAtOperationTime()` le permite empezar a observar los cambios que ocurrieron en una marca de tiempo específica o después de esta. Estas funciones basadas en el tiempo son particularmente valiosas en escenarios que requieren el procesamiento, la point-in-time recuperación o la sincronización de datos históricos entre sistemas.

El siguiente ejemplo de código recupera el evento asociado al documento de inserción, captura su token de reanudación y, a continuación, proporciona ese token para empezar a supervisar los eventos posteriores al evento de inserción. El evento se asocia al evento de actualización y, a continuación, obtiene la hora del clúster en que se produjo la actualización y utiliza esa marca de tiempo como punto de partida para el procesamiento posterior.

```
BsonDocument resumeToken;
BsonTimestamp resumeTime;

try (MongoChangeStreamCursor < ChangeStreamDocument < Document >> cursor = collection.watch().cursor()) {
    System.out.println("****************** Insert Document *******************");
    ChangeStreamDocument < Document > insertChange = cursor.tryNext();
    resumeToken = insertChange.getResumeToken();
    printJson(cursor.tryNext());
}
try (MongoChangeStreamCursor < ChangeStreamDocument < Document >> cursor = collection.watch()
    .resumeAfter(resumeToken)
    .cursor()) {
    System.out.println("****************** Update Document *******************");
    ChangeStreamDocument < Document > insertChange = cursor.tryNext();
    resumeTime = insertChange.getClusterTime();
    printJson(cursor.tryNext());
}
try (MongoChangeStreamCursor < ChangeStreamDocument < Document >> cursor = collection.watch()
    .startAtOperationTime(resumeTime)
    .cursor()) {
    System.out.println("****************** Delete Document *******************");
    printJson(cursor.tryNext());
  }
```

De manera predeterminada, el evento de cambio de actualización no incluye el documento completo, sino tan solo los cambios realizados. Si necesita acceder al documento completo que se actualizó, puede llamar al método `fullDocument()` en el objeto [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)). Tenga en cuenta que, cuando solicita que se devuelva un documento completo para un evento de actualización, se devolverá el documento que existía en el momento en que se realizó la llamada al flujo de cambios.

Este método toma un enumerador [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/FullDocument.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/FullDocument.html) como parámetro. Actualmente, Amazon DocumentDB admite solamente valores DEFAULT y `UPDATE_LOOKUP`. El siguiente fragmento de código muestra cómo solicitar el documento completo para los eventos de actualización cuando empieza a observar los cambios:

```
try (MongoChangeStreamCursor < ChangeStreamDocument < Document >> cursor = collection.watch().fullDocument(FullDocument.UPDATE_LOOKUP).cursor())
```

# Cómo utilizar la validación de esquemas JSON
<a name="json-schema-validation"></a>

Con el operador de consulta de evaluación de `$jsonSchema`, puede validar los documentos que se están insertando en sus colecciones.

**Topics**
+ [Cómo crear y utilizar la validación de esquemas JSON](#get-started-with-validation)
+ [Palabras clave compatibles](#json-supported-keywords)
+ [bypassDocumentValidation](#json-schema-bypass)
+ [Limitaciones](#json-schema-limitations)

## Cómo crear y utilizar la validación de esquemas JSON
<a name="get-started-with-validation"></a>

### Cómo crear una colección con validación de esquemas
<a name="create-collection-with-validation"></a>

Puede crear una colección con reglas de operación y validación de `createCollection`. Estas reglas de validación se aplican al insertar o actualizar documentos de Amazon DocumentDB. En el siguiente ejemplo de código se muestran las reglas de validación para un conjunto de empleados:

```
db.createCollection("employees", {
   "validator": {
      "$jsonSchema": {
         "bsonType": "object",
         "title": "employee validation",
         "required": [ "name", "employeeId"],
         "properties": {
            "name": {
                  "bsonType": "object",
                  "properties": {
                     "firstName": {
                        "bsonType": ["string"]
                     },
                     "lastName": {
                        "bsonType": ["string"]
                     }
                  },
                  "additionalProperties" : false 
            },
            "employeeId": {
               "bsonType": "string",
               "description": "Unique Identifier for employee"
            },
             "salary": {
               "bsonType": "double"
            },
            "age": {
               "bsonType": "number"
            }
         },
         "additionalProperties" : true 
      }
   },
   "validationLevel": "strict", "validationAction": "error"
} )
```

### Cómo insertar un documento válido
<a name="insert-valid-document"></a>

En el siguiente ejemplo, se insertan documentos que cumplen con las reglas de validación de esquemas anteriores:

```
db.employees.insert({"name" : { "firstName" : "Carol" , "lastName" : "Smith"}, "employeeId": "c720a" , "salary": 1000.0 })
db.employees.insert({ "name" : { "firstName" : "William", "lastName" : "Taylor" }, "employeeId" : "c721a", "age" : 24})
```

### Cómo insertar un documento no válido
<a name="insert-invalid-document"></a>

En el siguiente ejemplo, se insertan documentos que no cumplen con las reglas de validación de esquemas anteriores. En este ejemplo, el valor employeeId no es una cadena:

```
db.employees.insert({
    "name" : { "firstName" : "Carol" , "lastName" : "Smith"}, 
    "employeeId": 720 , 
    "salary": 1000.0 
})
```

En este ejemplo, se muestra una sintaxis incorrecta en el documento.

### Modificación de una colección
<a name="modify-collection"></a>

El comando `collMod` se utiliza para añadir o modificar las reglas de validación de la colección existente. En el siguiente ejemplo se añade un campo de salario a la lista de campos obligatorios:

```
db.runCommand({"collMod" : "employees", 
   "validator": {
      "$jsonSchema": {
         "bsonType": "object",
         "title": "employee validation",
         "required": [ "name", "employeeId", "salary"],
         "properties": {
            "name": {
                  "bsonType": "object",
                  "properties": {
                     "firstName": {
                        "bsonType": ["string"]
                     },
                     "lastName": {
                        "bsonType": ["string"]
                     }
                  },
                  "additionalProperties" : false 
            },
            "employeeId": {
               "bsonType": "string",
               "description": "Unique Identifier for employee"
            },
             "salary": {
               "bsonType": "double"
            },
            "age": {
               "bsonType": "number"
            }
         },
         "additionalProperties" : true 
      }
   }
} )
```

### Cómo abordar los documentos añadidos antes de que se cambiaran las reglas de validación
<a name="pre-validation-docs"></a>

Para abordar los documentos que se añadieron a su colección antes de que se cambiaran las reglas de validación, utilice los siguientes modificadores de `validationLevel`:
+ **estricto**: aplica reglas de validación a todas las inserciones y actualizaciones.
+ **moderado**: aplica reglas de validación a los documentos válidos existentes. Durante las actualizaciones, no se comprueban los documentos no válidos existentes.

En el siguiente ejemplo, tras actualizar las reglas de validación de la colección denominada “empleados”, el campo de salario es obligatorio. No se podrá actualizar el siguiente documento:

```
db.runCommand({ 
    update: "employees", 
    updates: [{ 
        q: { "employeeId": "c721a" }, 
        u: { age: 25 , salary : 1000}, 
        upsert: true }] 
})
```

Amazon DocumentDB devuelve el siguiente resultado:

```
{
"n" : 0,
    "nModified" : 0,
    "writeErrors" : [
        {
"index" : 0,
            "code" : 121,
            "errmsg" : "Document failed validation"
        }
    ],
    "ok" : 1,
    "operationTime" : Timestamp(1234567890, 1)
}
```

Si se actualiza el nivel de validación a `moderate`, se permitirá actualizar el documento anterior correctamente:

```
db.runCommand({
    "collMod" : "employees", 
    validationLevel : "moderate"
})

db.runCommand({ 
    update: "employees", 
    updates: [{ 
        q: { "employeeId": "c721a" }, 
        u: { age: 25 , salary : 1000}, 
        upsert: true }]
})
```

Amazon DocumentDB devuelve el siguiente resultado:

```
{
"n" : 1,
    "nModified" : 1,
    "ok" : 1,
    "operationTime" : Timestamp(1234567890, 1)
}
```

### Recuperación de documentos con el \$1jsonSchema
<a name="json-retrieve-docs"></a>

El operador de `$jsonSchema` se puede utilizar como filtro para consultar documentos que coincidan con el esquema JSON. Se trata de un operador de nivel superior que puede estar presente en los documentos de filtro como campo de nivel superior o utilizarse con operadores de consulta como `$and`, `$or` y `$nor`. En los siguientes ejemplos se muestra el uso de \$1jsonSchema como filtro individual y con otros operadores de filtro:

Documento insertado en una colección de “empleado”:

```
{ "name" : { "firstName" : "Carol", "lastName" : "Smith" }, "employeeId" : "c720a", "salary" : 1000 }
{ "name" : { "firstName" : "Emily", "lastName" : "Brown" }, "employeeId" : "c720b", "age" : 25, "salary" : 1050.2 }
{ "name" : { "firstName" : "William", "lastName" : "Taylor" }, "employeeId" : "c721a", "age" : 24, "salary" : 1400.5 }
{ "name" : { "firstName" : "Jane", "lastName" : "Doe" }, "employeeId" : "c721a", "salary" : 1300 }
```

Colección filtrada únicamente con el operador de `$jsonSchema`:

```
db.employees.find({ 
       $jsonSchema: { required: ["age"] } })
```

Amazon DocumentDB devuelve el siguiente resultado:

```
{ "_id" : ObjectId("64e5f91c6218c620cf0e8f8b"), "name" : { "firstName" : "Emily", "lastName" : "Brown" }, "employeeId" : "c720b", "age" : 25, "salary" : 1050.2 }
{ "_id" : ObjectId("64e5f94e6218c620cf0e8f8c"), "name" : { "firstName" : "William", "lastName" : "Taylor" }, "employeeId" : "c721a", "age" : 24, "salary" : 1400.5 }
```

Colección filtrada con el operador de `$jsonSchema` y otro operador:

```
db.employees.find({ 
       $or: [{ $jsonSchema: { required: ["age", "name"]}}, 
            { salary: { $lte:1000}}]});
```

Amazon DocumentDB devuelve el siguiente resultado:

```
{ "_id" : ObjectId("64e5f8886218c620cf0e8f8a"), "name" : { "firstName" : "Carol", "lastName" : "Smith" }, "employeeId" : "c720a", "salary" : 1000 }
{ "_id" : ObjectId("64e5f91c6218c620cf0e8f8b"), "name" : { "firstName" : "Emily", "lastName" : "Brown" }, "employeeId" : "c720b", "age" : 25, "salary" : 1050.2 }
{ "_id" : ObjectId("64e5f94e6218c620cf0e8f8c"), "name" : { "firstName" : "William", "lastName" : "Taylor" }, "employeeId" : "c721a", "age" : 24, "salary" : 1400.5 }
```

Colección filtrada con el operador de `$jsonSchema` y con `$match` en el filtro de agregación:

```
db.employees.aggregate(
    [{ $match: { 
        $jsonSchema: { 
            required: ["name", "employeeId"],  
            properties: {"salary" :{"bsonType": "double"}}
        }
       }
    }]
)
```

Amazon DocumentDB devuelve el siguiente resultado:

```
{ 
"_id" : ObjectId("64e5f8886218c620cf0e8f8a"),
 "name" : { "firstName" : "Carol", "lastName" : "Smith" },
"employeeId" : "c720a",
"salary" : 1000 
}
{
"_id" : ObjectId("64e5f91c6218c620cf0e8f8b"),
"name" : { "firstName" : "Emily", "lastName" : "Brown" },
"employeeId" : "c720b",
"age" : 25,
"salary" : 1050.2
}
{
"_id" : ObjectId("64e5f94e6218c620cf0e8f8c"),
"name" : { "firstName" : "William", "lastName" : "Taylor" },
"employeeId" : "c721a",
"age" : 24,
"salary" : 1400.5
}
{
"_id" : ObjectId("64e5f9786218c620cf0e8f8d"),
"name" : { "firstName" : "Jane", "lastName" : "Doe" },
"employeeId" : "c721a",
"salary" : 1300
}
```

### Cómo visualizar las reglas de validación existentes
<a name="view-validation-rules"></a>

Para ver las reglas de validación existentes en una colección, utilice:

```
db.runCommand({
    listCollections: 1, 
    filter: { name: 'employees' }
})
```

Amazon DocumentDB devuelve el siguiente resultado:

```
{
    "waitedMS" : NumberLong(0),
    "cursor" : {
        "firstBatch" : [
            {
                "name" : "employees",
                "type" : "collection",
                "options" : {
                    "autoIndexId" : true,
                    "capped" : false,
                    "validator" : {
                        "$jsonSchema" : {
                            "bsonType" : "object",
                            "title" : "employee validation",
                            "required" : [
                                "name",
                                "employeeId",
                                "salary"
                            ],
                            "properties" : {
                                "name" : {
                                    "bsonType" : "object",
                                    "properties" : {
                                        "firstName" : {
                                            "bsonType" : [
                                                "string"
                                            ]
                                        },
                                        "lastName" : {
                                            "bsonType" : [
                                                "string"
                                            ]
                                        }
                                    },
                                    "additionalProperties" : false
                                },
                                "employeeId" : {
                                    "bsonType" : "string",
                                    "description" : "Unique Identifier for employee"
                                },
                                "salary" : {
                                    "bsonType" : "double"
                                },
                                "age" : {
                                    "bsonType" : "number"
                                }
                            },
                            "additionalProperties" : true
                        }
                    },
                    "validationLevel" : "moderate",
                    "validationAction" : "error"
                },
                "info" : {
                    "readOnly" : false
                },
                "idIndex" : {
                    "v" : 2,
                    "key" : {
                        "_id" : 1
                    },
                    "name" : "_id_",
                    "ns" : "test.employees"
                }
            }
        ],
        "id" : NumberLong(0),
        "ns" : "test.$cmd.listCollections"
    },
    "ok" : 1,
    "operationTime" : Timestamp(1692788937, 1)
}
```

Amazon DocumentDB también conserva las reglas de validación en la etapa de agregación `$out`.

## Palabras clave compatibles
<a name="json-supported-keywords"></a>

Los comandos `create` y `collMod` admiten los siguientes campos:
+ **`Validator`**: admite el operador `$jsonSchem`a.
+ **`ValidationLevel`**: admite los valores `off`, `strict` y `moderate`.
+ **`ValidationAction`**: admite el valor `error`.

El operador \$1jsonSchema admite las siguientes palabras clave:
+ `additionalItems`
+ `additionalProperties`
+ `allOf`
+ `anyOf`
+ `bsonType`
+ `dependencies`
+ `description`
+ `enum`
+ `exclusiveMaximum`
+ `exclusiveMinimum`
+ `items`
+ `maximum`
+ `minimum`
+ `maxItems`
+ `minItems`
+ `maxLength`
+ `minLength`
+ `maxProperties`
+ `minProperties`
+ `multipleOf`
+ `not`
+ `oneOf`
+ `pattern`
+ `patternProperties`
+ `properties`
+ `required`
+ `title`
+ `type`
+ `uniqueItems`

## bypassDocumentValidation
<a name="json-schema-bypass"></a>

Amazon DocumentDB admite `bypassDocumentValidation` para los siguientes comandos y métodos:
+ `insert`
+ `update`
+ `findAndModify`
+ Etapa `$out` en el comando `aggregate` y en el método `db.collection.aggregate()`

Amazon DocumentDB no admite los siguientes comandos para `bypassDocumentValidation`: 
+ `$merge` en el comando `aggregate` y en el método `db.collection.aggregate()`
+ Comando `mapReduce` y método `db.collection.mapReduce()`
+ `applyOps`Comando de la 

## Limitaciones
<a name="json-schema-limitations"></a>

Las siguientes limitaciones se aplican a la validación de `$jsonSchema`:
+ Amazon DocumentDB devuelve el error “No se pudo validar el documento” cuando una operación no cumple con la regla de validación.
+ Los clústeres elásticos de Amazon DocumentDB no admiten `$jsonSchema`.