Converter NDJSON para Parquet

Aqui você converte NDJSON para Parquet de graça e sem conta: solte o arquivo acima e em poucos segundos o resultado está pronto para baixar. A conversão acontece dentro do seu próprio navegador, então o arquivo nunca é enviado. Funciona igual no Windows, no macOS e no Linux e também no iPhone e no Android, e continua funcionando mesmo se você cortar a conexão.

  • Onde roda No seu navegador. O arquivo nunca é enviado.
  • Reconstruído Parquet funciona de um jeito diferente de NDJSON. Não é a degradação gradual de um codec com perda: o que Parquet consegue expressar é reproduzido fielmente, e o que não tem equivalente ali não sobrevive de jeito nenhum.
  • Limite de tamanho Até 100 MB por arquivo, de graça e sem conta.
  • Vale saber Objetos aninhados são achatados em colunas. Dados muito aninhados perdem a forma.

Até 100 arquivos de uma vez. Formatos misturados não são problema.

Um arquivo de acréscimo contínuo deriva, e Parquet precisa de um esquema só

É isso que torna este par interessante. Cada linha de um arquivo NDJSON é independente, e é exatamente por isso que é o formato ao qual coisas são acrescentadas: um campo novo pode entrar no escritor numa terça-feira sem que nenhuma linha existente precise ser reescrita. Ao longo de alguns meses de deploys, um único arquivo acumula várias gerações de formato de registro.

Parquet não tolera isso. Seu rodapé declara um conjunto fixo de colunas com um tipo cada, e cada linha obedece — o que é o que permite a um mecanismo de consulta descrever um arquivo de dois gigabytes sem ler uma linha. Então a conversão precisa reconciliar as gerações num esquema só, e tudo abaixo é consequência de como ela faz isso.

O conjunto de colunas é a união entre todas as linhas, não uma amostra

Um campo que aparece pela primeira vez trezentas mil linhas dentro do arquivo ainda vira uma coluna, e as linhas anteriores recebem nulos. É a única reconciliação que não perde dado, e é o motivo de o leitor fazer uma passada completa pelo arquivo antes de escrever qualquer coisa.

A maioria das ferramentas amostra em vez disso — as primeiras mil linhas, o primeiro megabyte — e é por isso que uma carga que funcionou num extrato de teste falha em produção. A falha é pior que um erro: um esquema amostrado descarta silenciosamente todo campo que não viu. Ler tudo custa uma passada pelos dados e remove essa categoria de surpresa inteiramente, ao preço descrito na seção sobre memória adiante.

Um campo cujo tipo mudou vira uma coluna de texto

A outra metade da deriva é uma chave que manteve o nome e mudou de tipo. Um identificador que costumava ser número e agora vem entre aspas, um status que era booleano e virou string, uma versão que foi de 3 para «3.1» — todos aparecem em arquivos de eventos de vida longa, geralmente sem ninguém perceber na hora.

O tipo de cada coluna é decidido sobre todo valor nela: todos booleanos dá BOOLEAN, todos inteiros dentro da faixa de 32 bits dá INT32, outros números dão DOUBLE, e qualquer mistura dá uma coluna de texto com os valores numéricos escritos como texto junto. Uma coluna de texto é visível, convertível numa expressão só, e nunca descarta uma linha em silêncio — o que a alternativa, pegar o primeiro tipo e anular o resto, faz. Se uma coluna que você esperava numérica chega como texto, consultar os valores que falham ao converter é o jeito mais rápido de achar o dia em que o escritor mudou.

O que acontece com um campo de evento aninhado

Eventos estruturados aninham por convenção: um bloco de requisição, um bloco de usuário, um objeto de contexto carregando um identificador de rastreio. Parquet não tem onde colocar um objeto dentro de uma célula, então cada registro é achatado até suas folhas primeiro, e o caminho vira o nome da coluna — request.method, user.id, context.trace_id. Os tipos sobrevivem a isso, porque vêm do JSON, não de uma nova análise de texto.

