Roman González
Roman González

Reputation: 125

Make a conditional regex

I am making an API and I want to implement an option to filter so I want to validate the expression with a regexp. this is the expression send in request.

filter[price] = gt.5 and lt.10

The rules: the first characters represent the operator to be used example:

gt = '>'
ge = '>='
lt = '<'
le = '<='
eq = '='
ne = '<>'
like = 'like'
ilike = 'ilike'

then there is a point to separate the value to be compared and optionally there would be other expression separated by the 'and' or 'or' operators.

This is the regex that I have built

$re = '/([a-z]{2,5}\.[\w0-9]+)(\s)*(?(2)(and|or)\2+([a-z]{2,5}\.[\w0-9]+))/i';

I want to make the regex take as valid the following cases and that I take as invalid any other case different from those

case 1: gt.5 (valid)
case 2: gt.5 and lt.10 (valid)
case 3: gt.5 and lt.10 or eq.5 (valid)

when I test the regex it works for both cases but it also matches for the following cases but these should be considered invalid.

case 4: gt.5lt.10 (should be invalid)
case 5: gt.5and (should be invalid)
case 6: gt.5..lt.10 (should be invalid)

Upvotes: 2

Views: 152

Answers (1)

The fourth bird
The fourth bird

Reputation: 163217

What you might do instead of using an if clause is optionally repeat the second part with the and and or to match multiple parts, and use anchors to prevent partial matches.

Note that \w also matches a digit 0-9. In the example data, there are digits after the dot, so you could just use \d instead preventing to match gt.5and

 ^[a-z]{2,5}\.\d+(?:\h+(?:and|or)\h+[a-z]{2,5}\.\d+)*$

Regex demo

You could make the this part [a-z]{2,5} more precise using an alteration listing all the options (?:g[te]|l[te]eq|ne|i?like)


A more specific variant could be using the alternation, and recurse the subpattern using (?1) to make it a bit more succinct.

^([gl][te]|eq|ne|i?like)\.\d+(?:\h+(?:and|or)\h+(?1)\.\d+)*$

Explanation

  • ^ Start of string
  • ([gl][te]|eq|ne|i?like) Match any of the alternatives
  • \.\d+ Match 1 . and 1+ digits
  • (?: Non capture group
    • \h+(?:and|or)\h+ Match either and or or between 1+ horizontal whitespace chars
    • (?1)\.\d+ Recurse the first subpattern and match . and 1+ digits
  • )* Close group and repeat 0+ times to also allow a single gt.5
  • $ End of string

Regex demo

Upvotes: 2

Related Questions