Um dos grandes problemas de trabalhar com bancos de dados "schemaless" é que o esquema de dados não é explícito (por definição!), mas está implícito nas aplicações e na massa de dados existente.

O script schematize.py gera um esquema de dados ISIS-DM a partir da análise de uma massa de dados em formato ISIS-JSON (um formato JSON genérico para representar qualquer base ISIS).

O schematize.py pode ser usado assim:

$ schematize.py -h
usage: schematize.py [-h] [-f TAG:TYPE] [-q QUORUM] [-t] [-v] [INPUT.json]

Generate ISIS-DM schema from a sample database in ISIS-JSON format

positional arguments:
  INPUT.json            .json file to read

optional arguments:
  -h, --help            show this help message and exit
  -f TAG:TYPE, --filter TAG:TYPE
                        use TAG to filter only records of TYPE
  -q QUORUM, --quorum QUORUM
                        min. frequency required to generate a property from a
                        tag (useful to ignore uncommon tags)
  -t, --test            run tests (ignore input file)
  -v, --verbose-tests   run verbose tests (ignore input file)

Com os dados da base CDS (aquela do WinISIS), o schematize.py gera uma saída assim:

class SampleType(CheckedModel):
   v12 = SingularProperty(tag='12', subfields=u'dnp')
   v24 = SingularProperty(tag='24', required=True)
   v25 = PluralProperty(tag='25')
   v26 = SingularProperty(tag='26', subfields=u'abc')
   v30 = SingularProperty(tag='30', subfields=u'abc')
   v44 = SingularProperty(tag='44', subfields=u'v')
   v50 = SingularProperty(tag='50')
   v69 = SingularProperty(tag='69')
   v70 = PluralProperty(tag='70')
   v71 = PluralProperty(tag='71')
   v72 = SingularProperty(tag='72', subfields=u'dp')
   v74 = PluralProperty(tag='74')
   v76 = PluralProperty(tag='76', subfields=u'z')

# 150 records analyzed out of 150 total records
# 13 numeric tags found

Em linhas gerais, o processo é o seguinte:

  • varrer a base inteira, gerando uma Property para cada tag existente;
  • para cada tag:
    • se nunca existe mais de uma ocorrência, gerar uma SingularProperty, do contrário, PluralProperty
    • se o tag aparece em todos os registros, gerar um atributo required=True
    • se ocorrem subcampos, gerar um atributo subfields com os subcampos encontrados
    • se todas as ocorrências de um campo são do mesmo tamanho N, gerar um atributo fixed_len=N
    • se todas as ocorrências de um campo são numéricas, gerar um atributo numeric=True

Além disso, é possível filtrar os registros de entrada por tipo, para gerar um esquema específico para cada tipo de registro.

Por exemplo, na base xLILACS existem registros dos tipos C, R, S e E. Veja o resultado de gerar um esquema apenas para os registros do tipo R:

$ ./schematize.py ../fixtures/xlilacs/xlilacs.json -f5:R
class TypeR(CheckedModel):
   v01 = SingularProperty(tag='1', required=True,
                   subfields=u'eip')
   v02 = SingularProperty(tag='2', required=True)
   v03 = SingularProperty(tag='3', required=True,
                   subfields=u'af')
   v04 = PluralProperty(tag='4', subfields=u'eipt',
                   fixed_len=31)

# 8 records analyzed out of 117 total records
# 5 numeric tags found (filter tag omitted from model)

Neste caso, o tag 5, que foi usado para filtrar, é omitido da saída, porque seria redundante, já que por definição todos os registros do tipo R teriam o valor "R" no tag 5.

Para entender mais sobre o funcionamento do schematizer, veja os doctests.