Módulo PHP para trabalhar com dados hierárquicos no InterSystems IRIS

imagem Desde o início de seu tempo, o PHP é famoso (e criticado) por oferecer suporte à integração com uma série de bibliotecas, bem como com praticamente todos os DBMSs do mercado. No entanto, devido a algumas razões estranhas, ele não suportava bancos de dados hierárquicos em globais.

Globais são estruturas para armazenar informações hierárquicas. Eles lembram um pouco o banco de dados "chave -> valor", com a única diferença de que a chave pode ter vários níveis:

Set ^inn("1234567890", "city") = "Moscow" Set ^inn("1234567890", "city", "street") = "Req Square" Set ^inn("1234567890", "city", "street", "house") = 1 Set ^inn("1234567890", "year") = 1970 Set ^inn("1234567890", "name", "first") = "Vladimir" Set ^inn("1234567890", "name", "last") = "Ivanov" 

Neste exemplo, a linguagem ObjectScript incorporada no global ^ inn , que é armazenada no disco rígido (indicado pelo ícone ^ na frente do nome global), armazena informações em vários níveis.

Naturalmente, para trabalhar com globais do PHP, precisaremos de novas funções que serão adicionadas pelo módulo PHP, que serão discutidas abaixo.

Globals suportam muitas funções para trabalhar com hierarquia: ignorando chaves em cada nível separadamente, excluindo, copiando e colando árvores inteiras e nós individuais. Bem, assim como qualquer bom banco de dados de transações ACID. Tudo isso acontece com extrema rapidez (cerca de 10 5 a 10 6 operações de inserção por segundo no hardware comum), por dois motivos:

  1. Globals são uma abstração de nível inferior ao SQL,
  2. As bases globais estão em produção há décadas e, durante esse período, conseguiram lamber seu código e otimizá-lo completamente.

Mais sobre globais na série de artigos “Globals - espadas-pedreiros para armazenamento de dados”:

As árvores Parte 1
As árvores Parte 2
Matrizes esparsas. Parte 3

Neste mundo, os globais encontraram seu principal local de aplicação em sistemas de armazenamento de informações pouco estruturadas e esparsas, como: médicos, dados pessoais, bancos, etc.

Eu amo PHP (e desenvolvo isso) e queria brincar com os globais. Não havia módulo finalizado. Escrevi para a InterSystems pedindo para criá-lo. A espera não levou a nada e, no final, nós (juntamente com meu aluno de graduação) fizemos o módulo. A InterSystems patrocinou o desenvolvimento como parte de uma concessão educacional.

De maneira geral, o InterSystems IRIS é um DBMS de vários modelos, portanto, do PHP, você pode trabalhar com ele através do ODBC usando SQL, mas eu estava interessado em globais, e não havia esse conector.

Portanto, o módulo está disponível para o PHP 7.x (testado sob 7.0-7.2). Atualmente, ele só pode funcionar com o InterSystems IRIS e Caché instalado no mesmo host.

Página do módulo no OpenExchange (um catálogo de projetos e complementos para desenvolvedores no InterSystems IRIS e Caché).

Há uma seção de discussão útil na qual as pessoas compartilham suas experiências de uso.

Faça o download aqui:
https://github.com/intersystems-community/php_ext_iris
Faça o download do repositório na linha de comando:

 git clone https://github.com/intersystems-community/php_ext_iris 

Instruções para instalar o módulo em inglês e russo.

Funções do módulo:
Função phpDescrição do produto
Trabalhar com dados
iris_set ($ nó, valor)
Local de instalação.
  1. iris_set ($ global, $ subscript1, ..., $ subscriptN, $ value);
    iris_set ($ global, $ valor);

    Retorna: verdadeiro ou falso (em erro).
    Todos os parâmetros de função são cadeias ou números. O primeiro é o nome do global, depois os índices, o último parâmetro é o valor.

     iris_set('^time',1); iris_set('^time', 'tree', 1, 1, 'value'); 

    Analógico no ObjectScript

     Set ^time = 1 Set ^time("tree", 1, 1) = "value" 
  2. iris_set ($ arrayGlobal, $ value);
    Apenas 2 parâmetros: o primeiro é uma matriz na qual o nome do global e todos os seus índices são armazenados; o segundo é significado.

     $node = ['^time', 'tree', 1, 1]; iris_set($node,'value'); 

