Se você estiver acompanhando, lembrará que escrevi alguns tutoriais sobre a conversão de seus aplicativos Node.js com MongoDB para o Couchbase. Eles incluíam um Linguagem de consulta do MongoDB para N1QL bem como um tutorial De mangusto a otomano tutorial. Esses tutoriais de migração foram ótimos do ponto de vista do aplicativo, mas não informaram como obter as coleções existentes do MongoDB importadas como arquivos JSON.
Portanto, neste tutorial, vamos explorar como importar dados de coleta do MongoDB para o Couchbase. A linguagem de desenvolvimento não importa, mas Golang é muito rápida e poderosa, o que a torna uma candidata perfeita para o trabalho.
Antes de nos preocuparmos em escrever um script de migração de dados, vamos descobrir um conjunto de dados de amostra com o qual estamos trabalhando. O objetivo aqui é ser universal em nosso script, mas é útil ter um exemplo.
O modelo de coleção do MongoDB
Vamos supor que temos uma coleção chamada cursos que contém informações sobre os cursos oferecidos por uma escola. O modelo de documento para qualquer um desses documentos pode ser parecido com o seguinte:
1 2 3 4 5 6 7 8 |
{ "name" (nome): "Basket Weaving 101", "estudantes": [ "nraboy", "mgroves", "hgreeley" ] } |
Cada documento representaria um único curso com uma lista de alunos matriculados. Cada documento tem um valor de id e os alunos matriculados fazem referência a documentos de outra coleção com valores de id correspondentes.
Com o MongoDB instalado, você tem acesso ao seu mongoexportação utilitário. Isso nos permitirá exportar os documentos existentes em qualquer coleção para um arquivo de dados JSON.
Por exemplo, poderíamos executar o seguinte comando em nosso banco de dados MongoDB:
1 |
mongoexport --db example --collection courses --out courses.json |
O banco de dados em questão seria exemplo
e estamos exportando o cursos
em um arquivo chamado cursos.json. Se tentarmos abrir esse arquivo JSON, veremos dados semelhantes aos seguintes:
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á uma nova linha no arquivo, mas não será exatamente como o nosso esquema foi modelado. O MongoDB pegará todas as referências de documentos e as envolverá em um $oid
que representa um ID de objeto.
Então, onde isso nos deixa?
Planejamento do modelo de bucket do Couchbase
Como você provavelmente já sabe, o Couchbase não usa Coleções, mas sim Buckets. No entanto, os Buckets não funcionam da mesma forma que as Coleções. Em vez de ter um Bucket para cada tipo de documento, como faz o MongoDB, você terá um Bucket para cada aplicativo.
Isso significa que precisaremos fazer algumas alterações na exportação do MongoDB para que ela faça algum tipo de sentido dentro do Couchbase.
No Couchbase, é normal ter uma propriedade de documento em cada documento que representa o tipo de documento que ele é. Para nossa sorte, sabemos o nome da antiga coleção e podemos fazer alguma mágica. Como resultado final, nossos documentos do Couchbase devem ter a seguinte aparência:
1 2 3 4 5 6 7 8 9 10 |
{ "_id": "course-1", "_type": "courses", "name" (nome): "Basket Weaving 101", "estudantes": [ "nraboy", "mgroves", "hgreeley" ] } |
No exemplo acima, compactamos todos os $oid
e adicionou os valores _id
e _tipo
propriedades.
Desenvolvimento do script de importação de coleções da Golang
Agora que sabemos para onde estamos indo, podemos nos concentrar no script que fará as manipulações e o carregamento. No entanto, vamos pensar em nossa lógica Golang sobre como realizar o trabalho.
Sabemos que estaremos lendo linha por linha de um arquivo JSON. Para cada linha lida, precisamos manipulá-la e depois salvá-la. A leitura de um arquivo e a inserção no Couchbase são operações de bloqueio. Embora a leitura seja bastante rápida, a inserção de um único documento de cada vez em um modo de bloqueio para terabytes de dados pode ser bastante lenta. Isso significa que devemos iniciar goroutines para fazer as coisas em paralelo.
Crie um novo projeto em algum lugar em seu $GOPATH e criar um arquivo chamado main.go com o seguinte 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 |
pacote principal importação ( "bufio" "encoding/json" "bandeira" "fmt" "os" "sync" "github.com/couchbase/gocb" ) var waitGroup sync.WaitGroup var data chan string var bucket *gocb.Bucket func main() {} func worker(collection string) {} func cbimport(document string, collection string) {} func compressObjectIds(mapDocument map[string]interface{}) string {} |
O código acima é apenas um modelo do que vamos realizar. O código principal
será responsável por iniciar várias goroutines e ler nosso arquivo JSON. Não queremos que o aplicativo termine quando a função principal
termina, então usamos uma função Grupo de espera. Isso impedirá que o aplicativo termine até que todas as goroutines tenham terminado.
O trabalhador
será cada goroutine e chamará cbimport
que chamará compressObjectIds
para trocar qualquer $oid
com o equivalente compactado. Por compactado, quero dizer que não incluirá um invólucro $oid
propriedade.
Então, vamos dar uma olhada nisso principal
função:
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 o processo de importação...") flagInputFile := flag.String("input-file", "", "arquivo com caminho que contém documentos") flagWorkerCount := flag.Int("workers", 20, "trabalhadores simultâneos para importação de dados") flagCollectionName := flag.String("collection", "", "mongodb collection name") flagCouchbaseHost := flag.String("couchbase-host", "", "host do cluster do couchbase") flagCouchbaseBucket := flag.String("couchbase-bucket", "", "nome do balde do couchbase") 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) adiar arquivo.Close() data = make(chan string) scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) for i := 0; i < *flagWorkerCount; i++ { waitGroup.Add(1) go worker(*flagCollectionName) } para scanner.Scan() { dados <- scanner.Text() } close(data) waitGroup.Wait() fmt.Println("A importação foi concluída!") } |
A função acima receberá um conjunto de sinalizadores de linha de comando que serão usados na configuração do aplicativo. A conexão com o servidor Couchbase e o Bucket de destino será estabelecida e o arquivo de entrada será aberto.
Como estamos usando goroutines, precisamos usar variáveis de canal para evitar cenários de bloqueio. Todas as linhas lidas serão enfileiradas no canal de onde cada goroutine lerá.
Após a ativação das goroutines, o arquivo será lido e o canal será preenchido. Depois que o arquivo for completamente lido, o canal será fechado. Isso significa que, quando as rotinas lerem todos os dados, elas poderão ser encerradas. Esperaremos até que as goroutines terminem antes de encerrar o aplicativo.
Agora vamos dar uma olhada no trabalhador
função:
1 2 3 4 5 6 7 8 9 10 |
func worker(collection string) { adiar waitGroup.Done() para { document, ok := <-data if !ok { quebra } cbimport(document, collection) } } |
O nome da coleção do MongoDB será passado para cada trabalhador, e o trabalhador permanecerá ativo em um loop até que o canal seja fechado.
Para cada documento lido do canal, o cbimport
será chamada:
1 2 3 4 5 6 7 |
func cbimport(document string, collection string) { var mapDocument map[string]interface{} json.Unmarshal([]byte(document), &mapDocument) mapDocument["_type"] = coleção compressObjectIds(mapDocument) bucket.Insert(mapDocument["_id"].(string), mapDocument, 0) } |
Cada linha do arquivo será uma cadeia de caracteres que precisamos transformar em um mapa de interfaces. Sabemos o nome da coleção, portanto, podemos criar uma propriedade que conterá esse nome específico. Em seguida, podemos passar o mapa inteiro para a função compressObjectIds
para se livrar de qualquer $oid
Invólucros.
O compressObjectIds
se parece com o seguinte:
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[string]interface{}) string { var objectIdValue string para chave, valor := intervalo mapDocument { valor do interruptor.(tipo) { case string: se key == "$oid" && len(mapDocument) == 1 { valor de retorno.(string) } case map[string]interface{}: objectIdValue = compressObjectIds(value.(map[string]interface{})) se objectIdValue != "" { mapDocument[key] = objectIdValue } case []interface{}: for index, element := range value.([]interface{}) { objectIdValue = compressObjectIds(element.(map[string]interface{})) se objectIdValue != "" { value.([]interface{})[index] = objectIdValue } } } } retornar "" } |
No exemplo acima, estamos basicamente percorrendo todas as chaves do documento. Se o valor for um objeto aninhado ou uma matriz JSON, faremos a mesma coisa recursivamente até chegarmos a uma string com uma chave de $oid
. Se essa condição for atendida, garantimos que essa é a única chave nesse nível do documento. Isso nos permitirá saber que se trata de um id que podemos compactar com segurança.
Não é tão ruim, certo?
Execução do importador do MongoDB para o Couchbase
Supondo que você tenha a linguagem de programação Go instalada e configurada, precisamos criar esse aplicativo.
Na linha de comando, você precisará obter todas as dependências. Com o projeto como seu diretório de trabalho atual, execute o seguinte:
1 |
go get -d -v |
O comando acima obterá todas as dependências encontradas em nossos arquivos Go.
Agora, o aplicativo pode ser criado e executado, ou apenas executado. As etapas não são realmente diferentes, mas vamos apenas executar o código.
Na linha de comando, execute o seguinte:
1 2 3 4 5 6 |
./collectionimport \ --input-file FILE_NAME.json \ --collection COLLECTION_NAME \ --couchbase-host localhost \ --couchbase-bucket default \ --Trabalhadores 20 |
O comando acima nos permitirá passar qualquer sinalizador para o aplicativo, como informações do servidor Couchbase, número de goroutines de trabalho etc.
Se for bem-sucedida, a exportação do MongoDB deverá estar presente em seu banco de dados NoSQL do Couchbase.
Conclusão
Você acabou de ver como trabalhar com Golang e MongoDB para importar os dados de suas coleções para o Couchbase. É claro que o código que vimos pode ser otimizado, mas, do ponto de vista da simplicidade, tenho certeza de que você pode ver o que estávamos tentando realizar.
Deseja fazer o download desse projeto de importador e testá-lo por si mesmo? Eu fiz o upload para GitHubcom instruções adicionais para execução. Lembre-se de que ele não é oficial e não foi testado para grandes quantidades de dados. Trate-o como um exemplo para aprender como atender às suas necessidades de migração de dados.
Se você estiver interessado em saber mais sobre o Couchbase Server ou o Golang SDK, confira a página Portal do desenvolvedor do Couchbase.