Patrick
Patrick
Florianópolis
2022
Patrick Valencio Leguizamon
Florianópolis
2022
Ficha de identificação da obra
Este Trabalho de Conclusão de Curso foi julgado adequado para obtenção do Título de
“Bacharel em Sistemas de Informação” e aprovado em sua forma final pelo Curso de
Sistemas de Informação.
Banca Examinadora:
Nos últimos anos houveram diversos avanços no ramo da tecnologia da informação, tanto
no meio acadêmico como no industrial. Isso se dá pela constante necessidade de tecnologias
que buscam resolver problemas de forma simples e efetiva, muitas vezes voltadas para uso
específicos. Bancos de dados NoSQL existem há muito tempo, porém recentemente obti-
veram bastante popularidade por conta da sua flexibilidade, facilidade de escalonamento,
velocidade, simplicidade, disponibilidade e outros benefícios. Suas características permi-
tem que diferentes formatos de dados possam ser armazenados de forma eficiente para
consumo, como documentos JSON ou CSV, e dados baseados em grafos. Este trabalho
apresenta uma solução para o mapeamento automático de dados no formato de documento
JSON para o modelo de grafo, considerando uma aplicação que deseja migrar dados JSON,
amplamente utilizados por bancos de dados NoSQL orientados a documentos, para bancos
de dados NoSQL orientados a grafos. O foco deste trabalho é a persistência destes dados
no sistema de gerência de bancos de dados Neo4j, que é o principal representante de bancos
de dados NoSQL orientados a grafos na indústria. Uma avaliação preliminar demonstra
que a solução é escalável.
In recent years, there have been several advancements in the field of information technol-
ogy, both in the academic and industrial sectors. This is driven by the constant need for
technologies that aim to solve problems in a simple and effective manner, often tailored
for specific purposes. NoSQL databases have existed for a long time, however, they have
recently gained significant popularity due to their flexibility, scalability, speed, simplicity,
availability, and other benefits. Their features enable efficient storage of various data for-
mats, such as JSON or CSV documents, and graphic data. This work presents a solution
for the automatic mapping of JSON document data to the graph model, considering an
application that aims to migrate JSON data, widely used by document-oriented NoSQL
databases, to graph-oriented NoSQL databases. The focus is on persisting these data in the
Neo4j database management system, which is the leading graph-oriented NoSQL database
in the industry. Preliminary evaluation demonstrates that the solution is scalable.
1 INTRODUÇÃO . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1 OBJETIVOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.1.1 Objetivo Geral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.1.2 Objetivos Específicos . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.2 METODOLOGIA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.3 CONTEÚDO DA MONOGRAFIA . . . . . . . . . . . . . . . . . . . . . 16
2 FUNDAMENTAÇÃO TEÓRICA . . . . . . . . . . . . . . . . . . . 17
2.1 BANCOS DE DADOS RELACIONAIS . . . . . . . . . . . . . . . . . . . 17
2.1.1 Modelo relacional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.2 BANCOS DE DADOS NOSQL . . . . . . . . . . . . . . . . . . . . . . . 18
2.2.1 Bancos de dados de documento . . . . . . . . . . . . . . . . . . . . 18
2.2.2 Bancos de dados de grafo . . . . . . . . . . . . . . . . . . . . . . . . 19
2.3 MODELO DE ATORES . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3 TRABALHOS CORRELATOS . . . . . . . . . . . . . . . . . . . . 23
3.1 REVISÃO SISTEMÁTICA . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.2 MIGRATION OF DATA FROM RELATIONAL DATABASE TO GRAPH
DATABASE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.3 AMANDA: A MIDDLEWARE FOR AUTOMATIC MIGRATION BETWEEN
DIFFERENT DATABASE PARADIGMS . . . . . . . . . . . . . . . . . . 28
3.4 TRANSFORMATION OF SCHEMA FROM RELATIONAL DATABASE
(RDB) TO NOSQL DATABASES . . . . . . . . . . . . . . . . . . . . . 32
3.5 DISCUSSÃO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4 ABORDAGEM PROPOSTA . . . . . . . . . . . . . . . . . . . . . 35
4.1 ESTRATÉGIA DE MAPEAMENTO . . . . . . . . . . . . . . . . . . . . 35
4.1.1 MongoDB Reader . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.1.2 JsonToNode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.1.3 Schema API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.1.4 Neo4JClient . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.1.4.1 Regras de Mapeamento . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
4.2 IMPLEMENTAÇÃO DA FERRAMENTA . . . . . . . . . . . . . . . . . . 54
4.2.1 Turbo C2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.2.2 Execução baseada em eventos . . . . . . . . . . . . . . . . . . . . . 61
4.3 APLICAÇÃO WEB PARA VISUALIZAÇÃO E GERENCIAMENTO DE MI-
GRAÇÕES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.3.1 Painéis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.3.2 Migrações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
5 AVALIAÇÃO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.1 VALIDAÇÃO DOS DADOS MIGRADOS . . . . . . . . . . . . . . . . . . 75
5.2 AVALIAÇÃO DA MIGRAÇÃO DE DADOS . . . . . . . . . . . . . . . . . 76
5.2.1 Coleta de dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
5.2.2 Configuração do ambiente . . . . . . . . . . . . . . . . . . . . . . . 77
5.2.3 Métricas de avaliação . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.2.4 Resultados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
6 CONCLUSÃO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
REFERÊNCIAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
APÊNDICE A – EXEMPLO DE ESQUEMA JSON UTILIZADO
PARA VALIDAÇÃO DOS DADOS . . . . . . . . 89
APÊNDICE B – EXEMPLO DE DADO ALEATÓRIO GERADO
ATRAVÉS DO JSON ESQUEMA (TRUNCADO
DEVIDO AO TAMANHO DE 7543 LINHAS) . 101
APÊNDICE C – ARTIGO NO FORMATO SBC . . . . . . . . . . 106
13
1 INTRODUÇÃO
Em junho de 1970 foi publicado um artigo de Edgar F. Codd chamado "A Relational
Model of Data for Large Shared Banks". Neste artigo ele introduziu uma nova forma de
modelar dados, sendo sua principal preocupação evitar que o usuário tenha que saber
como o dado está armazenado logicamente.
Segundo ele, os usuários devem ser protegidos de ter que saber como os dados
estão armazenados internamente, sendo que caso haja mudanças em alguns aspectos do
armazenamento as aplicações devem continuar com seu funcionamento inalterado (CODD,
1970). Além disso, a estrutura que ele propôs tinha o potencial de responder questões
baseadas nos dados e o uso de disco seria mais eficiente.
Com base nessas ideias se deu início ao desenvolvimento de diversas tecnologias para
armazenamento de dados que poderiam ter relações entre si, sendo esse modelo chamado
de relacional. Os sistemas de gerência de bancos de dados relacionais, baseados no modelo
relacional, se caracterizam por uma abordagem de gerenciar dados usando o sistema lógico
de primeira ordem onde os dados são representados como tuplas agrupadas com relações
entre si. A popularidade que esse modelo ganhou tornou possível que diversos avanços
pudessem ser realizados, como transações ACID para assegurar integridade e consistência
dos dados, a linguagem SQL, estudos de performance e segurança, entre outros, sendo ele
bastante utilizado até os dias de hoje.
Os bancos de dados relacionais tiveram como arquitetura inicial a escalabilidade
vertical apenas, já que foram criados antes da internet ganhar a popularidade que tem hoje.
Tradicionalmente, eles foram pensados para estar em apenas um servidor que pode ser esca-
lado adicionando mais recursos, como memória, poder de processamento e armazenamento
(POKORNY, 2011).
Porém aos poucos a internet se tornou cada vez mais acessível e carregada de
informações. Isso se dá por conta do surgimento de sites agregadores de conhecimento,
como a Wikipédia, motores de busca, redes sociais, sites de vídeos e imagens, entre outros.
Ainda, as empresas perceberam que era possível conseguir uma infinidade de tipos de
informações relevantes aos negócios com base no comportamento dos usuários e assim
aumentar as chances de venda.
Devido a esses fatores o número de dados gerados e armazenados se tornou muito
grande, principalmente em empresas que tinham como foco a análise dos dados, que agora
se viam diante de diversos desafios. Os bancos de dados utilizados enfrentavam problemas
de performance quando o volume de dados crescia para estar de acordo com a demanda.
Além disso, durante o desenvolvimento e evolução de aplicações, os bancos de dados
relacionais não podiam ter seu esquema modificado com o tempo e também não lidavam
bem com diferentes tipos de dados. Por conta disso, novas tecnologias surgiram para
solucionar esses problemas, sendo uma delas os bancos de dados NoSQL (SAHATQIJA
Capítulo 1. Introdução 14
et al., 2018).
Em 1998, Carlo Strozzi nomeou seu banco de dados relacional que não usava SQL
como NoSQL. Atualmente o termo pode ser usado para descrever tanto sistemas que
não usam SQL ou, mais comumente, para descrever bancos de dados não relacionais que
podem suportar SQL ou linguagens semelhantes (Not Only SQL).
NoSQL é uma família de bancos de dados que se caracterizam pela flexibilidade
para o armazenamento de dados, sendo amplamente utilizados no contexto de Big Data
por oferecer uma boa escalabilidade (ABDELHEDI et al., 2017). Ele armazena dados
de forma diferente da abordagem relacional, permitindo uma persistência e recuperação
independente da estrutura e do conteúdo dos dados (MUS, 2019).
Um dos desafios do grande volume de dados é a variedade, já que eles podem
se apresentar de diversos tipos e formatos, como estruturados, semi-estruturados e não-
estruturados (HOLUBOVA; CONTOS; SVOBODA, 2021). Assim sendo, como mencionado
anteriormente, existem diversos tipos de bancos de dados NoSQL, sendo a escolha da
tecnologia ligada à natureza de dado e a forma que ele deve ser utilizado. Diversas soluções
sacrificam alguns aspectos considerados essenciais nos bancos de dados relacionais para
serem capazes de suprir as necessidades associadas ao uso. Dados nesses bancos de dados
podem ser armazenados em pares chave-valor, conjuntos de colunas, documentos ou grafos.
Além disso, existe a preocupação com a alta disponibilidade, que é provida geralmente com
o relaxamento da consistência dos dados e/ou por meio de diversas formas de redundância
(MEIER; KAUFMANN, 2019).
Dois importantes representantes da família de bancos de dados NoSQL são os
bancos de dados de documento e os bancos de dados orientados a grafos. O primeiro
é capaz de representar dados complexos, armazenando documentos no formato JSON
(principalmente) ou XML, e permitindo operações semelhantes ao que é possível em SQL
com o benefício de não ter necessariamente um esquema definido. Já os bancos de dados
baseados em grafos se caracterizam por conter nós e relações conectando diferentes nós,
sendo que cada um deles pode apresentar rótulos e propriedades (MEIER; KAUFMANN,
2019).
Os bancos de dados de documento, apesar de nem sempre terem esquemas definidos,
utilizam algumas convenções para armazenar os dados e assim conseguir realizar análises.
Por meio dessas convenções é possível perceber que alguns dados apresentam estruturas
que definem diversos tipos de relacionamentos entre os dados, e que seria mais natural uma
representação dessas estruturas em um formato de grafos (DE VIRGILIO; MACCIONI;
TORLONE, 2013), e consequentemente, a adoção de um banco de dados orientado a
grafos.
Entretanto, alguns desafios estão presentes para o caso de uma aplicação que deseja
migrar seus dados de uma tecnologia de banco de dados de documento para um banco de
dados orientado a grafos. Um exemplo está na etapa de migração, já que se houver muitos
Capítulo 1. Introdução 15
dados seriam necessárias muitas horas de trabalho manual para identificar todos os tipos
de relacionamentos entre os dados. Outro problema é a dificuldade em aprender novos
paradigmas e linguagens, já que bancos de dados de documentos possuem seus próprios
mecanismos de acesso a dados, que geralmente diferem dos mecanismos presentes nos
bancos de dados orientados a grafos.
Por conta disso, esse trabalho propõe um processo automatizado de mapeamento e
migração de dados de bancos de dados de documento para bancos de dados orientados a
grafos, com foco nos sistemas de gerência de bancos de dados NoSQL MongoDB e Neo4j,
que são os principais representantes de persistência de documentos e de grafos na indústria,
respectivamente, e pela possibilidade de utilização de ambos sem custos adicionais por
serem de código aberto. No caso do MongoDB, dados são mantidos em documentos em
formato JSON.
1.1 OBJETIVOS
Este trabalho tem como objetivo o desenvolvimento de uma aplicação que realiza
o mapeamento e migração de dados em formato JSON para o banco de dados orientado a
grafos Neo4j.
Para que o objetivo geral seja possível, os seguintes objetivos específicos são defini-
dos:
1.2 METODOLOGIA
Além disso, deve-se proceder uma pesquisa sobre o estado da arte e quais são os
métodos atuais para resolver os problemas de mapeamento e migração entre tecnologias
NoSQL. Esta etapa deve ser realizada através da leitura de artigos, livros, apresentações,
códigos fonte e qualquer tipo de trabalho que possa ser relevante.
Posterior a esta etapa é o projeto e desenvolvimento da aplicação, considerando
alternativas disponíveis atualmente e como esta aplicação irá se destacar dos outros
trabalhos. Por fim, deve-se apresentar os resultados obtidos com a avaliação da aplicação.
Além desta introdução, esta monografia apresenta mais seis capítulos. O capítulo
2 apresenta a fundamentação teórica, sendo ela composta pelos conceitos de bancos de
dados relacionais, transações, bancos de dados NoSql, alguns de seus formatos e usos e
por fim uma introdução ao Modelo de atores para processamento paralelo. O capítulo 3
apresenta trabalhos que se propõem a realizar migrações de dados de forma semelhante e
mapeia algumas das suas características para ser alvo de comparações com a ferramenta
proposta. O capitulo 4 apresenta a ferramenta proposta, sendo dividido entre a solução do
problema original e a evolução para o modelo de processamento paralelo e distribuído, com
o final dedicado a validação dos dados. O capítulo 5 demonstra a utilização da ferramenta
para migração de dados entre os bancos junto com as análises de qualidade e variação
das configurações para obter melhor performance, sendo a velocidade comparada com
uma ferramenta disponibilizada pelo próprio Neo4j. O capitulo 6 apresenta a conclusão
do trabalho, sendo analisado o cumprimento dos objetivos e as possibilidades de trabalhos
futuros.
17
2 FUNDAMENTAÇÃO TEÓRICA
Este capítulo apresenta os conceitos essenciais para este trabalho: bancos de dados
relacionais, bancos de dados NoSQL, MongoDB, Neo4j e o Modelo de Atores.
Bancos de dados relacionais são um tipo de banco de dados que utiliza o mo-
delo relacional para armazenamento. Eles são projetados para armazenar informações
estruturadas e permitir que os usuários realizem consultas complexas e análises.
A Figura 1 é um exemplo de como são representados os dados em um banco de
dados relacional. Temos as tabelas User, Follower, Tag, Blog e Comment. Pode-se perceber
que cada tabela tem colunas em sua primeira linha e valores nas demais. É possível notar
que cada coluna tem um tipo específico e que existem relações entre tabelas. Por exemplo,
a tabela Follower possui uma referencia para a tabela User na coluna fuser, sendo fuser
um id de usuário na tabela User.
O modelo relacional é a base dos bancos de dados relacionais. Ele define a estrutura
dos dados e a forma como os dados são organizados em relações com tuplas e atributos.
Uma relação no modelo relacional é materializada como uma tabela em um banco de dados
relacional composto de linhas e colunas. Tabelas são interconectadas por meio de chaves
para permitir a consulta e manipulação de dados de várias tabelas ao mesmo tempo.
Ele é baseado na álgebra relacional, que fornece um conjunto de operações para
manipulação de dados, como seleção, projeção, união e junção. Segundo (SILBERSCHATZ;
Capítulo 2. Fundamentação Teórica 18
Bancos de dados Not only SQL (NoSQL) são uma família de banco de dados que
não seguem o modelo relacional. Eles são projetados para armazenar e processar grandes
quantidades de dados estruturados, não estruturados ou semiestruturados.
Eles são amplamente utilizados em aplicações web, Big Data, Internet das Coisas
(IoT) e outros. Apesar de comumente estes bancos de dados não utilizarem SQL como sua
linguagem de consulta, o termo NoSQL não é interpretado como no SQL, mas sim como
not only SQL (POKORNY, 2011).
Os bancos de dados NoSQL são mais flexíveis do que os bancos de dados relacionais,
já que apresentam um esquema dinâmico e que não necessariamente precisa ser pré-
definido (SAHATQIJA et al., 2018). Estas características permitem que os desenvolvedores
adicionem novos campos ou tipos de dados à medida que surgem novos requisitos. Além
disso, os bancos de dados NoSQL são escaláveis horizontalmente, o que significa que é
possível adicionar mais servidores para lidar com um maior volume de dados e tráfego de
usuários.
A família de bancos de dados NoSQL é composta por bancos de dados chave-valor,
colunar, documento e grafos. Alguns desses modelos de dados são mostrados na Figura 2.
Esse trabalho enfatiza os bancos de dados de documentos e de grafos. Eles são detalhados
a seguir.
sendo muito utilizado para transmitir dados entre aplicações e armazenar informações
complexas. Esse formato suporta alguns tipos de dados (inteiros, reais e strings), além de
arrays e objetos, que são coleções de pares nome-valor que definem atributos. Arrays em
JSON são representados entre colchetes e funcionam como mapas de inteiros para valores
e objetos são delimitados entre chaves (SILBERSCHATZ; KORTH; SUDARSHAN, 2020).
A Figura 3 mostra um exemplo de um dado JSON.
O modelo de atores é uma teoria da computação que define os atores como elementos
básicos da computação concorrente, sendo utilizado para a compreensão da teoria de
concorrência e como base teórica em sistemas distribuídos (HEWITT, 2010). Cada ator
é independente e encapsula estado e comportamento, podendo se comunicar com outros
atores através de mensagens assíncronas. Essa abordagem permite que sistemas sejam
escaláveis e tolerantes a falhas em ambientes distribuídos.
O Modelo de Atores foi criado para integrar informações de forma robusta, mesmo
com inconsistências contínuas. Ele não tenta eliminar essas inconsistências, abordando
uma estratégia de aceitar e lidar com elas como uma característica observada e desejada.
Um ator pode ter diferentes formas e estar presente em diferentes lugares de uma
aplicação. Qualquer parte de um sistema que precise realizar computação pode ser um
ator (MICHAEL NASH, s.d.). Isso é demonstrado na Figura 6, onde pode-se criar um
ator que expõe métodos de uma entidade Person, sendo que esse ator pode se comunicar
com outros atores. No exemplo essa comunicação é utilizada para realizar buscas de datas
(atores Date) na agenda (ator Schedule) de forma concorrente. Neste caso, Date são atores
filhos de Schedule e possuem estados independentes para a realização de trabalho.
Capítulo 2. Fundamentação Teórica 22
3 TRABALHOS CORRELATOS
4. Seleção dos estudos: avaliação de títulos, resumos e até mesmo o texto completo,
seguindo de forma rigorosa os critérios de inclusão e exclusão para selecionar os
estudos relevantes;
5. Análise de qualidade: uso de diferentes escalas, como lista de Delphi, PEDro e outras,
visando obter um índice de qualidade metodológica e por meio dela eventualmente
excluir mais estudos;
Critérios de exclusão:
• Não detalha um método para mapeamento de dados para bancos de dados de grafo;
O resultado revelou que não foi encontrado nenhum estudo que realizasse a conver-
são de dados JSON (semiestruturado) para dados no formato de grafo, demonstrando o
ineditismo da proposta deste trabalho. Assim sendo, foram selecionados estudos que reali-
zam mapeamento do modelo relacional para o modelo de grafo. Como existem trabalhos
que se propõem a converter dados semiestruturados para relacionais, pode-se assumir que
teoricamente é possível a conversão para o formato de grafo, mesmo que seja necessário
utilizar um intermediário (JSON para estruturado e depois para grafo). Estes estudos são
detalhados a seguir.
Neste trabalho os autores afirmam que a maioria dos estudos anteriores apresenta
um foco na avaliação dos metadados do banco de dados, rastreamento de esquema e
conversão de diagrama entidade-relacionamento, não permitindo aos usuários selecionar
um subconjunto de banco de dados ou atributos de tabela para migrar. Outros estudos
dependem de ferramentas de terceiros, como Kafka e Apache Phoenix (QUEIROZ et al.,
2022).
Além disso, os trabalhos relacionados analisados não podem ser estendidos para
oferecer suporte a mais de um banco de dados de origem e destino, sendo um diferencial
Capítulo 3. Trabalhos Correlatos 29
Postgres para Dgraph. O tempo de CPU para a migração de MySQL para Dgraph foi de
0,57 segundos.
O ótimo desempenho do AMANDA demonstra que, por depender de um
schema.json fornecido antecipadamente contendo todas as informações de vértices e
arestas a serem migrados, ela é superior a uma abordagem baseada em algoritmos
automáticos de migração que busca os metadados do banco de dados ou os trabalhos
que utilizam um diagrama ER para identificar os vértices e arestas para serem migrados,
conforme realizado por trabalhos relacionados.
Para relacionamentos derivados de uma união de tipos, onde uma tabela A repre-
senta um tipo união e as demais tabelas B e C representam suas subclasses, é criado
também um nó para cada tabela que participa do relacionamento, como mostra a Figura
17. A diferença está nas arestas geradas, que agora partem dos nós B e C para o nó A.
3.5 DISCUSSÃO
1
https://www.ray.io/
35
4 ABORDAGEM PROPOSTA
4.1.2 JsonToNode
Para migrar dados, conforme mostra a linha 1, o componente recebe como parâ-
metros da função de migração a representação do objeto JSON em Python, sendo ele um
dicionário, em conjunto com o nível do dado, que inicialmente é 0, mas que muda conforme
a função realiza a recursão, o nome do campo pai, sendo ele presente apenas nos objetos
aninhados e uma instância de uma classe que gerencia as informações de identidade dos
dados, os esquemas, sendo essa classe chamada de SchemaAPI. Os Schemas armazenam
informações de atributos dos dados já processados. O nome do campo pai é utilizado para
compor a instância de Table, que armazena as informações de origem do objeto. Conforme
mostra a Figura 22, a classe Table é composta pelos campos:
• is_relationship: Booleano que marca se uma coluna é utilizada para criar uma
relação entre nós.
• relations: lista contendo todos os nós um (1) nível acima que formam uma relação
com esse nó. Não é obrigatório.
Capítulo 4. Abordagem proposta 44
A linha 14 marca o inicio da segunda etapa do algoritmo. Nessa etapa são realizadas
as recursões que criam novos níveis, sendo eles compostos pelos objetos aninhados e seus
atributos pai. Para isso, é feita uma iteração sobre todas as chaves e valores do dicionário,
sendo que o valor está representado como a variável campo no Algoritmo 1 e o seu nome
está representado como atributo (campo.nome).
Na linha 15 é feita a verificação se o valor do campo que está sendo iterado é do tipo
dicionário ou lista de dicionários, o que significa que ele é formado por objetos aninhados.
Caso seja, na linha 16 é feita uma recursão no procedimento, sendo fornecido o valor do
campo, o incremento de um nível e o nome do campo pai. O resultado, conforme mostra a
linha 17, é incluído no atributo relations da instância de Node que está sendo modificada.
No exemplo das questões do StackOverflow, o atributo respostas seria identificado
como lista de objetos aninhados e os objetos contidos seriam retroalimentados no procedi-
mento, recebendo o nível 1 e campoPai respostas. O resultado do algoritmo é demonstrado
na Figura 28.
Ainda nessa etapa, o atributo usuario seria identificado como objeto aninhado.
Dessa forma, ele seria retroalimentado para o procedimento recebendo o nível 2 e o
Capítulo 4. Abordagem proposta 45
Para fazer a identificação de esquema para um objeto, essa API utiliza um cálculo
de pontuação de diferença entre todos os Schemas que possui e o objeto. Esse cálculo de
pontuação é feito por meio da diferença entre os campos de todos os Schemas e os campos
do objeto. Caso as pontuações sejam menores que um limite dado, o Schema de menor
pontuação de diferença é atribuído ao objeto. O Algoritmo 2 detalha como isso ocorre.
criada uma variável para armazenar o Schema mais similar, que pode ser identificado no
próximo passo.
Das linhas 3 a 14 é feito o cálculo da pontuação. Para isso, a SchemaAPI possui
um atributo schemas, que armazena todos os Schemas criados, sendo eles representados
na Figura 32. Na linha 3 ele itera sobre todos esses Schemas que ela possui e na linha
4 ele coleta as informações de atributos desse esquema e colocar em um set. No Python,
set é uma coleção desordenada de elementos únicos e é utilizada por suportar operações
matemáticas como união, interseção e diferença. Na linha 5, as informações de atributos
do dicionário também são adicionadas em um set.
A linha 6 cria uma união dos atributos do set do Schema e do set dos atributos do
dicionário. Dessa forma, são obtidos todos os atributos únicos entre ambos os sets. Nas
linhas 7 e 8 é calculada a diferença, sendo a primeira dos atributos de esquema e dicionário
e depois do dicionário e esquema. Na linha 9 é feita a união das diferenças para que seja
possível calcular a quantidade de atributos que não estão presentes entre o esquema e o
dicionário.
Na linha 10 é calculada a pontuação, sendo ela o tamanho das diferenças dividido
pelo tamanho de atributos únicos entre os sets. Na linha 11 essa pontuação é utilizada
para se comparar com o esquema presente na variável de Schema mais similar, sendo
que na ausência de um esquema ou se a pontuação calculada for menor que a presente
na variável, este esquema é atribuído na variável junto com a sua pontuação. As demais
linhas apenas retornam como resultado None, caso não haja nenhum esquema que obteve
pontuação menor que a máxima, ou o esquema que obteve menor pontuação.
No caso do exemplo das questoes, questoes possui o campo respostas, respostas
possui como campos apenas usuario, enquanto que usuario possui como campos nome e
respostas. Quando a API recebe um dicionário respostas, ela analisa todos os Schemas
que possui e verifica as diferenças entre os campos. A Figura 33 mostra o algoritmo de
pontuação aplicado ao dicionário. Como o dicionário fornecido à API possui a pontuação
de 0 (0% de diferença) com o Schema respostas, a sua identificação será de respostas.
Capítulo 4. Abordagem proposta 50
4.1.4 Neo4JClient
A criação de nós em lote utiliza os mecanismos do Neo4J para que tudo seja criado
de uma só vez. Para isso foi desenvolvido um template, conforme mostra a Figura 35, onde
o rótulo do nó é o nome da tabela e os valores são fornecidos de uma só vez por meio
de uma lista de dicionário, sendo a chave o nome da coluna e o valor é o seu valor. Um
exemplo de código gerado para o Neo4j a partir deste template é mostrado na Figura 36.
Conforme mostra a Figura 37, para a criação das relações é utilizada uma consulta
com os comandos MATCH e MERGE, sendo o MATCH realizado com os identificadores
internos (chamados de __jtg_id), n o nome do nó origem e n1 o nome do nó destino,
seguido do MERGE entre eles. No caso do comando MERGE, a função entre chaves que
é utilizada para especificar o rótulo, chamada de self.scape_label realiza uma interpolação
de string, já que é uma função executada dentro de uma string. Um exemplo de resultado
é mostrado na Figura 38.
Esta solução realiza a escrita de nós por meio da árvore criada a partir dos objetos
JSON aninhados, conforme descrito na Seção 4.1.2. O Algoritmo 4 aplica as regras de
mapeamento. Para os dados de questoes (Figura 21), o nó patriarca é o primeiro a ser
escrito no Neo4j. Na Figura 31 estão todos os Nodes resultantes do processamento do
objeto JSON, sendo o patriarca (questoes) detentor do id 1, as respostas id 2 e o
usuario id 3. Essa árvore de Node é utilizada como ponto de partida para a escrita dos
nós e arestas.
4.2.1 Turbo C2
decorador neste contexto é uma função em Python que estende o comportamento de outra
função sem alterar seu código.
Ele também disponibiliza uma API para declaração de endpoints HTTP com o au-
xilio das bibliotecas FastApi e Pydantic, além da biblioteca Ray Serve para a distribuição
e controle de réplicas, conforme mostra a Figura 41. Para a coleta de métricas de recursos
utilizados, como CPU e memória, e para alimentar os dashboards com a contagem de nós
e arestas, foi utilizada a ferramenta Prometheus 2 devido a sua integração nativa com o
framework Ray e a sua facilidade de uso. Prometheus é uma ferramenta de monitoramento
e alertas de código aberto que é bastante utilizada em ambientes de microsserviços e
Kubernetes 3 . Já Kubernetes é uma plataforma de código aberto para automação de im-
plantação, dimensionamento e gerenciamento de aplicações em contêineres, sendo que o
framework Ray também oferece suporte para sua utilização, caso necessário.
A seguir são explicados alguns conceitos criados pelo framework para possibilitar
a execução dos componentes do Json2Node de forma paralela e distribuída utilizando o
modelo de atores, sendo eles os jobs, filas e handlers. Para simplificar a explicação, é
descrito um exemplo de uso que mostra uma aplicação mais familiar das ferramentas e
posteriormente é explicado como elas foram utilizadas para o contexto deste trabalho.
O exemplo de uso segue o seguinte contexto: uma faculdade deseja implementar
um sistema que notifica os alunos do curso TCC 1 sobre a chegada do prazo de postagem
2
https://prometheus.io/
3
https://kubernetes.io/pt-br/
Capítulo 4. Abordagem proposta 57
do resumo de TCC. Para isso, é desejado que haja uma verificação diária do prazo e se
for analisado que falta uma semana para o final, deve-se enviar um e-mail para o grupo
alunos_de_tcc_1 todos os dias até a chegada do prazo.
A Figura 42 demonstra um job criado para o exemplo, sendo o papel deste job
verificar diariamente se chegou o período de envio de e-mails, sendo este período de 7 a
1 dia antes do prazo final. Caso o período tenha chegado, ele deve avisar que os e-mails
devem ser enviados.
Um job é criado por meio de um decorador, chamado job, aplicado em uma função
Python. Esse decorador contém diversas informações referentes ao contexto de execução
da função e o nome da fila de entrada de informação (se houver) e as filas por onde vai
ocorrer a saída da informação (se houver). Ele é um ator que executa constantemente de
forma paralela, sendo que isso se traduz em outro processo Python.
Para o exemplo, serão necessários dois jobs, sendo um deles presente na Figura 42.
Conforme é visível na linha 2, temos o decorador @job, que se localiza acima da função
que irá realizar o monitoramento.
As linhas 3, 4 e 5 apresentam algumas configurações disponibilizadas pelo job,
sendo elas o wait_time, que indica que o job deve executar 1 vez por dia (a cada 86400
segundos), em uma réplica (uma execução paralela a outros jobs), conforme mostra a
linha 4, e a linha 5 indica que o job despacha um evento, sendo ele chamado de Prazo-
ParaPostagemDeRelatorioDeTCC1Chegando. Este evento indica que o prazo para
a postagem do relatório está chegando, e assim deve-se enviar o e-mail para os alunos.
As linhas 6 e 7 são parâmetros customizados da função. Tanto prazo_final quanto
data_para_enviar_email são declarados nessas linhas e serão repassados para a função
durante a execução, conforme mostram as linhas 10 e 11. O prazo final foi definido como
01/05/2024 às 23:59:59 enquanto que a data para começar a enviar os e-mails está como
24/04/2024 às 00:00:00.
Por fim, nas linhas 13, 14, 15 e 16 é feita a verificação se o dia de hoje é maior ou
Capítulo 4. Abordagem proposta 58
igual a data para iniciar o envio dos e-mails. Se for e se não houver chegado o prazo final, na
linha 16 ele despacha o evento PrazoParaPostagemDeRelatorioDeTCC1Chegando
junto com o prazo final. Em um job é possível despachar um evento apenas o retornando
como resultado da função. Este evento pode ser observado na Figura 43.
Uma fila, por sua vez, pode ser criada de forma natural, quando referenciada em
uma instancia ou no encadeamento de jobs, ou por meio do decorador @queue. As filas
estão abstraídas em uma API própria utilizando o conceito de atores, sendo cada fila um
ator, que pode executar em um processo separado ou até mesmo em outra máquina. A
Figura 44 mostra como ocorre a relação entre filas e jobs, sendo que um job pode enviar
dados para uma fila e também consumir dela.
Já um handler pode ser criado por meio de um decorador chamado @when. Ele é
composto por uma sentença lógica, sendo ela composta por operadores de eventos ou um
predicado (caso seja atômica).
Ele pode estar associado a comportamentos quando a sentença é verdadeira ou falsa.
Para que a sentença seja avaliada, é necessário que um evento que possua dados iguais
aos definidos de forma explícita na referencia aconteça. Caso todos os eventos aconteçam,
a sentença é avaliada como verdadeira e as instruções definidas são executadas.
A Figura 45 mostra como uma condição booleana pode ser transformada em um
handler. Dado um evento A, um evento B e uma função x, pode-se colocar as condições
Capítulo 4. Abordagem proposta 59
Por fim, o job demonstrado na Figura 48 é um pouco mais simples que o primeiro,
já que sua função é apenas ler da fila de e-mails a serem enviados e realizar o envio por
meio de um cliente de e-mail.
A linha 2 possui o decorador job, que indica que a função é um ator job. A linha 3 in-
dica que este job consome de uma fila chamada de fila_de_envio_de_notificacao_para
_alunos_de_tcc_1, sendo ela a mesma fila presente no handler, que irá alimentar
ela, conforme mostra a Figura 46. Sempre que a fila for alimentada, este job recebe o
seu conteúdo por meio do parâmetro content, conforme mostra a linha 5. Além deste
parâmetro, as linhas 6, 7 e 8 mostram os preparativos do estado do ator, sendo que é
utilizado o decorador on_first_run para indicar uma função que será executada apenas
na primeira vez que o job executar e ela cria uma instância do cliente de e-mail e salvar
em seu estado interno. Por fim, na linha 12, o job encerra sua execução realizando o envio
do e-mail para o grupo contido no objeto EmailParaAlunos criado no handler que
alimentou a fila, junto com o assunto.
A solução completa para o exemplo propõe o job monitorar_prazo_para
_postagem_de_relatorio_de_tcc_1, demonstrado na Figura 42, que diariamente
verifica se a data está entre 1 a 7 dias antes do prazo final da entrega do relatório. Se estiver,
ele dispara o evento PrazoParaPostagemDeRelatorioDeTCC1Chegando contendo
Capítulo 4. Abordagem proposta 61
Na próxima seção é explicado como esses três conceitos serão utilizados no trabalho
para possibilitar a execução do processamento de forma paralela e em streaming mantendo
o formato de pipeline.
Por meio dos conceitos apresentados na seção anterior foi criada uma estratégia
para processar os documentos JSON de forma paralela, com streaming dos dados em um
pipeline. Essa estratégia foi implementada transformando os componentes originais em
atores, seguindo o modelo de atores.
Conforme descrito anteriormente, pode-se utilizar um job para executar computação
e armazenar estado. Assim, os componentes originais MongoDB Reader e JsonToNode
se transformaram em um job, enquanto que o Neo4jClient foi utilizado por meio de dois
outros componentes, no formato de jobs, chamados de NodeToNeo4j, que realiza a
escrita de nós no Neo4j, e Neo4jCreateRelationship, que realiza a escrita de arestas no
Neo4j. Essa separação entre o tempo de escrita de nós e arestas acontece para possibilitar
o processamento de objetos de forma assíncrona, sendo criado um handler que coordena
a escrita assíncrona de arestas.
Capítulo 4. Abordagem proposta 62
Na Figura 40 estão presentes todos esses componentes, sendo que entre cada job
está presente uma fila para balancear a carga de trabalho e possibilitar a execução paralela
de computação entre os dados. Além disso, existem os componentes Schema API, que
se transformou em um ator, e um novo componente chamado de Id Manager, que
também é um ator. Por fim, o Turbo C2 está presente para realizar o gerenciamento de
criação de handlers e processamento de eventos, que são despachados no NodeToNeo4j
e Neo4jCreateRelationship.
Para possibilitar a escrita assíncrona de nós e posteriormente a criação de arestas, o
Algoritmo 1 foi modificado. Anteriormente ele executava de forma recursiva até que todos
os objetos aninhados do documento JSON em formato de dicionário fossem processados,
assim criando uma árvore de Nodes. Este comportamento pode ser perigoso no caso
de objetos JSON gigantes, que podem ocupar completamente o processamento dos jobs
disponíveis e até provocar falhas devido ao uso excessivo de memória. O novo Algoritmo 5,
mostrado a seguir, substitui a recursividade por uma retroalimentação de todos os objetos
aninhados, sendo que em conjunto com a retroalimentação é criado um handler que liga
o objeto pai ao filho.
O handler que está presente na Figura 50 estabelece uma ligação entre eventos de
Neo4JNodeCreated. Este evento é despachado pelo componente NodeToNeo4j após ser
realizada a escrita do nó, sendo ele demonstrado na Figura 51.
Dessa forma, o handler deve ser interpretado dessa forma: "quando o evento
Neo4JNodeCreated for despachado com o id referente ao nó de origem (pai) da aresta e
quando o evento de Neo4JNodeCreated for despachado com o processing_id referente ao
UUID do nó de destino (filho), execute as seguintes instruções.".
Tendo como base a Figura 52, esta sentença se tornaria (para a primeira
resposta): "quando o evento Neo4JNodeCreated for despachado com o id 1 (ques-
toes) e quando o evento de Neo4JNodeCreated for despachado com o processing_id
6d2560f31a654f32b25b64c7ef11398c (resposta 1), execute as seguintes instruções."
A instrução que é executada quando o handler for verdadeiro é a de enviar os
dados de aresta entre os dois Nodes para o componente Neo4jCreateRelationship, sendo
este comportamento ilustrado na Figura 53.
Dessa forma, como podemos notar na Figura 40, não existe uma fila onde o compo-
nente Neo4jCreateRelationship recebe os dados do componente anterior, chamado de
NodeToNeo4j, mas quando o handler executa as instruções, ele envia todas as informa-
ções necessárias para a criação de arestas por meio do Turbo C2. Quando as informações
forem enviadas, o Neo4jCreateRelationship cria a aresta, que no exemplo estabelece
uma ligação entre questoes e respostas.
Para exemplificar a forma como o handler deve se comportar e de que forma são
criados os dados de arestas, na Figura 54 está uma representação visual do handler criado
para esperar pelos eventos de criação de nó de questoes e respostas.
Por fim, para lidar com os identificadores únicos sequenciais, foi criado um compo-
nente chamado de Id Manager (ver Figura 40). Ele gerencia o contador de identificadores
para cada objeto, assegurando que nunca haja identificadores repetidos.
4.3.1 Painéis
A seção chamada de All time possui a subseção Status, que mostra as métricas
de migração referentes ao momento atual. Conforme demonstra a Figura 58, o painel de
Status da aplicação no momento do registro indicava que 12 nós estavam sendo criados
no Neo4j por segundo e 2 arestas estavam também sendo criadas por segundo.
A segunda seção, chamada de Daily metrics, possui painéis com métricas voltadas
para o acompanhamento das migrações passadas e atuais. A subseção Totals possui o
número total de métricas agregadas por migração no período de um dia e a subseção Daily
records migrated apresenta um gráfico de linha representando o número de migrações
finalizadas ao longo do tempo, com uma hora de intervalo entre os pontos durante uma
semana.
Conforme mostra a Figura 59, a aplicação no momento do registro possuía diversas
migrações acontecendo ao mesmo tempo, sendo os cinco primeiros cartões indicando o
número de nós migrados e os três últimos indicando o número de arestas criadas. Para
ilustração, o primeiro cartão indica que a migração Default Migration2 escreveu 714 nós
no Neo4j nas últimas 24 horas, enquanto que o sexto cartão indica que a migração Default
Migration0 escreveu 303 arestas nesse mesmo período.
Capítulo 4. Abordagem proposta 68
4.3.2 Migrações
A pagina de migrações contém uma listagem de todas as migrações criadas, que po-
dem ser oriundas da aplicação Web ou do código. Além disso, é possível obter informações
dos nomes das migrações, o seu tipo, o número total de réplicas em todas as instâncias
dos jobs, fazer o gerenciamento por meios dos botões de play, pause e stop e, por fim, criar
novas migrações.
Cada botão possui um comportamento único referente ao estado da migração. O
botão play está disponível caso a migração não possua réplicas, não possua o mínimo de
réplicas necessárias para uma migração completa, que seria pelo menos uma réplica para
cada job, ou esteja no estado pausado. O botão pause impede que o job consuma itens da
fila. Por fim, o botão de stop elimina todas as réplicas dos jobs.
A Figura 62 mostra a tela de migrações contendo todos os recursos criados desde
o inicio da aplicação. Na listagem estão presente 6 migrações, todas sem réplicas no
momento. Quando arrastamos o mouse para o botão de play, ele muda de cor para o verde,
sinalizando que os pré-requisitos para sua execução estão cumpridos, conforme demonstra
a Figura 63. Neste contexto, o botão de play cria uma réplica para cada job necessário e
começa a migração.
Capítulo 4. Abordagem proposta 70
Para criar uma nova migração, deve-se apertar o botão Add new migration. Ele
direciona o usuário para uma tela de criação de migrações, que espera receber todas as
informações necessárias. A Figura 64 demonstra a tela que o usuário se depara ao apertar
no botão. Nela, é possível notar que existem três seções (database, migration e advanced
settings), sendo que o usuário está presente na primeira seção (database).
Capítulo 4. Abordagem proposta 71
A seção database possui três campos obrigatórios para serem preenchidos, além de
um botão para teste de conexão. O primeiro campo espera receber uma url com todas as
informações de conexão para o Mongo DB. Já o campo Mongodb database espera receber o
database do banco de dados onde está presente a coleção a ser migrada. Por fim, o campo
Mongodb collection espera receber a coleção que possui os objetos json a serem migrados.
Uma vez informada a url do banco de dados é possível utilizar o botão de teste de
conexão para listar todas as databases e coleções disponíveis. A Figura 65 demonstra o
comportamento do botão de teste de conexão, que modifica o campo de Mongodb database
disponibilizando uma lista de todas as databases disponíveis para aquela conexão. Além
disso, uma nova seção chamada de logs aparece contendo o resultado do teste de conexão.
Capítulo 4. Abordagem proposta 72
Figura 65 – Tela para criação de uma nova migração com url preenchida
Após a seleção da database, pode-se ver na Figura 66 que o campo Mongodb collec-
tion também é modificado para mostrar todas as coleções disponíveis naquele database.
Figura 66 – Tela para criação de uma nova migração com database preenchida
Após configurada a conexão com o banco de dados é necessária apenas mais uma
informação obrigatória antes do botão de criação ser disponibilizado. Essa informação é o
Capítulo 4. Abordagem proposta 73
Após preencher todos os campos obrigatórios pode-se criar uma nova migração.
Na Figura 68 foi inserido o nome Test para satisfazer o requisito de nome, além do valor
padrão de tipo de migração.
5 AVALIAÇÃO
Para validar os dados migrados foi criado um job que cria modelos randômicos com
outros modelos aninhados que injeta eventos próprios durante a criação para conseguir
validar os dados assim que são escritos no Neo4j. A validação ocorre comparando os dados
simples escritos por meio de um evento que estabelece uma ligação entre o identificador
interno dado ao objeto e um UUID criado durante a geração dos dados. Assim, é possível
obter o dado por meio do seu id e comparar se todos os campos estão no Neo4j.
Para validar as relações, o validador adiciona um campo de metadados nos objetos
criados indicando que um campo contém objetos aninhados. Assim, após a escrita da
aresta, é disparado um evento que também liga o UUID do objeto ao seu id e o validador
verifica se foi estabelecido o mesmo número de relações que o número de objetos aninhados,
podendo ser um objeto simples ou uma lista de objetos. Ao final, é escrito um relatório
contendo o JSON Schema do modelo randômico criado, o seu estado original, o dado
escrito no Neo4j, encontrado por meio de seu id, e qual é o resultado da validação.
As validações foram executadas por diversas vezes e não houve nenhum erro re-
lacionado a dados faltando ou arestas não criadas. A Figura 70 mostra o resultado da
validação de um modelo aleatório, sendo seu esquema e exemplo de dados disponibilizados
nos Apêndices. Já a Figura 71 mostra os resultados de validação condensados por status,
sendo a primeira linha correspondente a todas as validações, a linha 3 referente aos nós
criados e a validação de propriedades presentes no banco de destino e a linha 6 referente
às arestas criadas.
Capítulo 5. Avaliação 76
Para avaliar a ferramenta, foram obtidos dados JSON aninhados de uma API
pública. Esses dados foram escritos no MongoDB em instância criada por meio do docker-
compose, sendo também criada uma instância do Neo4j. Após isso, foram definidas migra-
ções com configurações variadas de réplicas para comparação dos resultados.
Ao todo, foram obtidas 100000 questões com 58983 respostas dadas por 58983
usuários. Como o campo answers dentro do objeto Question é um array de objetos, existem
algumas questões que possuem mais de uma resposta, enquanto outras não possuem
nenhuma resposta.
Para a migração dos dados, foi utilizado um notebook Intel i7 1260P 4.70 GHz e
16 GB de memória RAM 5200 MHz. Toda a infraestrutura foi criada na mesma máquina,
por meio do docker-compose, e disponibilizada na rede local.
Para o teste distribuído, foi criado um cluster Ray na rede local contendo mais
um notebook 10a geração do Intel Core i5-10200H 4,1 GHz e 32GB de memória RAM
2666MHZ. Esse notebook foi preparado para receber a migração, sendo feita a instalação
das bibliotecas necessárias conforme especifica o gerenciador de dependências utilizado
para este trabalho, chamado de Poetry 1 .
sendo elas mapeadas para arestas entre os nós. Para identificar todos os nós e arestas que
devem ser escritas, foram utilizadas as seguintes consultas para análise de objetos JSON
no banco de dados MongoDB:
[
{
"$match": {"answers": {"$exists": True, "$not": {"$size": 0}}}
},
{
"$group": {
"_id": None,
"totalAnswers": {"$sum": {"$size": "$answers"}}
}
}
]
[
{
"$unwind": "$answers"
},
{
"$group": {
"_id": None,
"count": {"$sum": 1}
}
}
]
Esta consulta obteve como resultado 217966 objetos totais, sendo 100000 objetos
questions, 58983 objetos answers e o mesmo número de objetos owner. Dessa forma, é
esperado que sejam criados no Neo4j 100000 nós, sendo que a eles deve ser atribuído a
etiqueta de questions, além de mais 58983 nós correspondentes às respostas, com etiqueta
de answers, e também 58983 nós correspondentes aos usuários donos das respostas, com a
etiqueta owner. Para validar a quantidade de dados JSON convertidos e migrados, foram
efetuadas as seguintes consultas no Neo4j:
• Quantidade de relações
MATCH ()-[relationship]->()
RETURN TYPE(relationship) AS type, COUNT(relationship) AS amount
ORDER BY amount DESC;
Para validar a migração, além das consultas executadas no MongoDB para conta-
gem dos objetos, foi efetuada uma migração utilizando uma ferramenta, disponibilizada
pelo próprio Neo4j para a importação de documentos JSON, por meio de um plugin de
conexão com o banco de dados. Os resultados dessa migração foram analisados por meio
das consultas anteriores para garantir que os números de nós e arestas esperados poderiam
ser alcançados. Essa consulta foi adaptada segundo o site do Neo4j e executou no tempo de
um pouco mais de 1 hora. A consulta utilizada para a migração dos dados foi a seguinte:
CALL apoc.mongo.find(
"mongodb://192.168.0.34:27017/stackoverflow.questions?replicaSet=rs0"
) YIELD value as q
MERGE (question:questions {
_id: q.question_id,
title: coalesce(q.title, "no_title"),
share_link: q.share_link,
favorite_count: coalesce(q.favorite_count, 0),
tags: q.tags
})
FOREACH (a IN q.answers |
MERGE (question)<-[:ANSWERS]-(answer:answers {_id:a.answer_id})
MERGE (answerer:owner {
_id:coalesce(a.owner.user_id, "DELETED_" + a.owner.display_name),
display_name: a.owner.display_name
})
MERGE (answer)<-[:PROVIDED]-(answerer)
)
Capítulo 5. Avaliação 80
5.2.4 Resultados
A Tabela 4 mostra o desempenho por configuração, sendo ele composto pelo tempo
médio para criação de nós e arestas e o tempo total da migração. As métricas de memória
e CPU foram coletadas por meio das métricas do Ray, sendo os gráficos presentes no
Prometheus. As configurações que rodam em cluster possuem duas métricas, sendo a
primeira referente ao primeiro notebook, de 16GB, e a segunda para o notebook de 32GB.
A Configuração 1 executou no tempo total de 39 minutos e 7 segundos, sendo que,
conforme mostra a Figura 73, o uso de CPU teve alguns picos, mas se manteve próximo da
Capítulo 5. Avaliação 81
Tabela 4 – Desempenho
Desempenho Configuração 1 Configuração 2 Configuração 3
Tempo de execução total 39m:7s 30m:21s 27m:57s
Tempo médio de escrita
92,87/segundo 119,70/segundo 129,97/segundo
de nós
Uso de memória médio 6,011GB 4,498GB + 2,492GB 5,087GB + 2,560GB
Uso de CPU médio 19,55% 22,11% + 11% 24,64% + 11,86%
média de 19%, enquanto que o uso de memória, conforme mostra a Figura 74 aumentou
até o pico de um pouco mais de 6GB.
Vale observar ainda que a ferramenta proposta neste trabalho não necessita de
nenhum dado adicional, diferente dos métodos de acesso providos pelo plugin do Neo4j,
que necessitam definir manualmente as arestas e as propriedades dos objetos.
84
6 CONCLUSÃO
• Criação de mais painéis voltados a uma análise mais aprofundada das métricas;
• Possibilidade de criar relações com nomes diferentes dos padrões, podendo o usuário
definir ou então a escolha por meio de um modelo de linguagem.
REFERÊNCIAS
CODD, E. F. A Relational Model of Data for Large Shared Data Banks. Commun.
ACM, Association for Computing Machinery, New York, NY, USA, v. 13, n. 6,
p. 377–387, jun. 1970. ISSN 0001-0782. DOI: <10.1145/362384.362685>. Disponível em:
<https://doi.org/10.1145/362384.362685>.
MEIER, Andreas; KAUFMANN, Michael. SQL & NoSQL databases. [S.l.]: Springer,
2019. ISBN 978-3-658-24549-8.
MICHAEL NASH, Wade Waldron. Applied Akka Patterns. [S.l.: s.n.]. Disponível em:
<https://www.oreilly.com/library/view/applied-akka-
patterns/9781491934876/ch01.html>.
MORITZ, Philipp et al. Ray: a distributed framework for emerging AI applications. In:
PROCEEDINGS of the 13th USENIX Conference on Operating Systems Design and
Implementation. Carlsbad, CA, USA: USENIX Association, 2018. (OSDI’18), p. 561–577.
MUS, MAM. Comparison between SQL and NoSQL databases and their relationship
with big data analytics, 2019.
REFERÊNCIAS 87
SAHATQIJA, Kosovare et al. Comparison between relational and NOSQL databases. In:
IEEE. 2018 41st international convention on information and communication technology,
electronics and microelectronics (MIPRO). [S.l.: s.n.], 2018. P. 0216–0221.
SAMPAIO, RF; MANCINI, MC. Estudos de revisão sistemática: um guia para síntese
criteriosa da evidência científica. Brazilian Journal of Physical Therapy, Associação
Brasileira de Pesquisa e Pós-Graduação em Fisioterapia, v. 11, n. 1, p. 83–89, jan. 2007.
ISSN 1413-3555. DOI: <10.1590/S1413-35552007000100013>. Disponível em:
<https://doi.org/10.1590/S1413-35552007000100013>.
ÜNAL, Yelda; OĞUZTÜZÜN, Halit. Migration of data from relational database to graph
database. In: p. 1–5. DOI: <10.1145/3200842.3200852>.
Apêndices
89
{
"$defs": {
"RandomModel_2f2cde6cc9014f7a98fa218a5ae304d6": {
"properties": {
"data_uuid": {
"format": "uuid",
"title": "Data Uuid",
"type": "string"
},
"t4a0": {
"default": [],
"items": {
"type": "string"
},
"title": "T4A0",
"type": "array"
},
"t4a1": {
"title": "T4A1",
"type": "integer"
},
"t4a2": {
"title": "T4A2",
"type": "number"
},
"t4a3": {
"title": "T4A3",
"type": "string"
},
"t4a4": {
"title": "T4A4",
"type": "boolean"
},
"t4a5": {
"title": "T4A5",
"type": "integer"
APÊNDICE A. Exemplo de esquema JSON utilizado para validação dos dados 90
},
"t4a6": {
"title": "T4A6",
"type": "string"
},
"t4a7": {
"title": "T4A7",
"type": "string"
}
},
"required": [
"data_uuid",
"t4a1",
"t4a2",
"t4a3",
"t4a4",
"t4a5",
"t4a6",
"t4a7"
],
"title": "RandomModel_2f2cde6cc9014f7a98fa218a5ae304d6",
"type": "object"
},
"RandomModel_4c481c3a9d2447dca5b74cebb3d21100": {
"properties": {
"data_uuid": {
"format": "uuid",
"title": "Data Uuid",
"type": "string"
},
"t3a0": {
"title": "T3A0",
"type": "boolean"
},
"t3a1": {
"default": [],
"items": {
"type": "boolean"
},
APÊNDICE A. Exemplo de esquema JSON utilizado para validação dos dados 91
"title": "T3A1",
"type": "array"
},
"t3a2": {
"title": "T3A2",
"type": "number"
},
"t3a3": {
"title": "T3A3",
"type": "boolean"
},
"t3a4": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": None,
"title": "T3A4"
},
"t3a5": {
"title": "T3A5",
"type": "string"
},
"t3a6": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": None,
"title": "T3A6"
},
APÊNDICE A. Exemplo de esquema JSON utilizado para validação dos dados 92
"t3a7": {
"title": "T3A7",
"type": "boolean"
}
},
"required": [
"data_uuid",
"t3a0",
"t3a2",
"t3a3",
"t3a5",
"t3a7"
],
"title": "RandomModel_4c481c3a9d2447dca5b74cebb3d21100",
"type": "object"
},
"RandomModel_6d9a8f1a6b484d3c97a83d65a51694d2": {
"properties": {
"data_uuid": {
"format": "uuid",
"title": "Data Uuid",
"type": "string"
},
"t6a0": {
"title": "T6A0",
"type": "integer"
},
"t6a1": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": None,
"title": "T6A1"
},
APÊNDICE A. Exemplo de esquema JSON utilizado para validação dos dados 93
"t6a2": {
"anyOf": [
{
"items": {
"type": "integer"
},
"type": "array"
},
{
"type": "null"
}
],
"default": None,
"title": "T6A2"
},
"t6a3": {
"default": [],
"items": {
"type": "number"
},
"title": "T6A3",
"type": "array"
},
"t6a4": {
"title": "T6A4",
"type": "boolean"
},
"t6a5": {
"default": [],
"items": {
"type": "boolean"
},
"title": "T6A5",
"type": "array"
},
"t6a6": {
"title": "T6A6",
"type": "number"
},
APÊNDICE A. Exemplo de esquema JSON utilizado para validação dos dados 94
"t6a7": {
"default": [],
"items": {
"type": "integer"
},
"title": "T6A7",
"type": "array"
},
"t6a8": {
"title": "T6A8",
"type": "integer"
},
"t6a9": {
"title": "T6A9",
"type": "integer"
}
},
"required": [
"data_uuid",
"t6a0",
"t6a4",
"t6a6",
"t6a8",
"t6a9"
],
"title": "RandomModel_6d9a8f1a6b484d3c97a83d65a51694d2",
"type": "object"
},
"RandomModel_8af5a6c4f36e4d3792052e5ade486b69": {
"properties": {
"data_uuid": {
"format": "uuid",
"title": "Data Uuid",
"type": "string"
},
"t5a0": {
"anyOf": [
{
"type": "string"
APÊNDICE A. Exemplo de esquema JSON utilizado para validação dos dados 95
},
{
"type": "null"
}
],
"default": None,
"title": "T5A0"
},
"t5a1": {
"title": "T5A1",
"type": "integer"
},
"t5a2": {
"title": "T5A2",
"type": "integer"
},
"t5a3": {
"title": "T5A3",
"type": "boolean"
},
"t5a4": {
"title": "T5A4",
"type": "number"
},
"t5a5": {
"title": "T5A5",
"type": "integer"
},
"t5a6": {
"title": "T5A6",
"type": "string"
}
},
"required": [
"data_uuid",
"t5a1",
"t5a2",
"t5a3",
"t5a4",
APÊNDICE A. Exemplo de esquema JSON utilizado para validação dos dados 96
"t5a5",
"t5a6"
],
"title": "RandomModel_8af5a6c4f36e4d3792052e5ade486b69",
"type": "object"
},
"RandomModel_929eab692ccf4be2b9a074c174c49876": {
"properties": {
"data_uuid": {
"format": "uuid",
"title": "Data Uuid",
"type": "string"
},
"t7a0": {
"title": "T7A0",
"type": "number"
},
"t7a1": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"default": None,
"title": "T7A1"
}
},
"required": [
"data_uuid",
"t7a0"
],
"title": "RandomModel_929eab692ccf4be2b9a074c174c49876",
"type": "object"
},
"t2": {
"properties": {
APÊNDICE A. Exemplo de esquema JSON utilizado para validação dos dados 97
"data_uuid": {
"format": "uuid",
"title": "Data Uuid",
"type": "string"
},
"t2a0": {
"title": "T2A0",
"type": "string"
},
"t2a1": {
"title": "T2A1",
"type": "integer"
},
"t2a2": {
"title": "T2A2",
"type": "boolean"
},
"t2a3": {
"title": "T2A3",
"type": "integer"
},
"t2a4": {
"title": "T2A4",
"type": "integer"
},
"t2a5": {
"title": "T2A5",
"type": "integer"
},
"random_model_4c481c3a9d2447dca5b74cebb3d21100": {
"$ref": "#/$defs/RandomModel_
4c481c3a9d2447dca5b74cebb3d21100"
},
"random_model_2f2cde6cc9014f7a98fa218a5ae304d6": {
"$ref": "#/$defs/RandomModel_
2f2cde6cc9014f7a98fa218a5ae304d6"
},
"random_model_8af5a6c4f36e4d3792052e5ade486b69": {
"$ref": "#/$defs/RandomModel_
APÊNDICE A. Exemplo de esquema JSON utilizado para validação dos dados 98
8af5a6c4f36e4d3792052e5ade486b69"
},
"random_model_6d9a8f1a6b484d3c97a83d65a51694d2": {
"items": {
"$ref": "#/$defs/RandomModel_
6d9a8f1a6b484d3c97a83d65a51694d2"
},
"title": "Random Model 6D9A8F1A6B484D3C97A83D65A51694D2",
"type": "array"
}
},
"required": [
"data_uuid",
"t2a0",
"t2a1",
"t2a2",
"t2a3",
"t2a4",
"t2a5",
"random_model_4c481c3a9d2447dca5b74cebb3d21100",
"random_model_2f2cde6cc9014f7a98fa218a5ae304d6",
"random_model_8af5a6c4f36e4d3792052e5ade486b69",
"random_model_6d9a8f1a6b484d3c97a83d65a51694d2"
],
"title": "t2",
"type": "object"
}
},
"properties": {
"data_uuid": {
"format": "uuid",
"title": "Data Uuid",
"type": "string"
},
"t1a0": {
"title": "T1A0",
"type": "integer"
},
"t1a1": {
APÊNDICE A. Exemplo de esquema JSON utilizado para validação dos dados 99
"title": "T1A1",
"type": "boolean"
},
"t1a2": {
"title": "T1A2",
"type": "boolean"
},
"t1a3": {
"title": "T1A3",
"type": "integer"
},
"t1a4": {
"title": "T1A4",
"type": "boolean"
},
"t1a5": {
"anyOf": [
{
"items": {
"type": "boolean"
},
"type": "array"
},
{
"type": "null"
}
],
"default": None,
"title": "T1A5"
},
"t1a6": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
APÊNDICE A. Exemplo de esquema JSON utilizado para validação dos dados 100
"default": None,
"title": "T1A6"
},
"t2": {
"$ref": "#/$defs/t2"
},
"random_model_929eab692ccf4be2b9a074c174c49876": {
"$ref": "#/$defs/RandomModel_929eab692ccf4be2b9a074c174c49876"
}
},
"required": [
"data_uuid",
"t1a0",
"t1a1",
"t1a2",
"t1a3",
"t1a4",
"t2",
"random_model_929eab692ccf4be2b9a074c174c49876"
],
"title": "t1",
"type": "object"
}
101
{
"data_uuid": "a4972156-7ac1-494f-a0be-f5a2d08f3438",
"t1a0": -52380638,
"t1a1": true,
"t1a2": false,
"t1a3": 28481929,
"t1a4": false,
"t1a5": [
true
],
"t1a6": 90879003,
"t2": {
"data_uuid": "fcc0f664-232b-41b5-b89e-759051a6aebc",
"t2a0": "COP3dQLe",
"t2a1": 83411830,
"t2a2": false,
"t2a3": 24157531,
"t2a4": -30400224,
"t2a5": 32125767,
"random_model_4c481c3a9d2447dca5b74cebb3d21100": {
"data_uuid": "1da3e3c8-0b9b-40ca-85d5-5a085e276121",
"t3a0": false,
"t3a1": [
false,
false,
true,
true,
false,
true,
false,
false,
false,
false,
true,
true,
false,
APÊNDICE B. Exemplo de dado aleatório gerado através do JSON esquema (truncado devido ao
tamanho de 7543 linhas) 102
false,
false,
false,
false,
true,
false,
false,
true,
false,
true,
true,
true,
true,
true,
false,
true,
false,
true,
true,
false,
false,
false,
false,
false,
true,
false,
true,
false,
false,
true,
false,
true,
false,
false,
true,
false,
true,
true,
false,
APÊNDICE B. Exemplo de dado aleatório gerado através do JSON esquema (truncado devido ao
tamanho de 7543 linhas) 103
false,
true,
true,
false,
false,
false,
true,
true,
false,
true,
true,
true,
true,
false,
false,
true,
true,
true,
false,
false,
true,
false,
true,
false,
true,
false,
false,
false,
false,
true,
false,
false,
true,
true,
false,
false,
true,
false,
true,
APÊNDICE B. Exemplo de dado aleatório gerado através do JSON esquema (truncado devido ao
tamanho de 7543 linhas) 104
false,
false,
false,
true,
true,
false,
true,
true,
true
],
"t3a2": 86040820.0,
"t3a3": true,
"t3a4": null,
"t3a5": "GXmH6PSxgBd",
"t3a6": 21071659,
"t3a7": true,
"validation_model": true
},
"random_model_2f2cde6cc9014f7a98fa218a5ae304d6": {
"data_uuid": "eaaad889-b8e2-448d-aee4-55ad5d082f3a",
"t4a0": [
"2mq6A7GbasY6wkncPLqc8r0ekvPjsIGhTCyyLi8su",
"oqg5LNbMoCUjBPB5LJjmSNGCL7pFvOoW",
"V8edlSHmX6pwWcBMDSlQXhnmQOdOUF
b2lHZgUmD1HswTH0gRyOm41CJ16HnA7bTxQm8",
"KwZIJgAB1Bwa",
"fn6",
"Dxh",
"wi8QTdHel8w5yWekZaMFr82B5PmjcNM6hGBzIUtrEW2mEkQnmg
EW1mJN30to97sSvr4RKbQugPvAYgWVK3gtLyC6",
"zBpMYbwTC6QyDV7w6YkFkQao4Dfck9Cg8eTN5XVBwHNBURU0MI2sJ2vt",
"Puvr9xmarYh5Ld0FF",
"3Dw3rYLrPQMYmTcmrQ6MKDzYp1DANGTUrbT",
"h67fwghrA9q0Gp9rc1rpBS8rDEYxvqp2PwLnMX1LrmLMWkATJ",
"4CWHXUB9",
"s",
"1SWT8hf5BZSIrJZ4LsSgvJ5pnKLef9Hu0AqK7",
"lCqlpnPt7ei1XfFabQMqDKBidfEOJaJAUwza",
"Lvp07IlPf0ZZK257wxRhlSYsEJ7E4"
APÊNDICE B. Exemplo de dado aleatório gerado através do JSON esquema (truncado devido ao
tamanho de 7543 linhas) 105
],
"t4a1": 30197612,
"t4a2": 59900225.0,
"t4a3": "1egNJK4Uq05vkeEVAo7GAPStsiDPlVBEun0cYyj1LV4JTep1nzFyE
qIjk7UOiUfoPs5LsAkyBgNjJKAsPoIZAFKXVkWa808lwmrT",
"t4a4": false,
"t4a5": 48657761,
"t4a6": "uWKiLse4kbbFv580ELwmSrLNTrPAAc7e
RSU0bUyZ9cAY9bkeBbiBOdC4PffidCvPJ",
"t4a7": "7SI5Wh",
"validation_model": true
},
"random_model_8af5a6c4f36e4d3792052e5ade486b69": {
"data_uuid": "b9d5c609-0e71-4ffb-93fe-40e7c364a5b5",
"t5a0": "enKF1gz2IvXw40PyJ1JBQQHy3JJe07TlbX0naKP9J
GtkHsGGh5Afdobn34dsd7lsLx9eubBaK",
"t5a1": 23121770,
"t5a2": -64692867,
"t5a3": false,
"t5a4": -67702000.0,
"t5a5": -95912701,
"t5a6": "oDOQ8",
"validation_model": true
},
[...]
"validation_model": true
},
"random_model_929eab692ccf4be2b9a074c174c49876": {
"data_uuid": "51eee250-9444-4815-b399-4433ce681fbc",
"t7a0": 95210841.0,
"t7a1": null,
"validation_model": true
},
"validation_model": true
}
106
Abstract. NoSQL databases have existed for a long time and their features
enable efficient storage of various data formats, such as JSON or CSV docu-
ments, and graphic. This work presents a solution for the automatic mapping of
JSON document data to the graph model, considering an application that aims
to migrate JSON data, widely used by document-oriented NoSQL databases,
to graph-oriented NoSQL databases. The focus is on persisting these data in
the Neo4j database management system, which is the leading graph-oriented
NoSQL database in the industry. Preliminary evaluation demonstrates that the
solution is scalable.
Resumo. Bancos de dados NoSQL existem há muito tempo e suas carac-
terı́sticas permitem que diferentes formatos de dados possam ser armazenados
de forma eficiente para consumo, como documentos JSON ou CSV, e grafos.
Este trabalho apresenta uma solução para o mapeamento automático de da-
dos no formato de documento JSON para o modelo de grafo, considerando uma
aplicação que deseja migrar dados JSON, amplamente utilizados por bancos de
dados NoSQL orientados a documentos, para bancos de dados NoSQL orienta-
dos a grafos. O foco deste trabalho é a persistência destes dados no sistema de
gerência de bancos de dados Neo4j, que é o principal representante de bancos
de dados NoSQL orientados a grafos na indústria. Uma avaliação preliminar
demonstra que a solução é escalável.
1. Introdução
Em junho de 1970, Edgar F. Codd publicou um artigo chamado ”A Relational Model of
Data for Large Shared Banks”, que introduziu o modelo relacional de dados, que segundo
ele deveria proteger os usuários dos detalhes de armazenamento interno, assegurando
a continuidade das aplicações mesmo com mudanças no armazenamento [Codd 1970].
Esse modelo possibilitou avanços como transações ACID e a linguagem SQL, sendo am-
plamente utilizado até hoje.
Os bancos de dados relacionais inicialmente possuı́am escalabilidade verti-
cal, projetados para um único servidor que podia ser expandido adicionando recursos
[Pokorny 2011]. Com o aumento da internet e a geração de grandes volumes de da-
dos, especialmente em empresas focadas na análise de dados, surgiram novos desafios
para esses bancos, como problemas de performance e dificuldade em lidar com diferentes
tipos de dados. Os bancos de dados relacionais não podiam ter seu esquema modificado
com o tempo e também não lidavam bem com diferentes tipos de dados. Por conta disso,
novas tecnologias surgiram para solucionar esses problemas, sendo uma delas os bancos
de dados NoSQL [Sahatqija et al. 2018].
NoSQL é uma famı́lia de bancos de dados caracterizados pela flexibilidade no
armazenamento de dados e pela escalabilidade, sendo amplamente utilizados em contex-
tos de Big Data [Abdelhedi et al. 2017]. Eles armazenam dados de forma diferente dos
bancos relacionais, permitindo persistência e recuperação independentes da estrutura e do
conteúdo [MUS 2019].
Os bancos de dados NoSQL podem armazenar dados em pares chave-valor, con-
juntos de colunas, documentos ou grafos, priorizando alta disponibilidade e escalabili-
dade, muitas vezes sacrificando a consistência dos dados [Meier and Kaufmann 2019].
Entre os principais tipos de bancos de dados NoSQL estão os bancos de documentos,
que armazenam dados complexos em formatos como JSON, e os bancos de grafos, que
representam dados como nós e relações entre eles [Meier and Kaufmann 2019].
Os bancos de dados de documentos, apesar de não terem esquemas rı́gidos,
utilizam convenções que facilitam a análise de dados. Alguns dados apresen-
tam estruturas que seriam mais naturalmente representadas em formato de grafos
[De Virgilio et al. 2013], justificando a adoção de bancos de dados orientados a grafos.
No entanto, migrar dados de bancos de dados de documentos para bancos de da-
dos de grafos apresenta desafios, como a identificação manual de relacionamentos entre
dados e a necessidade de aprender novos paradigmas e linguagens. Este trabalho propõe
um processo automatizado de mapeamento e migração de dados de bancos de dados de
documentos para bancos de dados de grafos, focando nos sistemas MongoDB e Neo4j,
que são representativos em suas respectivas categorias e de código aberto. O MongoDB
armazena dados em documentos JSON, enquanto Neo4j utiliza uma estrutura de grafos.
2. Ferramenta
A ferramenta proposta é composta por um script em Python de migração de documentos
JSON que estão presentes no banco de dados MongoDB para o banco de dados de grafos
Neo4j de forma concorrente, distribuı́da e em streaming sem arquivo de configurações
ou instruções pré definidas. A arquitetura desse script é orientada a eventos, utilizando
estratégias para execução paralela dos componentes por meio de outros processos geren-
ciados pelo framework Ray, e utilizando filas para o balanceamento do trabalho, conforme
mostra a Figura 1.
1
https://www.ray.io/
Para viabilizar essa arquitetura foi desenvolvido, por intermédio do framework
Ray, um framework de orquestração distribuı́do que executa códigos em Python de forma
assı́ncrona, paralela e concorrente, com fácil configuração de escalabilidade. Este frame-
work, chamado de Turbo C22 , é utilizado pela solução de mapeamento proposta neste
trabalho, sendo disponibilizado como código aberto.
2.1. Turbo C2
Uma fila, por sua vez, pode ser criada de forma natural, quando referenciada em
uma instancia ou no encadeamento de jobs, ou por meio do decorador @queue. Cada fila
é um ator, que pode executar em um processo separado ou até mesmo em outra máquina.
Ela é responsável por armazenar dados e disponibilizar para os atores, conforme mostra a
Figura 3.
2
https://github.com/gorgonun/turbo-c2
Figura 3. Relação entre filas e jobs
Já um handler pode ser criado por meio de um decorador chamado @when. Ele
é composto por uma sentença lógica, sendo ela composta por operadores de eventos ou
um predicado (caso seja atômica). A Figura 4 mostra o handler utilizado pela ferramenta
para a escrita assı́ncrona de arestas. Ela usa o identificador único do objeto pai e o id de
processamento do objeto filho e quando ambos são criados o handler executa, enviando
os dados de relacionamento para o componente Neo4jCreateRelationship.
3
https://prometheus.io/
Figura 5. Arquitetura de alto nı́vel do Turbo C2
2.2. Componentes
Cada parte principal da migração foi separada em um componente, sendo todos eles jobs
com filas entre si. O primeiro componente é o MongoDB Reader, responsável por ler
dados BSON provenientes do MongoDB e os transformar em um formato que a aplicação
consegue processar (dicionário Python).
Seguido a este componente está o JsonToNode, que recebe dados no formato de
dicionário Python e processa suas informações, transformando-as em uma estrutura in-
terna (Node) por onde é possı́vel aplicar as regras de mapeamento. Para que ele funcione
é necessario dois componentes externos, sendo eles a SchemaAPI, que identifica objetos
JSON por meio da pontuação de diferença de seus atributos, e o Id Manager, que gerencia
identificadores únicos sequências criados pela aplicação.
Para a SchemaAPI identificar objetos JSON ela calcula uma pontuação de
diferença entre os schemas já identificados e os atributos do objeto. Essa pontuação é
obtida por meio da relação entre a união da diferença dos atributos entre esquema e objeto
sobre a união de todos os atributos únicos, sendo que o esquema com menor pontuação é
selecionado, desde que a diferença seja menor que o limite estabelecido. Na Figura 6 está
a pontuação resultante de um exemplo. Como o esquema usuario possui a pontuação de
0, enquanto que questoes tem a pontuação de 0,5, o Schema será usuario.
Figura 6. Pontuação para identificar um objeto usuario
Após esse componente está o NodeToNeo4j, que recebe a estrutura interna Node
e cria as consultas necessárias para a escrita do nó no Neo4j.
Para a escrita de arestas está o componente Neo4jCreateRelationship, que por
meio do Turbo C2 recebe informações de arestas e faz a consulta para sua criação de
forma assı́ncrona.
3. Regras de mapeamento
• Um objeto JSON mantido em uma coleção de nome Cx se torna um nó cujo rótulo
é Cx ;
• Um atributo simples de um objeto JSON Oi se torna uma propriedade do nó cor-
respondente à Oi ;
• Um atributo an de um objeto JSON Oi que mantém um objeto aninhado gera:
– Um nó Oj cujo rótulo é o nome de an ;
– Uma aresta direcionada de Oi e Oj .
Esta solução realiza a escrita de nós por meio da árvore criada a partir dos objetos
JSON aninhados. Para os dados de questoes (Figura 9), o nó patriarca é o primeiro a ser
escrito no Neo4j. Na Figura 10 estão todos os Nodes resultantes do processamento do
objeto JSON no componente JsonToNode, sendo o patriarca (questoes) detentor do id 1,
as respostas id 2 e o usuario id 3. Essa árvore de Node é utilizada como ponto de partida
para a escrita dos nós e arestas.
4. Conclusão
Este trabalho teve como objetivo o desenvolvimento de uma ferramenta que permite a
migração de dados semiestruturados, sendo o foco nos tipos JSON, armazenados em um
banco de dados, sendo o banco de dados escolhido MongoDB, para um banco de dados
grafos, no caso o banco de dados Neo4j. O código da aplicação está disponı́vel em um
repositório público do GitHub4 .
As principais contribuições desse trabalho são: desenvolvimento de uma pipeline
de processamento de dados JSON aninhados, as regras de mapeamento entre o modelo
de dados JSON e o modelo de dados de grafos de propriedades, a arquitetura distribuı́da
em streaming utilizando a linguagem de programação, a execução de código assı́ncrono
com base em sentenças lógicas provenientes da estrutura de eventos, a interface web que
possui painéis e ferramentas para criação e gerenciamento de migrações e a ferramenta
Json2Node, que realiza a migração de dados em streaming com execução concorrente e
distribuı́da sem a necessidade de nenhuma informação sobre os dados que serão migrados.
Como atividades futuras relacionadas a esse trabalho, podemos considerar: a
implementação de leitura concorrente no MongoDB pelo módulo MongoDBProducer,
a criação de mais painéis voltados a uma análise mais aprofundada das métricas, a adição
de métricas de acompanhamento, a análise de desempenho de cada módulo buscando
aperfeiçoar sua execução, melhorias no algoritmo de detecção de tabelas e esquemas,
buscando se tornar resiliente aos dados JSON com campos nulos escondidos e a possibil-
idade de criar relações com nomes diferentes dos padrões, podendo o usuário definir ou
então a escolha por meio de um modelo de linguagem.
Referências
Abdelhedi, F., Brahim, A. A., Atigui, F., and Zurfluh, G. (2017). Umltonosql: Automatic
transformation of conceptual schema to nosql databases. In 2017 IEEE/ACS 14th In-
4
https://github.com/gorgonun/json-to-graph
ternational Conference on Computer Systems and Applications (AICCSA), pages 272–
279.
Alotaibi, O. and Pardede, E. (2019). Transformation of schema from relational database
(rdb) to nosql databases. Data, 4(4).
Codd, E. F. (1970). A relational model of data for large shared data banks. Commun.
ACM, 13(6):377–387.
De Virgilio, R., Maccioni, A., and Torlone, R. (2013). Converting relational to graph
databases. In First International Workshop on Graph Data Management Experiences
and Systems, GRADES ’13, New York, NY, USA. Association for Computing Ma-
chinery.
Meier, A. and Kaufmann, M. (2019). SQL & NoSQL databases. Springer.
MUS, M. (2019). Comparison between sql and nosql databases and their relationship
with big data analytics.
Pokorny, J. (2011). Nosql databases: A step to database scalability in web environment. In
Proceedings of the 13th International Conference on Information Integration and Web-
Based Applications and Services, iiWAS ’11, page 278–283, New York, NY, USA.
Association for Computing Machinery.
Sahatqija, K., Ajdari, J., Zenuni, X., Raufi, B., and Ismaili, F. (2018). Comparison be-
tween relational and nosql databases. In 2018 41st international convention on in-
formation and communication technology, electronics and microelectronics (MIPRO),
pages 0216–0221. IEEE.