iris_get ($ node)
Leia o nó.
Retorna: valor (número ou sequência), NULL (valor não definido) ou FALSE (com erro).

  1. iris_get ($ global, $ subscript1, ..., $ subscriptN);
    iris_get ($ global);
    Todos os parâmetros de função são cadeias ou números. O primeiro é o nome do global, depois os índices. Um global pode não ter índices.

     $res = iris_get('^time'); $res1 = iris_get('^time', 'tree', 1, 1); 
  2. iris_get ($ arrayGlobal);
    O único parâmetro é a matriz na qual o nome do global e todos os seus índices são armazenados.

     $node = ['^time', 'tree', 1, 1]; $res = iris_get($node); 

iris_zkill ($ nó)
Excluindo um valor do nó.
Retorna: VERDADEIRO ou FALSO - com erro.

É importante observar que essa função exclui apenas o valor no nó e não toca nas ramificações subjacentes.

  1. iris_zkill ($ global, $ subscript1, ..., $ subscriptN);
    iris_zkill ($ global);
    Todos os parâmetros de função são cadeias ou números. O primeiro é o nome do global, depois os índices. Um global pode não ter índices.

     $res = iris_zkill('^time'); //    . $res1 = iris_zkill('^time', 'tree', 1, 1); 
  2. iris_zkill ($ arrayGlobal);
    O único parâmetro é a matriz na qual o nome do global e todos os seus índices são armazenados.

     $a = ['^time', 'tree', 1, 1]; $res = iris_zkill($a); 

iris_kill ($ node)
Exclua um nó e todas as ramificações descendentes.
Retorna: VERDADEIRO ou FALSO - com erro.

  1. iris_kill ($ global, $ subscript1, ..., $ subscriptN);
    iris_kill ($ global);
    Todos os parâmetros de função são cadeias ou números. O primeiro é o nome do global, depois os índices. Um global pode não ter índices; nesse caso, ele é excluído completamente.

     $res1 = iris_kill('^example', 'subscript1', 'subscript2'); $res = iris_kill('^time'); //   . 
  2. iris_kill ($ arrayGlobal);
    O único parâmetro é a matriz na qual o nome do global e todos os seus índices são armazenados.

     $a = ['^time', 'tree', 1, 1]; $res = iris_kill($a); 

iris_order ($ node)
Ignorar filiais globais em um determinado nível
Retorna: uma matriz que contém o nome completo do próximo nó global ou FALSE (em erro).

  1. iris_order ($ global, $ subscript1, ..., $ subscriptN);
    Todos os parâmetros de função são cadeias ou números. O primeiro é o nome do global, depois os índices.

    Formulário de uso do PHP e equivalente ao ObjectScript:

     iris_order('^ccc','new2','res2'); // $Order(^ccc("new2", "res2")) 
  2. iris_order ($ arrayGlobal);
    O único parâmetro é a matriz na qual o nome global e os índices do nó inicial são armazenados.

     $node = ['^inn', '1234567890', 'city']; for (; $node !== NULL; $node = iris_order($node)) { echo join(', ', $node).'='.iris_get($node)."\n"; } 

    Nos dará uma conclusão:

     ^inn, 1234567890, city=Moscow ^inn, 1234567890, year=1970 

iris_order_rev ($ node)
Ignorar filiais globais em um determinado nível na ordem inversa
Retorna: uma matriz que contém o nome completo do nó global anterior no mesmo nível ou FALSE (se ocorrer um erro).

  1. iris_order_rev ($ global, $ subscript1, ..., $ subscriptN);

    Todos os parâmetros de função são cadeias ou números. O primeiro é o nome do global, depois os índices.

    Formulário de uso do PHP e equivalente ao ObjectScript:

     iris_order_rev('^ccc','new2','res2'); // $Order(^ccc("new2", "res2"), -1) 
  2. iris_order_rev ($ arrayGlobal);

    O único parâmetro é a matriz na qual o nome global e os índices do nó inicial são armazenados.

     $node = ['^inn', '1234567890', 'name', 'last']; for (; $node !== NULL; $node = iris_order_rev($node)) { echo join(', ', $node).'='.iris_get($node)."\n"; } 

    Nos dará uma conclusão:

     ^inn, 1234567890, name, last=Ivanov ^inn, 1234567890, name, first=Vladimir 

iris_query ($ CmdLine)
Ignorando filiais globais com entrada para níveis mais baixos
Retorna: uma matriz que contém o nome completo do nó subjacente (se houver) ou o próximo nó global (se não houver nó aninhado).

  1. iris_query ($ global, $ subscript1, ..., $ subscriptN);
    Todos os parâmetros de função são cadeias ou números. O primeiro é o nome do global, depois os índices.

    Formulário de uso do PHP e equivalente ao ObjectScript:

     iris_query('^ccc', 'new2', 'res2'); // $Query(^ccc("new2", "res2")) 
  2. iris_query ($ arrayGlobal);
    O único parâmetro é a matriz na qual o nome global e os índices do nó inicial são armazenados.

     $node = ['^inn', 'city']; for (; $node !== NULL; $node = iris_query($node)) { echo join(', ', $node).'='.iris_get($node)."\n"; } 

    Nos dará uma conclusão:

     ^inn, 1234567890, city=Moscow ^inn, 1234567890, city, street=Req Square ^inn, 1234567890, city, street, house=1 ^inn, 1234567890, name, first=Vladimir ^inn, 1234567890, name, last=Ivanov ^inn, 1234567890, year=1970 

