MongoDB em AdvPL – JSON e BSON

Introdução

No post sobre a prova de conceito do MongoDB com AdvPL — MongoDB em AdvPL – Prova de Conceito —  foram montadas apenas algumas funções auxiliares para testar uma chamada de um comando no MongoDB usando conexão TCP — ( MongoDB Wired Protocol). Para transformar isso em um componente e realmente consumir os recursos do MongoDB, são necessários alguns passos para construir uma API.

JSON e BSON

A especificação JSON basicamente possui poucos tipos básicos no seu tratamento : String, Numero, Nulo, Verdadeiro / Falso, e seus agrupamentos — Objeto e Array. Para armazenar documentos e dados com tratamentos mais elaborados, precisamos de mais tipos básicos … A especificação BSON — ou Binary JSON — têm origem no MongoDB, foi concebida para ser leve e eficiente, desde armazenamento/persistência até comunicação/requisições.

Unindo o útil ao agradável

Mesmo no programa de testes ou prova de conceito, como existe apenas uma mensagem que encapsula todas as requisições ao MongoDB, onde a parte fixa é uma estrutura de header e sections envelopadas em uma string binária, onde a flexibilidade em submeter um comando e pegar um resultado resume-se ao envio e retorno de um BSON, usar a abordagem nativa do JSonObject() em AdvPL e fazer um conversor de JSON x BSON e vice-versa torna a implementação da API muito mais simples.

Porém, nem tudo são flores …. A linguagem AdvPL possui alguns tipos básicos a mais do que o JSON, mas não tem suporte nativo a vários tipos básicos do BSON. Logo, usar o objeto JSON do AdvPL para fazer a “ponte” com o BSON vai exigir alguns truques para garantir uma conversão integral entre os formatos.

Simplificando

Uma vez que o protocolo suporta a execução direta de comandos do MongoDB, onde o comando e os argumentos são informados em um pacote BSON, torna-se muito prático consumir qualquer comando do MongoDB criando um JSON em AdvPL, onde a camada de comunicação apenas converte o JSON para BSON, solicita o comando, recebe um BSON de retorno, e transforma ele em um JSON AdvPL para tratamento do retorno.

Uma vez construída uma API “Base” que realize esta tarefa, pode ser construída uma API de mais alto nível, encapsulando os comandos principais, reduzindo a quantidade final de código necessária para execução e tratamentos, e ainda assim permitindo a flexibilidade no uso da implementação. Vamos partir de um exemplo de uso básico, para ler os dados de uma coleção em um database no MongoDB.

Query no MongoDB – find e getMore

Os comandos find() e getMore() permitem, respectivamente, abrir um cursor para buscar e retornar objetos no MongoDB, onde o comando find() abre um cursor para retornar os dados, onde você pode limitar por parâmetros a quantidade de objetos retornados na primeira requisição — o default são 101 — e continuar recuperando mais objetos do cursor nas requisições subsequentes usando o comando getMore(). Primeiro, precisamos de uma conexão com o MongoDB — por hora usando apenas uma instância local, sem autenticação.

oMongoDB := ZMONGODBCLIENT():New()
IF !oMongoDB:Connect()
   Conout("ERROR - MongoDB Connect Failure")
Endif
oMongoDB:SetDB("teste")

Com as instruções acima, criamos uma conexão com o MongoDB, e configuramos o database “teste” em uso. A classe AdvPL ZMONGODBCLIENT() encapsula o tSocketClient — conexão — com o MongoDB e as execuções de comandos. Agora, para executar por exemplo o comando “find()”, consultamos a documentação dele no link https://docs.mongodb.com/manual/reference/command/find/ , e verificamos quais as propriedades que precisamos em um objeto JSON para executar este comando. Ele possio 22 propriedades, mas apenas uma é obrigatória: O nome da Collection (ou “tabela”) que você quer recuperar os objetos.

Para isso, podemos simplesmente criar um objeto JSON em AdvPL, criar a propriedade com o nome do comando, colocar como valor a tabela a ser pesquisada, e como no exemplo não será colocado nenhum filtro — todos os objetos serão retornados — apenas definimos que a primeira requisição deve retornar no máximo 10 objetos, usando a propriedade ‘batchSize

// Monta o JSON para requisitar o comando
oCmdFind := JSONOBJECT():new()
oCmdFind['find'] := 'pet001'
oCmdFind['batchSize'] := 10

// Submete a requisição ao MongoDB
oResponse := oMongoDB:RunCommand('find',oCmdFind)

// Tratamento da resposta 
nOk := oResponse['ok']

// Recupera o primeiro bloco de registros retornados
IF nOk == 1

  cCursorID :=  oResponse['cursor']['id']
  aRecords := oResponse['cursor']['firstBatch']

(...)

Cada elemento do array aRecords é um Objeto JSON recuperado pela busca. Agora, para continuar buscando por mais resultados, precisamos fazer uma nova requisição, aproveitando o cursor aberto no MongoDB.

// Monta a requisição para mais dados	
oCmdGetMore := JSONOBJECT():new()
oCmdGetMore['getMore'] := cCursorID
oCmdGetMore['collection'] := 'pet001'
oCmdGetMore['batchSize'] := 10
	
While !empty(cCursorID)

  oResponse := oMongoDB:RunCommand('getMore',oCmdGetMore,1,0)
  cCursorID :=  oResponse['cursor']['id']
  aRecords := oResponse['cursor']['nextBatch']
	
Enddo

Criamos um novo objeto JSON para realizar as próximas requisições, até retornar todos os objetos da coleção. Enquanto o id do cursor não for vazio, ainda existem dados a serem recuperados. Cada busca alimenta a propriedade nextBatch, com no máximo 10 objetos por requisição — mediante a propriedade batchSize.

Implementação em mais alto nível

Usando o método genérico RunCommand(), podemos implementar “na unha” as chamadas de qualquer comando. Agora, não ficaria mais fácil criar uma classe para encapsular um “cursor” no MongoDB ? Uma implementação final pode ficar simples como o exemplo abaixo:

USER Function MongoQry()
oMongoDB := ZMONGODBCLIENT():New()
oMongoDB:Connect()
oMongoDB:SetDB("teste")
oQry := JMONGODBQRY():New('pet001')
oQry:SetBatchSize(10)
oQry:Execute(oMongoDB) 
While !oQry:Eof()
  oObject := oQry:GetObject()
  oQry:Skip()
Enddo
oQry:Close()
oMongoDB:Disconnect()
Return

O exemplo ainda não está funcional, ainda estou estudando a abordagem de implementação, o exemplo sequer tem ainda os tratamentos de erro, mas dá pra se ter uma ideia do quanto encapsular uma funcionalidade torna a implementação simples para uso.

Conclusão

O primeiro nível de encapsulamento — classe ZMONGODBCIENT — está aos poucos tomando forma, e será disponibilizada no Projeto ZLIB no Github. Por hora os exemplos acima estão mais para um “TDD”, do que propriamente funcionando. O objetivo nesse momento é mostrar um pouco mais da ideia e do conceito, do que a implementação propriamente dita. Aguardem cenas do próximo capítulo, a jornada MongoDB está só começando!!!

Agradeço novamente a audiência e lhes desejo TERABYTES DE SUCESSO !!!

Referências

 

 

 

Um comentário sobre “MongoDB em AdvPL – JSON e BSON

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s