Si has estado al día, recordarás que escribí algunos tutoriales sobre cómo convertir tus aplicaciones Node.js con MongoDB a Couchbase. Estos incluían un Lenguaje de consulta MongoDB a N1QL así como un De mangosta a otomano tutorial. Estos fueron grandes tutoriales de migración desde una perspectiva de aplicación, pero realmente no te dicen cómo obtener sus colecciones existentes de MongoDB importados como archivos JSON.
Así que, en este tutorial, vamos a explorar cómo importar datos de colección de MongoDB a Couchbase. El lenguaje de desarrollo no importa realmente, pero Golang es muy rápido y muy potente por lo que es un candidato perfecto para el trabajo.
Antes de preocuparnos por escribir un script de migración de datos, pensemos en un conjunto de datos de ejemplo con el que podamos trabajar. El objetivo aquí es ser universal en nuestro script, pero ayuda tener un ejemplo.
El modelo de colección de MongoDB
Supongamos que tenemos una colección llamada cursos que contiene información sobre los cursos ofrecidos por una escuela. El modelo de documento para cualquiera de estos documentos podría parecerse a lo siguiente:
|
1 2 3 4 5 6 7 8 |
{ "name": "Cestería 101", "estudiantes": [ "nraboy", "mgroves", "hgreeley" ] } |
Cada documento representaría un único curso con una lista de alumnos matriculados. Cada documento tiene un valor de identificación y los estudiantes matriculados hacen referencia a documentos de otra colección con valores de identificación coincidentes.
Con MongoDB instalado tienes acceso a su mongoexport utilidad. Esto nos permitirá exportar los documentos existentes en cualquier Colección a un fichero de datos JSON.
Por ejemplo, podríamos ejecutar el siguiente comando contra nuestra base de datos MongoDB:
|
1 |
mongoexport --db ejemplo --colección cursos --out cursos.json |
La base de datos en cuestión sería ejemplo y estamos exportando el cursos a un archivo llamado cursos.json. Si intentamos abrir este archivo JSON, veríamos datos con un aspecto similar al siguiente:
|
1 2 |
{"_id":{"$oid":"course-1"},"name":"Basket Weaving 101","students":[{"$oid":"nraboy"},{"$oid":"mgroves"}]} {"_id":{"$oid":"course-2"},"name":"TV Watching 101","students":[{"$oid":"jmichaels"},{"$oid":"tgreenstein"}]} |
Cada documento será una nueva línea en el archivo, sin embargo no será exactamente como nuestro esquema fue modelado. MongoDB tomará todas las referencias a documentos y las envolverá en una línea $oid que representa un identificador de objeto.
¿Dónde nos deja esto?
Planificación del modelo de cubos de Couchbase
Como probablemente ya sabrás, Couchbase no utiliza Colecciones, sino Buckets. Sin embargo, los Buckets no funcionan igual que las Colecciones. En lugar de tener un Bucket por cada tipo de documento como hace MongoDB, tendrás un Bucket por cada aplicación.
Esto significa que tendremos que hacer algunos cambios en la exportación de MongoDB para que tenga algún tipo de sentido dentro de Couchbase.
En Couchbase es normal tener una propiedad document en cada documento que representa el tipo de documento que es. Por suerte para nosotros conocemos el nombre de la antigua Collection y podemos hacer un poco de magia. Como resultado final nuestros documentos Couchbase deberían tener este aspecto:
|
1 2 3 4 5 6 7 8 9 10 |
{ "_id": "curso-1", "_type": "cursos", "name": "Cestería 101", "estudiantes": [ "nraboy", "mgroves", "hgreeley" ] } |
En el ejemplo anterior hemos comprimido todos los $oid y añadimos los valores _id y Tipo propiedades.
Desarrollo del script de importación de colecciones Golang
Ahora que sabemos hacia dónde nos dirigimos, podemos centrarnos en el script que hará las manipulaciones y la carga. Sin embargo, vamos a pensar en nuestra lógica Golang sobre cómo realizar el trabajo.
Sabemos que vamos a leer línea por línea de un archivo JSON. Por cada línea leída necesitamos manipularla, y luego guardarla. Tanto leer de un archivo como insertarlo en Couchbase son operaciones bloqueantes. Mientras que leer es bastante rápido, insertar un único documento a la vez de forma bloqueante para terabytes de datos puede ser bastante lento. Esto significa que debemos iniciar goroutines para hacer las cosas en paralelo.
Cree un nuevo proyecto en algún lugar de su $GOPATH y crear un archivo llamado main.go con el siguiente código:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
paquete principal importar ( "bufio" "encoding/json" "bandera" "fmt" "os" "sync" "github.com/couchbase/gocb" ) var waitGroup sync.WaitGroup var datos chan cadena var bucket *gocb.Bucket func main() {} func trabajador(colección cadena) {} func cbimport(documento cadena, colección cadena) {} func compressObjectIds(mapDocument map[cadena]interfaz{}) cadena {} |
El código anterior es simplemente un plano de lo que vamos a lograr. El principal será responsable de iniciar varias goroutines y leer nuestro archivo JSON. No queremos que la aplicación termine cuando la función principal por lo que utilizamos una función Grupo de espera. Esto evitará que la aplicación termine hasta que todas las goroutines hayan terminado.
En trabajador será cada goroutine y llamará a cbimport que llamará a compressObjectIds para cambiar cualquier $oid con el equivalente comprimido. Por comprimido quiero decir que no incluirá un envoltorio $oid propiedad.
Veámoslo principal función:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
func main() { fmt.Println("Iniciando el proceso de importación...") flagInputFile := flag.String("input-file", "", "fichero con ruta que contiene documentos") flagWorkerCount := flag.Int("workers", 20, "trabajadores concurrentes para importar datos") flagCollectionName := flag.String("collection", "", "mongodb collection name") flagCouchbaseHost := flag.String("couchbase-host", "", "couchbase cluster host") flagCouchbaseBucket := flag.String("couchbase-bucket", "", "couchbase bucket name") flagCouchbaseBucketPassword := flag.String("couchbase-bucket-password", "", "couchbase bucket password") flag.Parse() cluster, _ := gocb.Connect("couchbase://" + *flagCouchbaseHost) bucket, _ = cluster.OpenBucket(*flagCouchbaseBucket, *flagCouchbaseBucketPassword) file, _ := os.Open(*flagInputFile) aplazar archivo.Cerrar() datos = make(cadena chan) scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) for i := 0; i < *flagWorkerCount; i++ { waitGroup.Add(1) go trabajador(*nombreDeLaColección) } para scanner.Scan() { data <- scanner.Text() } cerrar(datos) waitGroup.Wait() fmt.Println("¡La importación se ha completado!") } |
La función anterior tomará un conjunto de banderas de línea de comandos que se utilizarán en la configuración de la aplicación. Se establecerá la conexión con el Couchbase Server y Bucket de destino y se abrirá el archivo de entrada.
Como estamos usando goroutines, necesitamos usar variables de canal para evitar escenarios de bloqueo. Todas las líneas leídas se pondrán en cola en el canal desde el que leerá cada goroutine.
Después de activar las goroutines, el archivo será leído y el canal será llenado. Después de que el archivo sea leído completamente, el canal se cerrará. Esto significa que cuando las goroutines lean todos los datos, las goroutines podrán terminar. Esperaremos hasta que las goroutines terminen antes de terminar la aplicación.
Ahora echemos un vistazo al trabajador función:
|
1 2 3 4 5 6 7 8 9 10 |
func trabajador(colección cadena) { aplazar waitGroup.Done() para { documento, ok := <-datos if !ok { romper } cbimport(documento, colección) } } |
El nombre de la colección MongoDB se pasará a cada trabajador y el trabajador permanecerá en funcionamiento en un bucle hasta que el canal se cierre.
Por cada documento leído del canal, el cbimport se llamará a la función
|
1 2 3 4 5 6 7 |
func cbimport(documento cadena, colección cadena) { var mapDocument mapa[cadena]interfaz{} json.Unmarshal([]byte(document), &mapDocument) mapDocument["_tipo"] = colección compressObjectIds(mapDocument) bucket.Insert(mapDocument["_id"].(cadena), mapDocument, 0) } |
Cada línea del fichero será una cadena que tendremos que descomponer en un mapa de interfaces. Conocemos el nombre de la colección, así que podemos crear una propiedad que contenga ese nombre concreto. A continuación, podemos pasar todo el mapa a la propiedad compressObjectIds para deshacerse de cualquier $oid envoltorios.
En compressObjectIds tiene el siguiente aspecto:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
func compressObjectIds(mapDocument map[cadena]interfaz{}) cadena { var objectIdValue cadena para clave, valor := rango mapDocument { switch valor.(tipo) { case string: if key == "$oid" && len(mapDocument) == 1 { valor de retorno.(cadena) } case mapa[cadena]interfaz{}: objectIdValue = compressObjectIds(valor.(mapa[cadena]interfaz{})) if objectIdValue != "" { mapDocument[key] = objectIdValue } caso []interfaz{}: para índice, elemento := rango valor.([]interfaz{}) { objectIdValue = compressObjectIds(element.(map[string]interface{})) if objectIdValue != "" { value.([]interface{})[index] = objectIdValue } } } } return "" } |
En el ejemplo anterior, básicamente estamos recorriendo todas las claves del documento. Si el valor es un objeto anidado o una matriz JSON, hacemos lo mismo recursivamente hasta que encontremos una cadena con una clave de $oid. Si se cumple esta condición nos aseguramos de que es la única clave en ese nivel del documento. Esto nos permitirá saber que es un id que podemos comprimir con seguridad.
No está tan mal, ¿verdad?
Ejecutar el importador de MongoDB a Couchbase
Asumiendo que tienes el lenguaje de programación Go instalado y configurado, necesitamos construir esta aplicación.
Desde la línea de comandos, tendrás que obtener todas las dependencias. Con el proyecto como tu directorio de trabajo actual, ejecuta lo siguiente:
|
1 |
go get -d -v |
El comando anterior obtendrá cualquier dependencia encontrada en nuestros archivos Go.
Ahora la aplicación puede ser construida y ejecutada, o simplemente ejecutada. Los pasos no son realmente diferentes, pero sólo vamos a ejecutar el código.
Desde la línea de comandos, ejecute lo siguiente:
|
1 2 3 4 5 6 |
./collectionimport \ --fichero de entrada FILE_NAME.json \ --collection COLLECTION_NAME \ --couchbase-host localhost \ --couchbase-bucket default \ --trabajadores 20 |
El comando anterior nos permitirá pasar cualquier bandera a la aplicación como información del Servidor Couchbase, número de goroutines de trabajadores, etc.
Si tiene éxito, la exportación a MongoDB debería estar ahora presente en su base de datos Couchbase NoSQL.
Conclusión
Acabas de ver cómo trabajar con Golang y MongoDB para importar los datos de tus colecciones a Couchbase. Seguro que el código que vimos puede ser optimizado, pero desde el punto de vista de la simplicidad estoy seguro de que puedes ver lo que estábamos tratando de lograr.
¿Quieres descargarte este proyecto de importador y probarlo por ti mismo? Lo he subido a GitHubcon más instrucciones para ejecutarlo. Ten en cuenta que no es oficial y que no se ha probado con grandes cantidades de datos. Tómelo como un ejemplo para aprender a satisfacer sus necesidades de migración de datos.
Si está interesado en obtener más información sobre Couchbase Server o el SDK de Golang, consulte la página Portal para desarrolladores de Couchbase.