Skip to content

Feature param query builder

Vytor Calixto requested to merge feature_param_query_builder into development

Este middleware tem como propósito substituir o parseParams.js e adicionar novas funcionalidades:

  • Parse dos parâmetros feitos através do no novo middleware (reqQueryFields.js), assim como no parseParams.js
  • Construção automágica do SQL necessário a partir dos parâmetros passados na URL, definida de forma simplificada através de objetos

Veja a versão antiga do enrollment.js, especialmente o trecho das linhas 49 a 195:

// Parse the filters and dimensions parameter in the query
enrollmentApp.use('/', parseParams('filter', [
    'min_year',
    'max_year',
    'adm_dependency',
    'location',
    'education_level',
    'region',
    'state',
    'city',
    'school'
]), parseParams('dims', [
    'adm_dependency',
    'location',
    'education_level',
    'region',
    'state',
    'city',
    'school'
]), (req, res, next) => {
    log.debug(req.filter);
    log.debug(req.dims);

    // Do the joins
    if(typeof req.filter.adm_dependency !== 'undefined'
        || typeof req.dims.adm_dependency !== 'undefined') {
        req.sql.join('dependencia_adm', null, 'fk_dependencia_adm_id=dependencia_adm.pk_dependencia_adm_id');
    }

    if(typeof req.filter.education_level !== 'undefined'
        || typeof req.dims.education_level !== 'undefined') {
        req.sql.join('etapa_ensino', null, 'fk_etapa_ensino_id=etapa_ensino.pk_etapa_ensino_id');
    }

    if(typeof req.filter.region !== 'undefined'
        || typeof req.dims.region !== 'undefined') {
            req.sql.join('regiao', null, 'fk_regiao_id=regiao.pk_regiao_id');
    }

    if(typeof req.filter.state !== 'undefined'
        || typeof req.dims.state !== 'undefined') {
            req.sql.join('estado', null, 'fk_estado_id=estado.pk_estado_id');
    }

    if(typeof req.filter.city !== 'undefined'
        || typeof req.dims.city !== 'undefined') {
        req.sql.join('municipio', null, 'fk_municipio_id=municipio.pk_cod_ibge');
    }

    if(typeof req.dims.school !== 'undefined') {
        req.sql.join('escola', null, 'turma.cod_entidade=escola.cod_entidade');
    }

    if(typeof req.dims.location !== 'undefined') {
        req.sql.join('localizacao', null, 'turma.id_localizacao=localizacao.pk_localizacao_id')
    }

    // Dimensions (add fields)

    if(typeof req.dims.education_level !== 'undefined') {
        req.sql.field('desc_etapa', 'education_level')
            .group('desc_etapa')
            .order('desc_etapa');
    }

    if(typeof req.dims.region !== 'undefined') {
        req.sql.field('regiao.nome', 'region_name')
            .group('regiao.nome')
            .order('regiao.nome');
    }

    if(typeof req.dims.state !== 'undefined') {
        req.sql.field('estado.nome', 'state_name')
            .group('estado.nome')
            .order('estado.nome');
    }

    if(typeof req.dims.city !== 'undefined') {
        req.sql.field('municipio.nome', 'city_name')
            .group('municipio.nome')
            .order('municipio.nome');
    }

    if(typeof req.dims.school !== 'undefined') {
        req.sql.field('escola.cod_entidade', 'school_name')
            .group('escola.cod_entidade')
            .order('escola.cod_entidade');
    }

    if(typeof req.dims.adm_dependency !== 'undefined') {
        req.sql.field('dependencia_adm.nome', 'adm_dependency_name')
            .group('dependencia_adm.nome')
            .order('dependencia_adm.nome');
    }

    if(typeof req.dims.location !== 'undefined') {
        req.sql.field('localizacao.descricao', 'location_name')
            .group('localizacao.descricao')
            .order('localizacao.descricao');
    }

    if(typeof req.dims.region === 'undefined'
        && typeof req.dims.state === 'undefined'
        && typeof req.dims.city === 'undefined'
        && typeof req.dims.school === 'undefined') {
        req.sql.field("'Brasil'", 'name');
    }

    // Filter (add where)

    if (typeof req.filter.min_year !== 'undefined') {
        req.sql.where('turma.ano_censo>=?', parseInt(req.filter.min_year, 10));
    }

    if (typeof req.filter.max_year !== 'undefined') {
        req.sql.where('turma.ano_censo<=?', parseInt(req.filter.max_year, 10));
    }

    if (typeof req.filter.adm_dependency !== 'undefined') {
        req.sql.where('pk_dependencia_adm_id=?', parseInt(req.filter.adm_dependency, 10));
    }

    if (typeof req.filter.location !== 'undefined') {
        req.sql.where('turma.id_localizacao=?', parseInt(req.filter.location, 10));
    }

    if (typeof req.filter.education_level !== 'undefined') {
        req.sql.where('pk_etapa_ensino_id=?', parseInt(req.filter.education_level, 10));
    }

    if (typeof req.filter.region !== 'undefined') {
        req.sql.where('pk_regiao_id=?', parseInt(req.filter.region, 10));
    }

    if (typeof req.filter.state !== 'undefined') {
        req.sql.where('pk_estado_id=?', parseInt(req.filter.state, 10));
    }

    if (typeof req.filter.city !== 'undefined') {
        req.sql.where('turma.fk_municipio_id=?', parseInt(req.filter.city, 10));
    }

    if (typeof req.filter.school !== 'undefined') {
        req.sql.where('turma.fk_escola_id=?', parseInt(req.filter.school, 10));
    }
    log.debug(req.sql.toParam());
    next();
});