A ordem difere da ordem em que definimos, pois no global automaticamente quando você insere tudo é classificado em ordem crescente.
Funções de Serviço
iris_set_dir ($ FullPath)
Definindo um diretório com um banco de dados
Retorna: VERDADEIRO ou FALSO - com erro.

 iris_set_dir('/InterSystems/Cache/mgr'); 

Ele deve ser concluído antes de conectar-se ao banco de dados.
iris_exec ($ CmdLine)
Executar comando DB
Retorna: VERDADEIRO ou FALSO - com erro.

 iris_exec('kill ^global(6)'); //   ObjectScript    

iris_connect ($ login, $ pass)Conectar ao DB
iris_quit ()Interromper a conexão com o banco de dados
iris_errno ()Obter código de erro
iris_error ()Obter uma descrição em texto do erro

Se você quiser jogar com o módulo, então:

Especialmente para usuários do Habr, um Dockerfile foi criado para criar a imagem.
 git clone https://github.com/intersystems-community/php_ext_iris cd php_ext_iris/iris docker-compose build docker-compose up -d 

Testando a página de demonstração no localhost: 52080 no navegador.

Os arquivos PHP que podem ser editados e reproduzidos com eles estão na pasta php / demo e serão montados dentro do contêiner.

Para testar o IRIS, use o nome de usuário admin , a senha é SYS .

Para inserir as configurações do IRIS, use o seguinte URL:
http: // localhost: 52773 / csp / sys / UtilHome.csp

Para entrar no console IRIS deste contêiner, use o comando:
 docker exec -it iris_iris_1 iris session IRIS 


Especialmente para usuários de Habr e InterSystems Caché, a virtualka com módulo php foi levantada.

Para auto-instalação do módulo no InterSystems Caché
  1. Tenha Linux. Eu testei no Ubuntu, no Windows o módulo também deve ser construído, mas não o testei.
  2. Baixe a versão gratuita:
  3. Instale o módulo cach.so no PHP de acordo com as instruções .

No contêiner do docker do meu computador pessoal (AMD FX-9370 @ 4700Mhz 32GB, LVM, SATA SSD), por diversão, fiz dois testes primitivos sobre a velocidade de inserção de novos valores no banco de dados.

  • A inserção de 1 milhão de novos elementos no global levou 1,81 segundo ou 552 mil inserções por segundo.
  • A atualização de um valor no mesmo 1.000.000 vezes global levou 1,98 segundos ou 505K atualizações por segundo. Um fato interessante é que ocorre a inserção do que a atualização. Aparentemente, isso é uma consequência da otimização inicial do banco de dados para inserção rápida.

É claro que esses testes não podem ser precisos e úteis, pois são primitivos, feitos em um contêiner. Em um hardware mais poderoso com um sistema de disco SSD PCIe, é possível obter dezenas de milhões de inserções por segundo.

O que pode ser concluído e o estado atual


  1. Você pode adicionar funções úteis para trabalhar com transações (você ainda pode usá-las através do iris_exec).
  2. A função de retornar toda a estrutura do global não é implementada para que o PHP não ignore o global.
  3. A função de salvar a matriz PHP como uma subárvore não está implementada.
  4. O acesso às variáveis ​​locais do banco de dados não está implementado. Somente através do iris_exec, embora seja melhor através do iris_set.
  5. Não implementado ignorando o global em profundidade na direção oposta.
  6. O acesso ao banco de dados através do objeto usando métodos (semelhantes às funções atuais) não é implementado.

O módulo atual provavelmente ainda não está pronto para produção: não houve testes para altas cargas e vazamentos de memória. No entanto, se alguém precisar, entre em contato comigo (Sergey Kamenev updates@mail.ru).

Conclusão


Por um longo tempo, os mundos do PHP e as bases hierárquicas em globais praticamente não se cruzaram, embora os globais proporcionem funcionalidade forte e rápida em tipos de dados específicos (médicos, pessoais).

Espero que este módulo sirva de incentivo para experimentos de programadores PHP com globais e programadores ObjectScript para o desenvolvimento simples de interfaces da Web em PHP.

PS Obrigado por sua atenção!

Source: https://habr.com/ru/post/pt443440/


All Articles