Arrays são o caso que vale olhar antes de converter. Eles achatam por posição, então um array tags com a e b vira tags.0 e tags.1, e o conjunto de colunas é a união entre todas as linhas do arquivo — uma lista muito longa em algum ponto de um mês de eventos alarga o esquema para o mês inteiro. Onde um campo é genuinamente uma lista de tamanho variável, juntá-lo numa string única com jq primeiro dá um esquema que você consegue consultar em vez de ter de explicar.

Inteiros grandes chegam como DOUBLE, não INT64

Parquet tem um tipo inteiro de 64 bits, e esta conversão nunca o emite. Valores passam por números do JavaScript, que carregam 53 bits de precisão inteira, então qualquer coisa fora da faixa de 32 bits é escrita como DOUBLE em vez de reivindicar uma exatidão que não tem mais.

Arquivos de eventos estão cheios dos valores que isso afeta: carimbos de tempo epoch em milissegundos, identificadores estilo snowflake, contadores de bytes. Para um carimbo de tempo, DOUBLE está tudo bem — a precisão é muito além do que um milissegundo precisa. Para um identificador não está, e o conserto fica no escritor que produziu os eventos: emitir identificadores como strings JSON os mantém exatos por toda a passagem deste pipeline e os deixa numa coluna de texto, que é o que um identificador deveria ser.

A diferença de tamanho em 50 mil eventos

Cinquenta mil registros de pedido gerados com seis campos ficaram em 207 KB como Parquet contra 4,8 MB como NDJSON — melhor que vinte para um nesses dados. O formato da economia importa mais que a proporção, porque a proporção depende inteiramente dos dados.

Duas coisas acontecem. Cada linha de NDJSON repete cada chave, então num registro de seis campos os nomes dos campos são uma fração substancial dos bytes; num armazenamento colunar, cada nome é escrito uma vez só, no rodapé. E valores de um mesmo tipo sentados juntos comprimem muito melhor do que os mesmos valores espalhados por linhas, que é por que uma coluna de cidade ou tipo de evento custa quase nada enquanto uma coluna de texto livre único economiza comparativamente pouco.

A conversão não faz streaming, e o motivo é o esquema

Há uma ironia real aqui. NDJSON existe para que um consumidor nunca precise segurar o arquivo inteiro, e esta conversão segura o arquivo inteiro — porque a última linha pode acrescentar uma coluna ou mudar o tipo de uma, e o rodapé não pode ser escrito até isso ser conhecido.

Então o teto é memória, não um nível de plano. O plano gratuito aceita até 100 MB; dezenas de megabytes converte sem drama e algumas centenas é onde uma aba de navegador começa a sofrer. Acima disso, o formato de origem está do seu lado: `split -l 500000` produz arquivos válidos, cada um converte por conta própria, e um mecanismo de consulta lê um diretório de arquivos Parquet como uma tabela só. É o arranjo que esses mecanismos foram feitos para, e é uma resposta melhor do que um arquivo muito grande.

Lendo o esquema antes de ler as linhas

O DuckDB lê o arquivo numa cláusula FROM, o pandas numa chamada só, o Spark como uma tabela nativa. A primeira coisa que vale olhar é a lista de colunas e seus tipos, não as primeiras dez linhas, e há três perguntas específicas: quais colunas são majoritariamente nulas, quais chegaram como texto e deveriam ser números, e quantas colunas existem no total.

Cada resposta é um fato sobre a origem. Colunas majoritariamente nulas marcam onde o formato do registro mudou; colunas de texto que deveriam ser numéricas marcam onde um tipo mudou sob a mesma chave; uma contagem de colunas maior que a de campos marca um bloco aninhado ou um array que achatou mais largo do que você esperava. Vinte segundos no esquema economizam a hora que leva para notar qualquer um desses depois que o arquivo já foi cruzado com outra coisa.

Vários arquivos, uma tabela, e quando converter de novo

A unidade natural para esta conversão é um período, não um histórico inteiro: um dia de eventos, um mês, um log rotacionado. Cada um converte para seu próprio arquivo, e DuckDB, Spark e todo formato de tabela lakehouse leem um diretório deles como uma tabela única com os esquemas mesclados na leitura.

