0

The query criteria should support boolean operators and regular expressions. I've read about Booleano, but it doesn't support regular expressions.

If there is nothing out there which matches this requirements, which would be the best technology to start building upon?

The grammar in the example below is just an example, but the feature it offers should exist.

is True if ('client/.+' and 'user_a') but (not 'limited' unless ('.+special' or 'godmode'))

which equals to

is True if 'client/.+' and 'user_a' and (not ('limited' and (not ('.+special' or 'godmode'))))

applied on the following lists

is_true  = ['client/chat', 'user_a', 'limited', 'extraspecial']
is_false = ['client/ping', 'user_a', 'limited']
is_false = ['server/chat']
is_false = ['server/ping', 'ping']
Daniel F
  • 13,684
  • 11
  • 87
  • 116
  • In this case, I think using integers - bitwise operators, even - would be a better option. You could craft a sort of enumeration object with an example from [this link](http://stackoverflow.com/a/1695250/1079354), if you're comfortable with doing that. – Makoto Apr 23 '12 at 14:01
  • I don't see how this could be used for the regex queries. 'client/.+' is already a query in itself. – Daniel F Apr 23 '12 at 14:12
  • PyBoolRe comes pretty close to what I need, but it fails on complex nestings. I wish I could use the mongodb syntax and its engine to perform this type of queries on in-process lists instead of needing to place the lists in the db and querying it via sockets. – Daniel F Apr 23 '12 at 15:05

1 Answers1

1

I managed to solve the problem with the use of the pyparsing module.

import re
import pyparsing

class BoolRegEx(object):

  def Match(self, tags=[], query=""):
    self.tags = tags
    if ' ' not in query:
      return self.Search(query)
    else:
      return pyparsing.operatorPrecedence(
        pyparsing.Word(pyparsing.printables, excludeChars="()"), [
          (pyparsing.Literal("NOT"), 1, pyparsing.opAssoc.RIGHT, self.Not),
          (pyparsing.Literal("OR"),  2, pyparsing.opAssoc.LEFT,  self.Or),
          (pyparsing.Literal("AND"), 2, pyparsing.opAssoc.LEFT,  self.And),
        ]
      ).parseString(query, parseAll=True)[0]

  def Search(self, a):
    try:
      regex = re.compile(a.replace("<<", "#~").replace(">>", "~#").replace(">", ")").replace("<", "(").replace("#~", "<").replace("~#", ">"))
      for tag in self.tags:
        match = regex.match(tag)
        if match and len(match.group(0)) == len(tag):
          return True
      return False
    except:
      raise

  def And(self, t):
    for a in t[0][0::2]:
      if isinstance(a, basestring):
        v = self.Search(a)
      else:
        v = bool(a)
      if not v:
        return False
    return True

  def Or(self, t):
    for a in t[0][0::2]:
      if isinstance(a, basestring):
        v = self.Search(a)
      else:
        v = bool(a)
      if v:
        return True
    return False

  def Not(self, t):
    a = t[0][1]
    if isinstance(a, basestring):
      return not self.Search(a)
    else:
      return not bool(a)

print BoolRegEx().Match(['client/chat', 'user_a', 'limited', 'extraspecial'], "client/.+ AND user_a AND NOT ( limited AND NOT ( .+<r|i>special OR godmode ) )")
# False

print BoolRegEx().Match(['client/chat', 'user_a', 'limited', 'superspecial'], "client/.+ AND user_a AND NOT ( limited AND NOT ( .+<r|i>special OR godmode ) )")
# True

I had to replace the regexp () with <> in order to avoid collisions, but at this moment all of this seems to be the best solution.

Daniel F
  • 13,684
  • 11
  • 87
  • 116