O trecho, extremamente verboso, consiste basicamente da definição do SQL para cada parâmetro específico definido em ambos filter e dims. Não há como reaproveitar código, além de ter muita repetição.

O novo middleware busca solucionar isso da seguinte forma:

let rqf = new ReqQueryFields();
rqf.addField({
    name: 'filter',
    field: false,
    where: true
}).addField({
    name: 'dims',
    field: true,
    where: false
}).addValue({
    name: 'adm_dependency',
    table: 'dependencia_adm',
    tableField: 'nome',
    resultField: 'adm_dependency_name',
    where: {
        relation: '=',
        type: 'integer',
        field: 'pk_dependencia_adm_id'
    },
    join: {
        primary: 'pk_dependencia_adm_id',
        foreign: 'fk_dependencia_adm_id',
        foreignTable: 'turma'
    }
}).addValue({
    name: 'education_level',
    table: 'etapa_ensino',
    tableField: 'desc_etapa',
    resultField: 'education_level',
    where: {
        relation: '=',
        type: 'integer',
        field: 'pk_etapa_ensino_id'
    },
    join: {
        primary: 'pk_etapa_ensino_id',
        foreign: 'fk_etapa_ensino_id',
        foreignTable: 'turma'
    }
}).addValue({
    name: 'region',
    table: 'regiao',
    tableField: 'nome',
    resultField: 'region_name',
    where: {
        relation: '=',
        type: 'integer',
        field: 'pk_regiao_id'
    },
    join: {
        primary: 'pk_regiao_id',
        foreign: 'fk_regiao_id',
        foreignTable: 'turma'
    }
}).addValue({
    name: 'state',
    table: 'estado',
    tableField: 'nome',
    resultField: 'state_name',
    where: {
        relation: '=',
        type: 'integer',
        field: 'pk_estado_id'
    },
    join: {
        primary: 'pk_estado_id',
        foreign: 'fk_estado_id',
        foreignTable: 'turma'
    }
}).addValue({
    name: 'city',
    table: 'municipio',
    tableField: 'nome',
    resultField: 'city_name',
    where: {
        relation: '=',
        type: 'integer',
        field: 'pk_cod_ibge'
    },
    join: {
        primary: 'pk_cod_ibge',
        foreign: 'fk_municipio_id',
        foreignTable: 'turma'
    }
}).addValue({
    name: 'school',
    table: 'escola',
    tableField: 'cod_entidade',
    resultField: 'school_name',
    where: {
        relation: '=',
        type: 'integer',
        field: 'cod_entidade'
    },
    join: {
        primary: 'cod_entidade',
        foreign: 'cod_entidade',
        foreignTable: 'turma'
    }
}).addValue({
    name: 'location',
    table: 'localizacao',
    tableField: 'descricao',
    resultField: 'location_name',
    where: {
        relation: '=',
        type: 'integer',
        field: 'pk_localizacao_id'
    },
    join: {
        primary: 'pk_localizacao_id',
        foreign: 'fk_localizacao_id',
        foreignTable: 'turma'
    }
}).addValue({
    name: 'city',
    table: 'municipio',
    tableField: 'nome',
    resultField: 'city_name',
    where: {
        relation: '=',
        type: 'integer',
        field: 'pk_cod_ibge'
    },
    join: {
        primary: 'pk_cod_ibge',
        foreign: 'fk_municipio_id',
        foreignTable: 'turma'
    }
}).addValue({
    name: 'min_year',
    table: 'turma',
    tableField: 'ano_censo',
    resultField: 'year',
    where: {
        relation: '>=',
        type: 'integer',
        field: 'ano_censo'
    }
}).addValue({
    name: 'max_year',
    table: 'turma',
    tableField: 'ano_censo',
    resultField: 'year',
    where: {
        relation: '<=',
        type: 'integer',
        field: 'ano_censo'
    }
});

Embora o número de linha não tenha diminuído, o código se torna mais simples, fazendo tanto o papel do parse dos parâmetros quanto a construção do SQL. Ao definir os valores aceitos através de objetos, o código pode ser reaproveitado ao longo da API e por estar num middleware aplicado de forma global ou a um grupo de rotas.

Merge request reports

Loading