Isso também lida com deriva melhor do que um arquivo só. Quando o formato do registro muda, os arquivos novos carregam as colunas novas e os antigos não, e o mecanismo de consulta reconcilia na leitura em vez de o conversor reconciliar na escrita. Particionar por dia e converter cada dia uma vez é o formato que esses dados querem, e significa nunca ter de reconverter o histórico porque um campo foi acrescentado.

Os eventos ficam na sua máquina enquanto são transpostos

O escritor de Parquet e a análise de JSON são JavaScript comum, carregado sob demanda por esta página — não há módulo WebAssembly nem etapa de servidor. Nenhuma requisição carrega o arquivo para lugar nenhum; não há conta, fila nem nível de plano.

Para telemetria isso costuma ser o fator decisivo. Fluxos de eventos contêm identificadores de usuário, endereços IP, caminhos de requisição e o que mais a aplicação achou que devia registrar, e o fato de esse dado estar a caminho de um repositório de análise não torna um conversor web de terceiros uma parada aceitável no caminho. O registro também marca o suporte a Parquet como irregular por um motivo: é binário e não editável, então se o arquivo se destina a uma pessoa e não a um mecanismo de consulta, uma planilha é o alvo melhor.

Como converter NDJSON para Parquet

  1. Solte o seu arquivo NDJSON nesta página, ou clique para escolher um.
  2. Escolha Parquet como destino. A conversão acontece no seu navegador e o arquivo não é enviado.
  3. Baixe o arquivo Parquet pronto.

NDJSON ou Parquet: o que muda

NDJSON comparado com Parquet
NDJSONParquet
Nome completoNewline-Delimited JSONApache Parquet
Extensão do arquivo.ndjson, .jsonl.parquet
Tipo de mídiaapplication/x-ndjsonapplication/vnd.apache.parquet
CompressãoSem perdas — nada é descartado
Publicado pela primeira vez20132013
Publicado porApache Software Foundation
LicençaPadrão abertoPadrão aberto
Situação hojeAtualAtual
Abre no navegadorNenhum navegadorNenhum navegador
Considerado no lugarJSON, CSVCSV, JSON

Abrir o resultado

pandas lê tanto NDJSON quanto Parquet, então dá para comparar o resultado com o original sem um segundo programa.

Para que serve cada formato

Parquet vem da Apache Software Foundation é de 2013. pandas, Apache Spark e DuckDB leem o formato.

De NDJSON para Parquet: perguntas frequentes

Meu arquivo NDJSON é enviado para algum lugar?

Não. Esta conversão acontece inteiramente no seu navegador, então o arquivo não sai do seu aparelho. Você mesmo pode conferir: abra a aba de rede das ferramentas de desenvolvedor e converta alguma coisa. Você verá a própria página e as requisições de estatística e de publicidade com que este serviço é pago, e nenhuma que leve o seu arquivo. O motor deste par específico é parquet-wasm, uma compilação em WebAssembly do leitor do Apache Arrow; seu navegador baixa isso uma vez e guarda.

Converter NDJSON para Parquet é grátis?

É. Sem conta, sem marca d’água e sem cota diária para gastar: roda no seu próprio aparelho, então você pode voltar quantas vezes quiser. O navegador processa arquivos de até 100 MB, 100 por vez. parquet-wasm é baixado para a sua máquina e roda lá, e por isso não há contador.

Perde qualidade ao converter NDJSON para Parquet?

NDJSON e Parquet descrevem o conteúdo de maneiras radicalmente diferentes. A conversão é, portanto, uma reconstrução e não uma cópia: fiel, mas não idêntica byte a byte. Objetos aninhados são achatados em colunas. Dados muito aninhados perdem a forma.

Preciso instalar alguma coisa para abrir um arquivo Parquet?

Para a conversão, não: ela acontece no navegador que você já tem aberto. Para abrir o resultado você precisa depois do programa com que o seu aparelho normalmente exibe Apache Parquet.

Mais sobre esses formatos