Formula coloane agregate - Regex

Salut, am o mica problema cu implementarea unei formule. O aplicatie Angular, unde am o pagina in care imi ajung 3 coloane ale unei tabele:

1. Name
2. Price
3. Quantity

pe baza acestor coloane, pot crea alte coloane custom. Pana acum am facut ceva simplu, n-am avut nevoie de regex si formula in momentul de fata arata asa [NumeColoana]SEMN[NumeColoana] (semnul poate avea valorile +, -, *, /). Orice alt string care nu respecta formatul acesta rezulta in formula invalida. Verificarea o fac prin spargerea stringului cautand dupa “[” si “]” (n-am avut timp sa caut regex pentru a scoate text dintre paranteze :)))

ex: ColoanaNoua = [Price]*[Quantity]

Intre timp, cerinta s-a schimbat si formula trebuie extinsa la formatul urmator:
ACTIUNE[NumeColoana] SEMN ACTIUNE[NumeColoana], deci pe langa formula simpla aplicata pe coloanele deja existente, acum mai am agregari pe ele (SUM, COUNT, DISTINCT, MAX/MIN).

ex: ColoanaNoua = AVG[Price]*SUM[Quantity]

Prima varianta o rezolvasem usor cu cateva if-uri si substringuri (desi acolo chiar se putea aplica regex mai elegant), dar n-as vrea sa extind prostia in continuare, cu si mai multe de genul asta. Ma gandesc ca pot forta respectarea unui format prin spatii intre termeni si semne si asa sa verific tipul fiecaruia.

Intrebarea: este nevoie de/se poate face cu regex lucrul asta? Daca da, are cineva idee de vre’un serviciu care ajuta la generare lui cat mai usor pentru astfel de cazuri? :))

Nu conteaza limbajul, aceeasi validare va trebui sa o fac si in backend. Merci

Angular din cate stiu eu e un framework pentru frontend.

Din enunt inteleg ca e o problema de backend. Tu incerci pe frontend sa creezi niste date care ar trebui sa fie furnizate de backend.

Daca e necesar sa le faci din frontend, primul lucru la care ma gandesc cand vad functiile SUM, AVG, MIN/MAX e ca ai nevoie de un sir/array de valori. Backend-ul banuiesc ca le furnizeaza (altfel nu se poate obtine rezultatul indiferent de metoda) asa ca le salvezi intr-un array in JS si atunci va fi usor sa formezi noile coloane.

Una peste alta procesarea in frontend mi se pare ciudata. Arhitectura pe care o sustin eu e intotdeauna: procesare backend => afisare in frontend.

Altfel, e o idee foarte proasta sa iei date din DOM. La o adica schimb eu codul HTML via Inspect Element/Developer Tools si apoi datele o sa fie trimise in baza de date.

Nu, nu, sa lasam deoparte backendul momentan, cred ca am creat confuzie cu asta :smiley:

Userul primeste in pagina (browser) o lista de coloane a unei tabele din mysql cu rolul de data source pentru un viitor chart. Eh, pe langa acele coloane standard ale tabelei, userul trebuie sa-si poata crea coloane custom pe baza celor deja existente. Deci daca exista coloanele Price si Quantity, userul vrea sa-si creeze o alta coloana numita TOTAL, ce e calculata ca Price * Quantity. Eu trebuie sa verific cumva acel input este valid, pentru ca pe baza lui se va executa un query. Verificarea asta trebuie facuta in frontend, pentru a-i arata din start ce greseste (exemplu: introduce Quantyty - ii afisez mesaj “Coloana Quantyty nu exista in sursa”).

Ce spuneam de backend, este faptul ca toate acele coloane vor fi salvate in db intr-o tabela separat de surse, deci va trebui sa dublez validarea facuta din frontend, ca nu cumva sa scape ceva aiurea. E o aplicatie interna, dar backendul fiind un api, poti accesa din postman sau orice altceva endpointurile, trecand de validarile din frontend (de’asta raman fixist pe ideea de dubla validare, chiar daca nu e cazul pentru aplicatia curenta, dar cat sa-mi formez eu gandirea asta: backend/frontend => validare dubla).

Am atasat o imagine, Custom Columns au un camp “definition” ce e un input amarat cu validarile respective in spate. Asta ca sa nu-l sparg in mai multe inputuri si selecturi pentru actiuni (SUM, AVG, etc) - nici nu se doreste lucrul asta, se vrea un input in care sa poata fi scrisa formula liber.

http://zaa.ch/jison

1 Like

Poate poti sa folosesti treaba asta
https://mathjs.org/docs/expressions/parsing.html

Si la mine in aplicatie se pot scrie expresii matematice cu adunari, scaderi, inmultiri si impartiri. Validarea o face o biblioteca in java. O caut prin POM si actuallizez raspunsul.

Asta este

Este insa cam veche, poate gasesti o versiune mai noua sau alta biblioteca… Este in java :grin:

2 Likes

Cosmin, pare ceva echivalent cu eval() din js?

articolul dat de isti pare mai aproape de ce am nevoie (cred :joy: ). Intre timp, am reusit sa fac ceva rudimentar :)))

  validate(input: string): void {
    const signs = ['+', '-', '*', '/', '^'];
    const actions = ['SUM', 'AVG', 'COUNT'];

    const parts = input.split(' ');

    parts.forEach(x => {
      if (x.length === 1 && signs.includes(x)) {
        console.log(x + ' is a valid sign');
        return;
      }

      const startIndex = x.indexOf('[');
      const endIndex = x.lastIndexOf(']');

      if (startIndex === -1 || endIndex === -1) {
        console.log(x + ' is an invalid definition');
        return;
      }

      const action = startIndex > 0 ? x.substring(0, startIndex) : null;
      if (action) {
        const actionExists = actions.indexOf(action.toUpperCase());
        if (actionExists === -1) {
          console.log('action ' + action + ' doesnt exists');
          return;
        }
      }

      const colName = x.substring(startIndex + 1, endIndex);
      const isValid = this.isValid(colName);

      if (!isValid) {
        console.log('Column ' + colName + ' is invalid');
        return;
      }

      const exists = this.sourceColumns.find(y =>
        y.label.toLowerCase() === colName.toLowerCase()
      );

      if (!exists) {
        console.log('Column ' + colName + ' doesnt exists in defined list');
        return;
      }

      console.log(x + ' is ' + isValid);
    });
  }

  isValid(colName: string): boolean {
    return colName === colName.match('^[a-zA-Z0-9_.-]*$')?.toString();
  }

asta imi permite sa scriu formula de genul: SUM[ColName] + MAX[ColName]

reguli:

  • spatiu intre termeni si semne;
  • denumirea coloanelor scrise intre [ si ];

mai merge modificat/restrans, renuntat la anumite if-uri. Adevarat ca orice cerinta poate fi scrisa in programare doar cu if si else :joy: :man_facepalming:

// nu bagati de seama logurile si return din for, am vrut doar sa vad daca functioneaza :grin:

Eu vad o rezolvare mai simpla. Parsezi campul Definition si extragi coloanele folosite in expresie.

Extragi si operatiile, adica ce e in afara parantezelor cu un regex asemanator celui de mai sus.

Apoi validezi :

  • Daca coloanele folosite in Definition se regasesc printre coloanele de baza (sau poate si printre coloanele Custom)
  • Daca operatiile folosite in definitie sunt printre operatiile permise.
1 Like

Sunt și soluții de generare de language parser care îți permit sa definești propriul limbaj, exemplu ar fi
https://pegjs.org/online

2 Likes