Couchbase e XML - sem problemas!
Já ouvi essa frase dezenas de vezes: "Ei, o Couchbase é ótimo, mas eu uso XML."
Reconheço que, ao responder com "Couchbase pode seja seu banco de dados XML" é bastante ousado, especialmente para um banco de dados de documentos orientado para JSON. Algumas pessoas da comunidade do Couchbase podem até mesmo pensar duas vezes, mas espero que você entenda o que quero dizer até o final desta publicação.
É um fato que muitos aplicativos legados ainda dependem de XML, mas será que o Couchbase é realmente a solução certa para armazenar e processar seus dados XML? Não se preocupe: com relação ao Couchbase e ao XML, você pode ter seu bolo e comê-lo também.
Depois de apresentar alguns conceitos, este artigo mostra como converter automática e instantaneamente XML em equivalentes JSON no Couchbase com quase nenhum esforço - tudo por meio de uma função Eventing. Se você pensar em JSON como o formato intermediário de computador usado para armazenar seu XML, talvez já tenha percebido onde quero chegar.
Ao transformar seus dados XML em uma representação JSON nativa, você aproveita o rico ecossistema de serviços do Couchbase, incluindo N1QL, Indexação, Pesquisa de texto completo, Eventose Análises.
Couchbase é um banco de dados criado especificamente para ser montado de acordo com suas necessidades. Precisa apenas de um cache rápido? Você só precisa do Data Service ou do armazenamento de valores-chave. Deseja acesso semelhante ao SQL? Adicione os serviços Query e Index. Ou talvez você precise de Analytics ou Full-Text Search? Basta adicionar alguns nós do tipo adequado.
Cada um dos serviços acima é dimensionado de forma independente para que você acabe construindo - e, o que é mais importante, construindo, pagando por - apenas o que você precisa. No artigo abaixo, uso o Eventing para transformar o Couchbase em um banco de dados compatível com XML. Sei que isso parece bom demais para ser verdade, mas acredite, realmente funciona.
Se você estiver familiarizado com Couchbase, XML e JSON, sinta-se à vontade para Vá para a seção Pré-requisitos.
O modelo de dados do Couchbase
Servidor Couchbase é uma plataforma de dados distribuída e de código aberto. Ela armazena dados como itenscada um dos quais tem um chave e um valor. As operações de dados em menos de um milissegundo são fornecidas por serviços avançados de consulta e indexação, além de uma linguagem de consulta rica em recursos e orientada a documentos, N1QL. Várias instâncias do Couchbase Server podem ser combinadas em uma única agrupamento.
Chaves
Cada valor (binário ou JSON) é identificado por uma chave exclusiva, definida pelo usuário ou aplicativo quando o item é salvo. A chave é imutável: depois que o item é salvo, a chave não pode ser alterada. Observe que o Couchbase também se refere à chave de um item como seu id.
Cada chave:
-
- Deve ser uma cadeia de caracteres UTF-8 sem espaços. Caracteres especiais, como
(
,%
,/
,"
e_
são aceitáveis. - Não pode ter mais de 250 bytes.
- Deve ser exclusivo em seu intervalo.
- Deve ser uma cadeia de caracteres UTF-8 sem espaços. Caracteres especiais, como
Valores
O tamanho máximo de um valor é de 20 MiB. Um valor pode ser:
-
- Binário: Qualquer forma de binário é aceitável. Observe que um valor binário não pode ser analisado, indexado ou consultado. Ele só pode ser recuperado pela chave.
- JSON: Um valor JSON, chamado de documentopode ser analisado, indexado e consultado. Cada documento consiste em um ou mais atributos, cada um dos quais tem seu próprio valor. O valor de um atributo pode ser um tipo básico, como um número, uma cadeia de caracteres ou um booleano, ou um tipo complexo, como um documento incorporado ou uma matriz.
JSON é o banco de dados
Vamos falar mais sobre JSON. Você pode analisar, indexar, consultar e manipular documentos JSON. De fato, o Couchbase introduziu a linguagem de consulta N1QL (pronuncia-se "nickel") para atender às necessidades de consulta de bancos de dados distribuídos orientados a documentos. A N1QL é usada para manipular os dados JSON no Couchbase, assim como o SQL manipula os dados em um banco de dados relacional (RDBMS). Ele tem SELECIONAR
, INSERIR
, ATUALIZAÇÃO
, DELETE
e MERGE
para operar em dados JSON.
Você poderia armazenar XML como uma string ou apenas um blob binário, mas onde está a diversão - ou a utilidade - nisso? Por que não converter automaticamente seu XML em JSON? Ainda bem que você perguntou.
XML: Extensible Markup Language (Linguagem de Marcação Extensível)
XML é uma linguagem de marcação introduzida em 1996 que define um conjunto de regras para a codificação de documentos em um formato que pode ser lido por humanos e por máquinas. É uma recomendação do World Wide Web Consortium (W3C).
As metas de design do XML enfatizam a simplicidade, a generalidade e a facilidade de uso na Internet. É um formato de dados textuais com forte suporte via Unicode para diferentes idiomas humanos. Embora o design do XML se concentre em documentos, a linguagem é amplamente usada para a representação de estruturas de dados arbitrárias, como as usadas em serviços da Web.
Aqui está um exemplo de XML:
1 2 3 4 5 6 7 8 |
<CD> <TÍTULO>EmpireBurlesque</TÍTULO> <ARTISTA>BobDylan</ARTISTA> <PAÍS>EUA</PAÍS> <EMPRESA>Colúmbia</EMPRESA> <PREÇO>10.90</PRICE> 1985</YEAR> </CD> |
JSON: Notação de objeto JavaScript
Criado por volta de 2001, o JSON é um formato de arquivo padrão aberto bastante leve e um formato de intercâmbio de dados que usa texto legível por humanos para armazenar e transmitir objetos de dados que consistem em pares atributo-valor e matrizes (ou outros valores serializáveis).
O JSON é um formato de dados muito comum, com uma grande variedade de aplicações, como, por exemplo, aplicativos da Web que se comunicam com um servidor.
Aqui está um exemplo de JSON:
1 2 3 4 5 6 7 |
{ "email": "testme@example.org", "amigos": [ {"name" (nome): "rick"}, {"name" (nome): "cate"} ] } |
O JSON dominou o mundo
Atualmente, quando dois aplicativos se comunicam entre si pela Internet, é provável que o façam usando JSON, especialmente se se comunicarem em texto legível por humanos.
Se você é um fã de XML, não atire no mensageiro aqui. Tenho certeza de que o XML nunca desaparecerá, assim como o HTML como linguagem de marcação, mas gosto de pensar que as estatísticas não mentem quando se trata de desenvolver aplicativos da Internet que precisam se comunicar uns com os outros.
Pré-requisitos: Aprendendo sobre eventos
Neste artigo, usaremos a versão mais recente do Couchbase - versão 6.6.2 -, mas ela também deve funcionar bem em versões anteriores.
Se você não estiver familiarizado com o Couchbase ou com o serviço Eventing, consulte os recursos a seguir, incluindo pelo menos um exemplo de Eventing:
-
- Configurar um servidor Couchbase 6.6.2 funcional de acordo com as instruções em "Comece aqui!"
- Entenda como implementar uma função básica de Eventing como de acordo com as instruções do exemplo de enriquecimento de dados. Observe o "Caso 2", em que usaremos apenas o compartimento de "origem".
- Certifique-se de que você tenha um fonte de pelo menos 100 MB na visualização de Buckets da interface do usuário.
- Certifique-se de que você tenha um balde chamado metadados de pelo menos 100 MB na visualização de Buckets da interface do usuário.
- Consulte a documentação de etapas detalhadas sobre como criar um bucket.
Inserção de XML no Couchbase
Agora você deve ter um aplicativo que carrega dados, ou pode ter usado cbimport
para carregar seus dados XML em seu cluster do Couchbase.
No código abaixo, carrego meu primeiro documento com um chave xml::1
e um corpo consistindo em um tipo
e o id
usado na chave (esses valores são opcionais, mas úteis) e, por fim, uma propriedade in_xml
que contém a própria string XML.
1 2 3 4 5 |
{ "tipo": "xml", "id": 1, "in_xml": " } |
Não podemos fazer muita coisa com a string XML acima. Claro, poderíamos indexar a propriedade in_xml
como uma cadeia de caracteres, mas não obteremos muito desempenho ao fazer isso, a menos que usemos uma pesquisa de prefixo, que, na minha humilde opinião, provavelmente não vale a pena.
Poderíamos passá-lo para o produto Full-Text Search (FTS). Nesse caso, você obteria mais utilidade, mas teria que indexar toda a carga útil do XML (não apenas algo importante).
O que realmente queremos é a representação JSON da propriedade de string XML in_xml
Talvez algo como:
1 2 3 4 5 6 7 8 9 10 |
"out_json": { "CD": { "TITLE" (TÍTULO): "EmpireBurlesque", "ARTISTA": "BobDylan", "COUNTRY" (PAÍS): "EUA", "EMPRESA": "Columbia", "PREÇO": "10.90", "ANO": "1985" } } |
Usando a nova propriedade out_json
Agora, vemos propriedades JSON individuais em uma estrutura adequada: algo com o qual N1QL, indexação, FTS (Full-Text Search), Eventing e Analytics trabalham de forma nativa e eficiente.
Com essas etapas preliminares concluídas, você pode pensar em analisar seus dados e transformá-los em JSON antes de colocá-los no Couchbase. Mas a desvantagem é que agora temos mais bagagem para carregar e mais infraestrutura (fora do Couchbase) para implantar à medida que aumentamos a escala adicionando mais nós.
Se pudermos evitar, não queremos escrever um analisador personalizado. Isso não é divertido e é difícil de manter, pois seus requisitos e formatos de dados mudam com o tempo.
O ideal é que você queira uma solução no servidor que converta genericamente XML em JSON em tempo real, sem impacto nos carregadores de dados.
Conversão de XML para JSON em tempo real
Entre no Eventing Service do Couchbase.
Pense em Eventing como um "gatilho posterior" em um documento que responde a cada alteração (ou mutação) em seus dados. A ideia principal é que sempre que você inserir ou atualizar seu documento (consulte xml::1
acima), um pequeno fragmento de JavaScript, ou lambda, é executado no documento e encapsula a lógica comercial necessária para manipular o documento e garantir que ele esteja no formato desejado.
Nesse caso, queremos converter um documento como o que está abaixo:
1 2 3 4 5 |
{ "tipo": "xml", "id": 1, "in_xml": " } |
Em tempo real, queremos converter o documento acima em um documento enriquecido como o abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "tipo": "xml", "id": 2, "in_xml": " "out_json": { "CD": { "TITLE" (TÍTULO): "EmpireBurlesque", "ARTISTA": "BobDylan", "COUNTRY" (PAÍS): "EUA", "EMPRESA": "Columbia", "PREÇO": "10.90", "ANO": "1985" } } } |
O documento enriquecido mostrado acima pode ser pesquisado por meio de valor-chave ou por meio de um índice em qualquer campo específico usando N1QL.
Como antes, vemos propriedades JSON individuais em uma estrutura adequada, mas sob o out_json
que permite o uso eficiente de FTS (Full-Text Search), Analytics e até mesmo outras funções de Eventing.
Função de evento: convertXMLtoJSON
O Eventing permite que você escreva lógica comercial pura, e o serviço Eventing cuida de toda a infraestrutura necessária para gerenciar e dimensionar sua função (horizontal e verticalmente) em vários nós de forma confiável e eficiente.
Todas as funções do Eventing têm dois pontos de entrada OnUpdate(doc,meta)
e OnDelete(meta,options)
. Observe que, neste exemplo, não estamos preocupados com o último ponto de entrada.
Quando um documento é alterado ou sofre mutação (inserção, reinserção, substituição, etc.), uma cópia do documento e alguns metadados sobre o documento são passados para um pequeno ponto de entrada JavaScript OnUpdate(doc,meta)
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
função Sobre a atualização(doc, meta) { // filtragem de não XML se (!meta.id.startsWith("xml:")) retorno; // A KEY começou com "xml", tente processá-la // =========================================================== *** Faça outro trabalho necessário aqui em alterações que não sejam .in_xml *** // =========================================================== var jsonDoc = parseXmlToJson(doc.in_xml); registro(meta.id, "1. INPUT xml doc.in_xml :", doc.in_xml); registro(meta.id, "2. OUTPUT doc.out_json :", jsonDoc); doc.out_json = jsonDoc; // =========================================================== // enriquecer o bucket de origem com .out_json src_bkt[meta.id] = doc; } |
O OnUpdate(doc,meta)
A lógica acima executa quatro etapas em qualquer mutação.
-
- Primeiro, o prefixo do chave é verificado, se não começar com
xml:
não fazemos mais nada. Observe quemeta.id
é o chave do documento. - Em segundo lugar, apenas chamamos uma função
parseXmlToJson(doc.in_xml)
em que apenas passamos a string XML para a função. - Terceiro, o que retorna é adicionado como um novo campo à cópia do documento como a propriedade
out_json
. - Em quarto lugar, atualizamos o documento em tempo real com a representação JSON.
- Primeiro, o prefixo do chave é verificado, se não começar com
A lógica principal de conversão de XML para JSON
Esta é a lógica central da conversão de XML para JSON:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// A versão 6.6.0 não tem String.matchAll, precisamos de nossa própria função MatchAll função* MatchAll(str, regExp) { se (!regExp.global) { lançar novo TypeError('O sinalizador /g deve estar definido!); } const localCopy = novo RegExp(regExp, regExp.bandeiras); deixar partida; enquanto (partida = localCopy.executar(str)) { rendimento partida; } } // Um analisador simples de XML para JSON função parseXmlToJson(xml) { const json = {}; para (const res de MatchAll(xml,/(?:]*)*>)((?:(?!<\1).)*)(?:)|/gm)) { const chave = res[1] || res[3]; const valor = res[2] && parseXmlToJson(res[2]); json[chave] = ((valor && Objeto.chaves(valor).comprimento) ? valor : res[2]) || nulo; } retorno json; } |
Agradecimentos ao usuário MasterPiece
no Stack Overflow por compartilhar o método parseXmlToJson(xml)
com o mundo. O método usa uma expressão regular interessante na string XML e retorna um objeto JSON de primeira classe. (Sim, ele funciona e é elegante e compacto.)
Observe que, no Couchbase 7.0, o método MatchAll(str, regExp)
em nosso código JavaScript acima é não necessário porque o Couchbase agora tem um executor v8 mais atual que inclui o String.prototype.MatchAll()
função.
Vamos otimizar a função de eventos
O que temos até agora funciona muito bem, mas há um problema.
Suponha que seus dados tenham 100.000 mutações por segundo, todas com um chave prefixo de xml:
. Mesmo que o in_xml
nunca muda, nossa função Eventing atual a) realizará 100.000 conversões de XML para JSON e b) fará 100.000 gravações no serviço de dados (ou armazenamento de valores-chave). Além disso, é provável que seu XML seja muito mais complexo do que a nossa amostra in_xml
e pode ter um aninhamento profundo e milhares de campos.
Algumas reflexões que precisamos considerar:
-
- A conversão de um documento XML grande e aninhado por meio de uma rotina recursiva exige recursos de computação. O método
parseXmlToJson(xml)
será executado em cada mutação, mesmo que o XML não tenha sido alterado. - A gravação em bancos de dados envolve E/S, e queremos economizar ciclos ou gravações para trabalhos mais importantes. Com a função Eventing atual, executaremos uma gravação ou atualizaremos o documento no armazenamento de valores-chave em cada mutação, mesmo que o XML não tenha sido alterado.
- A conversão de um documento XML grande e aninhado por meio de uma rotina recursiva exige recursos de computação. O método
Para corrigir os problemas acima, vamos adicionar alguma lógica para atualizar apenas a propriedade do documento out_xml
se o in_xml
ou se ainda não tivermos um out_xml
propriedade.
Para implementar essa otimização, utilizaremos um soma de verificação
via Eventing's fast crc64()
no método in_xml
e armazená-la em nosso documento como uma propriedade: xmlchksum
.
Em seguida, podemos usar esse campo para minimizar o trabalho extra de converter desnecessariamente XML em JSON ou de gravar desnecessariamente um documento inalterado no serviço de dados.
Aqui está a otimização:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
função Sobre a atualização(doc, meta) { // filtragem de não XML se (!meta.id.startsWith("xml:")) retorno; // A KEY começou com "xml", tente processá-la // =========================================================== *** Faça outro trabalho necessário aqui em alterações que não sejam .in_xml *** // =========================================================== // vamos ver se precisamos recriar nossa representação json. var xmlchksum = crc64(doc.in_xml); // =========================================================== // Não reprocesse se o doc.in_xml não tiver sido alterado, isso pode ser // um grande ganho de desempenho se o documento tiver outros campos que sofrem mutação. // Fazemos isso por meio de uma soma de verificação da propriedade .in_xml. se (doc.xmlchksum && doc.xmlchksum === xmlchksum) retorno; // Ou essa é a primeira passagem ou a propriedade .in_xml foi alterada. var jsonDoc = parseXmlToJson(doc.in_xml); registro(meta.id,"1. INPUT xml doc.in_xml :", doc.in_xml); registro(meta.id,"2. CHECKSUM doc.in_xml :", xmlchksum); registro(meta.id,"3. OUTPUT doc.out_json :", jsonDoc); doc.out_json = jsonDoc; doc.xmlchksum = xmlchksum; // =========================================================== // enriquecer o bucket de origem com .out_json e .xmlchksum src_bkt[meta.id] = doc; } |
Veja o que acrescentamos ao original OnUpdate(doc,meta)
ponto de entrada.
-
- Primeiro, calculamos um
soma de verificação
na string XML acessada a partir dedoc.in_xml
. - Em segundo lugar, comparamos isso com um
soma de verificação
:doc.xmlchksum
(se existir). - Em terceiro lugar, se o
soma de verificação
estiver faltando ou for diferente, convertemos o XML em JSON e armazenamos tanto o novosoma de verificação
xmlchksum
e oout_json
no documento, gravando-o novamente no Data Service.
- Primeiro, calculamos um
A função final e completa do evento
Aqui está nossa função Eventing completa para converter XML em JSON com todas as otimizações mencionadas anteriormente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
função Sobre a atualização(doc, meta) { // filtragem de não XML se (!meta.id.startsWith("xml:")) retorno; // A KEY começou com "xml", tente processá-la // =========================================================== *** Faça outro trabalho necessário aqui em alterações que não sejam .in_xml *** // =========================================================== // vamos ver se precisamos recriar nossa representação json. var xmlchksum = crc64(doc.in_xml); // =========================================================== // Não reprocesse se o doc.in_xml não tiver sido alterado, isso pode ser // um grande ganho de desempenho se o documento tiver outros campos que sofrem mutação. // Fazemos isso por meio de uma soma de verificação da propriedade .in_xml. se (doc.xmlchksum && doc.xmlchksum === xmlchksum) retorno; // Ou essa é a primeira passagem ou a propriedade .in_xml foi alterada. var jsonDoc = parseXmlToJson(doc.in_xml); registro(meta.id,"1. INPUT xml doc.in_xml :", doc.in_xml); registro(meta.id,"2. CHECKSUM doc.in_xml :", xmlchksum); registro(meta.id,"3. OUTPUT doc.out_json :", jsonDoc); doc.out_json = jsonDoc; doc.xmlchksum = xmlchksum; // =========================================================== // enriquecer o bucket de origem com .out_json e .xmlchksum src_bkt[meta.id] = doc; } // A versão 6.6.0 não tem String.matchAll, precisamos de nossa própria função MatchAll função* MatchAll(str, regExp) { se (!regExp.global) { lançar novo TypeError('O sinalizador /g deve estar definido!); } const localCopy = novo RegExp(regExp, regExp.bandeiras); deixar partida; enquanto (partida = localCopy.executar(str)) { rendimento partida; } } // Um analisador simples de XML para JSON função parseXmlToJson(xml) { const json = {}; para (const res de MatchAll(xml,/(?:]*)*>)((?:(?!<\1).)*)(?:)|/gm)) { const chave = res[1] || res[3]; const valor = res[2] && parseXmlToJson(res[2]); json[chave] = ((valor && Objeto.chaves(valor).comprimento) ? valor : res[2]) || nulo; } retorno json; } |
Implementação da função de eventos
Agora é hora de implementar a função Eventing. Revisamos um pouco do código e do design do tradutor de XML para JSON, e agora é hora de ver tudo funcionando em conjunto.
Neste ponto, temos uma função em JavaScript, portanto, precisamos adicioná-la ao cluster do Couchbase e implantá-la em um estado ativo.
Este exemplo requer dois buckets: fonte (ou seja, seu armazenamento de documentos) e metadados (ou seja, um bloco de notas para Eventing que pode ser compartilhado com outras funções de Eventing). O fonte deve ter um tamanho de, pelo menos, 100 MB (se você quiser testar com mais de 10 milhões de documentos, talvez seja melhor criar o bloco fonte balde maior). O metadados deve ter o tamanho mínimo de 100 MB. Esses dois buckets já devem existir como de acordo com os pré-requisitos acima.
-
- Verifique a configuração atual do seu bucket acessando o Console da Web do Couchbase > Buckets página:
- Verifique a configuração atual do seu bucket acessando o Console da Web do Couchbase > Buckets página:
Adicionar manualmente a função convertXMLtoJSON
Para adicionar a primeira função Eventing da lista Console da Web do Couchbase > Eventos página, clique em ADICIONAR FUNÇÃOpara adicionar uma nova função. A função ADICIONAR FUNÇÃO A caixa de diálogo é exibida.
No ADICIONAR FUNÇÃO diálogo, forneça as informações abaixo para elementos de função individuais:
-
-
- Para o Balde de origem definido como fonte.
- Para o Balde de metadados definido como metadados.
- Certifique-se de que
converterXMLparaJSON
é o nome da função que você está criando no Nome da função caixa de texto. - [Etapa opcional] Digite o texto Conversão genérica de XML, no Descrição caixa de texto.
- Para o Configurações use os valores padrão.
- Para o Amarrações crie dois vínculos:
- Para a vinculação, o alias do balde, especifica
src_bkt
como o nome do alias do balde e selecione fonte como o bucket associado, e o modo deve ser leitura e gravação. - Depois de definir suas configurações, sua caixa de diálogo deverá ter a seguinte aparência:
- Depois de fornecer todas as informações necessárias no formulário ADICIONAR FUNÇÃO diálogo, clique em Próximo: Adicionar código. O
cron_impl_2func_651
é exibida. O diálogoconverterXMLparaJSON
contém inicialmente um bloco de código de espaço reservado. Você precisará substituir o código JavaScript que desenvolvemos nesse bloco. - Copie o código-fonte JavaScript da função Eventing a seguir (48 linhas) e cole-o no bloco de código de espaço reservado de
converterXMLparaJSON
:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748função Sobre a atualização(doc, meta) {// filtragem de não XMLse (!meta.id.startsWith("xml:")) retorno;// A KEY começou com "xml", tente processá-la// ===========================================================*** Faça outro trabalho necessário aqui em alterações que não sejam .in_xml ***// ===========================================================// vamos ver se precisamos recriar nossa representação json.var xmlchksum = crc64(doc.in_xml);// ===========================================================// Não reprocesse se o doc.in_xml não tiver sido alterado, isso pode ser// um grande ganho de desempenho se o documento tiver outros campos que sofrem mutação.// Fazemos isso por meio de uma soma de verificação da propriedade .in_xml.se (doc.xmlchksum && doc.xmlchksum === xmlchksum) retorno;// Ou essa é a primeira passagem ou a propriedade .in_xml foi alterada.var jsonDoc = parseXmlToJson(doc.in_xml);registro(meta.id,"1. INPUT xml doc.in_xml :", doc.in_xml);registro(meta.id,"2. CHECKSUM doc.in_xml :", xmlchksum);registro(meta.id,"3. OUTPUT doc.out_json :", jsonDoc);doc.out_json = jsonDoc;doc.xmlchksum = xmlchksum;// ===========================================================// enriquecer o bucket de origem com .out_json e .xmlchksumsrc_bkt[meta.id] = doc;}// A versão 6.6.0 não tem String.matchAll, precisamos de nossa própria função MatchAllfunção* MatchAll(str, regExp) {se (!regExp.global) {lançar novo TypeError('O sinalizador /g deve estar definido!);}const localCopy = novo RegExp(regExp, regExp.bandeiras);deixar partida;enquanto (partida = localCopy.executar(str)) {rendimento partida;}}// Um analisador simples de XML para JSONfunção parseXmlToJson(xml) {const json = {};para (const res de MatchAll(xml,/(?:]*)*>)((?:(?!<\1).)*)(?:)|/gm)) {const chave = res[1] || res[3];const valor = res[2] && parseXmlToJson(res[2]);json[chave] = ((valor && Objeto.chaves(valor).comprimento) ? valor : res[2]) || nulo;}retorno json;} - Após a colagem, a tela é exibida como mostrado abaixo:
- Clique em Salvar.
- Para retornar à tela Eventing, clique no botão < voltar para Eventing (abaixo do editor) ou clique no link Eventos opção.
- Para a vinculação, o alias do balde, especifica
-
Implementar a função
Agora, estamos prontos para iniciar as funções de Eventing. A partir do Console da Web do Couchbase > Eventos siga estas etapas:
-
-
-
- Clique no nome da função
converterXMLparaJSON
para expandir e expor os controles de função. - Clique em Implementar.
- No Confirmar a função de implantação diálogo, selecione Função de implantação na opção Limite de alimentação.
- A implantação de sua função levará cerca de 18 segundos para ficar ativa.
- Clique no nome da função
-
-
Teste a função de execução
Neste ponto, temos um lambda em execução que atualizará qualquer documento com um chave prefixo de xml:
e uma propriedade XML de in_xml
.
Para exercer a função Eventing converterXMLparaJSON
, vá para o Documentos na interface do usuário e faça o seguinte:
-
-
-
- Clique em ADICIONAR DOCUMENTO.
- Digite um chave de
xml::1
(apenas certifique-se de que o prefixo sejaxml:
) - Clique em Salvar.
- Digite um corpo da seguinte forma:
12345{"tipo": "xml","id": 1,"in_xml": "EmpireBurlesque BobDylanUSAColumbia10.901985"} - Clique em Salvar.
- Edite o documento que acabou de salvar; você perceberá que ele foi atualizado em tempo real
12345678910111213141516{"tipo": "xml","id": 1,"in_xml": "EmpireBurlesque BobDylanUSAColumbia10.901985","out_json": {"CD": {"TITLE" (TÍTULO): "EmpireBurlesque","ARTISTA": "BobDylan","COUNTRY" (PAÍS): "EUA","EMPRESA": "Columbia","PREÇO": "10.90","ANO": "1985"}},"xmlchksum": "02087b7be275d0d8"} - Ajuste o
in_xml
. Talvez acrescentar FEVEREIRO antes do título.
1"in_xml": "FEVEmpireBurlesqueBobDylanUSAColumbia10.901985", - Clique em Salvar.
- Novamente, edite o documento que acabou de salvar. Você perceberá que ele foi atualizado em tempo real mais uma vez:
1234567891011121314151617{"tipo": "xml","id": 1,"in_xml": "FEVEmpireBurlesqueBobDylanUSAColumbia10.901985","out_json": {"CD": {"MÊS": "FEB","TITLE" (TÍTULO): "EmpireBurlesque","ARTISTA": "BobDylan","COUNTRY" (PAÍS): "EUA","EMPRESA": "Columbia","PREÇO": "10.90","ANO": "1985"}},"xmlchksum": "06b5b40b276f160b"} - Adicionar uma nova propriedade acima
tipo
como, por exemplo"outro": "isto é divertido",
- Clique em Salvar.
- Mais uma vez, edite o documento. Observe que o
soma de verificação
não será atualizado
123456789101112131415161718{"outro": "Isso é divertido","tipo": "xml","id": 1,"in_xml": "FEVEmpireBurlesqueBobDylanUSAColumbia10.901985","out_json": {"CD": {"MÊS": "FEB","TITLE" (TÍTULO): "EmpireBurlesque","ARTISTA": "BobDylan","COUNTRY" (PAÍS): "EUA","EMPRESA": "Columbia","PREÇO": "10.90","ANO": "1985"}},"xmlchksum": "06b5b40b276f160b"}
-
-
Vamos processar 100 mil registros XML
Agora que você já viu como as coisas funcionam, vamos despejar uma grande quantidade de dados na função. Primeiro, precisamos gerar alguns dados.
-
-
-
- Usarei um script Perl simples chamado
xml_data_gen.pl
que escrevi para esta postagem do blog:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556#!/usr/bin/perluso Getopt::Longo qw(GetOptions);submarino randi {($lower_limit,$upper_limit) = @_;retorno int(rand($upper_limit-$lower_limit)) + $lower_limit;}meu $blk = 0;meu $num = 0;meu $help = 0;GetOptions('blk=i' => \$blk,'num=i' => \$num,'ajuda' => \$help,) ou morrer "Uso: $0 --blk # --num #\n";se ($num == 0 || $help != 0 || $blk < 1) {printf stderr "Uso: $0 --blk # --num #\n";printf stderr "examples:\n";printf stderr "./xml_data_gen.pl --blk 1 --num 100000 > data.json\n";printf stderr "cbimport json -c couchbase://127.0.0.1 -u \$CB_USERNAME " ."-p \$CB_PASSWORD -b source -d file://./data.json -f lines -t 4 -g xml::%%id%%\n";saída(1);}@artista = ("Elton John", "Lisa Lobb", "Sting", "Clash", "The Smiths","Bob Dylan", "The Yard Birds" (Os pássaros do quintal), "Jornada", "Led Zeppelin", "Adele");@título = ("Empire Burlesque", "Greatest Hits" (Melhores sucessos), "Songs Vol 1"., "Songs Vol 2".,"Songs Vol 3"., "Clássicos", "Hidden Tracks" (Trilhas ocultas));@país = ("EUA", "REINO UNIDO", "AU", "AR", "ES", "MO");@empresa = ("Columbia", "Capital", "Dog Boys" (Meninos Cães), "Épico", "Cerveja caseira", "EMG");$template = '{"type": "xml", "id":_XXX_, "in_xml":"_TTT_ ' .'_AAA__CCC__OOO_' .'_PPP__YYY_"}';meu $beg = 1 + ($blk-1) * $num;meu $max = $beg + $num - 1;meu $wrk = "";para ($j=$beg; $j<=$max; $j=$j+1) {$wrk = $template; $wrk =~ s/_XXX_/$j/;$tmp = $artista[randi(0,$#artist-1)]; $wrk =~ s/_AAA_/$tmp/;$tmp = $ítulo[randi(0,$#title-1)]; $wrk =~ s/_TTT_/$tmp/;$tmp = $país[randi(0,$#country-1)]; $wrk =~ s/_CCC_/$tmp/;$tmp = $companhia[randi(0,$#company-1)]; $wrk =~ s/_OOO_/$tmp/;$tmp = randi(850,1765)/100; $wrk =~ s/_PPP_/$tmp/;$tmp = randi(1,30)+1981; $wrk =~ s/_YYY_/$tmp/;printf $wrk . "\n";} - Para testar o script gerador acima
xml_data_gen.pl
apenas corra:./xml_data_gen.pl --blk 1 --num 5
- Você deverá ver algo como:
12345{"tipo":"xml","id":1,"in_xml":"Songs Vol 3. Elton JohnUSAEpic10.642003"}{"tipo":"xml","id":2,"in_xml":"Songs Vol 1. ClashAUEpic12.691989"}{"tipo":"xml","id":3,"in_xml":"Empire Burlesque JourneyAUEpic15.251999"}{"tipo":"xml","id":4,"in_xml":"Songs Vol 1. Bob DylanARDog Boys15.431997"}{"tipo":"xml","id":5,"in_xml":"Greatest Hits JourneyUKColumbia10.711983"} - Para criar um registro de 100K
xml_data_gen.pl
para carregar posteriormente em seu cluster do Couchbase, basta executar:./xml_data_gen.pl --blk 1 --num 100000 > data.json
- Agora precisamos carregar os dados. Usarei o
cbimport
utilitário localizado no./bin
de sua instalação do Couchbase em um de seus nós. Presumo que você tenha acesso ao shell do cluster do Couchbase e as credenciais necessárias.
1cbimport json -c couchbase://127.0.0.1 -u $CB_USERNAME -p $CB_PASSWORD -b fonte -d arquivo://./dados.json -f linhas -t 4 -g xml::%id% - Dê uma olhada. Todos os 100 mil itens terão sido convertidos instantaneamente à medida que forem carregados (na mutação causada pela inserção).
- Usarei um script Perl simples chamado
-
-
Se quiser obter mais desempenho, você pode aumentar a escala verticalmente aumentando o número de workers nas configurações de funções ou adicionando mais nós de Eventing. Se você quiser carregar mais dados e manter a residência de memória 100%, talvez seja necessário aumentar o tamanho do seu bucket.
Considerações finais
Espero que você tenha achado este passo a passo educativo e que tenha desenvolvido mais apreço por o serviço de eventos do Couchbase como um todo.
Anteriormente, otimizamos a função para usar menos recursos de computação. Também poderíamos otimizar o espaço de armazenamento. Observe que temos um in_xml
e um out_json
em nossos documentos depois de enriquecê-los. Com apenas um pequeno ajuste (adicionando duas linhas), poderíamos remover o in_xml
e eliminar o armazenamento redundante (economiza 30% de meu espaço em disco para mim). Optei por deixar isso de fora do exemplo para destacar melhor como a função foi executada, mas convido você a experimentar por conta própria usando o seguinte:
1 2 3 4 5 6 7 8 9 10 |
função Sobre a atualização(doc, meta) { // filtragem de não XML se (!meta.id.startsWith("xml:")) retorno; se (!doc.in_xml) retorno; // otimizar o espaço de armazenamento // TODAS AS OUTRAS LINHAS SÃO IGUAIS excluir doc.in_xml; // otimizar o espaço de armazenamento src_bkt[meta.id] = doc; } |
Coloquei esse exemplo no site de documentação principal do Couchbase como o Scriptlet de exemplo: Função: converterXMLparaJSON
. Ele não traduzirá atributos XML, mas isso foi fácil de adicionar (veja o próximo parágrafo). No entanto, o método simples parseXmlToJson()
que utilizei se ajustou perfeitamente aos dados iniciais.
Durante o desenvolvimento, comecei de forma simples e testei alguns documentos XML enormes, mas é claro que talvez eu não tenha coberto toda a gama de construções XML. Desenvolvi o segundo Scriptlet converterAdvXMLparaJSON
(não mostrado neste artigo), pois logo descobri que Muitos documentos XML utilizam atributos e têm algumas variantes de sintaxe interessantes.
1 2 3 4 5 6 7 8 |
<DOC_WITH_ATTRS> <ADVELEM1 adv_attrA="adv_valA" /> <ADVELEM2 adv_attrA="adv_valA" adv_attrB="adv_valB"> SUBDATA </ADVELEM2> </DOC_WITH_ATTRS> |
Isso me levou a criar uma função Eventing mais extensa que traduz os atributos XML e as variantes de sintaxe que encontrei. Você também encontrará o código-fonte dessa função Eventing mais longa e mais capaz no site de documentação principal do Couchbase como o Scriptlet de exemplo: Função: converterAdvXMLparaJSON
. Essa versão mais avançada converteu com sucesso grandes documentos XML, incluindo "arquivos de geometria do OpenStreetmap" e também "medições operacionais de celulares da Huawei".
Eu o desafio a tentar fazer o seguinte:
-
-
-
- Escreva uma função inversa: Em vez de
converterXMLparaJSON
, digaconverterJSONparaXML
- Alterar
converterAdvXMLparaJSON
para prefixar qualquer nome de propriedade, comoattr_
para ajudá-lo a identificar atributos XML na conversão final do JSON. - Adicionar um mapa JavaScript opcional e uma rotina para converter genericamente propriedades por nome em números reais e booleanos (em vez do tipo de string padrão)
- Implemente um aprimoramento para aplicar uma "Definição de tipo de documento" ou DTD aos dados de entrada, onde você lê a DTD de outro documento.
- Crie um tradutor para consultas XPath para abordar (apontar para) diferentes partes de uma representação JSON do documento convertido.
- Escreva uma função inversa: Em vez de
-
-
Não se esqueça de experimentar o SQL++ (N1QL) no Query Workbench e usar o Index Advisor para consultar e criar índices ideais para acessar seus dados XML (transformados em JSON).
Recursos
-
-
-
- Download: Baixar o Couchbase Server 6.6.2
- Scriptlet de eventos: Função:
converterXMLparaJSON
. - Scriptlet de eventos: Função:
converterAdvXMLparaJSON
.
-
-
Referências
Espero tê-lo convencido de que o Couchbase via Eventing é um banco de dados XML.
Gostaria muito de saber o que você achou dos recursos do Couchbase e do Eventing e como eles beneficiarão sua empresa no futuro. Compartilhe seu feedback nos comentários abaixo ou em os fóruns do Couchbase.