Intelligent GridDocumentação em português

Filtro avançado (construtor de consultas)

O botão Advanced filter na barra de navegação (navbar) abre um construtor de consultas visual: os usuários combinam regras em grupos AND / OR aninhados, negam grupos com NOT e acompanham a consulta find() do SorlaDB sendo montada. Ao aplicá-lo, a grade é filtrada; ele se combina (AND) com os filtros de coluna e com a busca.

Age ≥ 30  AND  ( Name = X  OR  City is one of A, B )
{ "$and": [
    { "age": { "$gte": 30, "$ne": "" } },
    { "$or": [ { "name": { "$eq": "X" } }, { "city": { "$in": ["A", "B"] } } ] }
] }

A consulta roda no SorlaDB: toda parte da árvore que o SorlaDB consegue expressar é executada por um único collection.find(). É preciso o sorla-db 1.5+ ($and / $or / $nor aninhados, vários operadores por campo, $regex).

No painel

Tipo de coluna Operadores
text, select equals, does not equal, is one of, is not one of, contains, does not contain, begins with, ends with, is blank, is not blank
number, integer, currency, percent, date, datetime =, ≠, >, ≥, <, ≤, between, is one of, is not one of, is blank, is not blank
boolean equals, is blank, is not blank

Uma consulta aplicada aparece como um chip legível na barra de navegação (Age ≥ 30 AND (Name = X OR City in A, B)), e o × dele a remove. "Clear filters" também a remove.

A igualdade é exata

A igualdade do construtor é o $eq do SorlaDB: Name = X não encontra x. Para comparar sem diferenciar maiúsculas e minúsculas, use contains / begins with / ends with, ou o menu de filtro da coluna, cujo "equals" ignora maiúsculas e minúsculas.

Comparação de texto

contains, does not contain, begins with, ends with viram condições $regex sem diferenciar maiúsculas e minúsculas ({ "name": { "$regex": "^x", "$options": "i" } }) e rodam no SorlaDB como todo o resto. Um backend MongoDB entende o mesmo JSON. Caracteres especiais são escapados, então a.b procura um ponto literal. Em colunas select, o texto é comparado com os rótulos que os usuários veem, e a consulta usa os valores de opção correspondentes ({ "status": { "$in": ["open"] } }).

A única regra que o SorlaDB não consegue expressar é "≠ um dia" em uma coluna datetime. A grade a avalia, e um grupo que a mistura com outras regras ainda envia todas as outras partes para o find().

API

grid.setQuery({
    type: 'group', join: 'and', children: [
        { type: 'rule', field: 'age', op: 'gte', value: 30 },
        { type: 'group', join: 'or', children: [
            { type: 'rule', field: 'name', op: 'eq', value: 'X' },
            { type: 'rule', field: 'city', op: 'in', values: ['A', 'B'] }
        ] }
    ]
});

grid.getQuery();                      // o modelo acima (null quando não há nenhum)
grid.toSorlaQuery();                  // o JSON do find()
grid.fromSorlaQuery(savedJson);       // carrega uma consulta salva antes
grid.setQuery(null);                  // remove a consulta
grid.toggleQueryBuilder(true);        // abre o painel a partir do seu próprio botão

Salvando e restaurando a consulta de um usuário:

grid.on('queryChange', e => localStorage.setItem('orders-query', JSON.stringify(e.detail.sorlaQuery)));
const saved = localStorage.getItem('orders-query');
if (saved) grid.fromSorlaQuery(JSON.parse(saved));

initialQuery aplica uma consulta ao carregar. navbar: { query: false } esconde o botão (a API continua funcionando).

Modelo

type QueryNode = QueryRule | QueryGroup;

interface QueryGroup {
    type: 'group';
    join: 'and' | 'or';
    not?: boolean;               // NOT (...) → $nor
    children: QueryNode[];
}

interface QueryRule {
    type: 'rule';
    field: string;               // field da coluna
    op: QueryOperator;
    value?: unknown;             // como foi digitado: '30', '01/12/2026' (locale), true
    value2?: unknown;            // limite superior do "between"
    values?: unknown[];          // "in" / "nin": valores armazenados
}

type QueryOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'between' | 'in' | 'nin'
    | 'contains' | 'notContains' | 'startsWith' | 'endsWith' | 'empty' | 'notEmpty';

Como as regras viram condições do SorlaDB:

Regra Condição do find()
age gte 30 { age: { $gte: 30, $ne: '' } } (operadores de intervalo ignoram valores em branco)
age between 20 and 40 { age: { $gte: 20, $lte: 40, $ne: '' } }
name eq 'X' { name: { $eq: 'X' } }
city in ['A', 'B'] { city: { $in: ['A', 'B'] } }
joined empty { joined: { $eq: '' } } (valores em branco são armazenados como '')
name startsWith 'x' { name: { $regex: '^x', $options: 'i' } }
name notContains 'x' { name: { $not: { $regex: 'x', $options: 'i' } } }
status contains 'abe' (select) { status: { $in: ['open'] } } (comparado pelos rótulos)
grupo or { $or: [...] }
grupo com not { $nor: [...] }

fromSorlaQuery lê essas construções de volta ($and, $or, $nor, $not, os operadores de comparação, $in/$nin e os padrões $regex acima) e lança um erro para qualquer outra coisa (por exemplo, $exists).

No servidor (modo append)

Com append: { serverSide: true }, mudar o filtro avançado recarrega a partir do offset 0, e load() o recebe das duas formas:

load: async ({ offset, limit, sort, filters, search, query, sorlaQuery }) => {
    // sorlaQuery é compatível com MongoDB: passe-a para uma coleção do Mongo ou traduza-a para SQL
    const response = await fetch('/api/customers/find', { method: 'POST', body: JSON.stringify({ where: sorlaQuery, offset, limit, sort }) });
    return response.json();
}

Valide e restrinja a uma lista permitida os campos e operadores no servidor: a consulta vem do navegador.

Eventos

Evento Detalhe
queryChange { query, sorlaQuery }
queryBuilderOpen / queryBuilderClose {}

Editar esta página no GitHub